I want to create a PWM on any PIN and therefore using ISRs to achieve this. The setup I chose, working with my ATMEGA328p and Timer0 is:

static inline void initTimer0(void) {
    // Timer Counter Control Register B
    // Must b4 /64 or more for ISR timing
    TCCR0B |= (1 << CS00) | (1 << CS01);

    // Timer Counter Interrupt Mask Register
    // both output compare interrupts
    TIMSK0 |= ((1 << OCIE0A) | (1 << OCIE1B));

    // Timer Counter Overflow Interrupt Enable
    TIMSK0 |= (1 << TOIE0);


/* Timer/Counter0 Overflow */
// called whenever TCNT0 overflows
ISR(TIMER0_OVF_vect) {
    PORTB |= (1 << 0);
    OCR0A = brightnessA;

// ISR fired when a match occurs
    PORTB ^= (1 << 0);

int main(void)
    int d = 10;

    while (1) 
        for (int i = 0; i < 255; i++) {
            brightnessA = i;


I would have expected that the LED on PORTB would dim up but nothing happens. I think I have a gap in understanding here. I would have assumed that TIMER0_COMPA_vect ISR would fire everytime I reach the new OCR0A compare value and turn the LED of, therefore control the duty cycle.

Can you help me?



2 Answers 2


You need to configure the waveform generation mode as well in order to get this working. Page 128 of ATmega328 Datasheet describes exactly how WGM bits need to be set.

19.5. Output Compare Unit

The 8-bit comparator continuously compares TCNT0 with the Output Compare Registers (OCR0A and OCR0B). Whenever TCNT0 equals OCR0A or OCR0B, the comparator signals a match. A match will set the Output Compare Flag (OCF0A or OCF0B) at the next timer clock cycle. If the corresponding interrupt is enabled, the Output Compare Flag generates an Output Compare interrupt. The Output Compare Flag is automatically cleared when the interrupt is executed. Alternatively, the flag can be cleared by software by writing a '1' to its I/O bit location. The Waveform Generator uses the match signal to generate an output according to operating mode set by the WGM02, WGM01, and WGM00 bits and Compare Output mode (COM0x[1:0]) bits. The max and bottom signals are used by the Waveform Generator for handling the special cases of the extreme values in some modes of operation.

This means you need to configure the TCCR0A register as well accordingly.


Did you remember the following line?

DDRB |= 1 << 0;

Also, I don't see where you call the function initTimer0() in int main().

You also probably want to declare brightnessA as volatile.

  • \$\begingroup\$ I don't you need to declare brightnessA as volatile unless it is written to in an ISR, right? \$\endgroup\$
    – vicatcu
    May 3, 2017 at 4:04
  • 1
    \$\begingroup\$ Oh sorry.. yep their is a setup function call as well where I initiate. void setup() { // Set Bank B to Output DDRB = 0xff; initTimer0(); initUSART(); } \$\endgroup\$
    – sesc360
    May 3, 2017 at 6:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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