binhminh2015 / arduino

Automatically exported from code.google.com/p/arduino
Other
0 stars 0 forks source link

millis() fails to increment when interrupts are disabled. #187

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
When IRQs are disabled at the time when timer0 overflows, micros() will
return wrong results.

What steps will reproduce the problem?
1. call t1=micros() just before a overflow of timer0
2. disable IRQ
3. wait after overflow of timer0 and after TCNT0 > 0
4. call t2=micros()
5. enable IRQ
6. call t3=micros()

now t2 is smaller than t1 because the ISR TIMER0_OVF_vect in wiring.c has
not been called and the overflow handling in micros() is wrong:

wrong, because it only works when t==0:
    if ((TIFR0 & _BV(TOV0)) && (t == 0))
        t = 256;

much better, but doesn't handle longer times with disabled IRQs:
    if (TIFR0 & _BV(TOV0))
        t += 256;

best: see attachment of patch

What is the expected output?
t1=2010104
t2=2010116
t3=2010124
ovl1=0 ovl2=1 novl2=0
t2-t1=12  t3-t1=20  t3-t2=8

What do you see instead?
t1=2010104
t2=2009092
t3=2010124
ovl1=0 ovl2=1 novl2=1
t2-t1=-1012  t3-t1=20  t3-t2=1032

What version of the Arduino software are you using? On what operating
system?  Which Arduino board are you using?
I'm using Arduino-17 with Windows XP+SP3 and Arduino Duemilanove-328.

Please provide any additional information below.
The attachment "MicrosTimerOverflowTest.pde" contains a test case which
reproduces the problem. t2 must always be greater than t1.

The patch "wiring.patch" contains a fix which corrects the problem and also
solves the missing timer overflows problem when IRQs are disabled for a
longer time, as long as micros() is called at least once between two timer
overflows (around 1 microsecond on a ATmega 16Mhz).

Original issue reported on code.google.com by Michael....@gmail.com on 15 Jan 2010 at 11:00

GoogleCodeExporter commented 9 years ago
Cleaned up the patch

Original comment by Michael....@gmail.com on 16 Jan 2010 at 9:50

Attachments:

GoogleCodeExporter commented 9 years ago
This is a duplicate of #55 "Incorrect check for timer 0 overflow in micros()". I
haven't found #55 because of a wrong filter setting in the search dialog.

Original comment by Michael....@gmail.com on 16 Jan 2010 at 11:54

GoogleCodeExporter commented 9 years ago
millis() has similar issues, it doesn't count up when IRQs are disabled. The 
attached
patch addresses this also.

Original comment by Michael....@gmail.com on 16 Jan 2010 at 12:10

GoogleCodeExporter commented 9 years ago
Updated test sketch with additional test case for millis().

What is the expected output?
t1=2011128
t2=2011140
t3=2011148
ovl1=0 ovl2=1 ovl3=0
t2-t1=12  t3-t1=20  t3-t2=8
tm1=2019
tm2=2020
tm3=2020

What do you see instead (Arduino-17)?
t1=2021368
t2=2020356
t3=2021388
ovl1=0 ovl2=1 ovl3=1
t2-t1=-1012  t3-t1=20  t3-t2=1032
*** Error: t2 < t1  ==> missed a timer overflow
tm1=2034
tm2=2034
tm3=2035
*** Error: tm2==tm1 ==> missed a timer overflow

What do you see instead (Arduino-18-rc1-2)?
t1=2016248
t2=2016260
t3=2016268
ovl1=0 ovl2=1 ovl3=1
t2-t1=12  t3-t1=20  t3-t2=8
tm1=2024
tm2=2024
tm3=2025
*** Error: tm2==tm1 ==> missed a timer overflow

Original comment by Michael....@gmail.com on 16 Jan 2010 at 1:30

Attachments:

GoogleCodeExporter commented 9 years ago
Can you explain a bit more about the problem with millis() and how your 
solution works?

Original comment by dmel...@gmail.com on 28 Jan 2010 at 1:58

GoogleCodeExporter commented 9 years ago
The millis() problem is only a small problem. I don't know how relevant it is 
in real
applications. I just added it for completeness and because the fix is the same 
as for
the micros() problem which is much more important.

Problem:
millis() is updated on timer0 overflow. When IRQs are disabled, the update is 
also
disabled. The result is, that at the time of reading tm2 in the example above 
you get
the old (not updated) value of millis() which means the result is inaccurate. 
Please
look at the code of the test sketch MicrosTimerOverflowTest.pde for this 
scenario.

The second problem is, that the clock loses time when IRQs are disabled for 
more than
two timer overflows, so a complete update cycle is missed.

Solution:
What I have done is to check the overflow flag not only via the IRQ but 
additionally
directly in the millis() and micros() function. When overflow is set, the time
variables are updated, the overflow flag is cleared (so the IRQ is skipped) and 
the
updated value is returned.

This makes it possible to use millis() / micros() in a loop when IRQs are 
disabled as
long as it is called regularly (at least once in a ms) to check the overflow 
flag
manually.

  MikeT

Original comment by Michael....@gmail.com on 30 Jan 2010 at 8:54

GoogleCodeExporter commented 9 years ago
When IRQs are disabled, the delay() function doesn't work, because it relies on 
millis():

void delay(unsigned long ms)
{
    unsigned long start = millis();

    while (millis() - start <= ms)
        ;
}

Using the patch makes it possible to use delay() while IRQs are disabled 
because the
timer overflow is handled additionally in millis() and not only via the ISR.

Original comment by Michael....@gmail.com on 30 Jan 2010 at 12:06