I am currently playing with Arduino timers (on UNO currently) and I am building a library that has a function that must be called every millisecond.
There is plenty of code samples on Internet so I went with something that seemed perfect for my needs. I have then updated it to make it a reusable C++ class.
Here is my code (simplified to just isolate the issue):
#define BIT(x) (0x01 << (x))
static volatile uint32_t milliseconds = 0;
class Timer
{
public:
Timer()
{
// Disable interrupts
uint8_t savedStatus = SREG;
cli();
// Use Timer2 with 1 ms interrupts
// OC2A & OC2B disconnected, mode 2 (CTC, Clear Timer on Compare match)
TCCR2A = BIT(WGM21);
// Don't force output compare (FOCA & FOCB), mode 2, Clock Select clk/128 (CS = 5)
TCCR2B = BIT(CS22) | BIT(CS20);
// Set timer counter compare match (when value reached, 1ms has elapsed)
OCR2A = 125 - 1;
// Reset Timer2 counter
TCNT2 = 0;
// Set Timer2 interrupt mode (Set interrupt on OCR2A compare match)
TIMSK2 = BIT(OCIE2A);
// Restore interrupt enabling state
SREG = savedStatus;
}
};
static void callEveryMillisecond(uint32_t ms)
{
// Do something here
}
// Attach interrupt routine to the Timer Compare Interrupt
ISR(TIMER2_COMPA_vect)
{
milliseconds++;
callEveryMillisecond(milliseconds);
}
After checking ATmega328 datasheet (section 17) several times, I came to the conclusion that what I do in Timer
class constructor above is correct, and for Arduino UNO, the values used must lead to one call of the ISR every ms:
- CPU frequency = 16MHz
- Prescaler = 128
- -> timer frequency = 16MHz/128 = 125KHz
- -> max counter value = 125
I decided to use the CTC (Clear Timer on Compare match) mode, which means that when the timer TCNT2
(which is incremented at 125KHz frequency), reaches OCR2A
value, then it will trigger the ISR, and then will be reset on next tick; the additional tick to reset the counter explains why we use 125 - 1
for OCR2A
and not just 125
.
Now comes the weird stuff. In order to check that my code worked, I decided to blink the LED on pin 13 every 10 seconds, hence I have added the following code to my program:
static void callEveryMillisecond(uint32_t ms)
{
if (ms % 10000 == 0)
digitalWrite(13, digitalRead(13) ^ 1);
}
static Timer timer;
void setup()
{
pinMode(13, OUTPUT);
}
void loop()
{
}
Then I measured the time during which pin 13 LED is lit on (or off, both measures are the same). Measures are not very accurate (I just used my wristwatch) but good enough for my check.
I was surprised to see the measured time to be about 20 seconds instead of 10s.
Initially I thought about a mismatch in CPU frequency, but after performing a lot of small changes, here is what I found which worked perfectly:
//static Timer timer;
void setup()
{
static Timer timer;
pinMode(13, OUTPUT);
}
Just moving the Timer
instance into setup()
did the trick: my measures rightly indicated 10 seconds between 2 states of the pin 13 LED.
Now my questions are:
Why the second code sample works perfectly, whereas the first does not?
Is there, in standard Arduino included code, some code that would overwrite some of the settings I perform for Timer2 in
Timer::Timer()
?Is there a way to make the first code sample work? Note that I don't like the second sample because it does not seem natural coding, also, I will need access to
Timer
timer
instance in several locations of my future program, hence it can't be possible if it is declared in the scope ofsetup()
.
Is there, in standard Arduino included code, some code that would overwrite some of the settings I perform for Timer2 in Timer::Timer()?
You can easily check it, read the timer registers in the start of setup and send them through the serial port in the console to see if they match the values they should have.Serial
to the picture but that could help. However that will not tell what changed these register. I'll check that tonight and report back on the results.