06-ESP8266 模拟多线程多任务处理

文章介绍了单片机如何在没有多核心处理器的情况下通过计时器实现多任务处理。通过创建计时器并在loop循环中判断时间间隔来调度任务,使得CPU时间得到更有效的利用。示例代码展示了如何使用这种方法控制LED闪烁和读取旋钮值。
摘要由CSDN通过智能技术生成

Author:teacherXue

一、什么是多线程

从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。

简单总结就是,他让我们感觉设备同时在处理很多事情,例如敲着代码听着歌。单线程模式的化,我们可能敲一下键盘,歌曲停一下。而且实际上,显示器显示本身也是一个任务,所以你不得不象在卡PPT一样的体验中操作设备。

  1. 单片机多任务问题

单片机系统没有对称多处理机,在没有多核心的情况下,貌似没有办法进行多线程处理。上一章节提到过,在旋钮控灯的代码中注释或者删掉之前的呼吸灯代码。你可以尝试重新让呼吸灯代码生效,就会发现,旋钮的控制变得非常的卡顿。

这是因为loop方法内部本身是单线程运行,呼吸灯在循环进行亮度逐步改变的过程中,是不会执行到后面的读取旋钮值的代码的。于是只能在完成一次明暗转换的过程后才能读取一次旋钮值。

  1. 榨干CPU的运算时间

ESP8266 等芯片虽然属于单片机系统,但象ESP8266这样的芯片,80MHZ运行频率已经赶上我最早的PC系统了,执行速度远远大于人的反应速度。所以很多情况下我们需要人为的让CPU运算闲置,例如前面代码中的delay操作。

如果完全利用主程序的loop循环方法,通过计时器在每次任务循环中判断程序执行耗时,来确定是否执行,就可以让cpu的尽可能得多运算时间被利用。通俗的说,就是只要我足够快,你就不知道我作弊。

Arduino开发框架中可以使用状态机,定时器,中断等来实现多任务,也可以使用Arduino封装好的库函数,比如TaskScheduler.h库函数。

二、定时器实现多任务处理

我们先使用较为简单的计时器来实现多任务并行操作,其核心原理就是在任务开始时进行计时,并在每次loop循环中判断是否满足等待时间延迟。满足再调度任务,不满足轮空即可,参考下图理解一下。

需要注意的是单片机运算能力还是有限,某些任务需要较高响应速度,不能一味的往上堆任务。在旋钮控灯的基础上,先让LED1闪烁起来吧(呼吸效果需要较多任务计时,先搞清楚基本原理)。

  1. 新建项目

1)创建项目Lamp_ task_v1.0并确定。
2)修改platformio.ini配置文件增加串口通讯波特率monitor_speed = 115200
  1. 代码实现

1)编辑main.cpp主文件,为旋钮控灯和闪烁控灯分别创建计时器。
unsigned long currentTime=0; //当前时间,每次loop时判断一次,因此一个即可
//闪灯计时器
unsigned long intervalTime1=1000; //等待间隔
unsigned long previousTime1 = millis();//注册开始时间
bool switch_led1=false;//灯光开关状态

//旋钮计时器
unsigned long intervalTime2=30;//等待间隔
unsigned long previousTime2 = millis();//注册开始时间
2)loop中先取当前时间
currentTime = millis();/
3)判断当前时间和计时器登记时间差是不是达到等待要求时间,达到则调用任务,同时,再重置计时器登记时间。两个任务都是这样处理。
void loop()
{
  currentTime = millis();//获得当前loop的时间
  //判断当前时间和计时器登记时间是不是超过等待时间间隔
  if(currentTime-previousTime1>=intervalTime1){
    switch_led1=!switch_led1;//改变灯光开关状态
    previousTime1=currentTime;//重新登记计时器1时间
  }
  //根据开关状态控制LED_1开关
  digitalWrite(LED_1, switch_led1?HIGH:LOW);

  //判断当前时间和计时器登记时间是不是超过等待时间间隔
  if(currentTime-previousTime2>=intervalTime2){
    knobValue=analogRead(analogInPin);
    // 读取模拟数值
    analogWrite(LED_2, map(knobValue, 15, 1008, 0, 255));
    // 打印串行监视器中的读数
    Serial.print("sensor = ");
    Serial.println(knobValue);
    previousTime2=currentTime;//重新登记计时器2时间
  }
}

4)完整代码

#include <Arduino.h>
#define analogInPin A0 // 模拟输入引脚A0
#define LED_1 D5       // D5白色LED引脚,本例中先不使用
#define LED_2 D6       // D6绿色LED引脚

unsigned long currentTime=0; //当前时间,每次loop时判断一次,因此一个即可
//闪灯计时器
unsigned long intervalTime1=1000; //等待间隔
unsigned long previousTime1 = millis();//注册开始时间
bool switch_led1=false;//灯光开关状态

//旋钮计时器
unsigned long intervalTime2=30;//等待间隔
unsigned long previousTime2 = millis();//注册开始时间

int knobValue = 0;
void setup()
{
  // put your setup code here, to run once:
  Serial.begin(115200); // 设置串口通信波特率
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
}

void loop()
{
  currentTime = millis();//获得当前loop的时间
  //判断当前时间和计时器登记时间是不是超过等待时间间隔
  if(currentTime-previousTime1>=intervalTime1){
    switch_led1=!switch_led1;//改变灯光开关状态
    previousTime1=currentTime;//重新登记计时器1时间
  }
  //根据开关状态控制LED_1开关
  digitalWrite(LED_1, switch_led1?HIGH:LOW);

  //判断当前时间和计时器登记时间是不是超过等待时间间隔
  if(currentTime-previousTime2>=intervalTime2){
    knobValue=analogRead(analogInPin);
    // 读取模拟数值
    analogWrite(LED_2, map(knobValue, 15, 1008, 0, 255));
    // 打印串行监视器中的读数
    Serial.print("sensor = ");
    Serial.println(knobValue);
    previousTime2=currentTime;//重新登记计时器2时间
  }
}
4)烧录代码成功后,控制旋钮观察两盏LED的状态。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盐池虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值