AVR timer 1 interrupt

hello all! i need to control multiple servos using the arduino for which i need to produce 1-2ms pulse at a frequency of 50Hz.
since i have to control 5 servos, i thought i could use the interrupts to achieve so. but i am running into some problem. although the interrut is executed every 20ms,yet the output is not accurate

volatile unsigned long man;
volatile unsigned long chan;

volatile int tim = 20000; //the actual pulse
void setup() {
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for  50Hz
  OCR1A = 39999;
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS11 for  prescaler 8
  TCCR1B |= (1 << CS11);  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei();//allow interrupts


void loop() {
  // put your main code here, to run repeatedly:

ISR(TIMER1_COMPA_vect){//timer1 interrupt 50Hz 

PORTD |= (1<<PIND7);

 man = micros();
 chan = man + tim;
 while(micros()<= chan)
PORTD &= ~(1<<PIND7);

from what i know,this should produce a pwm signal of 50Hz and since the dutycycle(tim =20000) which is half of the top value, i should get a reading of 2.5 v on the multimeter, but i see almost 5 volts on the multimeter.

i am a noob in interrupts

You can't read PWM with your multimeter. The PWM signal will NEVER be 2.5V at 50% duty cycle. It will be 5V for a bit and then 0 for a bit and then 5V for a bit and then 0 for a bit.

What you need is an oscilloscope.

while(micros()<= chan)

AN interrupt is no place to be waiting around for a while.

shubh9835: hello all! i need to control multiple servos using the arduino for which i need to produce 1-2ms pulse at a frequency of 50Hz.

Isn't that what the Servo library does ?


The sketch, as posted, doesn't compile - there seems to be a stray character early in the code. That makes me wonder if this is the code that you actually intended to post, or maybe some earlier version.

Here's why it doesn't do what you expect:

Variable tim, which sets the pulse length, is set to 20000, in microseconds. That comes to 20 milliseconds, the interval at which the ISR runs. If everything else worked, it would turn on the output, wait 20 milliseconds, turn the output off, exit the ISR, and immediately find another TIMER1_COMPA interrupt waiting for service. That would account for your seeing about 5V steadily on the voltmeter. Did you intend to initialize variable tim to 2000?

But, some other things don't seem to be working. The ISR waits for the entire pulse length before it exits. Meanwhile, no other interrupts occur, and, in particular, the TIMER0_OVF interrupt doesn't occur. That means that the Timer0 interrupt counter never advances. The value of micros() depends on that counter. So, when Timer0's counter stops advancing, the value of micros() stops advancing, too. The ISR will never see micros() get big enough for the ISR to exit. That's why, as Delta_G says,

Delta_G: An interrupt is no place to be waiting around for a while.

So, the ISR turns the output on, but never sees that it's time to turn it off, and it runs forever. That, too, would account for a steady 5V.

To see it demonstrated, add these lines to setup():

  pinMode(13, OUTPUT);

and this line as the last line of the ISR:


The ISR will turn the pin 13 LED on when it exits. If the ISR ever terminated, you'd see the LED light up. The fact that it doesn't suggests that the ISR never ends. To see it work, initialize variable tim to 1, and see the LED illuminate.

Alternatives to waiting in the ISR are: - Turn on the output and grab the value of micros() in the ISR, and let loop() watch the clock and turn it off after the interval. - Establish a TIMER1_COMPB interrupt to turn the output off, and then disable itself. If you do that, you'll need to be sure and clear the pending interrupts in the TIMER1_COMPA ISR. - Use OCR1B PWM on Timer1, with OCR1A at 39999, and OCR1B at 2000 to 4000 for a 1 to 2 millisecond pulse. That may not work for you, since you want to control multiple devices, and there's only one B output for Timer1 PWM.