I am trying to use the timer interrupt on an Arduion Uno.
Here is a imple example code for displaying a progressive digit on a lcd display, which is updated with timer interupt every second:
#include <LCD_I2C.h>
LCD_I2C lcd(0x27);
volatile bool timerFlag = false; // Timer-Flag (timer-Interrupt)
int displayWidth = 16; // display width
int digit = 0; // current digit
void setup() {
lcd.begin();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Initializing...");
noInterrupts();
TCCR1A = 0;
TCCR1B = (1 << CS12) | (1 << CS10); // set Prescaler to 1024
TCNT1 = 0;
OCR1A = 15624; // should be 1 second with a prescaler of 102
TIMSK1 |= (1 << OCIE1A); // Enable timer interrupt
interrupts();
}
ISR(TIMER1_COMPA_vect) {
timerFlag = true;
}
void loop() {
static int position = 0;
char displayString[17]; // +1 null termination
if (timerFlag) {
lcd.clear(); // clear display
// update digit and progress
digit = (digit + 1) % 10;
position = (position + 1) % (displayWidth + 1);
memset(displayString, ' ', displayWidth);
displayString[displayWidth] = '0円'; add null termination
displayString[position] = '0' + digit; // Character for the current digit
// Show updated progress on the display
lcd.setCursor(0, 1);
lcd.print(displayString);
timerFlag = false;
}
}
The values for setting up he inteupt timer 1 for 1 second is arround everywhere. BUt the updates generated by this code is clearly much slower than 1 second, it's more about 4 seconds. So i did a comparison with a delay based version of the same example:
#include <LCD_I2C.h>
LCD_I2C lcd(0x27);
int displayWidth = 16; // display width
void setup() {
lcd.begin();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Initializing...");
}
void loop() {
for (int digit = 0; digit < 10; digit++) {
char displayString[17]; // +1 null termination
int position = digit % (displayWidth + 1);
// Update progression
memset(displayString, ' ', displayWidth);
displayString[displayWidth] = '0円'; // add null termination
displayString[position] = '0' + digit; // Character for the current digit
// Show updated progress on the display
lcd.clear(); // Clear display
lcd.setCursor(0, 1);
lcd.print(displayString);
delay(1000);
}
}
This one works as expected and updates every second.
So what is wrong with my interrupt settings?
2 Answers 2
There is a flag in TCCR1A/B
called WGM12
which sets the wave generation mode (WGM)
to Clear Timer on Compare Match (CTC)
.
This causes the timer to count up to the value in OCR1A and then automatically reset to 0.
This flag was forgotten, so the interrupt simply ignores the setting OCR1A = 15624;
and counts through to 65535, which corresponds to the observed factor of about 4.
Therefore the setting line for TCCR1B must be changed to
TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);
// Set CTC mode and set Prescaler to 1024
Now the interrupt is triggered correctly every second.
-
"This still got me some time to find out what it means and what i needed to do, so:" I'm pleased that it was a useful learning exercise for you and your description looks good. Much more valuable than receiving a ready made and complete solution.6v6gt– 6v6gt2023年08月18日 11:01:56 +00:00Commented Aug 18, 2023 at 11:01
It is ignoring this OCR1A = 15624;
and simply rolling over at 65535 instead., hence the error factor of 4. You have to set the wave generation mode as CTC in registers TCCR1A/B.
-
Thank you for the answer. Unfortunately, I won't be able to try it out until after the weekend. I will see if it works (but I am pretty confident)maddes8cht– maddes8cht2023年08月11日 19:36:07 +00:00Commented Aug 11, 2023 at 19:36
-
This still got me some time to find out what it means and what i needed to do, so: The ctc mode means "Clear Timer on Compare Match" and is controled by the WGM12 flag in the TCCR1A/B register. As I'm using the TCCR1b, the line gets: ` TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);`maddes8cht– maddes8cht2023年08月18日 10:10:31 +00:00Commented Aug 18, 2023 at 10:10