多任务处理将计算机带入了一场革命,其中一个或多个程序可以同时运行,从而提高了效率、灵活性、适应性和生产力。在嵌入式系统中,微控制器还可以处理多任务并同时执行两个或多个任务,而不会停止当前指令。
在本教程中,我们将学习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 = 假;
}
}
}
全部0条评论
快来发表一下你的评论吧 !