亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

如何对 Arduino 进行多线程(原线程教程)

组件和用品

Arduino UNO
× 1
Sun Founder Blue 16x2 液晶显示器 (LCD)
× 1
面包板(通用)
当然还有电线。
× 1
旋转电位器(通用)
不确定电阻等级,大概 1Kohm 可以。
× 1

关于这个项目

该视频描述了您在萌芽的原型设计生涯中可能想做的事情,即哄骗单核 arduino 一次做 3 件事。在这种情况下,我们是:

  • 以恒定速率不中断地脉冲背光
  • 每秒递增一个整数并不间断地将其写入显示器
  • 每隔几秒轮换几条消息并不间断地将它们写入显示器

你看到标题了!

Protothreading 是一种在 Arduino 上执行通常是多任务操作的方法(一次或以不同的时间间隔执行两件或多件事情) .换句话说,它是“多线程的”!但是,Sparky,Arduino 是一个带有程序代码的单核芯片,所以真正的多线程是不可能的。为什么?原型线程有何不同?

“真正的”多线程与原始线程

了解原型线程 正确地,我们首先需要了解为什么它不是真正的多线程。

还记得英特尔在奔腾处理器上向我们出售这种新的“超线程”产品的那一天吗?不?你还没出生?是时候上历史课了,儿子!超线程是英特尔采用的一种技术,用于使处理器上的单核“表现”为两个核心,或者两个核心“表现”为 4 个核心,等等。但是为什么,这与 Arduino 有什么关系?答案是周期。

微控制器和 CPU 都以“周期”工作。他们做的速度有多快(一秒多少)就是时钟频率。您已经看过 CPU 的 Ghz 等级,您可能知道它与它的速度有关。 Ghz 越多越好,对吧?但为什么?因为这是处理器可以实现的每秒周期数(不会过热和着火 - 真的!)。

如果您是数据表迷,您可能知道 Arduino Uno 的微处理器芯片 Atmel ATMega328P 开箱即用,运行频率为 16Mhz。它能够达到 20Mhz,但会回拨,因此它不会搞砸诸如将数据写入内存(或者,你知道,着火)之类的事情。 16Mhz 意味着每秒钟,您的 Arduino 正在处理 16,000,000 个周期,也就是做 1600 万件工作。现在,这些不是代码行——那会非常快,而 Arduino 相对较慢。这些是处理器指令,例如将数据移入和移出寄存器。低于本概述的级别是相当技术性的,因此我将其作为练习留给读者,但这就是要点:)

所以,如果我们只能在最好的芯片着火之前在一个内核上运行这么快,我们会永远停留在那个速度上吗?这是我们可以做的最快的工作吗?事实证明,不!输入多核 CPU 和多线程。在计算机 CPU 上,多线程应用程序是两个独立的进程,它们在 CPU 的不同内核上相互并行工作。这些流程相互作用以共同完成工作,但不一定像您想象的那样平均分配工作。通常有一个主进程/“线程”作为其他线程的管理器,然后它管理一个或多个工作线程,每个线程可能执行特定的任务。一个很好的例子是 Chrome。 Chrome 是所有网页标签(线程)的管理器,但由于 chrome 是多线程的,每个标签都是它自己的小程序。这意味着如果您有多个内核来分布每个选项卡,它不仅可以运行得更快,而且还有其他好处,例如当一个选项卡崩溃时不会使整个浏览器崩溃。这是 Protothreading 不是多线程的第一个原因——我们只有一个内核可以在 MCU 上工作,所以传统的多线程是不可能的。我们只需要管理一个内核上的工作,但仍然可以同时做多项工作。我们需要原型线程。

好的,那么 Protothreading 有什么不同?

在某种程度上,原线程与我提到的超线程非常相似。超线程将模拟第二个核心,并通过假装是两个虚拟核心来真正地划分一个核心正在执行的工作。这是有效的,因为它们确实存在于同一个核心上,因此共享相同的资源空间。由于 arduino MCU 不支持超线程,因此我们无法在此处执行此操作。原线程是类似的,除了代替 CPU 周期和指令之外,我们可以通过草图执行的“循环”或“行”代码来分解工作。正如您可能想象的那样,如果我们做更多的事情,循环将花费更长的时间,因此每个项目的“每秒循环数”将大不相同。 protothreading 有不同的实现方式,我在这里使用的一种可能是劣质的,但它确实有效。 基本上,每个循环我们都没有其他工作要做,我们会在主循环中做一些要求不高或不那么频繁的工作(或者什么都不做)。 当我们不忙时,我们会检查是否是时候做其他工作之一了。如果是这样,我们分支并去做。重要的是要注意“阻塞”的操作,这意味着它们必须一次完成而不会中断,因此会占用 MCU 一段时间(例如从 SD 卡读取数据和一些其他任务)还是会屏蔽 其他 protothreads 不会“准时”发生,但是对于像两个循环同时执行快速操作(例如变量更改或更改输出值)这样的简单事情,它将非常有效。这或多或少是我们将在这里做的事情。一些 MCU 支持实时操作系统 (RTOS),该系统可以提供更多类似超线程的多任务处理能力,有助于缓解“阻塞”任务引起的问题。

