How to Use Arduino
millis(): How it works and How to use it. Create Delays, One-Shots and simple Schedulers with simple
analysis of millis() code; Plus - Find out why it Lies (a bit)!
Arduino millis():
How to create non blocking delays using millis().
How to make simple event schedulers.
Find out exactly how millis() works including empirical and simulation results.
Unlike the delay() function, the millis() function does not stop the processor.
Find out why millis() will never output the value 42ms (or approx. multiple of it)!
Why does millis() jitter in its output?
The millis() function returns milliseconds since reset. On this page you can find
out how to use it effectively for non blocking event timing and delays, and scheduling as well as
learning how it works in detail.
Using Arduino millis as a Delay Timer
The millis() function returns the current time in milliseconds
(1/1000th of a second) from when you powered up the board (or reset
it). It gives you a way of measuring time from within your program, which
is quite different to the delay() function that gives no feedback about time at all.
Warning: Arduino millis uses a timer
interrupt, interrupts must be on. They are on by default but you may
need to turn them off when using pulsein.
Measuring a time period using millis(), is simply a matter of comparing current time to
the time value stored in a variable. As you go round a loop you continuously
perform a simple bit of maths:
millis() - stored_time
This gives you the elapsed time in milliseconds from the "stored time value".
This action is shown in the pseudo code below:
LOOP
...
// An event happens
if (event==true) stored_time = millis();
...
elapsed_time = millis() - stored_time;
...
END_LOOP
The above pseudo code snippet results in the variable "elapsed_time"
measuring time in milliseconds since the event occurred. An event could be a
button press or some action from another part of the program.
Perhaps you want to send out a serial message at one second intervals so in
this case the event would be message transmission and you would wait until
elapsed time was greater than 1000 (1000ms = 1 second) before sending the next
serial message (and storing the time again).
The millis() function is driven by a millisecond timer interrupt that
increments an unsigned long every time it activates and just returns the value
of that variable.
Arduino millis LED Flashing
The following example shows you how to use millis() to give a non blocking
delay. A non blocking delay is a type of delay that allows other code to
operate even though there is a delay operation elsewhere. This is very
different to using the function "delay()" where your code stops processing
(except for interrupts) and does nothing useful for the duration of the delay
period.
The pseudo code shown below gives a non blocking delay of 500ms and this
delay is repeatedly triggered. This shows you how to create an
Arduino millis timer that can have a delay time of any length (up to 49.7
days).
if((millis()-oldtime)>500){
oldtime=millis();
// Do something every after 500ms}
For an action every 12 hours you would use a delay interval of (12 * 60 * 60 *
1000) = 43200000ms replacing 500 with 43200000L (The L indicates a long value
to the comiler). For days you need more milliseconds e.g. 3 days would require
(3 * 24 * 60 * 60 * 1000) =259200000L again replacing 500 with 259200000L. To
stop the repetition of the action see here.
Code Operation : Arduino millis as delay
operation
The Arduino code above shows the loop function which, in this case, is
the only code required; Make the contents of the setup() function empty as you don't
need to initialise anything i.e. you don't need to start the serial port or
SPI interfaces in this instance.
Loop Code operation : For Non Blocking delays
The value of oldtime is set to a value of millis() at the start. if the
value of millis(), which is increasing every millisecond, is greater than 500
counts above oldtime then the conditional expression in the if statement
becomes true. This means 500 milliseconds have past since the value of oldtime
was set i.e. a delay of 500ms.
Within the body of the if-statement the LED is toggled from its previous
state. This means the LED is flashing at a rate of 1Hz (500ms on and 500ms off)
or once a second.
Since millis() is only tested by the conditional if statement, you can add
other code within the loop to do other useful work i.e. using Arduino millis()
in this way does not stop the processor.
TIP: You must update the variable 'oldtime' within the
if-statement
to the millis() value so that the next delay period can be performed,
otherwise the if-statement expression would hang (always true).
Note: The type uint32_t is the same type as
"unsigned long". uint32_t is used in embedded programming as
it directly specifies the number of bits within the type, whereas "unsigned
long" may have a different number of bits for different compilers.
Arduino millis limit
So how long can you measure (and why would you care?). The function millis() returns a
magic number that appears out of the depths of Arduino code but as an engineer
you need to know what it is and how it is created. You need to know because
all systems have limits which trip you up and make your system fail.
The maximum time that can be measured depends on the type of variable used
to store the millis() data which is an unsigned long and using this type allows
you to measure just over 49 days. If your project will never be on for longer
than 49 days then you don't have a problem.
For the Arduino the max value from millis() is :
4,294,967,295 or (0xffffffff)
This is because the Arduino millis data type is :
unsigned long (which can also be written as
uint32_t)...
...in which you can more easily see the number of bits in the type.
Maximum number of days for millis()
The count that an unsigned long is capable
of holding is: pow(2,32)-1 or 4,294,967,295 or 4 billion 294 million 967
thousand and 295. So if every count is worth a millisecond then how many days
is that?
First divide by 1000 for the seconds, then by 60 for the minutes then by 60
for the hours then by 24 for the days = ~ 49.71 days.
After approximately 50 days (or a bit more than 49.71 days) the timer wraps
round to zero and this is the Arduino millis overflow problem.
Note: Arduinos may have a resonator (3 pin) instead of a
crystal (2 pin) and these are not as accurate as crystals.
If you are designing a project that must time for more than 49 days then
this could cause a problem because at the wrap around point, time that was
increasing incrementally in milliseconds, suddenly goes to zero. If you
recorded a time stamp for a data logging device using millis() after 49.71 days
the time stamp would return to the start time i.e. wrong.
Another problem is using the timer for delays e.g. to flash an LED, where
you wait for the timer to reach the current time plus 500ms say, if the timer
wrapped within that 500ms then the time to wait would instead be 50 days which
is a bit of a long time to flash an LED off!
TIP: If you want to go beyond 50 days, add another variable
that acts as extra bits in front of the unsigned long e.g. an unsigned char
would extend time by 256 * 50 days. Increment it every time the millis() time
wraps around. The downside is that you would need to include that 8 bit
quantity in all time calculations so you would need to make your own "greater
than" operation etc.
How to avoid Arduino Millis overflow
Lets say that you want to have a repeated action every 100ms. So you write code similar to this:
// LED Flash
if((millis()-ledtime)>100){
ledtime=millis();
tog=~tog;// Invert
if(tog)digitalWrite(LED,HIGH);elsedigitalWrite(LED,LOW);}
Usually you just assume that the board is not really going to be left on
for greater than ~50 days! so you don't really consider overflow. Lets
consider it and see what happens in this case.
Millis() overflow solution
The following solution avoids the overflow problem but only if you
write the time dejection part exactly as shown - if you reverse the
terms or move them around you will make it fail!
The overflow can be avoided but only for measuring periods smaller
than the maximum overflow time (the use case) and this is due to a property of
how the numbers are stored in memory i.e. modulo arithmetic and the way integers are calculated. For the
unsigned long 4 bytes are
used.
In modulo maths, (in C modulo is represented by the percent sign %)
values are constrained to 1 less than the modulo number For example:
9 % 10 returns 9
but 10 % 10 returns 0
similarly
19 % 10 returns 9
and 20 % 10 returns 0
The numbers wrap around and this is exactly how to solve the overflow
problem. But instead of actually invoking the modulo operator it is
already active because of the constraint of the 4 byte memory storage for an
unsigned long. So the unsigned long automatically constrains the value from 0 to pow(2,32)-1.
Since integer numbers also use two's complement representation you get the right numbers when subtracting and adding.
Example of unsigned long millis overflow
Lets say ledtime is set to the maximum value of the unsigned long:
ledtime = 0xffffffff or pow(2,32)-1 or 4294967295
That means when the next timeout detection is performed then millis()
will retrurn a positive value and lets say that millis() returns a value of 1 on
the next detection then for normal maths you would have:
The the twos complement and modulo maths means for an unsigned long this represents:
-4294967294 == 2
So this is the right answer even though millis() value is smaller than the
real value of ledtime. Twos complement maths means that $fffffff is
interpreted as a negative number ($fffffff == -1). So:
ledtime (1) - ($ffffffff) == (1) - (-1) == 2.
So the timer detection code has been written in such a way that overflows are ignored!
...with the caveat that this method can only be accurate for measuring
delta periods and the code must be written as follows:
if((millis()-ledtime)>100)
Time Conversions for
Arduino millis
Arduino millis to Seconds
Since millis is a shortened engineering term for milliseconds and milli
stands for 1/1000th there are 1000 milliseconds in one second. Therefore to
count seconds divide millis by 1000.
Seconds = millis()/1000;
Often you will want to work with millis() in fractions of a second e.g. for
flashing an LED every 400ms you would compare millis() to 200 - using an on/off
toggle for every 200ms results in a time period of 400ms.
Arduino millis to Hours
If you want to get to hours when using Arduino millis as a Timer you need to
do some more division:
Minutes = ( millis()/1000 ) / 60;
Hours = ( ( millis()/1000 ) / 60 ) / 60;
Arduino
millis to Days
The full set of time values is here:
Days = Hours/24;
Hours = Minutes / 60;
Minutes = Seconds / 60;
Seconds = millis()/1000;
As noted previously the variable days can only have a maximum value of 49.7 days.
Note: Since Hours. Minutes and Seconds and Days can only have
a maximum value below 255 you can use type uint8_t for each of the above
quantities. uint8_t is the same as "unsigned char".
Arduino millis vs delay
Lets just say at the start of this discussion - "Don't Use delay()". This
will save you time. Read on to find out why...
Arduino milis() is an interrupt driven function meaning that it is always
operating in the background while your code is working. Interrupts are used to
update the value that millis() outputs so that after every millisecond that
value will increase by one. You can access the current value of the millis() at
any time simply by calling the function millis() and placing the result into
your own unsigned long variable (or you can compare the millis() output to
another constant).
The function delay() uses interrupts to calculate time but is actually "Do Nothing" loop to waste processor time - it does not
return any value. The function delay() depends on the interrupt driven output
from Timer0. Therefore delay() can not be used within an interrupt service
routine since within an ISR is interrupts are turned off.
It just
effectively stops the processor from doing anything else while it counts down a
delay time.
Note: The above discussion is talking about main code
operation - interrupts still interrupt main code and operate e.g. timers or I2C
interfaces etc. still work.
Note: millis() does not always increment by
one due to the way the timing is calculated within the interrupt routine.
The link takes you to a detailed simulation of millis() in this page.
Arduino millis not accurate
No it is not! Well, it does quite a good job.
There are two reasons :
The clock source - This is true of all crystal oscillator based systems
but some Arduino boards use resonator that performs worse than a
crystal.
The implementation - In general standard crystal oscilaltor frequences
that are easy for humans to read e.g.16MHz are not divisible by a decimal
number so you can't get an exact output period. Arduino code adjusts for
this problem using a correction algorithm.
Reason One - The Clock Source
Arduino millis() is not accurate and one reason for this is that Arduino
boards sometimes have a 16MHz resonator fitted. This is a cheap oscillation
component that is OK for most applications. it will drift around with
temperature but will always be approximately 16MHz.
If you are looking for accuracy, you will need an Arduino with a crystal on
board (or make up your own bare-bones version with a crystal). You can tell if
yours only has a resonator as resonators are three pin devices, whereas crystals
are two pin devices.
The problem is that to retro fit a crystal (it can be done!) to an Arduino
board requires two extra components to make the crystal oscillate correctly
(capacitive loads are required). These are two capacitors, each connected to
one pin of the crystal and then to ground. They range from 12pF to 33pf - check
for the correct capacitor specified by the manufacturer - or just use a couple
of 15pF. You of course also need to attach the crystal to the clock inputs of
the microcontroller (same connections as the resonator - but not the middle
ground one).
A better solution
is to use an external RTC
The accuracy of the crystal is specified as ppm and is usually around 100ppm
(a 32kHz watch crystal can get to 20ppm - not much use for a microcontroller
unless you need low power operation).
If you really want an accurate timestamp then use an ovenised timekeeping
chip (a cheap 2ppm one is the DS3232). This does not output a clock for direct
use by the microcontroller so you could keep the resonator and use the
DS3232 as the accurate timestamp device.
Reason Two - Timer 0
implementation
This second reason is not really that Arduino millis is not accurate (it
becomes accurate) but more about the implementation of the timer which is
clever but will cause a small jitter. The interrupt for the millisecond timer
(using Timer 0 in wiring.c) uses prescalers to divide down the main clock 16MHz
but the output of the timer is off by a small amount (you can not divide down a
16MHz clock using divide by 2 divider hardware to get an exact millisecond
output - -the closest you can get is 1024us). When the error gets big enough a
correction factor is used to adjust the timer value.
So in the long term the millis() timer is accurate but in the short term it
could be out by a small amount.
Timer 0 is setup so that it has a prescaler of 64. It is an 8 bit timer so
overflows every 256 counts. Therefore for a 16MHz clock, the repeat time for
timer 0 is (1.0/16e6)*256*64 = 0.001024 of 1024 us which is close to 1ms but
not actually there.
Millis() Operation
At each interrupt of 1024us the millis() timer is incremented.
Since 1024us is greater than 1000us, the millis() timer is too slow and needs correcting.
The idea is to store the error and
accumulate it until it surpasses a threshold, upon which the
millisecond timer output is corrected.
So to correct to a 1ms output,
The number of interrupts before a correction is needed is:
1000.0/24 = 41.66.
The above equation represents the number of 24us periods that add up
to 1ms i.e after 41.66 interrupts the error will be 1ms. Of course you
can't get 41.66 interrupts so you have to wait for the following
interrupt to detect when the error is greater than 1000us. This will be
when 42 interrupts have occurred.
The clever part of the algorithm is that the error accumulator is
incremented by 24 every interrupt (the division is not performed - that
just lets you see the idea). When this variable is greater than 41.66*24
i.e 42*24 = 1008 then the error is corrected.
The next really, really, clever part of the algorithm is that the
error variable is not reset to zero - you just subtract the 1ms value
(since that was what was corrected) leaving the last value of the error
in the accumulator i.e. it will be 8 on this occasion. This error then
accumulates again and the millis() timer is again adjusted when the error is greater then 1ms.
From the analysis below the millis() timer will be
continuously corrected and is not in error by more than 2ms (See simulation and real output results below).
You can explore this further by looking at the code
that follows below. One thing to note is that the values are fitted into
bytes because they are all multiples of 8 (but this only works for 16MHz and 8MHz
clocks):
1024.0 / 8 = 128.0 ; 1024 >> 3 is exactly 128 i.e. fits in a byte.
1000.0 / 8 = 125.0 ; 1000 >> 3 is exactly 125 i.e. fits in a byte.
24.0 / 8 =
3.0 ; 24
>> 3 is exactly 3 i.e. fits in a byte.
These numbers are used in the code below.
Arduino millis() Timer 0 code operation
There are two variables used in the correction and a few macro
definitions:
clockCyclesPerMicrosecond gives a
result of 16 for a 16MHz clock.
Timer 0 is prescaled by 64 and overflows after 256 prescaled clocks so it
triggers an interrupt after 64 * 256 clocks cycles.
Which just shows it is a long winded way of doing a simple calculation that
does have the advantage of being generic so it means for different clocks a
different result will occur. However the code comments state:
// the fractional number of milliseconds per timer0
overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)
So the comments state that the The fractional timings calculation (below)
work for 8 and 8Mhz and 16MHz.
The above when used later inserts 1.024 into the code at the point of use
which is in fact when an unsigned long is incremented so that value would be
incremented by one.
// the fractional number of milliseconds per timer0
overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)
The reason stated for doing the above right shifts is so that the numbers
fit into a byte.
Three variables are used in the correction and output of the millis value
(timer0_millis - below).
unsigned long
timer0_overflow_count - only used in microseconds
calculation.
unsigned long timer0_millis - the
value output by millis().
Byte timer0_fract
Every time in the interrupt:
timer0_millis is increased by
MILLIS_INC (or by 1) - this is the millis() output value.
timer0_fract is increased by
FRACT_INC (or by 3).
timer0_overflow_count is increased by one - this is
the unadjusted Timer0 interrupt count.
If necessary a correction is made to timer0_millis
when the accumulated error gets too big.
Interrupt code (ISR) for
TIMER0
The following code is contained within the Interrupt Service Routine and
performs the millisecond update which updates the last 3 variables that are
globals. The variable timer0_millis is the value returned by the
millis() function.
Fractional adjustment in the interrupt vector:
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsignedlongm=timer0_millis;
unsignedcharf=timer0_fract;
m+=MILLIS_INC;
f+=FRACT_INC;
if(f>=FRACT_MAX){
f-=FRACT_MAX;
m+=1;
}
timer0_fract = f; timer0_millis = m; timer0_overflow_count++;
In the above code, m and f are defined as local variables to allow the
compiler to use registers which are faster (check this actually happens by
examining output assembler code).
The fractional action is that if timer0_fract is greater or equal than
FRACT_MAX (125) then subtract FRACT_MAX and increase timer0_millis by one
(>= is used since 125 is not a multiple of 3).
So every time the interrupt fires, 3 is added until the accumulated
error (timer0_fract) is greater or equal to 125 which will be when time0_fract
1st reaches 126 or 42 interrupt calls. Interrupts are called with a time period
of 1024 microseconds therefore the time before a millisecond correction is made
is 42 *1024 = 43008us.
Therefore you will get a correction of the Arduino millisecond timer every
~43ms.
Since the timer0 interrupt is running slow the millisecond timer is
incremented by 1 every ~43ms
The first time round the loop after correction 1 is left in timer0_fract.
The second time 2 is left in timer0_fract.
So small fractional errors eventually get corrected after >125 times
~43ms.
TCL simulation
The following TCL program simulates the action of the interrupt code for
Timer 0:
The first adjustment is when the millis output is at 42 and the fractional
part has just passed 125. Instead of the expected output of 42 an adjusted
output of 43 is output instead i.e the correction algorithm jumps past 42 and
so millis() does not output the value of 42 at all. At about every 42 ms this
correction is made again.
Arduino
Program showing millis and micros output
The next program shows the actual output from an Arduino Uno. This is
the physical result on real hardware showing the millisecond jitter at
42 ms.
The program takes samples of the current millis() and micros() timer
output values sending them as fast as possible to the serial terminal.
It outputs more than one result since the interrupt timer is
asynchronous to the main loop code i.e. you don't know when it is going to
change, so to see changes you have to make the loop run faster than the
millisecond timer (hence the high baud rate and higher sample number 2100 which terminates output).
voidsetup(){
Serial.begin(230400);
}
voidloop(){
staticunsignedintc=0,stop=0;
unsignedlongmil,mic;
// Note Reduce this parameter the more is done in the loop! // for 3~4 outputs per loop iteration
// delayMicroseconds(50);
if(c<2100&&stop==0){
c++;mil=millis();// Capture as close together as possible
mic=micros();Serial.print(mil);
Serial.print(" ");
Serial.println(mic);}elsestop=1;
}
The following table shows millis() on the left and micros() on the right.
The relevant section is here (the first adjustment at millis()==42 ):
The micros() value is 4us
inaccurate so is much better than millis(). Observe the transition point
at 42 (where the expected value:42 which is not output by millis() at
all) i.e. this is the same as the simulation output
which is reassuring!
You can also see that the millis() output is quite a way out just before the
transition i.e. reading millis() at the 41st change means you should be reading
from 42.0 to 42.9 instead you get 41 (the worst value is just before the
adjustment where millis() outputs 41ms and the actual time is 42.9ms.
You get a jump as the correction factor is added so the millis() function
never outputs 42ms and instead outputs 43 as the next result - this brings the
millis() output back into alignment with the correct time. This correction
process repeats all the time millis() is operating.
However the correction factor at the 42nd interrupt corrects the
millis() output to 43.3 for millis() output 43, so in the long term the millisecond
timer is accurate. This oscillation around the correct output is called
jitter.
Other values of Arduino
Crystal Clock
Elsewhere in the code (microsecond delay) it talks about the following
frequencies:
'Assumes a 1, 8, 12, 16, 20 or 24 MHz'
(the 24MHz clock is for overclocking - doing something not specified by the
data sheet).
The question is: Would millis() be accurate for these crystals?
The following
results show that for most cases the millis() output would be accurate but not
for 12, 20 and 24MHz. You would need to change the operation of the Arduino
code to make millis() output an accurate result.
Microseconds per timer0 overflow:
(64 * 256.0) / 1 = 16384 - 1MHz - no error.
(64 * 256.0) / 2 = 8192 - 2MHz - no error.
(64 * 256.0) / 4 = 4096 - 4MHz - no error.
(64 * 256.0) / 8 = 2048 - 8MHz - no error.
(64 * 256.0) / 12 = 1365.33 - 12Mhz - millis() would have an error.
(64 * 256.0) / 16 = 1024 - 16MHz - no error.
(64 * 256.0) / 20 = 819.2 - 20MHz - millis() would have an error.
(64 * 256.0) / 24 = 682.66 - 24MHz - millis() would have an error.
The calculation below is FRACT_INC (microseconds_per_interrupt % 1000) >> 3
Frequency (MHz)
FRACT_INC increments
1
48
2
24
4
12
8
6
12
45 (Not accurate millis o/p).
16
3
20
102 (Not accurate millis o/p).
24
85 (Not accurate millis o/p).
Note: Observe how FRACT_INC values blow up when the frequency used is not an exact
factor of the Timer0 prescaler and overflow result. Since pre-scalers and timers
use 8 or 16 bits it follows that for this scheme the clock frequency must be a
base2 value i.e. 1,2,4,8,16,32 etc.
Note: At lower crystal frequencies the value of FRACT_INC is
larger but the shifted right value of 1000 ms calculations remains the same -
it has a value of 125 - to fit into a byte) . Therefore the jitter will be far
greater using lower frequences i.e. the millisecond clock will be corrected more
often. e.g. for 16MHz 125/3 is 41.6 but 125/48 is 2.6 - so at 16Mhz the clock
is corrected every 42 ms while at 1MH it is corrected every 3ms.
What happens if
you use Arduino millis long?
If you decide to use a "long" type
definition for the comparison storage variable then you are using a signed
quantity instead of the unsigned quantity output by the millis() function.
If you were to make a timer using "long" and output the value of long using
a Serial command (or any command that converts an integer quality to a string)
such as:
Serial.println(timer);
...where timer is specified as long and updates, perhaps to seconds as:
timer = millis()/1000;
Then at some point in the operation of your code (when the timer reaches the
mid point) the leftmost bit of the unsigned output of millis() will become high
and the print function will interpret this a the sign bit. Therefore it will
output a negative number and Arduino millis() goes negative (or appears to be
negative). For a signed calculation the value is interpreted as negative and
the print function treats long as a signed quality and therefore outputs a
negative number.
Try this code in your Arduino
uint32_tulong;
int32_tslong,minus1,plus1,s1;
voidsetup(void){
Serial.begin(115200);
minus1=-1;
plus1=1;
Serial.println("\n View plus 1");
s1=plus1;
ulong=(unsignedlong)s1;
Serial.print(" signed plus 1 dec :");Serial.println(s1);
Serial.print(" signed plus 1 hex :");Serial.println(s1,HEX);
Serial.print(" unsigned plus 1 dec :");Serial.println(ulong);
Serial.print(" unsigned plus 1 hex :");Serial.println(ulong,HEX);
Serial.println("\n View minus 1");
s1=minus1;
ulong=(unsignedlong)s1;
Serial.print(" signed dec :");Serial.println(s1);
Serial.print(" signed hex :");Serial.println(s1,HEX);
Serial.print(" unsigned dec :");Serial.println(ulong);
Serial.print(" unsigned hex :");Serial.println(ulong,HEX);
Serial.println("\nif millis() reaches unsigned value 0xffffffff-1");
Serial.println(" Observe the signed value");
s1=(long)(0xffffffff-1);
ulong=(unsignedlong)s1;
Serial.print(" signed minus 1 dec :");Serial.println(s1);
Serial.print(" signed hex :");Serial.println(s1,HEX);
Serial.print(" unsigned dec :");Serial.println(ulong);
Serial.print(" unsigned hex :");Serial.println(ulong,HEX);
Serial.println("\n View signed value of 0x7fffffff - no problem");
s1=(long)(0x7fffffff);
ulong=(unsignedlong)s1;
ulong=(unsignedlong)s1;
Serial.print(" signed dec :");Serial.println(s1);
Serial.print(" signed hex :");Serial.println(s1,HEX);
Serial.print(" unsigned dec :");Serial.println(ulong);
Serial.print(" unsigned hex :");Serial.println(ulong,HEX);
Serial.println("\n View signed value of 0x80000000 (The sign bit)");
s1=(long)(0x80000000);
ulong=(unsignedlong)s1;
ulong=(unsignedlong)s1;
Serial.print(" signed dec :");Serial.println(s1);
Serial.print(" signed hex :");Serial.println(s1,HEX);
Serial.print(" unsigned dec :");Serial.println(ulong);
Serial.print(" unsigned hex :");Serial.println(ulong,HEX);
}
voidloop(void){
}
This is the result you'll get:
View plus 1
signed dec :1
signed hex :1
unsigned dec :1
unsigned hex :1
View minus 1
signed dec :-1
signed hex :FFFFFFFF
unsigned dec :4294967295
unsigned hex :FFFFFFFF
if millis() reaches unsigned value 0xffffffff-1
Observe the signed value
signed dec :-2
signed hex :FFFFFFFE
unsigned dec :4294967294
unsigned hex :FFFFFFFE
View signed value of 0x7fffffff - no problem
signed dec :2147483647
signed hex :7FFFFFFF
unsigned dec :2147483647
unsigned hex :7FFFFFFF
View signed value of 0x80000000 (The sign bit)
signed dec :-2147483648
signed hex :80000000
unsigned dec :2147483648
unsigned hex :80000000
You can see that the sign bit is very important (the left most bit) and if
you use signed types you will get negative output numbers displayed, even
though the unsigned version is correct i.e. it has the expected bit value - or
hex value shown.
Also shown is the flip over point where using signed long is OK until you
reach 2147483647 (0x7fffffff) add one to that and you get -2147483648
(0x80000000). In terms of days a timer will appear to work fine for ~25 days and then adding one results in a negative output.
The explanation of number of days that millis() covers is here.
The easy way round that is to use unsigned long (uint32_t) when dealing with
millis().
More Examples
How to make a one-shot timer with Arduino millis
This code only does one serial output action action after a set time and
then stops. Although using Arduino millis() allows other operations to continue
i.e. the LED keeps flashing but only one message is output.
#defineLED13
voidsetup(void){
Serial.begin(115200);
Serial.println("Simple Scheduler");
pinMode(LED,OUTPUT);
}
voidloop(void){
staticuint8_ttog=0,s1done=0;staticuint32_tledtime=millis(),s1=ledtime;
// LED Flash
if((millis()-ledtime)>500){
ledtime=millis();
tog=~tog;// Invert
if(tog)digitalWrite(LED,HIGH);elsedigitalWrite(LED,LOW);}
// 3 second message - one shot
if(!s1done&&(millis()-s1)>3000){
s1=millis();
s1done=1;
Serial.println("-3- SECONDS ONE SHOT ONLY");
}
}
How to
make a simple scheduler using Arduino millis
The aim of this Arduino millis example is to make a simple scheduler
algorithm to start different actions at different times. This is only a simple
example and you can find multitasking schedulers that transfer operation to a
different task saving variables so that tasks can be interrupted stopped and
restarted. There will also be the concept of flags that allow communication
between tasks. This simple example is definitely not that type but it can be
useful nevertheless.
Scheduling initial times for offset start using Arduino millis
TIP: To avoid everything happening at the same time set
the initial conditions to be offset from each othe.
You can offset the start times of each timer so that they are not all a multiple of 1000, because if
they are then they will fire more or less at the same time and serial output
will be generated, and serial output takes a bit of time thus changing the
actual time that subsequent time matches happen.
For example you could write the following initialisation (using random
offsets):
This would mean that the starting times of each timed output are offset from
each other - only the start times - the subsequent repeat times would be at the
repeat time specified in the code - they would still be offset so they would
not happen at the exact same time.
So the processor would not have to do the
actions associated with each timeout at the same time. Therefore the code will
operate more smoothly and not have to do a big processing burp!
Conclusions
You can use the millis() function to create non-blocking delays so
that the processor can carry on doing other operations even while the
delay is in progress. This the opposite of using the delay() function
where the processor has to stop and do nothing.
The problem is that delay() is easier to use whereas mllis() is a
little bit more involved. However, once you are familiar with the
operation it is quite easy.
The millis function in an 8 bit processor does skip a beat every now
and then but the algorithm does catch up giving you an overall accurate
millisecond timer. You can explore this missing value operation using
this page.
Also on this page you can find code using millis for one shot timers and a simple scheduler operation.
How to get accurate DHT22 digital humidity sensor readings with an Arduino. Did you know it also measures temperature as Well? Find out why, in this page...
A PIR sensor lets your Arduino sense movement without contact. This tutorial covers PIR sensor basics, connecting one to an Arduino board and coding a motion detector.
Arduino Hall Effect Sensor: Add magnetic sensing superpowers to your Arduino projects with an easy-to-use hall effect sensor. With full code and layout...
Comments
Have your say about what you just read! Leave me a comment in the box below.
Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.