Help on Atmega168/328 PWM with Interrupts - Page 1
Forum section: electronics / general

author: amspire
author_rank: Super Contributor
timestamp: April 18, 2012, 01:51:28 pm
content: 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:TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); // Fast PWMTCCR2B = _BV(CS20) ; //No PrescalerTIMSK2 = _BV(TOIE2); // Set for interrupt when Timer reaches TOP (Fast PWM) or BOTTOM (Phase Correct PWM)sei() ; // Enable interruptsHere is the data sheet:http://www.atmel.com/Images/doc8161.pdfI have tried setting and clearing the TOV2 bit in TIFR2 top see if that helps, but it doesn't.Richard.

author: markus_b
author_rank: Regular Contributor
timestamp: April 18, 2012, 03:27:31 pm
content: Richard,such a question has a good chance to get an answer in the avrfreaks.net forums. Plenty of AVR gurus there.Markus

author: amspire
author_rank: Super Contributor
timestamp: April 18, 2012, 03:34:03 pm
content: Thanks Markus, I will take a look at the forum.Richard

author: TerminalJack505
author_rank: Super Contributor
timestamp: April 18, 2012, 05:55:09 pm
content: 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.

author: cybergibbons
author_rank: Frequent Contributor
timestamp: April 18, 2012, 07:17:54 pm
content: The ISR vector for compare is TIMER2_COMP_vect but TIMER2_OVF_vect for overflow - did you change this?

author: amspire
author_rank: Super Contributor
timestamp: April 18, 2012, 08:56:42 pm
content: Quote from: cybergibbons on April 18, 2012, 07:17:54 pmI 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.

author: TerminalJack505
author_rank: Super Contributor
timestamp: April 18, 2012, 09:05:44 pm
content: Quote from: markus_b on April 18, 2012, 03:27:31 pmBy the way, how do they treat the Arduino folks over there on AVRFreaks? Just curious.

author: cybergibbons
author_rank: Frequent Contributor
timestamp: April 18, 2012, 09:35:14 pm
content: Quote from: amspire on April 18, 2012, 08:56:42 pmIt's the same mistake I made, and spent several hours trying to work out...

author: amspire
author_rank: Super Contributor
timestamp: April 18, 2012, 10:10:58 pm
content: 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: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 slowOCR2A = (byte) (pwm_accum / 0x01000000L ) ; // 16 cycles - Send the top byte of the PWM accumulator to the PWMpwm_accum &= 0x00FFFFFFL ; // 7 cycles - Delete the top byte of the 32 bit PWM accumulator, leaving the errorpwm_accum += value ; // 11 cycles - Add the intended PWM to the error for use in the next cyclePORTC &= 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: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.

author: Psi
author_rank: Super Contributor
timestamp: April 19, 2012, 12:42:36 am
content: 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.

author: amspire
author_rank: Super Contributor
timestamp: April 19, 2012, 02:23:07 am
content: Quote from: Psi on April 19, 2012, 12:42:36 amI 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