Author Topic: Help on Atmega168/328 PWM with Interrupts  (Read 6531 times)

0 Members and 1 Guest are viewing this topic.

Offline amspireTopic starter

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Help on Atmega168/328 PWM with Interrupts
« on: April 18, 2012, 01:51:28 pm »
I am really stuck trying to get a useful interrupt from Timer 2 in Fast or Phase Correct 8 bit PWM. The processor is an Atmega 168 or 328. I am using an Arduino, if that has anything to do with it.

I am using the interrupt so I can change the PWM value from cycle to cycle. The Atmega168 has double buffering so the change will only occur at the start of a PWM cycle.

If I use the interrupt generated from the counter match with the PWM value, then with the double buffering of the PWM value, I do not know if the new value will be set on the next cycle or the one after. That makes it pretty useless.

If I can get the interrupt always occurring at the start of a new PWM cycle, then I know the updated PWM value will be active at the start of the following cycle.

I can get an interrupt out on the match of the counter with the set PWM value, but  For this I need an interrupt when the PWM reaches the TOP value (ie 0xFF) and supposedly the TOV2 flag gets set. Then by setting the TOIE2 flag in the TIFR2 register, then in theory this should happen. In practice there is no interrupt.

The documentation talks of the TOV2 interrupt flag being set in both the Fast and Phase Correct PWM modes at TOP or at 0x00, but I cannot get any interrupt to be generated.

Here is the settings for setting up fast PWMs on Timer2 which I think should work, but I must be missing something.

The _BV(COM2A1) macro seems to do the same thing as ( 1 << COM2A1) - generates a byte with the wanted flag bit set and all others not set.

Code: [Select]
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); // Fast PWM
  TCCR2B = _BV(CS20) ;  //No Prescaler
  TIMSK2 = _BV(TOIE2);   // Set for interrupt when Timer reaches TOP (Fast PWM) or BOTTOM (Phase Correct PWM)
   sei() ; // Enable interrupts

Here is the data sheet: http://www.atmel.com/Images/doc8161.pdf

I have tried setting and clearing the TOV2 bit in TIFR2 top see if that helps, but it doesn't.

Richard.
 
 

Offline markus_b

  • Regular Contributor
  • *
  • Posts: 115
  • Country: ch
Re: Help on Atmega168/328 PWM with Interrupts
« Reply #1 on: April 18, 2012, 03:27:31 pm »
Richard,

such a question has a good chance to get an answer in the avrfreaks.net forums. Plenty of AVR gurus there.

Markus
Markus

A good scientist is a person with original ideas. A good engineer is a person who makes a design that works with as few original ideas as possible.
 

Offline amspireTopic starter

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: Help on Atmega168/328 PWM with Interrupts
« Reply #2 on: April 18, 2012, 03:34:03 pm »
Thanks Markus,  I will take a look at the forum.

Richard
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: Help on Atmega168/328 PWM with Interrupts
« Reply #3 on: April 18, 2012, 05:55:09 pm »
Richard, I don't see any obvious problems with your code.  How is your ISR declared?  I'm not sure how you're supposed to do that on an Arduino (I have one but never mess with it.)  Maybe the interrupt is occurring but going elsewhere. 

The only other thing is the Arduino angle like you mentioned.  Maybe that timer is being used for something else.
 

Offline cybergibbons

  • Frequent Contributor
  • **
  • Posts: 400
Re: Help on Atmega168/328 PWM with Interrupts
« Reply #4 on: April 18, 2012, 07:17:54 pm »
The ISR vector for compare is TIMER2_COMP_vect but TIMER2_OVF_vect for overflow - did you change this?
« Last Edit: April 18, 2012, 09:34:19 pm by cybergibbons »
 

Offline amspireTopic starter

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: Help on Atmega168/328 PWM with Interrupts
« Reply #5 on: April 18, 2012, 08:56:42 pm »
The ISR vector for compare is TIMER2_COMP_vect but TIMER2_OVF_vect - did you change this?

I don't have my code in front of me, but I bet I have an error in the interrupt vector.  I copied it from code I found, and I was spending so much effort trying to comprehend the way the PWM registers worked, I didn't check the ISR vector was right.

It happens when you are working late at night. The problem has to be in the vector as I really did try everything else. Thanks.

Richard.
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: Help on Atmega168/328 PWM with Interrupts
« Reply #6 on: April 18, 2012, 09:05:44 pm »
Richard,

such a question has a good chance to get an answer in the avrfreaks.net forums. Plenty of AVR gurus there.

