What timer does millis arduino function use. Arduino multitasking with the millis function

Latency in Arduino plays a very important role. Without them, even the simplest example of Blink, which blinks an LED after a specified period of time, cannot work. But most novice programmers know little about time delays and only use Arduino delay without knowing the side effects of this command. In this article, I will talk in detail about temporary functions and the features of their use in the Arduino IDE development environment.

The Arduino has several different commands that are responsible for working with time and pauses:

  • delay()
  • delayMicroseconds()
  • millis()
  • micros()

They differ in accuracy and have their own characteristics that should be considered when writing code.

Using the arduino delay function

Syntax

Arduino delay is the simplest command and most often used by beginners. In fact, it is a delay that pauses the program for the number of milliseconds indicated in brackets. (There are 1000 milliseconds in one second.) The maximum value can be 4294967295 ms, which is approximately equal to 50 days. Let's look at a simple example that illustrates how this command works.

Void setup() ( pinMode(13, OUTPUT); ) void loop() ( digitalWrite(13, HIGH); // signal pin 13 high delay(10000); // pause 10000ms or 10 seconds digitalWrite13, LOW); // send a low signal to pin 13 delay(10000); // pause 10000ms or 10 seconds)

In method setup we prescribe that pin 13 will be used as an output. In the main part of the program, first a high signal is applied to the pin, then we make a delay of 10 seconds. At this time, the program seems to be suspended. Then a low signal is given and again a delay and everything starts all over again. As a result, we get that the pin is alternately supplied, then 5 V, then 0.

You need to clearly understand that during the pause using delay, the program is suspended, the application will not receive any data from the sensors. This is the biggest disadvantage of using the Arduino delay function. You can get around this limitation using interrupts, but we will talk about this in a separate article.

Delay example with blinking LED

An example diagram to illustrate how the delay function works.
You can build a circuit with an LED and a resistor. Then we will succeed standard example– blinking LED. To do this, on the pin, which we designated as the output, you need to connect the LED with a positive contact. We connect the free leg of the LED through a resistor of approximately 220 ohms (a little more can be) to ground. You can determine the polarity if you look at its insides. The large cup inside is connected to the minus, and the small leg is connected to the plus. If your LED is new, then you can determine the polarity by the length of the leads: a long leg is a plus, a short leg is a minus.

delayMicroseconds function

This function is a complete analog of delay, except that its units are not milliseconds, but microseconds (1000000 microseconds in 1 second). The maximum value will be 16383, which is equal to 16 milliseconds. The resolution is 4, meaning the number will always be a multiple of four. An example snippet would look like this:

DigitalWrite(2, HIGH); // send pin 2 high delayMicroseconds(16383); // pause 16383µs digitalWrite(2, LOW); // send a low signal to pin 2 delayMicroseconds(16383); // pause 16383µs

The problem with delayMicroseconds is exactly the same as with delay - these functions completely "hang" the program and it literally freezes for a while. At this time, it is impossible to work with ports, read information from sensors and perform mathematical operations. For flashers, this option is fine, but experienced users do not use it for large projects, since such failures are not needed there. Therefore, it is much better to use the functions described below.

millis function instead of delay

The millis() function will allow you to perform a delay without delay on arduino, thereby bypassing the shortcomings previous methods. The maximum value of the millis parameter is the same as for the delay function (4294967295ms or 50 days).

Using millis, we do not stop the execution of the entire sketch, but simply indicate how long the arduino should simply “bypass” exactly the block of code that we want to pause. Unlike delay millis by itself does not stop anything. This command simply returns to us from the built-in timer of the microcontroller the number of milliseconds that have passed since the start. With each call to loop We ourselves measure the time elapsed since the last call to our code, and if the time difference is less than the desired pause, then we ignore the code. As soon as the difference becomes more than the desired pause, we execute the code, get the current time using the same millis and remember it - this time will be the new reference point. In the next cycle, the countdown will already be from the new point and we will again ignore the code until the new difference between millis and our previously stored value reaches the desired pause again.

Delaying without delay with millis requires more code, but it can blink the LED and pause the sketch without stopping the system.

Here is an example that clearly illustrates the work of the command:

unsigned long timing; // Variable for storing the reference point void setup() ( Serial.begin(9600); ) void loop() ( /* This is where the execution of delay() analogue begins. Calculate the difference between the current moment and the previously saved reference point. If the difference is greater than If not, do nothing */ if (millis() - timing > 10000)( // Instead of 10000, substitute the pause value you need timing = millis(); Serial. println ("10 seconds") ; ) )

First, we introduce a timing variable, which will store the number of milliseconds. By default, the value of the variable is 0. In the main part of the program, we check the condition: if the number of milliseconds since the start of the microcontroller minus the number written to the timing variable is greater than 10000, then an action is performed to output a message to the port monitor and the current time value is written to the variable. As a result of the program's operation, the message 10 seconds will be displayed in the port monitor every 10 seconds. This method allows you to blink the LED without delay.

micros function instead of delay

This function can also delay without using the delay command. It works in exactly the same way as millis, but it counts not milliseconds, but microseconds with a resolution of 4 µs. Its maximum value is 4294967295 microseconds or 70 minutes. On overflow, the value is simply reset to 0, don't forget that.

Summary

The Arduino platform provides us with several ways to implement delay in our project. With delay, you can quickly pause the execution of the sketch, but at the same time block the operation of the microcontroller. Using the millis command allows you to get rid of the delay in Arduino, but this requires a little more programming. choose The best way depending on the complexity of your project. As a rule, in simple sketches and with a delay of less than 10 seconds, delay is used. If the operation logic is more complicated and a large delay is required, then it is better to use millis instead of delay.

The first thing that a beginner who masters Arduino encounters is the unpleasant property of the delay () function - blocking the execution of the program. Many examples on the Internet use this function, but practical application somehow hints that it is better to do without it.

As befits a beginner, I invented a bicycle and made my own implementation of a non-blocking delay. The task was like this:

  • Provide pseudo multitasking so that different events occur at their own time, at their own intervals and do not block each other.
  • It was convenient to use it.
  • It could be designed as a library and easily included in other projects without copy-paste.
Seeing that most of the Arduino libraries are made using OOP, I also decided not to show off and wrote the SmartDelay class, which can be obtained from github as a zip to add to the Arduino IDE or do a git clone in ~/Arduino/libraries/

The result is this.

#include SmartDelay foo(1000000UL); // in microseconds void loop () ( if (foo.Now()) ( // The code here is executed every interval in microseconds specified in the constructor above. ) //Other code )
The Now() method returns true if the interval has passed. In this case, the countdown starts again at the same interval. That is, Now () each time "reloads" automatically.

The classic LED blinking can be immediately complicated to two blinking. For example, the lights connected to pins 12 and 11 should blink at 1s and 777ms intervals respectively.

#include SmartDelay led12(1000000UL); SmartDelay led11(777000UL); setup () ( pinMode(12,OUTPUT); pinMode(11,OUTPUT); ) byte led12state=0; byte led11state=0; void loop () ( if (led12.Now()) ( digitalWrite(12,led12state); led12state=!led12state; ) if (led11.Now()) ( digitalWrite(11,led11state); led11state=!led11state; ) )
You can do something else in the loop, the blinking of the LEDs will not block the execution of this code.

It is clear that this is not a complete replacement for delay(), which stops the thread at given time, you must always write the program as an MFA (finite automaton mechanism). That is, store the state and, depending on it, go to the right place in the code.

Old version:

Action1(); delay(1000); action2(); delay(500); action3(); ...
New option:

Byte state=0; SmartDelayd(); ... switch (state) ( case 0: action1(); d.Set(1000000UL); state=1; break; case 1: if (d.Now()) ( action2(); d.Set(500000UL) ; state=2; ) break; case 2: if (d.Now()) ( action3(); d.Stop(); state=0; ) break; ) ...
The Set(interval) method sets a new interval and returns the old one. You can just look at the interval using the Get() method;

Stop() stops processing and Now() always returns false.

Start() resumes and Now() starts working as usual.

If you need to slow down the counting of time, but not stop it completely, then there is the Wait () method. For example, if LED 12 is blinking, and when the button is pressed, it does not blink, just add the following code to loop() in the example with two diodes above:

If (digitalRead(9)) led12.Wait(); ...
So, with a high signal level at leg 9, the diode at 12 will not blink and will continue when 0 appears there.

When a screen is drawn by such a “timer”, for example, and buttons are processed in parallel, then it may be necessary to redraw the screen or part immediately after pressing the button, and not wait for the interval to end. The Reset() method is used for this, after which the next call to Now() will return true. For instance:

SmartDelay display(1000000UL); void loop() ( if (btClick()) display.Reset(); // clicked on the button, we need to draw the screen. if (display.Now()) screenRedraw(); // draw the screen. )
From the bugs, I only see that the overflow of the microsecond counter is not taken into account, but otherwise, yes, we need to clean up the code. I don't like the way Reset() is done while thinking.

If you want to “pause” the microcontroller, then you just need to enter the delay instruction at the right place in the program. But it becomes a real hurdle when you try to do other things, like track a button click. In this case, it is necessary to implement a kind of multitasking.



Yes, it will add a few lines of code to your programs, but this in turn will make you a more experienced programmer and increase the potential of your Arduino. To do this, you just need to learn how to use the millis function.


It should be understood that the delay function pauses the execution of your Arduino program, making it unable to do anything else during this period of time. Instead of pausing our entire program for a certain amount of time, we will learn how to count how much time has passed before the completion of the action. This, of course, is done with the millis() function and a few friend variables to store our data. To make things easy to understand, we'll start with the first tutorial sketch called "Blink", but in this case we'll blink the LED without delay.


The beginning of this program is the same as any other standard Arduino program. First comes the declaration of all the necessary variables and I / O lines (for example, line 13 for the LED). Here we also need type variable integer to store the current state of the LED. It will be set to LOW because the initial state of the LED is off. Then we declare the variable "previousMillis" of type "unsigned long". Unlike "int", long unsigned variables are 32 bits, this is needed for variables whose value can become very large - as a potential time we can wait until an action is taken. The variable previousMillis will be used to store the time when the LED was last blinking. There is also a "const long" type, it is also 32-bit, but it does not change the value, that is, it is for constants (in this case, for the interval constant). We'll set it to 1000 and use it as the pause time, measured in milliseconds.


const int ledPin = 13; // define the output of the LED // Variables will change: int ledState = LOW; // ledState is used to determine the state of the LED unsigned long previousMillis = 0; // store the time when the LED was last updated // constants won't change: const long interval = 1000; // flashing interval in milliseconds void setup() ( // setting output line 13 pinMode(ledPin, OUTPUT); )

Then we go into an infinite loop. Remember that instead of a delay, we want to count how much time has passed since our last blink, in our case 1000ms. If the specified time has passed, it's time to change the state of our LED.


First, we'll set the unsigned long "currentMillis" to "millis()", which specifies the current time in milliseconds. This will help us find out if the difference between the current time and the previous time has exceeded 1000ms. To do this, we say, "If the current time minus the previous time our LED blinked is greater than or equal to our assigned value of 1000ms, store the last blink time as the previous one." This will help us remember how much time has passed since the last blink on the next iteration of the loop. Then, if the LED state is LOW, make it HIGH, otherwise make it LOW. Then use the digitalWrite command to print the current state to the LED.


void loop() ( unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) ( // store the time of the last LED state change previousMillis = currentMillis; // if the LED is off, turn it on, and vice versa if (ledState == LOW) ( ledState = HIGH; ) else ( ledState = LOW; ) // LED output digitalWrite(ledPin, ledState); ) )

Hello Andrei. Your approach to transferring the baggage of knowledge and experience that you have accumulated is very interesting. It helps a lot in getting started. Well, I, starting to master arduino, have a desire to progress. Moreover, with outside help, I can do it faster. So: at first my task was to make a robot moving along the line. Did it - everything is fine. But further, providing him with additional options, he did not understand why he stopped responding correctly to the line. I came across this article and understood why.

Now I have a question for you: in the sketch below and ready, given the problems with delay, do I need to switch to millis wherever this function is present? If so, then I understand that almost the entire sketch will have to be redone? And it's not entirely clear how to use millis in measuring distance? Thank you.

//Robot with white line following function

// ********************* Set motor leads ********************* *

int MotorLeftSpeed ​​= 5; // Left (A) Motor SPEED - ENA

int MotorLeftForward = 4; // Left (A) motor FORWARD - IN1

int MotorLeftBack = 3; // Left (A) Motor BACK - IN2

int MotorRightForward = 8; // Right (B) Motor FORWARD - IN3

int MotorRightBack = 7; // Right (B) Motor BACK - IN4

int MotorRightSpeed ​​= 9; // Right (B) motor SPEED - ENB

// *********************Install the outputs of ultrasonic sensors************************* *

inttrigPinL = 14; // setting the output number of the left trig ultrasonic sensor

int echoPinL = 15; // setting the output number of the left echo ultrasonic sensor

inttrigPinC = 10; // setting the output number of the central trig ultrasonic sensor

int echoPinC = 11; // setting the output number of the central echo ultrasonic sensor

inttrigPinR = 12; // setting the output number of the right trig ultrasonic sensor

int echoPinR = 13; // setting the output number of the right echo ultrasonic sensor

// ********************* Setting the outputs of the line sensors *******************

const int LineSensorLeft = 19; // left line sensor input

const int LineSensorRight = 18; // input of the right line sensor

intSL; // left sensor status

intSR; // right sensor status

// ********************* Setting the output of light and sound alarms ****************

int Light = 2; // setting the light signal output number

int Zumm = 6; // set buzzer output number

int ledState = LOW; // set the state of the LED to this variable

long previousMillis = 0; // store the time of the last switching of the LED

long interval = 300; // interval between turning on / off the LED (0.3 seconds)

// ********************* Variable distance measurement by sensors***************

unsigned int impulseTimeL=0;

unsigned int impulseTimeC=0;

unsigned int impulseTimeR=0;

long distL=0; // distance measured by the left ultrasonic sensor

long distC=0; // distance measured by the central ultrasonic sensor

long distR=0; // distance measured by the right Uz sensor

// ******************************* SETUP *********** ********************

Serial.begin(9600); // start serial port (speed 9600)

//*************** Set motor contacts********************

pinMode(MotorRightBack, OUTPUT); // Right (B) Motor BACK

pinMode(MotorRightForward, OUTPUT); // Right (B) Motor FORWARD

pinMode(MotorLeftBack, OUTPUT); // Left (A) Motor BACK

pinMode(MotorLeftForward, OUTPUT); // Left (A) Motor FORWARD

delay(duration);

//*************** Set the strip sensor contacts******************

pinMode(LineSensorLeft, INPUT); // defining the pin of the left line sensor

pinMode(LineSensorRight, INPUT); // defining the pin of the right line sensor

// ***************Setting the output modes of ultrasonic sensors*************************

pinMode(trigPinL, OUTPUT); // setting the operating mode of the output of the left trig ultrasonic sensor

pinMode(echoPinL, INPUT); // setting the operating mode of the output of the left echo ultrasonic sensor

pinMode(trigPinC, OUTPUT); // setting the operating mode of the output of the central trig ultrasonic sensor

pinMode(echoPinC, INPUT); // setting the operating mode of the output of the central echo ultrasonic sensor

pinMode(trigPinR,OUTPUT); // setting the operating mode of the output of the right trig ultrasonic sensor

pinMode(echoPinR, INPUT); // setting the output mode of the output of the right echo ultrasonic sensor

// *************** Set contacts for light and sound alarms****************************** *****

pinMode(Zumm,OUTPUT); // setting the operation mode of the buzzer output

pinMode(Light,OUTPUT); // setting the operation mode of the light signaling output

// ******************** Basic movement commands ******************

void forward (int a, int sa) // FORWARD

analogWrite(MotorRightSpeed, sa);

analogWrite(MotorLeftSpeed, sa);

void right (int b, int sb) // ROTATE RIGHT (one side)

digitalWrite(MotorRightBack, LOW);

digitalWrite(MotorLeftBack, LOW);

digitalWrite(MotorLeftForward, HIGH);

analogWrite(MotorLeftSpeed, sb);

void left (int k, int sk) // LEFT TURN (one side)

digitalWrite(MotorRightBack, LOW);

digitalWrite(MotorRightForward, HIGH);

analogWrite(MotorRightSpeed, sk);

digitalWrite(MotorLeftBack, LOW);

void stopp(int f) // STOP

digitalWrite(MotorRightBack, LOW);

digitalWrite(MotorRightForward, LOW);

digitalWrite(MotorLeftBack, LOW);

digitalWrite(MotorLeftForward, LOW);

// ************************** Distance measurement******************** *

void izmdistL () // measurement of distance by the left ultrasonic sensor

digitalWrite(trigPinL, HIGH);

digitalWrite(trigPinL, LOW); // 10mS pulse to the trig output of the ultrasonic sensor for distance measurement

impulseTimeL = pulseIn(echoPinL, HIGH); // read distance from ultrasonic sensor

distL=impulseTimeL/58; // Convert to centimeters

void izmdistC () // distance measurement by the central ultrasonic sensor

digitalWrite(trigPinC, HIGH);

digitalWrite(trigPinC, LOW); // 10mS pulse to the trig output of the ultrasonic sensor for distance measurement

impulseTimeC = pulseIn(echoPinC, HIGH); // read distance from ultrasonic sensor

distC=impulseTimeC/58; // Convert to centimeters

void izmdistR () // distance measurement by the central ultrasonic sensor

digitalWrite(trigPinR, HIGH);

digitalWrite(trigPinR, LOW); // 10mS pulse to the trig output of the ultrasonic sensor for distance measurement

impulseTimeR = pulseIn(echoPinR, HIGH); // read distance from ultrasonic sensor

distR=impulseTimeR/58; // Convert to centimeters

// ******************************* LOOP *********** *********************

// ********************* LINE Follow Mode ********************* ***

// *********************light and sound signaling******************

tone(Zumm,900); // turn on the sound at 900 Hz

tone(Zumm,900); // turn on the sound at 800 Hz

unsigned long currentMillis = millis();

if (currentMillis - previousMillis > interval) // check if the required interval has passed, if it has passed then

previousMillis = currentMillis; // store last switch time

if (ledState == LOW) // if the LED is off, then we light it, and vice versa

ledState = HIGH;

digitalWrite(Light, ledState); // set output states to turn the LED on or off

// ************************** Distance measurement************************* **

Serial.println(distL);

Serial.println(distC);

Serial.println(distR);

if (distL>50 && distC>50 && distR>50) // if the measured distance is more than 50 centimeters - go

SL=digitalRead(LineSensorLeft); // read the signal from the left lane sensor

SR=digitalRead(LineSensorRight); // read the signal from the right lane sensor

// ************************* Following the black line ******************* ****

// ROBOT on the lane - we go straight

if (SL == LOW & SR == LOW) // WHITE - WHITE - go STRAIGHT

forward(10, 100);// FORWARD(time, speed)

// ROBOT starts to shift from the lane - we taxi

else if (SL == LOW & SR == HIGH) // BLACK - WHITE - turn LEFT

left (10, 100);// turn LEFT (time, speed)

else if (SL == HIGH & SR == LOW) // WHITE - BLACK - turn RIGHT

right (10, 100); // turn RIGHT (time, speed)

// FINISH - ROBOT sees a lane with both sensors

else if (SL == HIGH & SR == HIGH) // BLACK - BLACK - STOP

stopp(50); // STOP

else // if the measured distance is less than or equal to the minimum, stop

The millis() function allows you to count the time elapsed since the start current program. The function returns a value in the "unsigned long" format and allows you to count values ​​up to 50 days from the moment the program was launched. After this time, the countdown will start again. The following is an example of using the millis() function:

unsigned long time; void setup()( Serial.begin(9600); ) void loop()( Serial.print("Turn on time: "); time = millis(); // store time value Serial.println(time); // send information via serial port delay(1000); )

In the above example, every second information about the time that has passed since the program was started will be sent to the port monitor. Since time is measured in milliseconds, each subsequent value will differ by 1000. The reading accuracy depends on the stability of the Arduino quartz resonator.

micros() function

The micros() function is similar to the millis() function, the difference lies in the accuracy of the measurement. Using the micros() function, we get the time elapsed since the start of the current program in microseconds. The counter of counted microseconds will be reset after 70 minutes. The following is an example of using the micros() function:

unsigned long time; void setup()( Serial.begin(9600); ) void loop()( Serial.print("Time since start: "); time = micros(); Serial.print(time); Serial.println(" ms "); delay(1000); )

Just like in the example with the millis() function, here every second information about the measured time will be sent to the port monitor, the only difference is that in this case the time is measured in microseconds.

delay() function

The delay() function allows you to suspend the execution of the current program for the time specified in the parameter. The command syntax is as follows:

//commands delay(500); //delay for 0.5 sec //delay(1000); //delay for 1s

Time is specified in milliseconds (1 sec = 1000 ms). This parameter can be of type "unsigned long", which ranges from 0 to 4294967295. Below is an example of using the delay() command:

#define ledPin 13 void setup() ( pinMode(ledPin,13); ) void loop() ( digitalWrite(ledPin,HIGH); //turn on LED delay(500); //wait 500ms (0.5 sec) digitalWrite( ledPin,LOW); // turn off LED delay(1000); // wait 1000 ms (1 sec) )

In the example above, the LED turns on for 0.5 seconds, then goes off for 1 second, and so on until the Arduino is powered off.

delayMicroseconds() function

The delayMicroseconds() function is a variation of the delay() function. The difference lies in the number and accuracy of timing. The delay() function allows you to count the time with an accuracy of 1 millisecond, while delayMicroseconds() with an accuracy of 1 microsecond.

The value that can be specified in the parameter ranges from 0 to 16383. For longer time intervals, use the delay() function or use delayMicroseconds() multiple times.

#define outPin 8 void setup() ( pinMode(outPin, OUTPUT); // pin 8 as output ) void loop() ( digitalWrite(outPin, HIGH); // pin 8 high delayMicroseconds(50); // pause 50 microseconds digitalWrite(outPin, LOW); // pin 8 low delayMicroseconds(50); // pause 50 microseconds )

This example generates a square wave with a period of 100 microseconds and 50% padding.