Arduino 101: Timers and Interrupts

Damn, you’ve got me. Yes, I

Damn, you’ve got me. Yes, I confess, I have built a time machine. Greetings from the future.

BTW, The lottery numbers from 7/20/2013 are: 4 8 15 16 23 42

Thanks

This is awesome!  Nice job on explaining these concepts.

What am I missing?

Essentially, I’ve started experimenting with timer interrupts with the goal of using the Arduino Uno Plus to control GE Color Effects lights, which are apparently fussy about timing. So, to start, I decided to try making a 20us time delay using an interrupt and basically copying your example and adding the clock pre-scaling table in the comments so I don’t have to keep looking it up.

Watching an LED blink for 20us is not informative :-), so I’ve swapped out the LED toggle line for setting a semaphore and reported the state of the semaphor back via the Serial monitor. I’ve introduced the variable “semaphore” because I ultimately want my main program to be able to tell if the 20us is up before moving on the the next instruction. The library I was originally working from uses delay() to do this and the author admits that it needs to be manually tuned for each arduino. I’ve tried this, and short of dragging out my old oscilloscope to verify the timing, it’s very hit-and-miss, so I thought I would turn to the timer driven interrupt method.:

===============================================

/Table 14-9.
Clock Select Bit Description from ATmega328P Documentation p.137
CS12 CS11 CS10 Description
0 0 0 No clock source (Timer/Counter stopped)
0 0 1 clk I/O /1 (No prescaling)
0 1 0 clk I/O /8 (From prescaler)
0 1 1 clk I/O /64 (From prescaler)
1 0 0 clk I/O /256 (From prescaler)
1 0 1 clk I/O /1024 (From prescaler)
1 1 0 External clock source on T0 pin. Clock on falling edge.
1 1 1 External clock source on T0 pin. Clock on rising edge.

Delays should be 10, 20 and 30us
10us = .000 01s which is obtained by dividing the 16MHz clock divided by 160
20us is therefore the 16Mhz clock divided by 80
30us is 16Mhz / 53

Therefore a good clock pre-scale setting would be 8 (CS11=1)

Timer will be running at 16Mhz/8 = 2Mhz or .5us per tick
Count to 20 for 10us
Count to 40 for 20us
Count to 60 for 30us
/

int semaphore=0;

void setup()
{
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
Serial.println(“Semaphore cleared”);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;

OCR1A = 20; // compare match register for 10us delay
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS12); // 8 prescaler
TCCR1B |= (1 << CS10);
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts
}

ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine
{
 semaphore=1;
}

void loop()
{
while(semaphore==0){Serial.println(“semaphore = 0”);}
Serial.println(“Semaphore set”);
while (true){};
 }

==================================================

This produces the output …

Semaphore cleared
semaphore = 0
semaphore = 0
semaphore = 0
semaphore = 0
semaphore = 0
Semaphore set

… which is what I would expect.

 Obviously, since I’m really using the interrupt in place of a delay(), I don’t want to be constantly printing “semaphore = 0”, so I simply removed it. From here on I’ll focus only on the void loop() portion of the program. I’ve changed it to …

=============================================

void loop()
{
while(semaphore==0){}
Serial.println(“Semaphore set”);
while (true){};
 }

=============================================

… but now all I get is:

 Semaphore cleared

The semaphore is not being set, or at least, the main program isn’t seeing it.

I had thought that while(semaphore==0){} may not be valid with an empty proceedure, so I tested it …

========================================

void setup(){Serial.begin(115200);}
void loop(){
Serial.println(“before loop”);
while(true){}
Serial.println(“after loop”);
}

======================================

 which only prints “before loop” so that wasn’t it.

 So, back to original loop(). This also works …

======================================

void loop()
{
while(semaphore==0){delay(0);}
Serial.println(“Semaphore set”);
while (true){};
Serial.println(“not stuck in a loop”);
 }

=====================================

but this does not …

 …

while(semaphore==0){true==true;}

