1

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

VE7JRO
2,51519 gold badges27 silver badges29 bronze badges
asked Jan 8, 2017 at 4:53
1
  • Interrupt enabled on OCR0A - don't forget to change the comments if you change the code! You have an interrupt on TIMER0_COMPB_vect! Commented Jan 8, 2017 at 6:17

1 Answer 1

2

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.

answered Jan 8, 2017 at 6:16
3
  • 1
    You might want to set OCR0B to a defined value, instead of relying on the default value of 0. Commented Jan 8, 2017 at 10:07
  • Thank you, that worked! Can you please explain why changing to CTC mode has this effect? Commented Jan 8, 2017 at 10:55
  • See amended answer. Commented Jan 9, 2017 at 0:02

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.