让我们开始吧。

我们首先弄清楚我们需要执行哪些任务。就我而言,我选择了 (a) 将 LCD 面板的背光淡入淡出以获得整洁的“脉冲”效果,同时 (b) 以慢得多的(可能是不可整除的)间隔计算数字,并且(c) 以更慢的间隔轮换一些字符串消息。要确保此过程顺利进行,要遵循的一些准则是将您的功能从阻塞最少到阻塞最多。需要更长时间的操作(从现在起我们称它们为“函数”),例如读取数据或具有其他长延迟,以及触发间隔较大的函数是阻塞最多的函数。非常频繁地触发(如果不是每个循环)并且不需要很长时间才能完成的函数是阻塞最少的函数。最起码的阻塞功能是您应该用作主要“线程”的功能。你能猜出上面是哪个吗?

没错,它是“a”,使背光源脉冲进出。这将是定期且非常快的间隔,除了完成工作之外,火灾之间不会有任何延迟,并且工作本身非常快。完美的经理线程。

我们将使用这个线程(以及其中的任何循环)来检查其他线程是否需要做任何工作。此时最好通读代码 - 它有大量文档。看到底部的主循环。你可以看到我在调用 numberThread.check() 的地方检查线程是否需要任何工作 和 textThread.check() .

我也需要在主线程中的任何循环中执行此操作,因为如果我不这样做,它们将阻塞直到完成。当我在 init 或代码的设置部分初始化它们时,我设置了线程需要触发的时间间隔。如果是时候触发这些线程,.check() 在继续主线程之前会看到并执行他们的工作。

简而言之,这真的是它的其余部分,您可能可以通过逐步执行代码来弄清楚自己。最后让我说,虽然我可能听起来很像,但无论如何我都不是原型线程专家,这只是我编写的一个简单示例。如果您有任何提示或我有任何错误,我鼓励您提供反馈和更正!谢谢:)

代码

  • 多线程 LCD 代码 - multithread.ino(已更新,v1.1)