It occurs to me that both “delay(0);” and “Serial.println(”"); are using timers whereas {} and {true=true;} do not, so it’s possible that these functions is doing something with the timers that my code is not. This would imply that there’s something wrong with my setup() routine, but then again, the original example (which toggled the LED) worked, so clearly I’m not understanding something.

In fairness, I realize that putting delay(0); is not a hardship, but this is more about clarifying my understanding of why it doesn’t work without it than about saving instruction cycles.

 

LEGOManiac

I guess, you must declare

I guess, you will need to declare your semaphore variable as ‘volatile’. To let the compiler know, that this value can be changed anytime outside the loop() function.

volatile int semaphore;

Thanks for replying but you’ve missed the point of the post

It’s not about trying to change the value of the variable “semaphore”. It does change successfully within the Interrupt Service Routine, as indicated in my examples.

What I’m trying to understand is why the the while(){} loop that tests the value of “semaphore” works properly if I use:

 while(…){delay(0);}

or

while(…){Serial.println(“Print something”);}

but

while(…){}

does not.

In the other example I gave, while(…){} works perfectly well as an endless loop until a contition is satisfied.

 

It doesn’t make sense.

That is what I’m trying to

That’s what I’m trying to tell you. You will need to declare the semaphore as volatile. Point. 

Read (and understand) about using the volatile keyword:  http://arduino.cc/en/Reference/Volatile

Than it will (hopefully) make sense for you.

Yes, it worked - thanks - but I still don’t see the logic in it

Thanks for the help. I changed the line “int semaphore = 0” to “volatile int semaphore = 0” and it works consistantly now.

The use of the keyword “volatile” solves the overall problem. Thanks for pointing that out because I would never have looked at it on my own.

 

The question I had (and still have) is: in the original program, why do

while(semaphore==0){delay(0);}            and

while(semaphore==0){Serial.println(“anything”);}

both work but

while(semaphore==0){}

does not?

 That’s what’s puzzling me. Logically, if it was strictly a failure to designate “semaphore” as volatile, It wouldn’t have mattered what I put in the code block of thw while statement.

For some who’s not a native English speaker

You have a good command of the language. Very clear explanation of many minute details. I woke up with a headache last night and couldn’t sleep.  Found this surfaced again and read it. I’ve decided this is one of the best written tutorials ever here on LMR. Thankyou Robotfreak, information worth paying for.
One thing I spotted I need to ask you about is in the first examples you use noInterrupts() and Interrupts() but in the final
encoder example you used cli and sei.
For one thing I was surprised you could run inline assembly with arduino. The question is if you look at the arduino commands for noInterrupts() and Interrupts() is this all they are doing running the assembly commands or is there more going on?

Thank you for the kind

Thank you for the kind words, merser.

The 3rd example is an older examples I converted from AVR C to Arduino.

In the arduino.h Header file you will find the declarations for both functions. 

#define interrupts() sei()
#define noInterrupts() cli() 

So, these are only different names for the same functions. sei and cli are C macros with inline assembly code. You can find there declarations in avr\include\avr\interrupt.h:

 

# define sei()  asm volatile (“sei” ::slight_smile:
# define cli()  asm volatile (“cli” ::slight_smile:

So, yes you can do inline assembly in your C/C++ or Arduino programm too. I saw a lot of inline assembly in Adafruits NeoPixel light painter code for time critical functions.

The compiler is playing

The compiler is playing tricks with you. As OddBot mentioned, you will need to look at the assembly code to find out what goes wrong. The volatile keyword tells the compiler: “don’t play any tricks”

stopwatch code

I’m trying to make a stopwatch with 4 separate 7 segment displays using timer register. I already have a functional circuit and I found a code that uses this register. BUT. This code is showing in MM:SS format. And I want SS:100/SS. What I need to change in the code to have this format? Please let me know. I’ll be very grateful.

I can upload whole code if you want but I think that main setting is this part of code:

 

TIMSK1=0x01; // enabled global and timer overflow interrupt;
TCCR1A = 0x00; // normal operation 148 page (mode0);
TCNT1=0x0BDC;
TCCR1B = 0x00; // stop hardware timer

Thank you

Jan

stopwatch code

I’m trying to make a stopwatch with 4 separate 7 segment displays using timer register. I already have a functional circuit and I found a code that uses this register. BUT. This code is showing in MM:SS format. And I want SS:100/SS. What I need to change in the code to have this format? Please let me know. I’ll be very grateful.

I can upload whole code if you want but I think that main setting is this part of code:

 

TIMSK1=0x01; // enabled global and timer overflow interrupt;
TCCR1A = 0x00; // normal operation 148 page (mode0);
TCNT1=0x0BDC;
TCCR1B = 0x00; // stop hardware timer

void loop ()
{

  if (!digitalRead(Button_start)&&!b) {
 
  if (!set_mode) { if (run) {TCCR1B=0x00; run=0;} else {TCCR1B=0x04; run=1;}}

 

Thank you

Jan

This is an old thread but a good one.
I think there is a bug in Timer5 (and others) with the .reset() method or maybe I misunderstanding.

I am looking to write a program that will timeout after 30 seconds if nothing comes in on the serial line for exmple (like a watchdog timer), if something DOES come in, reset the timer back to 30 seconds.

I call Timer5.reset(); but the ISR gets called all the time. Looking at the Timer5.cpp, the reset() method sets TCNT5 = 0, shouldn’t it set it back to somethig else?

How do I reset the timer? timer5.stop() , timer5.start() does not do it.
?

1 Like

That link (https://www.arduino.cc/reference/en/language/variables/variable-scope–qualifiers/volatile/) is dead.
Try https://www.arduino.cc/reference/en/language/variables/variable-scope-qualifiers/volatile/

1 Like

Also thank you very much from my side! You helped me a lot! I was able to program an arduino timer interrupt myself: https://nerd-corner.com/arduino-timer-interrupts-how-to-program-arduino-registers/

Thank you so much for the explanation! I am working with ARDUINO UNO Minima R4. Can I use these same commands there too? Can I run this same program there? Will it work?