I know this is a problem that has come up many times but I can't seem to get this code fragment to update the count variable on an ATMEGA328P. I am using an Arduino Uno as a development board, with the LED on pin 13 set to flash for debugging. I have confirmed the timer and LED part work by toggling port B pin 5 in the ISR routine.
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint8_t count;
void init_100us_tick(void) {
TCCR0A = _BV(WGM01) | _BV(WGM00); // WGM mode 7 with OCRA as top
TCCR0B = _BV(WGM02) | _BV(CS01); // CLK / 8 prescaler
TIMSK0 = _BV(OCIE0B) | _BV(TOIE0); // Interrupt enabled on OCR0A
OCR0A = 200; // 16 MHz / 8 / 200 = 100 us
sei(); // Enable interrupts
}
ISR(TIMER0_COMPB_vect) {
count++;
}
int main(void) {
DDRB |= _BV(DDB5); // PB5 output
init_100us_tick(); // Initialise 100 us tick
count = 0; // In case of micro reset
for (;;) {
if (count >= 100) { // Every 10 ms
count = 0;
PORTB ^= _BV(DDB5); // Toggle PB5
}
}
return 0;
}
This is being compiled and burned to an Arduino Uno with the following:
avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o test.o test.c
avr-gcc -mmcu=atmega328p test.o -o test
avr-objcopy -O ihex -R .eeprom test test.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyUSB0 -b 115200 -U flash:w:test.hex
I have tried disabling the optimisation with no effect. AVR-GCC is version 4.9.2
1 Answer 1
You want CTC mode for that to work. As in:
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint8_t count;
void init_100us_tick(void) {
TCCR0A = _BV(WGM01); // CTC mode 2 with OCRA as top
TCCR0B = _BV(CS01); // CLK / 8 prescaler
TIMSK0 = _BV(OCIE0B) | _BV(TOIE0); // Interrupt enabled on OCR0B
OCR0A = 200; // 16 MHz / 8 / 200 = 100 us
sei(); // Enable interrupts
}
ISR(TIMER0_COMPB_vect) {
count++;
}
int main(void) {
DDRB |= _BV(DDB5); // PB5 output
init_100us_tick(); // Initialise 100 us tick
count = 0; // In case of micro reset
for (;;) {
if (count >= 100) { // Every 10 ms
count = 0;
PORTB ^= _BV(PORTB5); // Toggle PB5
}
}
return 0;
}
That toggles every 10 ms.
(Edited to add)
The PWM mode you chose will work providing you enable the correct interrupt. This also outputs 10 ms pulses:
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint8_t count;
void init_100us_tick(void) {
TCCR0A = _BV(WGM01) | _BV(WGM00); // Fast PWM mode 7 with OCRA as top
TCCR0B = _BV(WGM02) | _BV(CS01); // CLK / 8 prescaler
TIMSK0 = _BV(OCIE0A); // Interrupt enabled on OCR0A ***
OCR0A = 200; // 16 MHz / 8 / 200 = 100 us
sei(); // Enable interrupts
}
ISR(TIMER0_COMPA_vect) { // Compare A vector ***
count++;
}
int main(void) {
DDRB |= _BV(DDB5); // PB5 output
init_100us_tick(); // Initialise 100 us tick
count = 0; // In case of micro reset
for (;;) {
if (count >= 100) { // Every 10 ms
count = 0;
PORTB ^= _BV(PORTB5); // Toggle PB5 ***
}
}
return 0;
}
Changed lines indicated by ***
Note that you should not have enabled _BV(TOIE0)
because you have no interrupt handler for Timer 0 overflow. Without an interrupt handler the compiler probably generated code to jump to the reset vector.
The interrupt occurs when the timer matches the Compare A amount (not Compare B) so you need an interrupt on the A side.
-
1You might want to set
OCR0B
to a defined value, instead of relying on the default value of0
.Gerben– Gerben01/08/2017 10:07:12Commented Jan 8, 2017 at 10:07 -
Thank you, that worked! Can you please explain why changing to CTC mode has this effect?user29824– user2982401/08/2017 10:55:25Commented Jan 8, 2017 at 10:55
-
Interrupt enabled on OCR0A
- don't forget to change the comments if you change the code! You have an interrupt onTIMER0_COMPB_vect
!