0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何使用Arduino millis函数执行多任务处理

科技观察员 来源:circuitdigest 作者:Abhimanyu Pandit 2022-09-06 14:41 次阅读

多任务处理将计算机带入了一场革命,其中一个或多个程序可以同时运行,从而提高了效率、灵活性、适应性和生产力。在嵌入式系统中,微控制器还可以处理多任务并同时执行两个或多个任务,而不会停止当前指令。

在本教程中,我们将学习Arduino 如何使用 Arduino millis 函数执行多任务处理。通常在 Arduino 中使用delay()函数来执行LED 闪烁等周期性任务,但此 delay() 函数会暂停程序一段确定的时间,并且不允许执行其他操作。所以这篇文章解释了我们如何避免使用 delay() 函数并将其替换为 millis()以同时执行多个任务并使 Arduino 成为一个多任务控制器

什么是多任务处理?

多任务处理只是意味着同时执行多个任务或程序。几乎所有操作系统都具有多任务处理功能。这种操作系统被称为MOS(多任务操作系统)。MOS 可以是移动或桌面 PC 操作系统。计算机中多任务处理的一个很好的例子是,当用户同时运行电子邮件应用程序、互联网浏览器、媒体播放器、游戏时,如果用户不想使用该应用程序,如果不关闭,它就会在后台运行。最终用户同时使用所有这些应用程序,但操作系统采用这个概念有点不同。让我们讨论一下操作系统如何管理多任务。

pYYBAGMW60iAYtxhAABWB9UQiL8404.png

如图所示,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 的开/关状态。所以三个任务将同时执行。

poYBAGMW60KALgZpAATs0CDzeeE572.png

所需组件

Arduino UNO

三个 LED(任何颜色)

电阻(470、10k)

跳线

面包板

电路原理

演示使用Arduino Millis() 函数的电路图 非常简单,无需附加太多组件,如下所示。

poYBAGMW6zuABeMrAACZlx-F4jI138.png

为多任务处理编程 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
收藏 人收藏

    评论

    相关推荐

    关于多任务处理的问题

    本帖最后由 mr.pengyongche 于 2013-4-30 03:08 编辑 我用DSP6713BIOS做多任务处理,一个硬中断,一个软中断,一个任务任务优先级最低,作为
    发表于 07-10 16:11

    多任务编程多任务处理是指什么

    嵌入式Linux应用编程-多任务编程多任务处理是指用户可在同一时间内运行多个应用程序,每个正在执行的应用程序被称为一个任务。Linux就是一
    发表于 12-22 08:30

    任务裸机系统与多任务系统的区别在哪

    总体概述与任务裸机系统与多任务系统的区别任务的定义与切换裸机系统与多任务系统的区别裸机系统包括轮询系统,前后台系统轮询系统:在进行初始化过后,循环
    发表于 02-18 07:03

    什么是多任务系统?FreeRTOS任务与协程简析

    功能,初学者必须先掌握——任务的创建、删除、挂起和恢复等操作。本章节分为如下几部分:*什么是多任务系统*FreeRTOS任务与协程*初次使用*任务状态*
    发表于 02-18 06:38

    【晶心科技ADP-Corvette-T1开发板试用体验】corvette T1的多任务调度学习

    arduino多任务并行首先是声明自己的回调函数void t1Callback();void t2Callback();void t3Callback();然后创建任务也就是Task的
    发表于 07-10 23:36

    多任务系统中的堆栈使用

    现场及保存任务上下文(CPU寄存器)。在多任务处理开始之前执行的C代码将使用C栈。只有当应用程序的第一个任务开始运行时,栈才会从C栈中转移到
    发表于 01-05 13:52

    使用<cont.h>进行协作式多任务处理

    编程,但我是电子产品的菜鸟,所以我很高兴它运行得很好)并且想要某种协作的多任务处理框架来管理它。我真的很喜欢但我无法让它工作。即使在玩弄 Arduino 链接器参数时,我也没有运气。我查看了其他几个库,但我
    发表于 02-23 06:57

    求助,有没有人能够实现协作式多任务处理

    当前实现的“多任务Arduino-on-ESP8266 才能为二进制文件的 RF 部分提供服务;这意味着使用 delay(0) 或 yield() 或确保 loop() 函数完成时间不超过
    发表于 02-24 06:35

    51单片机多任务同时执行

    51单片机多任务同时执行。RTX51 Tiny是一种实时操作系统(RTOS),可以用它来建立多个任务函数)同时执行的应用(从宏观上看是同时
    发表于 08-03 16:58

    arduino平台millis()函数有什么作用?

    有关arduino平台millis()函数,有什么作用?
    发表于 10-20 06:30

    uCOS-II多任务编程设计

    uCOS-II多任务编程设计方法,在用户任务函数中,必须包含至少一次对操作系统服务函数的调用,否则比其优先级低的任务将无法得到运行机会,这是
    发表于 10-27 15:25 8次下载
    uCOS-II<b class='flag-5'>多任务</b>编程设计

    如何使用51单片机进行多任务机制及应用

    传统的单片机程序一般采用单任务机制,单任务系统具有简单直观、易于控制的优点。然而由于程序只能按顺序依次执行,缺乏灵活性,只能使用中断函数实时地处理
    发表于 04-15 18:24 8次下载
    如何使用51单片机进行<b class='flag-5'>多任务</b>机制及应用

    使用millis函数作为使用延迟的替代方法

    电子发烧友网站提供《使用millis函数作为使用延迟的替代方法.zip》资料免费下载
    发表于 10-18 09:50 0次下载
    使用<b class='flag-5'>millis</b><b class='flag-5'>函数</b>作为使用延迟的替代方法

    PicoSem:Arduino框架下的Raspberry多任务

    电子发烧友网站提供《PicoSem:Arduino框架下的Raspberry多任务.zip》资料免费下载
    发表于 06-13 09:21 1次下载
    PicoSem:<b class='flag-5'>Arduino</b>框架下的Raspberry<b class='flag-5'>多任务</b>

    Arduino上的多任务处理

    电子发烧友网站提供《Arduino上的多任务处理.zip》资料免费下载
    发表于 06-14 11:05 0次下载
    <b class='flag-5'>Arduino</b>上的<b class='flag-5'>多任务</b><b class='flag-5'>处理</b>