Millis arduino время

Содержание

Arduino

Работа со временем

Функция millis()

Описание

Возвращает время в миллисекундах, прошедшее с начала выполнения программы на плате Arduino. Это число будет переполнено и сброситься до 0 примерно через 50 дней выполнения.

Синтаксис
Параметры
Возвращаемое значение

Количество миллисекунд, прошедших с момента запуска программы

Пример

Выводим значение, возвращаемое функцией millis() и ждем 1 секунду:

Функция micros()

Описание

Возвращает время в микросекундах, прошедшее с начала выполнения программы на плате Arduino. Это число будет переполнено и сброситься до 0 примерно через 70 минут выполнения.

Синтаксис
Параметры
Возвращаемое значение

Количество микросекунд, прошедших с момента запуска программы

Пример

Выводим значение, возвращаемое функцией micros() и ждем 1 секунду:

Примечания

На платах Arduino 16 МГц (например Uno и Nano) эта функция имеет разрешение в четыре микросекунды, поэтому значение всегда кратно четырем. На платах Arduino 8 МГц (например, LilyPad) эта функция имеет разрешение восемь микросекунд.

Функция delay()

Описание

Приостанавливает выполнение программы на заданноое время в миллисекундах.

Синтаксис
Параметры

ms — время в миллисекундах, на которое нужно приостановить программу

Возвращаемое значение
Пример

Мигаем встроенным светодиодом:

Примечания

Несмотря на то, что с помощью функции delay() легко создать мигающий светодиод и многие другие простые скетчи, использование функции имеет существенные недостатки. Во время приостановки программы невозможны любые манипуляции с пинами, например опрос датчиков. Также не будут работать многие математические функции. Поэтому рекомендуется в качестве альтернативы использовать функцию millis() .

Однако некоторые функции во вовремя выполнение delay() все же продолжают работать. Например, прерывания.

Функция delayMicroseconds()

Описание

Приостанавливает выполнение программы на заданное время в микросекундах.

Синтаксис
Параметры

us — время в микросекундах, на которое нужно приостановить программу

Возвращаемое значение
Пример

Посылаем последовательность импульсов с периодом раз в 100 микросекунд:

Источник

Функции времени

Задержки

Простейшей с точки зрения использования функцией времени является задержка: программа “зависает” внутри функции задержки и ожидает указанное время. Задержка позволяет очень удобно и наглядно организовать работу простой “однозадачной” программы, у нас есть два варианта задержек:

  • delay(time)
    • Задержка на указанное количество миллисекунд (мс). 1 секунда = 1’000 мс.
    • time принимает тип данных unsigned long и может приостановить выполнение на срок от 1 до 4 294 967 295 мс (

    50 суток) с разрешением 1 мс.

  • Работает на системном таймере, поэтому не работает внутри прерывания и при отключенных прерываниях.
  • delayMicroseconds(time)
    • Задержка на указанное количество микросекунд (мкс). 1 секунда = 1’000’000 мкс.
    • time принимает тип данных unsigned int и может приостановить выполнение на срок от 4 до 16383 мкс (да, меньше чем максимум для этого типа данных) с разрешением 4 мкс.
    • Работает не на таймере, а на пропуске тактов процессора, поэтому может работать в прерывании и при отключенных прерываниях.
    • Иногда не совсем корректно работает с переменными, поэтому нужно стараться использовать константы ( const или просто число).
    • Часто используется в библиотеках для эмуляции цифровых интерфейсов связи.
  • Задержки использовать очень просто:

    Мышление “задержками” – главная проблема новичков. Организовать работу сложной программы при помощи задержки – невозможно, поэтому дальше рассмотрим более полезные инструменты.

    Функция yield()

    Разработчики Arduino позаботились о том, чтобы функция delay() не просто блокировала выполнение кода, но и позволяла выполнять другой код во время этой задержки. Данный “костыль” получил название yield() и работает следующим образом: если объявить функцию

    то расположенный внутри неё код будет выполняться во время работы любой задержки delay() в программе! Это решение хоть и кажется нелепым, но в то же время позволяет быстро и без написания лишних костылей и таймеров реализовать пару параллельно выполняющихся задач. Это вполне соответствует идеологии Arduino – максимально простая и быстрая разработка прототипа. Рассмотрим простой пример: стандартный мигающий светодиод, но с опросом кнопки:

    Функции счёта времени

    Данные функции возвращают время, прошедшее с момента запуска программы, так называемый аптайм (англ. uptime). Таких функций у нас две:

      millis() – миллисекунды, тип unsigned long , от 1 до 4 294 967 295 мс (

    50 суток), разрешение 1 мс. После “переполнения” отсчёт начинается с нуля.
    micros() – микросекунды, тип unsigned long , от 4 до 4 294 967 295 мкс (

    70 минут), разрешение 4 мкс. После “переполнения” отсчёт начинается с нуля.

    Эти функции позволяют организовать программу практически любой сложности с любым количеством параллельно выполняющихся по таймеру задач. Подробнее об этом поговорим в уроке про многозадачность.

    millis() в часы и секунды

    Миллисекунды – не самый удобный способ оценить время работы программы. Можно перевести его в более человеческие часы, минуты и секунды при помощи нехитрых математических операций:

    Видео

    Источник

    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/1000 th 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.

    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:В

    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).
    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.

    Example 1 : Delay code

    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.

    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 =

    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.

    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!

    (But see below for the solution to this problem).

    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:
    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

    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:

    millis() — ledtimeВ == 1 — 4294967295 = -4294967294

    The the twos complement and modulo maths means for an unsigned long this represents:

    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:

    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.

    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:

    Hours = Minutes / 60;

    Minutes = Seconds / 60;

    As noted previously the variable days can only have a maximum value of 49.7 days.

    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.

    Arduino millis not accurate

    No it is not! Well, it does quite a good job.

    There are two reasons :

    1. The clock source — This is true of all crystal oscillator based systems but some Arduino boards use resonator that performs worse than a crystal.
    2. 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 bug 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.

    How does millis() work in Arduino

    Code location of millis() timer:

    If you have a 32bit machine (above is for 64bit) the path will be:

    Timer0 interrupt Clock Cycles

    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.

    Two other macros are:

    clockCyclesToMicroseconds(a) = ( (a) / clockCyclesPerMicrosecond )

    microsecondsToClockCycles(a) = ( (a) * clockCyclesPerMicrosecond )

    Within wiring.c the following definitions are made:

    MICROSECONDS_PER_TIMER0_OVERFLOW =
    В В В clockCyclesToMicroseconds( 64 * 256) = 16384 / 16 = 1024

    Since clockCyclesPerMicrosecond is 16

    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 following calculations are for a 16MHz clock.

    MILLIS_INC = ( MICROSECONDS_PER_TIMER0_OVERFLOW / 1000 ) = 1.024

    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.)

    FRACT_INC = ( MICROSECONDS_PER_TIMER0_OVERFLOW % 1000 >> 3 )
    В В В = 24 >> 3 = 3 (16MHz)

    FRACT_MAX = ( 1000 >>3 )
    В В В = 125

    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:

    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

    Since the timer0 interrupt is running slow the millisecond timer is incremented by 1 every

    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

    TCL simulation

    The following TCL program simulates the action of the interrupt code for Timer 0:

    Some of the output it gererates is shown below (left is the millis increment and the right is the fractional part).:

    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).

    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 42 nd 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.

    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:

    . where timer is specified as long and updates, perhaps to seconds as:

    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

    This is the result you’ll get:

    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.

    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

    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!

    Источник

    Adblock
    detector