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

  • Interrupt enabled on OCR0A - don't forget to change the comments if you change the code! You have an interrupt on TIMER0_COMPB_vect! – Nick Gammon Jan 8 '17 at 6:17
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.

  • 1
    You might want to set OCR0B to a defined value, instead of relying on the default value of 0. – Gerben Jan 8 '17 at 10:07
  • Thank you, that worked! Can you please explain why changing to CTC mode has this effect? – Matthew Curlis Jan 8 '17 at 10:55
  • See amended answer. – Nick Gammon Jan 9 '17 at 0:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.