I noticed that the millis() code executes cli(). Does that mean that if I have an interrupt attached to a pin, that I could miss the interrupt when millis() is executing at that specific moment?
If you are wondering about using millis() in your interrupt handler, interrupts are disabled already, so there is no need to worry about missing an interrupt because millis() disables interrupts while it copies a multi-byte value.
I think that you will find that the millis() value will not change while you are in an ISR, but it is valid to use its value within the ISR.
breedj:
I noticed that the millis() code executes cli(). Does that mean that if I have an interrupt attached to a pin, that I could miss the interrupt when millis() is executing at that specific moment?
No, but it means that the timer ISR that millis() uses, may be executed before your pin ISR is executed. You won't miss it, but there is no guarantee that it will be the first.
If the purpose of your pin interrupt is to capture the exact time of a pin change, a timer input capture is the way to go.
cli(), sei(), interrupts(), noInterrupts() all affect the global interrupt enable flag. This is
nothing to do with configuring whether interrupts happen, only when they are processed.
Each kind of interrupt is queued up independently until it can be processed, which is when
the global interrupt enable flag is enabled and no higher-priority interrupt type is pending.
This check happens betwen every instruction being executed.
Once a particular interrupt has been allowed to happen a call is made to its ISR, global
interrupts disabled and the specific queue for that type of interrupt cleared (its a queue of
length 1, note). The return-from-interrupt instruction restores the previous global
interrupt flag (this means ISRs can be recursive I believe).
breedj:
I noticed that the millis() code executes cli(). Does that mean that if I have an interrupt attached to a pin, that I could miss the interrupt when millis() is executing at that specific moment?
It depends on.
As a first step, interrupts are made "waiting" while you are in a interrupt handler and a interrupt occurs.
The Timer0 interrupt is to be executed each 1/1024s = ca. each 976 microseconds
So if you are in an interrupt handling routine which runs for less than 976 microseconds, the missing Timer0 interrupt is fetched later and everything is fine with further millis() counting.
But if your interrupt handling routines is executing for longer than 976 microseconds, it would be possible that 2 or more Timer0 interrupts may occur while your interrupt handler is blocking all the other interrupts. Only one waiting Timer0 interrupt will be executed later, so you may lose counts with millis() if your interrupt handlers need too long time to execute.
jurs:
But if your interrupt handling routines is executing for longer than 976 microseconds, it would be possible that 2 or more Timer0 interrupts may occur while your interrupt handler is blocking all the other interrupts. Only one waiting Timer0 interrupt will be executed later, so you may lose counts with millis() if your interrupt handlers need too long time to execute.
Without looking at the code (disclaimer)... millis uses the timer value to keep track, and that increments in hardware. So I don't think it will lose counts that way.
What I actually meant was: Can calling millis() in the main loop result in missing an interrupt because millis() calls cli() internally. Here's the code:
unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG;
cli(); <--- Can this cause missing an interrupt at this time when calling from loop.
m = timer0_millis;
SREG = oldSREG;
return m;
}
The same with micros();
Thanks
breedj:
What I actually ment was. Can calling millis() in the main loop result in missing an interrupt because millis() calls cli().
No. Interrupts are queued. After calling cli(), millis() calls sei() very soon afterwards.
aarg:
No. Interrupts are queued. After calling cli(), millis() calls sei() very soon afterwards.
Well, one instance of each type, anyway.
breedj:
What I actually meant was: Can calling millis() in the main loop result in missing an interrupt because millis() calls cli() internally.
No, everything is "safe" and the way it is coded you can use "millis()" with normal code in the loop as well as within an interrupt handling routine.
The interrupts MUST be disabled when reading a "volatile variable" from normal code
- which changes within an interrupt handler
- which is more than one single byte in size
What would occur if interrupts are not disabled and the counter changes from
0x000000FF incremented by one to result in 0x00000100?
In that case there would be an intermediate number at one point in RAM:
0x000000FF incremented becomes
0x000001FF and then
0x00000100 ==> finished
For a short moment you can find in RAM an invalid number sometimes, when incrementing a 4-byte value. So to get always either the number before incrementing it OR after incrementing, but never an intermediate (wrong) number from RAM, you always have to disable interrupts while reading the 4 bytes into another variable (or put it on the stack).
If you don't disable interrupts while reading the four bytes, you might read a wrong intermediate variable, because an interrupt can happen at ANY time, even after 3 bytes are read from a 4-byte "volatile" variable.
breedj:
The same with micros();
Yes, Sir!
You'll have to do the same with your own interrupt handling routines
- if you change volatile variables within your interrupt handler
- the volatile variable is more than 8-bit (1-byte) in size
- and you read the variable from within normal code
Interrupts that would have occured while they are globally disabled will stay "waiting" and will execute after interrupts are enabled again, so no problems with millis() counting. Except: millis() will never change its value while you are in an interrupt handler, because Timer0-interrupts will not be handled while another interrupt handler is executing.
See also: Read–modify–write - Wikipedia
Thanks jurs I understand what you mean.
There's one thing in the millis() function what caught my eye. And I'm asking out of curiousity. Why did the writer of millis() choose to make a copy of the SREG register and restore it after reading the timer value instead of calling sei(). I would have written the function as follow:
unsigned long millis()
{
unsigned long m;
cli();
m = timer0_millis;
sei()
return m;
}
Does it have to do with the 'waiting' of interrupts jurs mentioned? I actually would have guessed that calling cli() will not queue all interrupts and that they all will be dequeued after calling sei(). Because there might be hundreds of queued interrupts when interrupts are disabled for a long time. Or is restoring SREG the clue?
But the AVR instruction set document says:
"Sets the Global Interrupt Flag (I) in SREG (Status Register). The instruction following SEI will be executed
before any pending interrupts."
The mention 'pending interrupts' here, but there is no mention about queueing and pending interrupts anywhere else in the documentation. Also about how many there can be queued. So I'm still a but confused. Maybe someone does know where to find documention about this matter for AVR.
In interrupt functions, you should not normally use cli() and sei(). Those actions are taken care of automatically when the processor handles an interrupt and the return from interrupt.
There are exceptions to this rule but you should not worry about them now.
I would have written the function as follow:
unsigned long millis()
{
unsigned long m;
cli();
m = timer0_millis;
sei()
return m;
}
millis() is designed to be called both inside and outside an ISR. Your proposed code would turn interrupts on (the sei() call) when you don't want it turned on. In other words you assume that interrupts are on, you turn them off, and back on again. This is not a good idea.
You are worrying about nothing. If turning interrupts off for a few nanoseconds made the processor miss future interrupts then nothing would work.
Does it have to do with the 'waiting' of interrupts jurs mentioned? I actually would have guessed that calling cli() will not queue all interrupts and that they all will be dequeued after calling sei(). Because there might be hundreds of queued interrupts when interrupts are disabled for a long time.
Calling cli() merely stops the processor responding to interrupts. They are still "queued". Really, when an interrupt event happens a flag is set in the processor. These flags are checked in interrupt priority order (if interrupts are enabled, and at the end of each instruction). Since there is one flag per interrupt type then you cannot "queue" hundreds of interrupts. There is no place for such a queue (as PaulS said in reply #9).
You may as well worry about an interrupt occurring during the middle of an instruction.
Yes, queue was an unfortunate choice of words in my post, as it implies an order. What I was trying to get across is that if many interrupts coincide, they are "pooled" because they remain asserted until the control bit associated with each one is cleared. As part of the response to an interrupt is to place a "return from IRQ" frame on the stack, they will be processed sequentially in a LIFO manner.
Some processors have prioritized interrupts which adds to this picture but doesn't change the fundamentals of the point I'm making. I admit I don't know about the queuing in the AVR. I will look at it someday, no doubt.
But since each IRQ generating device of any kind has an individual enable flag which is reset by the ISR, there is no possibility of an interrupt being "missed" because of how the main program runs, or what other interrupts do.
millis() isn't the main program, but it consists of a driver and ISR portion. Since the timer IRQ flag remains set until the ISR runs, there is no normal possibility that it could be "missed" or "skipped" because of other activities.
However, as it depends on a timely response to the IRQ to function properly, it is important that every active ISR execute as quickly as possible. Otherwise, it will not be "skipped", but it will be delayed.
Just to add to what aarg said, the interrupt could be missed if too long a time elapses. For example, if you turn off interrupts (for whatever reason) for a second, then multiple timer interrupts would be missed, as the first event sets the "there is a timer interrupt flag", and when the second event occurs, the flag is already set, so it can't be set again.
Basically the processor would have a state machine internally, and at the completion of each instruction (which may be one or more clock cycles) it would check, if interrupts are enabled, all of the interrupt flags for things like timers, serial comms, SPI, etc. When it finds one set, it then enters interrupt processing for that interrupt. The order in which it does these tests (which is documented in the datasheet) is effectively the interrupt priority.
If multiple interrupt flags are set, then the lower priority ones will still be done - eventually - but not necessarily the moment that the event that triggers the flag being set happens.
Its clear to me now. Thanks a lot all of you.
breedj:
I would have written the function as follow:unsigned long millis()
{
unsigned long m;
cli();
m = timer0_millis;
sei()
return m;
}
This is fully correct code if you want to use that function ONLY IN NORMAL CODE.
But the Arduino function with saving/restoring SREG is also SAFE FOR USE IN INTERRUPT HANDLER functions.
If you would use your function from within an interrupt handler, the interrupts are always enabled after your function call. With the other code and saving/restoring SREG, it is like that:
- if the interrupts are enabled before function call, interrupts are enabled after function call
- if interrpts are disabled before function call, interrupts are disabled after function call
You function is different and after your code is executed, interrupts are always enabled, no matther whether the interrupts were enabled or disabled on entry of the function call.