前言
本章节是如何使用Arduino 的多线程技术。截止目前,Arduino 的性能很棒,但是如果我们想在不同的时间间隔中,完成两件、多件的任务呢? 此时,我们就需要使用一种类多线程技术来处理项目。
Arduino 的多线程 \ 类多线程?
为了正确理解多线程技术和Arduino 之间的关系,我们需要理解为什么它不是真正的多线程。
在计算机CPU上,多线程应用程序是两个独立的进程,它们在CPU的不同核心上彼此并行工作。这些过程相互作用以共同完成工作,但不一定按照你的假设均匀地分割工作。通常有一个主进程/“线程”,它充当其他线程的管理器,然后是它管理的一个或多个工作线程,每个线程可以执行特定的任务。一个很好的例子是Chrome浏览器。Chrome是所有网页标签(线程)的管理者,但由于chrome是多线程的,因此每个标签都是其自己的小程序。这意味着,如果你有多个核心来分配每个选项卡,它不仅可以更快地运行,还有其他好处,例如当一个选项卡崩溃时不会崩溃整个浏览器。
我们经常使用的Arduino UNO 的微处理器芯片Atmel ATMega328p ,运行速度为16Mhz 。它能够达到20Mhz ,但是会被拨回来,所以他不会搞乱像将数据写入内存(或者Boom,烧掉)。16Mhz 意味着每一秒, 你的Arduino正在处理16 000 000 个周期,也就是做1600万件工作。这些不是代码行(速度非常快),Arduino相对较慢。例如将数据移入和移出寄存器,这些是处理器指令。
严格意义上,Arduino 虽然可以实现多线程的效果,但是它只是类多线程技术。
可实现 类多线程的库
以下笔者总结了3 种常见的库,并提供了下载链接 \ 文档地址 \ 使用示例。
Metro 库
GitHub地址: https://github.com/thomasfredericks/Metro-Arduino-Wiring
示例:
/*
Metro 库, 可作为类多线程来使用
*/
#include <Metro.h>
const int blinkPin1 = 13; //定义 LED 引脚13
const int blinkPin2 = 12; //定义 LED 引脚12
boolean blink1State = false; //定义 blink1State 为false状态
boolean blink2State = false; //定义 blink2State 为false状态
Metro blink1Metro = Metro(100); //把 blink1Metro 实例化 Metro 对象 ,并设置间隔时间
Metro blink2Metro = Metro(1000); //把 blink2Metro 实例化 Metro 对象 ,并设置间隔时间
void setup()
{
pinMode(blinkPin1, OUTPUT); //设置blinkPin1 为输出模式
pinMode(blinkPin2, OUTPUT); //设置blinkPin2 为输出模式
}
void loop()
{
if (blink1Metro.check()) { //检查blink1Metro间隔(我的理解是计时器每隔100毫秒就会返回true,就执行以下程序)
blink1State = !blink1State; //反转blink1State的逻辑真或假(false or true)
digitalWrite(blinkPin1, blink1State); //数字引脚,设置为blink1State的状态
}
if (blink2Metro.check()) {
blink2State = !blink2State;
digitalWrite(blinkPin2, blink2State);
}
}
参考函数:
void reset() 重新启动/重置。
Metro(unsigned long interval) 实例化一个 Metro 对象,设置间隔时间(单位为毫秒)。
Chrono 库
GitHub地址: https://github.com/SofaPirate/Chrono
注: 此为Metro库的更新版本。
示例:
#include <Chrono.h>
int ledPinA = 13;
int ledPinB = 9;
int ledStateA = HIGH;
int ledStateB = HIGH;
Chrono chronoA;
Chrono chronoB;
bool isStop = false;
void setup()
{
Serial.begin(9600);
pinMode(ledPinA,OUTPUT);
digitalWrite(ledPinA,ledStateA);
pinMode(ledPinB,OUTPUT);
digitalWrite(ledPinB,ledStateB);
}
void loop()
{
// 使用Chrono作为节拍器,间隔为250毫秒:
if ( chronoA.hasPassed(250) )
{
// 如果自启动以来已通过250ms,则返回true
chronoA.restart(); // 重新启动crono,以便稍后再次触发
ledStateA = !ledStateA; // 将状态从0切换到1或从1切换到0
digitalWrite(ledPinA,ledStateA);
}
// 使用Chrono作为节拍器,间隔为125毫秒:
if ( chronoB.hasPassed(125) )
{
// 如果自启动以来已经超过125毫秒,则返回true
chronoB.restart(); // 重新启动crono,以便稍后再次触发
ledStateB = !ledStateB;
digitalWrite(ledPinB,ledStateB);
}
// 向串口发送指令'o', \ 'k', 可以查看相应的输出
if(Serial.read() == 'o')
{
Serial.println("Pin_9 is stop... ...");
chronoB.stop();
}
if(Serial.read() == 'k')
{
Serial.println("Pin_9 is start... ...");
chronoB.restart();
}
}
SCoop 库
使用请在GitHub搜索: SCoop arduino
修改版: https://github.com/Varsion/SCoop-Arduino
官方版: https://github.com/fabriceo/SCoop
示例:
格式一
#include <SCoop.h>//引入头文件
defineTask(Task1);//定义线程一
void Task1::setup()
{
pinMode(13, OUTPUT);
//多线程的setup
}
void Task1::loop()
{
digitalWrite(13, HIGH);
sleep(1000);
digitalWrite(13, LOW);
sleep(1000);
//多线程的loop
}
void setup() {
mySCoop.start();
}//形式setup
void loop()
{
yield();
}//形式loop
格式二
#include <SCoop.h>//引入头文件
defineTaskLoop(Task2)//快速定义 省去setup
{
digitalWrite(12, HIGH);
sleep(100);
digitalWrite(12, LOW);
sleep(100);}
void setup() {
mySCoop.start();
pinMode(12, OUTPUT);
}
void loop()
{
yield();
}