Missing first PWM pulse? Here is a solution.

Go To Last Post
3 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I've struggled a bit with a project where I needed to get a controlled number of PWM pulses (a burst), activated by an external interrupt edge.

My main trouble was that I was always missing the first PWM pulse. This post helped me a bit on the way: https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=55581&start=0&postdays=0&postorder=asc&highlight=pwm+first+pulse
And then I figured out the rest by myself.

Now, for others in a similar situation, I'll write what seems to work for me. The code is for an ATmega1281.

There is an external interrupt which activates the burst, and a timer interrupt keeping track of the number of pulses. PWM output on OC1A, external interrupt on INT5.

ISR(INT5_vect)
{
  if (bit_is_clear(TCCR1A,COM1A1)) // Just to make sure that a burst isn't already running
  {
    TCCR1B=0x00;     // Switch off timer AND pwm mode completely. Part of the key to success.
    TCCR1A=0x00;
    burst_count=burst_pulses;
    TCNT1=new_TCNT1; // new_TCNT1 = ICR1 - 1
    TIMSK1|=0x02;    // Enable OC1A match (COMPA) interrupt 
    TIFR1=0x02;      // Clear OC1A match interrupt flag - just in case
    TCCR1A=0x82;     // Set pwm mode bits and output mode bits
    TCCR1B=0x18 | prescaler_bits; // Set pwm mode bits and prescaler
  }
}


ISR(TIMER1_COMPA_vect)
{
  burst_count--;  // Decrement counter variable
  if (burst_count)
  {
    TCCR1A&=0x3F; // Disable PWM output
    TIMSK1=0;     // Disable TIMER1_COMPA interrupt
  }
}


int main(void)
{
  ICR1 = (whatever is necessary to get the desired frequency);
  prescaler_bits = (whatever is necessary to get the desired frequency);
  OCR1A = (whatever is necessary to get the desired pulse width;
  new_TCNT1=ICR1-1; // This is another part of the key to success. PWM cycle starts by clearing TCNT1, and setting PWM output high.
  burst_pulses = (the number of burst pulses you want);
  EICRB = 0x0C; // INT5 interrupts on rising edge
  EIMSK = 0x20; // INT5 interrupt on
}

Of course, you can't compile and use this as is, but it should show the principle of things.

Now, I'm sure that the gurus can find some easier ways to do these things, but this works for me.

Br, Erik

You're absolutely right. This member is stupid. Please help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try this.

ISR(TIMER1_COMPA_vect) 
 { 
  // burst_count--;  // Decrement counter variable 
   if (burst_count--) 
   { 
     TCCR1A&=0x3F; // Disable PWM output 
     TIMSK1=0;     // Disable TIMER1_COMPA interrupt 
   } 
 }

Using post decrement will give you one more count.

It all starts with a mental vision.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I realise that there is an error in the example. The TIMER1_COMPA interrupt should be like this:

ISR(TIMER1_COMPA_vect)
{
  burst_count--;  // Decrement counter variable
  if (!burst_count)
  {
    TCCR1A&=0x3F; // Disable PWM output
    TIMSK1=0;     // Disable TIMER1_COMPA interrupt
  }
}

Yes, it's true that post decrement will give one more count, but in my case
1. it hardly matters whether the maximum burst count is 65535 or 65536.
2. customers have to transmit which number of pulses they want, and I don't want to spend time explaining over the phone (or in emails) why the customer gets two pulses when they ask for one.

But, surely, if it is important to use the entire range, post decrementing is a possibility.

Br, Erik

You're absolutely right. This member is stupid. Please help.