- RTOS如何运作?
- RTOS中的常用术语
- 安装Arduino FreeRTOS库
- 电路原理图
- Arduino FreeRTOS示例-在Arduino IDE中创建FreeRTOS任务
- Arduino IDE中的FreeRTOS任务实现
嵌入式设备内部存在的OS称为RTOS(实时操作系统)。在嵌入式设备中,实时任务至关重要,而定时在其中起着非常重要的作用。实时任务是时间确定性的,这意味着对任何事件的响应时间始终是恒定的,因此可以保证任何特定事件将在固定时间发生。RTOS旨在以非常精确的时序和高度的可靠性运行应用程序。RTOS还可以通过单个内核帮助执行多任务。
我们已经介绍了如何在嵌入式系统中使用RTOS的教程,您可以在其中了解有关RTOS的更多信息,通用OS和RTOS之间的区别,不同类型的RTOS等。
在本教程中,我们将从FreeRTOS开始。FreeRTOS是一类用于嵌入式设备的RTOS,其规模很小,可以在8/16位微控制器上运行,尽管它的使用不限于这些微控制器。这是一个完全开源的代码,可以在github上找到。如果我们了解RTOS的一些基本概念,那么使用FreeRTOS就会非常容易,因为它具有文档齐全的API,可以直接在代码中使用它们,而无需了解代码的后端部分。完整的FreeRTOS文档可以在这里找到。
由于FreeRTOS可以在8位MCU上运行,因此它也可以在Arduino Uno板上运行。我们只需要下载FreeRTOS库,然后开始使用API来实现代码。本教程适用于完整的初学者,以下是主题,我们将在此Arduino FreeRTOS教程中进行介绍:
- RTOS如何运作
- RTOS中一些常用术语
- 在Arduino IDE中安装FreeRTOS
- 如何使用示例创建FreeRTOS任务
RTOS如何运作?
在开始运行RTOS之前,让我们看看什么是任务。任务是一段可调度在CPU上执行的代码。因此,如果要执行某些任务,则应使用内核延迟或中断来安排任务。这项工作由内核中存在的Scheduler完成。在单核处理器中,调度程序可帮助任务在特定时间段内执行,但似乎不同的任务正在同时执行。每个任务都根据赋予它的优先级运行。
现在,让我们看看如果要创建一个以一秒为间隔的LED闪烁任务并将该任务置于最高优先级的情况,RTOS内核中会发生什么。
除了LED任务外,内核还会创建另一个任务,称为空闲任务。。当没有任务可执行时,将创建空闲任务。此任务始终以最低优先级(即0优先级)运行。如果我们分析上面给出的时序图,可以看出执行从一个LED任务开始,并运行了指定的时间,然后在剩余的时间里,空闲任务一直运行到滴答中断发生为止。然后,内核根据任务的优先级和LED任务的总经过时间来决定必须执行的任务。 1秒完成后,内核会再次选择执行引导任务,因为它比空闲任务具有更高的优先级,我们也可以说LED任务抢占了空闲任务。如果有两个以上具有相同优先级的任务,则它们将以循环方式运行指定的时间。
在状态图下方,它显示了非运行任务切换到运行状态。
每个新创建的任务都进入“就绪”状态(不处于运行状态的一部分)。如果创建的任务(Task1)的优先级高于其他任务,则它将进入运行状态。如果此正在运行的任务被另一个任务抢占,则它将再次回到就绪状态。否则,如果使用阻塞API阻止了task1,则CPU将不会参与该任务,直到用户定义了超时。
如果使用Suspend API将Task1挂起处于运行状态,则Task1将进入“挂起”状态,并且调度程序无法再次使用它。如果在挂起状态下恢复Task1,则它将返回到就绪状态,如在框图中所示。
这是任务如何运行和更改其状态的基本思想。在本教程中,我们将使用FreeRTOS API在Arduino Uno中实现两项任务。
RTOS中的常用术语
1.任务:这是一段可在CPU上调度执行的代码。
2.计划程序:负责从就绪状态列表到运行状态中选择任务。经常实施调度程序,以便它们使所有计算机资源处于繁忙状态(例如在负载平衡中)。
3.抢占:这是暂时中断已经执行的任务的目的,目的是在没有合作的情况下将其从运行状态中删除。
4.上下文切换:在基于优先级的抢占中,调度程序将每个 systick 中断上正在运行的任务的优先级与就绪任务列表的优先级进行比较。如果列表中有任何任务的优先级高于正在运行的任务,则会发生上下文切换。基本上,在此过程中,不同任务的内容将保存在各自的堆栈存储器中。
5.调度策略的类型:
- 抢先式调度:在这种类型的调度中,任务以相等的时间片运行,而不考虑优先级。
- 基于优先级的抢占式:高优先级任务将首先运行。
- 协作调度:上下文切换仅在运行任务的协作下发生。任务将连续运行,直到调用任务产量为止。
6.内核对象: 为了发信号通知任务执行某些工作,使用了同步过程。要执行此过程,将使用内核对象。一些内核对象包括事件,信号量,队列,互斥量,邮箱等。我们将在后续教程中了解如何使用这些对象。
通过以上讨论,我们对RTOS概念有了一些基本的想法,现在我们可以在Arduino中实现FreeRTOS项目。因此,让我们开始在Arduino IDE中安装FreeRTOS库。
安装Arduino FreeRTOS库
1.打开Arduino IDE并转到 Sketch-> Include Library-> Manage Libraries 。搜索FreeRTOS并安装库,如下所示。
您可以从github下载该库,然后在 Sketch-> Include Library-> Add.zip 文件中 添加.zip 文件。
现在,重新启动Arduino IDE。该库提供了一些示例代码,也可以在 文件->示例-> FreeRTOS中找到 ,如下所示。
在这里,我们将从头开始编写代码以了解其工作原理,稍后您可以检查示例代码并使用它们。
电路原理图
下面是在Arduino上使用FreeRTOS创建闪烁LED任务的电路图:
Arduino FreeRTOS示例-在Arduino IDE中创建FreeRTOS任务
让我们看一下编写FreeRTOS项目的基本结构。
1.首先,将Arduino FreeRTOS头文件包含为
#包括
2.提供要编写的所有要执行的函数的函数原型,写为
无效Task1(无效* pvParameters); void Task2(void * pvParameters); .. …。
3.现在,在 void setup() 函数中,创建任务并启动任务计划程序。
为了创建任务,在 安装程序 函数中使用某些参数/参数调用 xTaskCreate() API 。
xTaskCreate(TaskFunction_t pvTaskCode,const char * const pcName,uint16_t usStackDepth,void * pvParameters,UBaseType_t uxPriority,TaskHandle_t * pxCreatedTask);
创建任何任务时应传递6个参数。让我们看看这些参数是什么
- pvTaskCode:它只是指向实现任务的函数的指针(实际上只是函数的名称)。
- pcName:任务的描述性名称。FreeRTOS不使用此功能。纯粹是出于调试目的将其包括在内。
- usStackDepth:每个任务都有自己的唯一堆栈,内核在创建任务时会将其分配给任务。该值指定堆栈可以容纳的字数,而不是字节数。例如,如果堆栈为32位宽,并且 usStackDepth 作为100传入,那么将在RAM中分配400字节的堆栈空间(100 * 4字节)。明智地使用此选项,因为Arduino Uno只有2KB的RAM。
- pvParameters:任务输入参数(可以为NULL)。
- uxPriority:任务的优先级(0是最低优先级)。
- pxCreatedTask:可用于向正在创建的任务传递句柄。然后,可以使用此句柄在API调用中引用任务,例如,更改任务优先级或删除任务(可以为NULL)。
任务创建示例
xTaskCreate(task1,“ task1”,128,NULL,1,NULL); xTaskCreate(task2,“ task2”,128,NULL,2,NULL);
在这里,Task2具有更高的优先级,因此首先执行。
4.创建任务后,使用vTaskStartScheduler();以无效设置启动调度程序。API。
5.虚空loop() 函数将保持为空,因为我们不想手动无限地运行任何任务。因为任务执行现在由调度程序处理。
6.现在,我们必须实现任务功能,并在这些功能内编写要执行的逻辑。函数名称应与 xTaskCreate() API的第一个参数相同。
无效task1(void * pvParameters) { while(1){ .. ..//您的逻辑 } }
7.大多数代码需要使用delay函数来停止正在运行的任务,但是在RTOS中,不建议使用 Delay() 函数,因为它会停止CPU,因此RTOS也将停止工作。因此,FreeRTOS具有内核API,可以在特定时间内阻止任务。
vTaskDelay(const TickType_t xTicksToDelay);
该API可用于延迟目的。此API将任务延迟给定的滴答数。任务保持阻塞的实际时间取决于滴答率。常量 portTICK_PERIOD_MS 可用于根据滴答率计算实时性。
这意味着如果您想要200ms的延迟,只需编写此行
vTaskDelay(200 / portTICK_PERIOD_MS);
因此,对于本教程,我们将使用这些FreeRTOS API来实现三个任务。
使用的API:
- xTaskCreate();
- vTaskStartScheduler();
- vTaskDelay();
为本教程创建的任务:
- LED在数字引脚8上以200ms的频率闪烁
- LED在数字引脚7上以300ms的频率闪烁
- 在串行监视器中以500ms的频率打印数字。
Arduino IDE中的FreeRTOS任务实现
1.从上面的基本结构说明中,包括Arduino FreeRTOS头文件。然后制作函数原型。由于我们要完成三个任务,因此要创建三个函数及其原型。
#include void TaskBlink1(void * pvParameters); void TaskBlink2(void * pvParameters); void Taskprint(void * pvParameters);
2.在 void setup() 函数中,以每秒9600位的速度初始化串行通信,并使用 xTaskCreate() API创建所有三个任务。首先,将所有任务的优先级设置为“ 1”,然后启动调度程序。
void setup(){ Serial.begin(9600); xTaskCreate(TaskBlink1,“ Task1”,128,NULL,1,NULL); xTaskCreate(TaskBlink2,“ Task2”,128,NULL,1,NULL); xTaskCreate(Taskprint,“ Task3”,128,NULL,1,NULL); vTaskStartScheduler(); }
3.现在,为任务1 LED闪烁执行以下所示的所有三个功能。
void TaskBlink1(void * pvParameters) { pinMode(8,OUTPUT); while(1) { digitalWrite(8,HIGH); vTaskDelay(200 / portTICK_PERIOD_MS); digitalWrite(8,LOW); vTaskDelay(200 / portTICK_PERIOD_MS); } }
同样,实现TaskBlink2函数。Task3函数将写为
无效Taskprint(void * pvParameters) { int counter = 0; while(1) { counter ++; Serial.println(counter); vTaskDelay(500 / portTICK_PERIOD_MS); } }
而已。我们已经成功完成了Arduino Uno的FreeRTOS Arduino项目。您可以在本教程的结尾找到完整的代码以及视频。
最后,在数字引脚7和8上连接两个LED,并将代码上传到Arduino板上,然后打开串行监视器。您将看到计数器以任务名称每500ms运行一次,如下所示。
另外,观察LED,它们以不同的时间间隔闪烁。尝试在 xTaskCreate 函数中使用priority参数。更改编号并观察串行监视器和LED上的行为。
现在,您可以了解创建模拟读取和数字读取任务的前两个示例代码。这样,您可以仅使用Arduino Uno和FreeRTOS API进行更高级的项目。