Skip to main content
Added more explanations.
Source Link
Nick Gammon
  • 38.9k
  • 13
  • 70
  • 126

(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.


(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.

Source Link
Nick Gammon
  • 38.9k
  • 13
  • 70
  • 126

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.