使用 millis() 函数在 Arduino 中进行多任务处理
Multitasking in Arduino using millis() function
我正在考虑用报废的汽车零件制作一辆 DIY 卡丁车越野车。作为电子产品,我计划使用我的 Arduino Uno 来控制基本上所有的东西。所以我只是制作了一个齿轮显示器,让我知道我目前使用的是哪个齿轮。还有一个简单的停车传感器。
如果我离物体太近,驻车传感器蜂鸣器会闪烁。而且我不能用 delay() 函数闪烁,因为如果我在它闪烁时尝试向上移动,输入将不会注册,因为延迟是一个阻塞函数。所以我尝试改用 millis,但似乎也没有注册。
这个概念很简单。我的变速杆两侧各有两个按钮。当我向上移动时,按钮被按下,因此 7 段显示器增加了一个。还有一个用于降档。
我还没有加倒档,但是当驻车传感器功能起作用时,我仍然有很长一段时间无法按下按钮。
我希望你们能理解我想说的。我刚开始在 Arduino 中编码,所以我没有足够的经验。我将 .ino 文件作为代码块保留。我试图在代码中解释一切。希望有人知道解决方案。提前致谢...
// Setting up the pins for 7-Segment-Display.
const int E = 13;
const int D = 12;
const int C = 11;
const int DP = 10;
const int G = 9;
const int F = 8;
const int A = 7;
const int B = 6;
// Setting up the counter for gear shifting.
int gearCount = 0;
// Setting up the button states and button pin for state-change detectors.
int buttonAddPin = 5;
int buttonAddState = 0;
int previousButtonAddState = 0;
// And this is for downshifting.
int buttonSubstractPin = 4;
int buttonSubstractState = 0;
int previousButtonSubstractState = 0;
// Adding buzzer for parking sensor.
const int buzzerPin = 3;
int buzzerTone = 0;
// Adding parking sensor.
const int trigPin = 2;
const int echoPin = 1;
long duration;
int distance;
// Adding non-delay blinker with millis for buzzer to blink when sensor detects an object.
unsigned long previousTime = 0;
/*---------------------------------- FUNCTIONS FOR SHIFTING -------------------------------------------------*/
// "zero" function is the series of commands for displayıng 0 on the 7-Segment corresponding to neutral gear.
void zero()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
digitalWrite(DP, HIGH);
}
// "one" function is the series of commands for displaying 1 on the 7-Segment-Display corresponding to 1st gear.
void one()
{
digitalWrite(A, HIGH);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
digitalWrite(DP, HIGH);
}
// "two" function is the series of commands for displaying 2 on the 7-Segment-Display corresponding to 2nd gear.
void two()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, HIGH);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, HIGH);
digitalWrite(G, LOW);
digitalWrite(DP, HIGH);
}
// "three" function is the series of commands for displaying 3 on the 7-Segment-Display corresponding to 3rd gear.
void three()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, LOW);
digitalWrite(DP, HIGH);
}
// And now the main function that we are going to call in "void loop" to determine which gear we are on.
void gearDisplay()
{
buttonAddState = digitalRead(buttonAddPin); // And now the button state change detector for shifting up is ready. As you can see when I push the button
// program adds 1 to gearCount variable. So now gearCount is 1. Therefore the 7-Segment-Display will show
if(buttonAddState != previousButtonAddState){ // 1 on it. We will do this a bit later.
if(buttonAddState == HIGH){
gearCount++;
}
delay(50);
}
previousButtonAddState = buttonAddState;
// Now for downshifting.
buttonSubstractState = digitalRead(buttonSubstractPin);
if(buttonSubstractState != previousButtonSubstractState) {
if(buttonSubstractState == HIGH){
gearCount--;
}
delay(50);
}
previousButtonSubstractState = buttonSubstractState;
// Now we will call one of the 7-Segment-Display number functions according to the gearCount variable.
if(gearCount == 0){
zero();
}
else if(gearCount == 1){
one();
}
else if(gearCount == 2){
two();
}
else if(gearCount == 3){
three();
}
else if(gearCount > 3){ // Just making sure if we press buttons accidentally more than enough, it sets gearCount back to closest gear available.
gearCount--;
}
else if(gearCount < 0){ // Same with this one.
gearCount++;
}
}
// That was al for the gear panel. Now the hard part. The parking sensor.
/*---------------------------------------------- SHIFTING DONE ----------------------------------------------------*/
/*--------------------------------------------- PARKING SENSOR ------------------------------------------------------*/
void parkingSensor()
{
digitalWrite(trigPin, LOW); // Resetting trigPin
delayMicroseconds(2); // Delaying to prevent any issiues. ( I saw this online, most people do it like this so I also did.)
digitalWrite(trigPin, HIGH); // Creating a short soundwave.
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH); // Detecting the time that passes for the soundwave to reach back to the receiver.
distance = duration*0.034/2; // Simple math for calculating the distance between the obstacle and sensor.
unsigned long currentTime = millis(); // Store the current time for blinking the buzzers.
if((distance > 0) && (distance < 31)){ // If the distance is in between 0-31 buzz non-stop.
buzzerTone = 1000;
tone(buzzerPin, buzzerTone);
}
else if((distance > 30) && (distance < 76)){ // If the distance is between 30-76 buzz every 100 miliseconds.
if(currentTime - previousTime >= 100){
previousTime = currentTime;
if(buzzerTone == 1000){
buzzerTone = 0;
}
else {
buzzerTone = 1000;
}
tone(buzzerPin, buzzerTone);
}
}
else if((distance > 75) && (distance < 101)){ // If the distance is between 75-101 buzz every 300 miliseconds.
if(currentTime - previousTime >= 300){
previousTime = currentTime;
if(buzzerTone == 1000){
buzzerTone = 0;
}
else {
buzzerTone = 1000;
}
tone(buzzerPin, buzzerTone);
}
}
else if(distance > 100){ // If the distance is more than 100 don't buzz.
buzzerTone = 0;
tone(buzzerPin, buzzerTone);
}
}
/*------------------------------- PARKING SENSOR DONE -----------------------------------*/
// And this was, I guess, all I had to do but unfortunately it's not working...
void setup() {
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(E, OUTPUT);
pinMode(F, OUTPUT);
pinMode(G, OUTPUT);
pinMode(DP, OUTPUT);
pinMode(buttonAddPin, INPUT);
pinMode(buttonSubstractPin, INPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
void loop() {
gearDisplay();
parkingSensor();
}
编辑:原来pulseIn()
的默认持续时间是1秒。它也是一个阻止按钮在每个循环上注册输入 1 秒的阻止功能。将其更改为较小的值(例如在我的情况下为 10 毫秒)就足够了并解决了问题。所有功劳都归功于解决了这个问题的@CherryDT。非常感谢。
顺便说一下,代码在 10 毫秒内应该是这样的:
pulseIn(echoPin, HIGH, 10000)
// 注意 pulseIn()
中的持续时间以微秒为单位。
编辑: 原来我误解了这个问题。不管怎样,把这个答案留在这里,因为它回答了 "how to blink without blocking" 这可能也很有用。
主要问题的答案是:您正在使用 pulseIn
,它也会阻塞直到收到脉冲。 pulseIn
的默认超时为一秒,因此如果您的停车传感器视野内没有物体,您每次都会阻塞 1 秒!由于无论如何您可能只对相当小的距离感兴趣,因此您可以将超时设置为 10ms (10000us):
pulseIn(echoPin, HIGH, 10000)
如果这不符合您的要求或仍然延迟太多,请考虑编写您自己的代码,而不是在不阻塞的情况下测量时间,同时处理按钮按下。请参阅 this answer 那里有人遇到了完全相同的问题并以这种方式解决了它。
基本上,您可以这样做:
unsigned long pulseTimer = 0;
int lastEchoState = HIGH; // Initially HIGH so that the actual LOW triggers a "change"
void loop () {
int currentEchoState = digitalRead(echoPin);
// To set a timeout of the pulse, simulate a pulse if already waiting for too long
if (lastEchoState == LOW && micros() - pulseTimer > 1000000ul) {
currentEchoState = HIGH;
}
if (currentEchoState != lastEchoState) {
lastEchoState = currentEchoState;
if (currentEchoState == LOW) {
// Echo pulse over (or it's the initial loop iteration)
// Start counting time until echo
// We do this before sending the trigger because pulseIn would
// normally measure until the pulse is *over*, but for simplicity
// we measure until the pulse *starts*, so this implicitly shifts
// the result by our pulse duration, as trigger and echo pulse are
// usually of the same length
pulseTimer = micros();
// Send new trigger pulse
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
} else {
// Pulse came in, measure time
unsigned long pulseDelayTime = micros() - pulseTimer;
// **** DO SOMETHING WITH pulseDelayTime HERE! ****
}
}
// **** DO YOUR REGULAR BUTTON PROCESSING HERE! ****
}
您可以通过将 LED 输出设置为来实现闪烁:
(millis() / 500) % 2
...在每次循环迭代时使其闪烁*。
这基本上是 500 毫秒的低电平和 500 毫秒的高电平,因为将 millis()
除以 500(不会有任何小数部分,因为我们在这里处于整数世界)会给出一个递增的数字500 毫秒,% 2
取模 2,即奇数为 1
,偶数为 0
。 1
和 0
与 Arduino 的 HIGH
和 LOW
相同。
示例:
+----------+--------------+------------------+
| millis() | millis()/500 | (millis()/500)%2 |
+----------+--------------+------------------+
| ... | | |
| 1499 | 2 | 0 (LOW) |
| 1500 | 3 | 1 (HIGH) |
| 1501 | 3 | 1 (HIGH) |
| ... | | |
| 1999 | 3 | 1 (HIGH) |
| 2000 | 4 | 0 (LOW) |
| 2001 | 4 | 0 (LOW) |
| ... | | |
| 2499 | 4 | 0 (LOW) |
| 2500 | 5 | 1 (HIGH) |
| 2501 | 5 | 1 (HIGH) |
| ... | | |
+----------+--------------+------------------+
顺便说一句,很酷的项目。
*:如果你想从中获得更多的性能,你可以将最后一个引脚状态保存在一个单独的变量中,并且只在它改变时才执行 digitalWrite
。那是因为 digitalWrite
实际上非常慢,因此您可以避免每次 调用它 除非需要实际更改。
我正在考虑用报废的汽车零件制作一辆 DIY 卡丁车越野车。作为电子产品,我计划使用我的 Arduino Uno 来控制基本上所有的东西。所以我只是制作了一个齿轮显示器,让我知道我目前使用的是哪个齿轮。还有一个简单的停车传感器。
如果我离物体太近,驻车传感器蜂鸣器会闪烁。而且我不能用 delay() 函数闪烁,因为如果我在它闪烁时尝试向上移动,输入将不会注册,因为延迟是一个阻塞函数。所以我尝试改用 millis,但似乎也没有注册。
这个概念很简单。我的变速杆两侧各有两个按钮。当我向上移动时,按钮被按下,因此 7 段显示器增加了一个。还有一个用于降档。
我还没有加倒档,但是当驻车传感器功能起作用时,我仍然有很长一段时间无法按下按钮。
我希望你们能理解我想说的。我刚开始在 Arduino 中编码,所以我没有足够的经验。我将 .ino 文件作为代码块保留。我试图在代码中解释一切。希望有人知道解决方案。提前致谢...
// Setting up the pins for 7-Segment-Display.
const int E = 13;
const int D = 12;
const int C = 11;
const int DP = 10;
const int G = 9;
const int F = 8;
const int A = 7;
const int B = 6;
// Setting up the counter for gear shifting.
int gearCount = 0;
// Setting up the button states and button pin for state-change detectors.
int buttonAddPin = 5;
int buttonAddState = 0;
int previousButtonAddState = 0;
// And this is for downshifting.
int buttonSubstractPin = 4;
int buttonSubstractState = 0;
int previousButtonSubstractState = 0;
// Adding buzzer for parking sensor.
const int buzzerPin = 3;
int buzzerTone = 0;
// Adding parking sensor.
const int trigPin = 2;
const int echoPin = 1;
long duration;
int distance;
// Adding non-delay blinker with millis for buzzer to blink when sensor detects an object.
unsigned long previousTime = 0;
/*---------------------------------- FUNCTIONS FOR SHIFTING -------------------------------------------------*/
// "zero" function is the series of commands for displayıng 0 on the 7-Segment corresponding to neutral gear.
void zero()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
digitalWrite(DP, HIGH);
}
// "one" function is the series of commands for displaying 1 on the 7-Segment-Display corresponding to 1st gear.
void one()
{
digitalWrite(A, HIGH);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
digitalWrite(DP, HIGH);
}
// "two" function is the series of commands for displaying 2 on the 7-Segment-Display corresponding to 2nd gear.
void two()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, HIGH);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, HIGH);
digitalWrite(G, LOW);
digitalWrite(DP, HIGH);
}
// "three" function is the series of commands for displaying 3 on the 7-Segment-Display corresponding to 3rd gear.
void three()
{
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, LOW);
digitalWrite(DP, HIGH);
}
// And now the main function that we are going to call in "void loop" to determine which gear we are on.
void gearDisplay()
{
buttonAddState = digitalRead(buttonAddPin); // And now the button state change detector for shifting up is ready. As you can see when I push the button
// program adds 1 to gearCount variable. So now gearCount is 1. Therefore the 7-Segment-Display will show
if(buttonAddState != previousButtonAddState){ // 1 on it. We will do this a bit later.
if(buttonAddState == HIGH){
gearCount++;
}
delay(50);
}
previousButtonAddState = buttonAddState;
// Now for downshifting.
buttonSubstractState = digitalRead(buttonSubstractPin);
if(buttonSubstractState != previousButtonSubstractState) {
if(buttonSubstractState == HIGH){
gearCount--;
}
delay(50);
}
previousButtonSubstractState = buttonSubstractState;
// Now we will call one of the 7-Segment-Display number functions according to the gearCount variable.
if(gearCount == 0){
zero();
}
else if(gearCount == 1){
one();
}
else if(gearCount == 2){
two();
}
else if(gearCount == 3){
three();
}
else if(gearCount > 3){ // Just making sure if we press buttons accidentally more than enough, it sets gearCount back to closest gear available.
gearCount--;
}
else if(gearCount < 0){ // Same with this one.
gearCount++;
}
}
// That was al for the gear panel. Now the hard part. The parking sensor.
/*---------------------------------------------- SHIFTING DONE ----------------------------------------------------*/
/*--------------------------------------------- PARKING SENSOR ------------------------------------------------------*/
void parkingSensor()
{
digitalWrite(trigPin, LOW); // Resetting trigPin
delayMicroseconds(2); // Delaying to prevent any issiues. ( I saw this online, most people do it like this so I also did.)
digitalWrite(trigPin, HIGH); // Creating a short soundwave.
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH); // Detecting the time that passes for the soundwave to reach back to the receiver.
distance = duration*0.034/2; // Simple math for calculating the distance between the obstacle and sensor.
unsigned long currentTime = millis(); // Store the current time for blinking the buzzers.
if((distance > 0) && (distance < 31)){ // If the distance is in between 0-31 buzz non-stop.
buzzerTone = 1000;
tone(buzzerPin, buzzerTone);
}
else if((distance > 30) && (distance < 76)){ // If the distance is between 30-76 buzz every 100 miliseconds.
if(currentTime - previousTime >= 100){
previousTime = currentTime;
if(buzzerTone == 1000){
buzzerTone = 0;
}
else {
buzzerTone = 1000;
}
tone(buzzerPin, buzzerTone);
}
}
else if((distance > 75) && (distance < 101)){ // If the distance is between 75-101 buzz every 300 miliseconds.
if(currentTime - previousTime >= 300){
previousTime = currentTime;
if(buzzerTone == 1000){
buzzerTone = 0;
}
else {
buzzerTone = 1000;
}
tone(buzzerPin, buzzerTone);
}
}
else if(distance > 100){ // If the distance is more than 100 don't buzz.
buzzerTone = 0;
tone(buzzerPin, buzzerTone);
}
}
/*------------------------------- PARKING SENSOR DONE -----------------------------------*/
// And this was, I guess, all I had to do but unfortunately it's not working...
void setup() {
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(E, OUTPUT);
pinMode(F, OUTPUT);
pinMode(G, OUTPUT);
pinMode(DP, OUTPUT);
pinMode(buttonAddPin, INPUT);
pinMode(buttonSubstractPin, INPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
void loop() {
gearDisplay();
parkingSensor();
}
编辑:原来pulseIn()
的默认持续时间是1秒。它也是一个阻止按钮在每个循环上注册输入 1 秒的阻止功能。将其更改为较小的值(例如在我的情况下为 10 毫秒)就足够了并解决了问题。所有功劳都归功于解决了这个问题的@CherryDT。非常感谢。
顺便说一下,代码在 10 毫秒内应该是这样的:
pulseIn(echoPin, HIGH, 10000)
// 注意 pulseIn()
中的持续时间以微秒为单位。
编辑: 原来我误解了这个问题。不管怎样,把这个答案留在这里,因为它回答了 "how to blink without blocking" 这可能也很有用。
主要问题的答案是:您正在使用 pulseIn
,它也会阻塞直到收到脉冲。 pulseIn
的默认超时为一秒,因此如果您的停车传感器视野内没有物体,您每次都会阻塞 1 秒!由于无论如何您可能只对相当小的距离感兴趣,因此您可以将超时设置为 10ms (10000us):
pulseIn(echoPin, HIGH, 10000)
如果这不符合您的要求或仍然延迟太多,请考虑编写您自己的代码,而不是在不阻塞的情况下测量时间,同时处理按钮按下。请参阅 this answer 那里有人遇到了完全相同的问题并以这种方式解决了它。
基本上,您可以这样做:
unsigned long pulseTimer = 0;
int lastEchoState = HIGH; // Initially HIGH so that the actual LOW triggers a "change"
void loop () {
int currentEchoState = digitalRead(echoPin);
// To set a timeout of the pulse, simulate a pulse if already waiting for too long
if (lastEchoState == LOW && micros() - pulseTimer > 1000000ul) {
currentEchoState = HIGH;
}
if (currentEchoState != lastEchoState) {
lastEchoState = currentEchoState;
if (currentEchoState == LOW) {
// Echo pulse over (or it's the initial loop iteration)
// Start counting time until echo
// We do this before sending the trigger because pulseIn would
// normally measure until the pulse is *over*, but for simplicity
// we measure until the pulse *starts*, so this implicitly shifts
// the result by our pulse duration, as trigger and echo pulse are
// usually of the same length
pulseTimer = micros();
// Send new trigger pulse
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
} else {
// Pulse came in, measure time
unsigned long pulseDelayTime = micros() - pulseTimer;
// **** DO SOMETHING WITH pulseDelayTime HERE! ****
}
}
// **** DO YOUR REGULAR BUTTON PROCESSING HERE! ****
}
您可以通过将 LED 输出设置为来实现闪烁:
(millis() / 500) % 2
...在每次循环迭代时使其闪烁*。
这基本上是 500 毫秒的低电平和 500 毫秒的高电平,因为将 millis()
除以 500(不会有任何小数部分,因为我们在这里处于整数世界)会给出一个递增的数字500 毫秒,% 2
取模 2,即奇数为 1
,偶数为 0
。 1
和 0
与 Arduino 的 HIGH
和 LOW
相同。
示例:
+----------+--------------+------------------+
| millis() | millis()/500 | (millis()/500)%2 |
+----------+--------------+------------------+
| ... | | |
| 1499 | 2 | 0 (LOW) |
| 1500 | 3 | 1 (HIGH) |
| 1501 | 3 | 1 (HIGH) |
| ... | | |
| 1999 | 3 | 1 (HIGH) |
| 2000 | 4 | 0 (LOW) |
| 2001 | 4 | 0 (LOW) |
| ... | | |
| 2499 | 4 | 0 (LOW) |
| 2500 | 5 | 1 (HIGH) |
| 2501 | 5 | 1 (HIGH) |
| ... | | |
+----------+--------------+------------------+
顺便说一句,很酷的项目。
*:如果你想从中获得更多的性能,你可以将最后一个引脚状态保存在一个单独的变量中,并且只在它改变时才执行 digitalWrite
。那是因为 digitalWrite
实际上非常慢,因此您可以避免每次 调用它 除非需要实际更改。