多任务处理将计算机带入了一场革命,其中一个或多个程序可以同时运行,从而提高了效率、灵活性、适应性和生产力。在嵌入式系统中,微控制器还可以处理多任务并同时执行两个或多个任务,而不会停止当前指令。
在本教程中,我们将学习Arduino 如何使用 Arduino millis 函数执行多任务处理。通常在 Arduino 中使用delay()函数来执行LED 闪烁等周期性任务,但此 delay() 函数会暂停程序一段确定的时间,并且不允许执行其他操作。所以这篇文章解释了我们如何避免使用 delay() 函数并将其替换为 millis()以同时执行多个任务并使 Arduino 成为一个多任务控制器。
什么是多任务处理?
多任务处理只是意味着同时执行多个任务或程序。几乎所有操作系统都具有多任务处理功能。这种操作系统被称为MOS(多任务操作系统)。MOS 可以是移动或桌面 PC 操作系统。计算机中多任务处理的一个很好的例子是,当用户同时运行电子邮件应用程序、互联网浏览器、媒体播放器、游戏时,如果用户不想使用该应用程序,如果不关闭,它就会在后台运行。最终用户同时使用所有这些应用程序,但操作系统采用这个概念有点不同。让我们讨论一下操作系统如何管理多任务。
如图所示,CPU 将时间分成三个相等的部分,并将每个部分分配给每个任务/应用程序。这就是大多数系统中多任务处理的方式。Arduino Multitasking的概念几乎相同,只是时间分布会有所不同。由于 Arduino 与笔记本电脑/手机/PC 相比以低频运行且 RAM 运行,因此分配给每个任务的时间也会有所不同。Arduino 还有一个广泛使用的delay()函数。但在开始之前,让我们讨论一下为什么我们不应该在任何项目中使用delay()函数。
为什么要使用 millis() ?
为了克服使用延迟带来的问题,开发人员应该使用millis()函数,一旦你习惯了它就很容易使用,它会使用100%的CPU性能而不会在执行指令时产生任何延迟。millis()是一个函数,它只返回自 Arduino 板开始运行当前程序而不冻结程序以来经过的毫秒数。大约 50 天后,该时间数将溢出(即回到零)。
就像Arduino有delayMicroseconds()一样,它也有micro版本的millis()作为micros()。micros 和 millis 之间的区别在于,micros() 将在大约 70 分钟后溢出,而 millis() 则为 50 天。因此,根据应用程序,您可以使用millis() 或micros()。
使用毫秒()而不是延迟():
要使用millis()进行计时和延迟,您需要记录并存储动作发生的时间以开始时间,然后每隔一段时间检查定义的时间是否已经过去。如前所述,将当前时间存储在一个变量中。
无符号长 currentMillis = millis();
我们需要另外两个变量来确定是否已经过了所需的时间。我们已将当前时间存储在currentMillis变量中,但我们还需要知道计时周期何时开始以及该周期有多长。因此声明了 Interval 和previousMillis。间隔将告诉我们时间延迟,previosMillis 将存储事件最后发生的时间。
unsigned long previousMillis; 无符号长周期 = 1000;
为了理解这一点,让我们以一个简单的闪烁 LED 为例。period = 1000 将告诉我们 LED 将闪烁 1 秒或 1000 毫秒。
常量 int ledPin = 4; // 连接的 LED 引脚号 int ledState = LOW; // 用于设置 LED 状态 unsigned long previousMillis = 0; //将存储上次 LED 闪烁的时间 const long period = 1000; // 以毫秒为单位闪烁的周期 void setup() { pinMode(ledPin, OUTPUT); // 将 ledpin 设置为输出 } void loop() { unsigned long currentMillis = millis(); // 存储当前时间 if (currentMillis - previousMillis >= period) { // 检查是否经过了 1000ms previousMillis = currentMillis; // 保存上次闪烁 LED 的时间 if (ledState == LOW) { // 如果 LED 关闭,则将其打开,反之亦然 ledState = HIGH; } 其他 { ledState = 低; } digitalWrite(ledPin, ledState);//设置带ledState的LED再次闪烁 } }
在这里,语句《if (currentMillis - previousMillis 》= period)》检查 1000 毫秒是否已过。如果 1000 毫秒过去了,则 LED 闪烁并再次进入相同状态。这种情况还在继续。就是这样,我们已经学会了使用毫秒而不是延迟。这样它就不会在特定的时间间隔内停止程序。
Arduino 中的中断与其他微控制器中的工作方式相同。Arduino UNO 板有两个独立的引脚,用于在 GPIO 引脚 2 和 3 上附加中断。我们在Arduino 中断教程中详细介绍了它,您可以在其中了解有关中断及其使用方法的更多信息。
在这里,我们将通过同时处理两个任务来展示 Arduino 多任务处理。这些任务将包括两个 LED 以不同的时间延迟闪烁以及一个按钮,该按钮将用于控制 LED 的开/关状态。所以三个任务将同时执行。
所需组件
Arduino UNO
三个 LED(任何颜色)
电阻(470、10k)
跳线
面包板
电路原理图
演示使用Arduino Millis() 函数的电路图 非常简单,无需附加太多组件,如下所示。
为多任务处理编程 Arduino UNO
为多任务编程 Arduino UNO 只需要上面解释的 millis() 工作原理背后的逻辑。建议在开始对 Arduino UNO 进行多任务编程之前,一次又一次地练习使用millis闪烁 LED ,以使逻辑清晰并让自己对 millis() 感到满意。在本教程中,中断还与 millis() 同时用于多任务处理。该按钮将是一个中断。因此,只要产生中断,即按下按钮,LED 就会切换到 ON 或 OFF 状态。
编程从声明连接 LED 和按钮的引脚号开始。
诠释 led1 = 6; 诠释 led2 = 7; int toggleLed = 5; int 按钮 = 2;
接下来我们编写一个变量来存储 LED 的状态以备将来使用。
诠释 ledState1 = 低; 诠释 ledState2 = 低;
正如上面闪烁示例中所解释的,period 和 previousmillis 的变量被声明为比较并为 LED 生成延迟。第一个 LED 每 1 秒闪烁一次,另一个 LED 在 200ms 后闪烁。
unsigned long previousMillis1 = 0; 常量长周期1 = 1000; unsigned long previousMillis2 = 0; 常量长周期2 = 200;
另一个毫秒函数将用于生成去抖动延迟,以避免多次按下按钮。将有与上述类似的方法。
int debouncePeriod = 20; int debounceMillis = 0;
这三个变量将用于存储按钮的状态为中断、切换 LED 和按钮状态。
bool buttonPushed = false; int ledChange = 低; 诠释最后状态=高;
定义引脚的动作,哪个引脚将作为 INPUT 或 OUTPUT 工作。
pinMode(led1,输出); pinMode(led2,输出); pinMode(toggleLed,输出); pinMode(按钮,输入);
现在通过附加中断与 ISR 和中断模式的定义来定义中断引脚。请注意,建议在声明attachInterrupt()函数时使用digitalPinToInterrupt(pin_number)将实际的数字引脚转换为特定的中断号。
attachInterrupt(digitalPinToInterrupt(pushButton), pushButton_ISR, CHANGE);
中断子程序被编写,它只会改变buttonPushed标志。需要注意的是,中断子程序要尽可能的短,所以尽量写,尽量减少多余的指令。
无效 pushButton_ISR() { buttonPushed = true; }
循环首先将毫秒值存储在 currentMillis 变量中,该变量将存储每次循环迭代时经过的时间值。
无符号长 currentMillis = millis();
多任务处理共有三个功能,1 秒闪烁一个 LED,200 毫秒闪烁第二个 LED,如果按下按钮,则关闭/打开 LED。所以我们将写三个部分来完成这个任务。
第一个是通过比较经过的毫秒数每 1 秒切换一次 LED 状态。
if (currentMillis - previousMillis1 >= period1) { previousMillis1 = currentMillis; 如果(ledState1 == 低){ ledState1 = 高; } 其他 { ledState1 = 低; } digitalWrite(led1, ledState1); }
类似地,第二次它通过比较经过的毫秒数每 200 毫秒后切换一次 LED。解释已经在本文前面进行了解释。
if (currentMillis - previousMillis2 >= period2) { previousMillis2 = currentMillis; 如果(ledState2 == 低){ ledState2 = 高; } 其他 { ledState2 = 低; } digitalWrite(led2, ledState2); }
最后,buttonPushed标志被监控,在产生 20ms 的去抖动延迟后,它只是切换 LED 的状态,对应于作为中断附加的按钮。
if (buttonPushed = true) // 检查是否调用了 ISR { if ((currentMillis - debounceMillis) > debouncePeriod && buttonPushed) // 产生 20ms 的去抖延迟以避免多次按下 { debounceMillis = currentMillis; // 保存最后的去抖动延迟时间 if (digitalRead(pushButton) == LOW && lastState == HIGH) // 按下按钮后改变LED { ledChange = ! 领导改变; digitalWrite(toggleLed, ledChange); 最后状态 = 低; } else if (digitalRead(pushButton) == HIGH && lastState == LOW) { lastState = HIGH; } buttonPushed = 假; } }
这样就完成了Arduino millis() 教程。请注意,为了习惯使用millis(),只需练习在其他一些应用程序中实现此逻辑即可。
/* 使用 Arduino millis() 函数进行多任务处理
作者:CircuitDigest (circuitdigest.com)
*/
诠释 led1 = 6; // led1 连接在引脚 6
int led2 = 7; // led1 连接在引脚 7
int toggleLed = 5; // 按钮控制的 LED 连接在引脚 5
int pushButton = 2; // 将按钮连接到引脚 2,这也是中断引脚
诠释 ledState1 = 低;// 判断 led1 和 led2 的状态
int ledState2 = LOW;
unsigned long previousMillis1 = 0; //存储上次 LED1 闪烁的时间
const long period1 = 1000; // led1 闪烁的时间,单位为 ms
unsigned long previousMillis2 = 0; //存储上次 LED2 闪烁的时间
const long period2 = 200; // led1 闪烁的时间,单位为 ms
int debouncePeriod = 20; // 20ms 的去抖动延迟
int debounceMillis = 0; // 类似于previousMillis
bool buttonPushed = false; // 中断例程按钮状态
int ledChange = LOW; // 跟踪 LED 状态 last
int lastState = HIGH; // 跟踪最后一个按钮状态
无效设置(){
pinMode(led1,输出);// 将引脚定义为输入或输出
pinMode(led2, OUTPUT);
pinMode(toggleLed,输出);
pinMode(按钮,输入);
attachInterrupt(digitalPinToInterrupt(pushButton), pushButton_ISR, CHANGE); // 使用中断 pin2
}
无效 pushButton_ISR()
{
buttonPushed = true; // ISR 应该尽可能短
}
void loop() {
unsigned long currentMillis = millis(); // 存储当前时间
if (currentMillis - previousMillis1 >= period1) { // 检查是否经过了 1000ms
previousMillis1 = currentMillis; // 保存上次闪烁 LED 的时间
if (ledState1 == LOW) { // 如果 LED 关闭,则将其打开,反之亦然
ledState1 = HIGH; //更改下一次迭代的 LED 状态
} else {
ledState1 = LOW;
}
digitalWrite(led1, ledState1); //用ledState设置LED再次闪烁
}
if (currentMillis - previousMillis2 >= period2) { // 检查是否经过了 1000ms
previousMillis2 = currentMillis; // 保存上次闪烁 LED 的时间
if (ledState2 == LOW) { // 如果 LED 关闭,则将其打开,反之亦然
ledState2 = HIGH;
} 其他 {
ledState2 = 低;
}
digitalWrite(led2, ledState2);//设置带ledState的LED再次闪烁
}
if (buttonPushed = true) // 检查是否调用了 ISR
{
if ((currentMillis - debounceMillis) > debouncePeriod && buttonPushed) // 产生 20ms 的去抖延迟以避免多次按下
{
debounceMillis = currentMillis; // 保存最后的去抖动延迟时间
if (digitalRead(pushButton) == LOW && lastState == HIGH) // 按下按钮后改变LED
{
ledChange = ! 领导改变;
digitalWrite(toggleLed, ledChange);
最后状态 = 低;
}
else if (digitalRead(pushButton) == HIGH && lastState == LOW)
{
lastState = HIGH;
}
buttonPushed = 假;
}
}
}
-
函数
+关注
关注
3文章
4304浏览量
62413 -
Arduino
+关注
关注
187文章
6463浏览量
186617 -
多任务处理
+关注
关注
0文章
2浏览量
4789
发布评论请先 登录
相关推荐
评论