Is it? You want to control a solenoid, which have switching time in ms-range.
Despite, I think you can do it without any ISR routine (at least for the PWM-part). With the manual of ATmega328 please have a look on the following:
-
When, e,g, configured in Mode 8 (PWM, Phase and Frequency Correct), TOP can be defined through value stored in ICR1
-
You can adjust PWM frequency by both pre-scaler and ICR1
-
Duty Cycle is defined by OCR1A (and OCR1B) while OCR1A = 0...ICR1, i.e. duty cycle OCR1A/ICR1 * 100%
-
With OCR1A = 0 duty cycle is 0% (!) - which equals "off", with OCR1A = ICR1 duty cycle is 100% - which is "on". In both cases no(!) narrow spikes are created (this applies not for all PWM modes).
-
OCR1A are doubled buffered registers. Thus they can be written in your loop()-function - which makes your ISR obsolete to do all all the switching (on, off, some PWM)
Just one remark: this is my theory from reading the data sheet. I have actually done this in my project ... and, well, it works.
So code may look like this (adapted from mine):
// Duty-Cycle is set by adjusting OCR1A and OCR1B, ranging from 0..ICR1 (ICR1 provides resolution)
// OCR1x = 0 results in permantetly off
// OCR1x = ICR1 results in permanently on
//
// Timer 1 is also used to trigger ISR on counter overflow.
TCCR1A = 0; // set timer controller register to zero to ...
TCCR1B = 0; // ... know from what we start (TCCR1A and TCCR1B)
TCNT1 = 0; // initialize counter value to 0
// Control Register
//
// Digital pins shall be cleared on compare match with OCR1A and OCR1B
TCCR1A |= (1 << COM1A1); // OCR1A: Connect to pin PB1
TCCR1A |= (1 << COM1B1); // OCR1B: PB2
// Use Mode 8, with ICR1 as TOP, Phase and Frequency Correct PWM, OCR1x updated at BOTTOM
TCCR1B |= (1 << WGM13);
TCCR1B |= (0 << WGM12);
TCCR1A |= (0 << WGM11);
TCCR1A |= (0 << WGM10);
// Prescaler = 8, PWM-Freq = 16*10^6/(2*8*TOP)) = 1960Hz, smallest pulse 2*Prescaler/f_clk =1us
TCCR1B |= (0 << CS12);
TCCR1B |= (1 << CS11);
TCCR1B |= (0 << CS10);
// Set TOP Register (with TOP 512 1960Hz is obtained)
ICR1 = 512;
// OCR1A and OCR1B will define duty-cycle, ranging from 1 to ICR1
OCR1A = 0; // Range 0..ICR1, 0: 0% (totally off)
OCR1B = 0; // , ICR1: 100% (totally on )
// Output pins are OC1A (=PB1, HW-Pin 15) and OC1B (=PB2, HW-Pin 16), stupid Arduino pinNo apply
pinMode(pinPB1, OUTPUT); // set Data Direction Register by pinMode()
pinMode(pinPB2, OUTPUT); // dito
// enable timer compare interrupt via TOV1 (1960Hz) (--> TIMER1_OVF)
TIMSK1 |= (1 << TOIE1);
You would need to replace pinPB1, and pinPB2 with Arduino-Naming-Konvention. Also not sure if I have all things corrected for your application (mine is using some extra feature) ... so check against data sheet.
As said, OCR1A and OCR1B do all stuff an can be set in loop(): on, off, some (visible) PWM. Just highlighting "visible" because both on and off can be seen as PWM signal as well - as said: 0% or 100%. Simply write, e.g.,
OCR1A = 0;
to turn your solenoid off. And that's it!
(or OCR1A = 384 to obtain a duty cycle of 75% (with ICR1 = 512))