多任务处理将计算机带入了一场革命,其中一个或多个程序可以同时运行,从而提高了效率、灵活性、适应性和生产力。在嵌入式系统中,微控制器还可以处理多任务并同时执行两个或多个任务,而不会停止当前指令。
在本教程中,我们将学习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 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 (digitalRe
这样就完成了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 = 假;
}
}
}
这里还没有内容,您有什么问题吗?
电子电路资源推荐
- EMC基础知识---华为
来源:下载中心
- PCB封装库:Miscellaneous Connectors
来源:下载中心
- 是德两分钟导师系列课程第一季
来源:大学堂
- 单片机运行原理的一点点总结
来源:电路图
- 电子基础元器件
来源:大学堂
- 叠加原理:概念及其求解过程
来源:电路图
推荐帖子 最新更新时间:2024-11-21 18:50
- 想问一下关于嵌入式专业考研的问题
- 本人想考研,请问国内那些学校的嵌入式专业不错啊?不胜感激 想问一下关于嵌入式专业考研的问题 推荐你性价比比较高的吧 西安电子科技大学 成都电子科技大学 接分先!
- xuliqun嵌入式系统
- CCS的实例工程代码
- 文件太大,上传失败,有需要的朋友留下地址 总算上传成功,多谢论坛的帮助 CCS的实例工程代码 我想要,邮箱sally-shenyl@163.com 谢谢 Re: CCS的实例工程代码 我想要,你把内容发送到我的邮箱里好吗? gufeiyuhouchun@126.com 回复:C
- 1234嵌入式系统
- TI - 提升智能音箱的设计思路
- 智能音箱行业的竞争愈发激烈,各大制造商都在争相开发功能更强大、使用更方便的产品。当今的智能音箱拥有语音交互、人工智能、智能家居控制及多媒体播放等丰富的功能。要在竞争中占据先机,智能音箱的设计必须在总体系统成本、电池续航能力、热效应、回声消除和音质方面具备优势。 总体系
- 火辣西米秀模拟与混合信号
- 手动型和全自动型烧录器
- 本帖最后由 jameswangsynnex 于 2015-3-3 20:02 编辑 烧录一般是指使用刻录机把数据刻录(也称烧录)到刻录盘。现在有CD、DVD两种刻录盘,后者容量要比前者大的多,烧录就象COPY一样。把你电脑里的东西COPY在别的文件载体上,就象1.4寸的磁盘就
- ctguhl移动便携
- 内视镜相关的日本企业
- 2015 SONY 发布了 4K 内视镜 2017 日立预定 发布8K 另外 olympus,做胃镜的朋友都知道的 内视镜有待解决的问题蛮多的, 镜头只能越来越小,不能变大 拍的camera模块,要小---估计这两年不会一下子到5nm工艺 图像太暗,---需要新的技术出现
- 5525医疗电子
- 二极管发烫,该怎么解决
- 简单的mic口充电输入电源,并联接两个二极管,严重发烫,该怎么解决,有谁用过并联两个二极管的输入电路么 二极管发烫,该怎么解决 自顶 “二极管,严重发烫,该怎么解决” 先确定你的电路设计是否正确。 楼主,我觉得你基础欠缺,电路分析基础和模拟电子的书应该先翻个两三遍。 是不是二
- 一株草电源技术
- 可以发个Proteus8.6的安装文件我吗?
- 请教数组问题!!!
- 嵌入式软件开发之: 复位和初始化
- 关于调用目录下的文件
- 这些 怎么定义 state_machine rcvdat retval
- ce1的问题
- vxworks源代码问题
- AD16突然导入不了DDB文件
- ucos 编译的错误
- 路由器重新初始化问题!
- 压敏电阻应用注意事项
- ucos 消息队列 取消息数据的问题
- LCD课件资料
- 异形pcb制版时面积怎么算
- 【Follow me第二季第1期】入门任务、基础任务一:点灯
- F28335中的程序移植到F28M36系列中方便吗?
- 请教一下仿真的问题
- 平头哥RISC-V开发板 --STEP2--智能电压电流采集控制器设计准备篇1
- PC上如何仿真运行CE应用程序
- 测评汇总:《CMake构建实战:项目开发卷》