多线程 LCD 代码 - multithread.ino(已更新,v1.1)Arduino
这段代码使用 库在一个 Arduino Uno 处理器上同时以不同的间隔执行 3 个重复操作。它将 (a) 使背光淡入淡出,同时 (b) 增加一个数字,以及 (c) 在几个文本字符串之间旋转。请参阅上面的演示视频 :)
/*Arduino Protothreading Example v1.1by Drew Alden (@ReanimationXP) 1/12/2016- 更新:v1.1 - 8/18/17 Arduino 1.6.6+ 原型已更改,小修正。 (在使用之前创建函数,删除 foreach 和相关库)。请注意, TimedAction 现在已过时。请务必阅读有关 TimedAction 和 WProgram.h / Arduino.h 错误的说明。*///COMPONENTS/*此代码是使用 Sun Founder Arduino 入门套件的蓝色 LCD 制作的。它可以在 Amazon.com 的各种套件中找到.*///第三方库//这些必须手动添加到您的Arduino IDE安装中//TimedAction//允许我们设置动作以在不同的时间间隔执行//http://playground.arduino.cc/Code /TimedAction//http://wiring.uniandes.edu.co/source/trunk/wiring/firmware/libraries/TimedAction#include //注意:此库在较新版本的 Arduino 上存在问题。 // 下载库后,您必须进入库目录并// 编辑 TimedAction.h。在里面,用 Arduino.h//NATIVE LIBRARIES#include  /* LiquidCrystal Library - Hello World 覆盖 WProgram.h 演示使用 16x2 LCD 显示器。 LiquidCrystal 库适用于所有与 Hitachi HD44780 驱动程序兼容的 LCD 显示器。它们中有很多,您通常可以通过 16 针接口告诉它们。一个示例电路: * LCD RS 引脚到数字引脚 12。 * LCD Enable/E/EN 引脚到数字引脚 11 * LCD D4 引脚到数字引脚 5 * LCD D5 引脚到数字引脚 4 * LCD D6 引脚到数字引脚 3 * LCD D7 引脚到数字引脚 2 * LCD R/W 引脚到地 * LCD VSS 引脚到地 * LCD VCC/VDD 引脚到 5V * 10K 电阻:* 结束到 +5V 和地 * 雨刷器(中间)到 LCD VO 引脚(引脚 3) *背光显示: * LCD K 引脚接地(如果存在) * LCD A 引脚连接到 220 ohm(红红黑黑(棕色))电阻器,然后电阻器连接到引脚 9 此示例代码在公共领域。 http://www.arduino.cc/en/Tutorial/LiquidCrystal *///GLOBALSint 背光引脚 =9; // 用于背光淡出int timerCounter =0; // 递增计数器。最终会崩溃。int stringNo =0; //显示哪个文本字符串// "16 CHARACTER MAX"char* stringArray[]={"Check it out... ", "I have 3 threads", "going at once...", "Cool, huh ?!:D"}; //INIT// 这可能应该在 setup() 中完成,但无论如何。// 使用接口管脚的编号初始化 LCD 库LiquidCrystal lcd(12, 11, 5, 4, 3, 2);//FUNCTIONS/ /这是我们的第一个任务,打印一个递增的数字到 LCDvoid incrementNumber(){ // 将光标设置到第 0 列,第 1 行 //(注意:第 1 行是第二行,因为计数从 0 开始):lcd。 setCursor(0, 1); // 向计数器加一,然后显示它。 timerCounter =timerCounter + 1; lcd.print(timerCounter);}//我们的第二个任务,每隔几秒触发一次并旋转文本 stringsvoid changeText(){ // 向 LCD 打印一条消息。 lcd.setCursor(0, 0); lcd.print(stringArray[stringNo]); // 获取数组元素数量的讨厌黑客 if (stringNo>=sizeof(stringArray)/sizeof(char *)){ stringNo =0;更改文本(); } else{ stringNo =stringNo + 1; }}//创建几个定时器,每 x ms 重复触发//编辑:这些行曾经位于 incrementNumber 和 changeText// 函数之前。这不起作用,因为函数还没有定义!TimedAction numberThread =TimedAction(700,incrementNumber);TimedAction textThread =TimedAction(3000,changeText);// 我们的第三个任务在哪里?好吧,它是主循环本身:) 最常重复的任务// 应该用作循环。其他 // 任务能够“中断”最快的重复 task.void setup() { //定义 LCD 的列数和行数:lcd.begin(16, 2); //触发 changeText 一次以绘制初始字符串 [0] changeText();}void loop() { //检查我们的线程。基于系统已经运行了多长时间,它们是否需要启动并工作?如果是这样,那就去做吧! numberThread.check(); textThread.check(); //第三个任务,从最小到最大亮度淡入背光//以5点为增量:digitalWrite(13, HIGH); for (intfadeValue =0;fadeValue <=255;fadeValue +=10) { // 等一下,我为什么要检查这里的线程?因为//这是一个for循环。您必须在发生的任何 // 循环期间检查您的线程,包括主要的! numberThread.check(); textThread.check(); //设置值(范围从0到255):analogWrite(backlightPin,fadeValue); // 等待 20 毫秒以查看调光效果 // 在主循环上保持延迟 SHORT.这些将阻止 // 其他线程按时触发。延迟(20); } //从最大值到最小值以 5 点为增量淡出:digitalWrite(13, LOW); for (int flasheValue =255;fadeValue>=0;fadeValue -=10) { //再次检查我们的线程 numberThread.check(); textThread.check(); //设置值(范围从0到255):analogWrite(backlightPin,fadeValue); //等待20毫秒看调光效果 delay(20); } /* 为了将来一些滚动消息的乐趣... lcd.setCursor(15,0); // 将光标设置到第 15 列第 0 行 for (int positionCounter1 =0; positionCounter1 <26; positionCounter1++) { lcd.scrollDisplayLeft(); //将显示内容向左滚动一格。 lcd.print(array1[positionCounter1]); // 向 LCD 打印一条消息。延迟(时间); //等待 250 微秒 } lcd.clear(); //清除LCD屏幕并将光标定位在左上角。 lcd.setCursor(15,1); // 将光标设置到第 15 列第 1 行 for (int positionCounter =0; positionCounter <26; positionCounter++){ lcd.scrollDisplayLeft(); //将显示内容向左滚动一格。 lcd.print(array2[positionCounter]); // 向 LCD 打印一条消息。延迟(时间); //等待 250 微秒 } lcd.clear(); //清除LCD屏幕并将光标定位在左上角。 */ }

制造工艺

  1. 如何在 OpenSensors 上测量空气质量
  2. Arduino RFID 锁教程
  3. 如何破解红外遥控器
  4. 你有多高?
  5. 使用热敏电阻有多容易?!
  6. 如何用 Arduino 制作音乐
  7. 如何在 Arduino 中使用 NMEA-0183
  8. Arduino 指纹传感器教程
  9. 如何在 Arduino 中使用 Modbus
  10. Arduino 教程:迷你钢琴
  11. Arduino 教程:JARVIS v1 |如何制作家庭自动化
  12. Arduino教程01:入门