Markus

By the way, how do they treat the Arduino folks over there on AVRFreaks?  Just curious.
 

Offline cybergibbons

  • Frequent Contributor
  • **
  • Posts: 400
Re: Help on Atmega168/328 PWM with Interrupts
« Reply #7 on: April 18, 2012, 09:35:14 pm »
The ISR vector for compare is TIMER2_COMP_vect but TIMER2_OVF_vect - did you change this?

I don't have my code in front of me, but I bet I have an error in the interrupt vector.  I copied it from code I found, and I was spending so much effort trying to comprehend the way the PWM registers worked, I didn't check the ISR vector was right.

It happens when you are working late at night. The problem has to be in the vector as I really did try everything else. Thanks.

Richard.

It's the same mistake I made, and spent several hours trying to work out...
 

Offline amspireTopic starter

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: Help on Atmega168/328 PWM with Interrupts - SOLVED
« Reply #8 on: April 18, 2012, 10:10:58 pm »
Thanks to TerminalJack505 and cybergibbons, it is all working now. I just thought I would post the results:



The fixed ISR routine looks like this:

Code: [Select]
ISR(TIMER2_OVF_vect) {
  PORTC |= B00000001 ; // Debugging  - Set A0 to HIGH so I can see the interrupt on the scope
                                    // Cannot use digitalWrite(A0, HIGH) because it is extremely slow
  OCR2A = (byte) (pwm_accum / 0x01000000L ) ;  // 16 cycles - Send the top byte of the PWM accumulator to the PWM
  pwm_accum &= 0x00FFFFFFL ; // 7 cycles - Delete the top byte of the 32 bit PWM accumulator, leaving the error
  pwm_accum +=  value ;  // 11 cycles - Add the intended PWM to the error for use in the next cycle
  PORTC &= B11111110 ; // Set A0 to LOW
}

All looks really good now. I am impressed with the optimization of the C code. Do not need to touch assembler at all for the interrupt code. If you are wondering about the 2uS delay before the interrupt routine runs, that is the result of another issue I had to solve. The Arduino LCD routine (LiquidCrystal library) uses lots of delayMicroseconds() function calls for timing. The delayMicroseconds() routine has a wonderful property in that is disables all interrupts for the duration of the timing period.

So I added my own routine to replace delayMicroseconds()

Code: [Select]
void delayMicroseconds2(long delaytime) {
  long j;
  for (j = 0L; j <= delaytime/2L; j++)delayMicroseconds(1);
}

This at least other interrupts have a chance to run with a 2uS worse case delay. I can live with that.

Edit: I took a closer look at the assembler code for the interrupt, and most of that 2usec delay is actually the pushing of registers at the start of the interrupt routine. There is something like 28 cycles before the code starts. The delayMicroseconds2(0 routine above can add a little more delay, but I am still well within the PWM cycle window, so I do not have to look at more optimization.

Interestingly I have been using the dreaded Arduino 1.0 and so far it is great. I have had problems, but none of them are due to 1.0. There are changes in some old library header files to call "Arduino.h", but it is not hard to fix, and more and more libraries are getting modified.

Richard.
« Last Edit: April 19, 2012, 12:09:38 am by amspire »
 

Online Psi

  • Super Contributor
  • ***
  • Posts: 9951
  • Country: nz
Re: Help on Atmega168/328 PWM with Interrupts
« Reply #9 on: April 19, 2012, 12:42:36 am »
If you're experienced enough to get down to that level of register detail you could abandon the arduino library and work on the raw avr micro quite easily.
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline amspireTopic starter

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: Help on Atmega168/328 PWM with Interrupts
« Reply #10 on: April 19, 2012, 02:23:07 am »
If you're experienced enough to get down to that level of register detail you could abandon the arduino library and work on the raw avr micro quite easily.

I could, but I like the fact that Arduino gets you started very quickly, and for me, the hardest part of micro programming is getting started. I do know that once you get really set up with the AVR programming environment, it is probably easier then trying to work within the Arduino framework, but I am not really a programmer. I won't be programming regularly, and for me, it would be easier to return to an Arduino job in 12 months then a sophisticated AVR setup. I am very happy if I never have to see a compiler or linker directive.

Even though code to access switches is not that hard to write, I was very grateful to fine a library called "ClickButton" so I didn't have to write the code. I did have to look at about 6 other libraries first and they all turned out to be junk  - including the standard Arduino Button library, or over-complicated. A real programmer would have written their own code in half the time I spent looking for a library and it probably would be better.

Richard
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf