1

To get this out of the way first: I have already looked at "Timer2 does not work as it should" and "Timer2 “Clear OC2B on Compare Match” not working as expected in CTC mode" but have not found the answers there very useful.

Now on to my problem:

I'm trying to use the Timer2 in CTC mode to have an interrupt every 500us. For this, I have the following code:

#include "TimerTwo.h"

TimerTwo Timer2;

ISR(TIMER2_COMPA_vect) { //ISR on Compare Match A
  Timer2.isrCallback();
}

void TimerTwo::initialize() {
  cli();
  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0

  // set compare match register for 2khz increments
  OCR2A = 124;// = (16*10^6) / (2000*64) - 1 (must be <256)

  // turn on CTC mode
  TCCR2A |= _BV(WGM21);

  // Set CS22 bit for 64 prescaler
  TCCR2B |=  _BV(CS22);

  // enable timer compare interrupt
  TIMSK2 |= _BV(OCIE2A);
  sei();
}

void TimerTwo::attachInterrupt(void (*isr)()) {
  TIMSK2 |= _BV(OCIE2A);
  isrCallback = isr;
}

void TimerTwo::detachInterrupt() {
  TIMSK2 &= ~_BV(OCIE2A);
  isrCallback = nullptr;
}

and

#ifndef TIMERTWO_H
#define TIMERTWO_H

#include <Arduino.h>

class TimerTwo
{
  public: //methods
    void initialize();
    void attachInterrupt(void (*isr)());
    void detachInterrupt();
    void (*isrCallback)();
};

extern TimerTwo Timer2;

#endif

It was inspired by the TimerOne library.

However, when I measure the period at the output pin (which I have attached inside some other code) I only get a period of 1ms. Even if I set ORC2A to 63, this fact doesn't change.

I have the constructor of this called inside my setup() so the registers should not be overwritten by the wiring.c.

I have also looked at the datasheet of the ATmega328p and checked that all registers are set correctly.

Thanks in advance for your help. If you need any further information, let me know.

6
  • What's in the isrCallback after the initialize? You should enable timer compare A interrupt in the attachInterrupt method only. And in the attachInterrupt it should be set first, than enabled.
    – KIIV
    Commented Jun 4, 2018 at 9:19
  • A timer can only run as fast as the ISR that it triggers. If your ISR takes 1ms to run you will get no faster than 1ms - no matter what you set the period to (below 1ms).
    – Majenko
    Commented Jun 4, 2018 at 9:44
  • It does not take 1ms, as I've already run it at faster speeds already just not with that timer.
    – Lithimlin
    Commented Jun 4, 2018 at 12:38
  • @KIIV even after only activating it in the attachIntterupt it still does not work.
    – Lithimlin
    Commented Jun 4, 2018 at 12:43
  • Try doubling OCR2A and see in you get a 2ms interval, or not. What Arduino board are you using? E.g. the Pro Mini 3.3V runs at 8Mhz, so half the speed of the 16Mhz your code is assuming.
    – Gerben
    Commented Jun 4, 2018 at 15:13

1 Answer 1

1

So you're right that CTC is acting weird. I've noticed this few years ago too on different board, but I've always suspected asychronous mode (I've added 32k RTC Xtal on Arduino Mega).

However Fast PWM mode with OCR2A as a TOP value seems to be working:

class TimerTwo
{
  public: //methods
    void initialize();
    void attachInterrupt(void (*isr)());
    void detachInterrupt();
    void (*isrCallback)() = nullptr;
};

TimerTwo Timer2;

ISR(TIMER2_COMPA_vect) { //ISR on Compare Match A
  Timer2.isrCallback();
}

void TimerTwo::initialize() {
  cli();

  OCR2A = 124;                        // = (16*10^6) / (2000*64) - 1 (must be <256)
  TCCR2A = _BV(WGM21) | _BV(WGM20);   // turn on fast PWM mode with top in OCR2A
  TCCR2B = _BV(WGM22) | _BV(CS22);    // Set CS22 bit for 64 prescaler
  TCNT2  = 0;                         // initialize counter value to 0

  sei();
}

void TimerTwo::attachInterrupt(void (*isr)()) {
  isrCallback = isr;
  TIMSK2 |= _BV(OCIE2A);
}

void TimerTwo::detachInterrupt() {
  TIMSK2 &= ~_BV(OCIE2A);
  isrCallback = nullptr;
}

volatile uint32_t counter = 0;
void isrHandler() {
  ++counter;
}

void setup() {
  Serial.begin(115200);
  Timer2.initialize();
  Timer2.attachInterrupt(&isrHandler);
}

void loop() {
    static uint32_t counter_old = 0;
    Serial.println(counter - counter_old);
    counter_old = counter;
    delay(500);
}

EDIT: Ok, so after some digging on this issue I've found that setting CTC mode might cause OCR2A corruption (I guess?) . It seems to be working if you reorder the settings to:

  TCCR2B = 0;          // stop the timer and reset WGM22
  TCCR2A = _BV(WGM21); // turn on CTC mode with top in OCR2A
  OCR2A  = 124;        // = (16*10^6) / (2000*64) - 1 (must be <256)
  TCNT2  = 0;          //initialize counter value to 0
  TCCR2B = _BV(CS22);  // Set CS22 bit for 64 prescaler (for starting the timer)
1
  • Thanks, that really helped. Another thing I found out, however, is that the code in my ISR takes longer than 1ms apparently. So I'll have to find a solution for that now quickly.
    – Lithimlin
    Commented Jun 5, 2018 at 14:42

Your Answer

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

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