自从某次看到FreeRTOS也支持AVR类的单片机,我就想做这一件事(事实也做成了,但是贼麻烦,不仅不符合Arduino的思想,也不适合各位)。最近看到一篇博客,方法非常巧妙且简单。
一、添加FreeRTOS库
在库管理那里搜索FreeRTOS,找到这一个库并且安装。
二、添加头文件
#include <Arduino_FreeRTOS.h>
三、FreeRTOS的调度机制
FreeRTOS任务的状态和调度并不难理解。看图主要分两部分——状态和方法
四状态:运行态(干活了)、就绪态(在排队)、阻塞态(到点了再去排队)、挂起态(不用干活)
方法:主要看图
四、新建任务
函数原型如下:
xTaskCreate(TaskFunction_t pvTaskCode, //任务函数
const char * const pcName, //任务名
uint16_t usStackDepth, //任务堆栈
void * pvParameters, //任务输入参数
UBaseType_t uxPriority, //任务优先级
TaskHandle_t * pxCreatedTask) //任务句柄
TaskFunction_t pvTaskCode:任务函数,俗称要做什么;
const char * const pcName:任务名,随便都行;
uint16_t usStackDepth:任务堆栈,内核在创建任务时将其分配给任务。该数值指定了堆栈可以 容纳的字数,而不是字节数。
例如,如果堆栈为32位宽,并且usStackDepth作为100传入,那么将在RAM中分配400字节的堆栈空间(100 * 4字节)。请非常合理地使用此项,因为Arduino Uno只有2KB的RAM。
void * pvParameters:任务输入参数(可以为NULL),有时候可以用来存放一些东西。
UBaseType_t uxPriority:任务优先级(0是最低优先级);
TaskHandle_t * pxCreatedTask):任务句柄,非常重要,控制完全靠他。
示例如下:
xTaskCreate(TaskPrint1, "Print1", 128, NULL, 1, &task1Handle); //创建任务1
这里还需要提前去写好和声明好任务函数哦,具体的可以最后看源码。
五、FreeRTOS里的延时函数
函数原型如下,使用也很方便,可以参考示例的用法
vTaskDelay(const TickType_t xTicksToDelay)
示例如下,1000为延时1000ms。
vTaskDelay(1000 / portTICK_PERIOD_MS)
六、挂起与释放
这里就不讲太多深入的东西,我们只要记住,在任务里挂起和释放直接用对应的函数然后参数设置为需要操作的任务的句柄即可。
1,挂起任务(俗称干掉任务)
函数原型如下:
vTaskSuspend(handler);
示例入下,挂起场景分三种:挂起别人(干掉别人);被挂起(被别人干掉);自己挂起自己(干掉自己);
vTaskSuspend(task1Handle);
2,释放
函数原型如下
vTaskResume(handler);
示例入下,这里就只能被别人救了,被挂起函数没法自己释放自己;
vTaskResume(task1Handle);
七、工程附件(源码)
#include <Arduino_FreeRTOS.h>
void TaskPrint1(void *param); //声明打印任务1
void TaskPrint2(void *param); //声明打印任务2
TaskHandle_t task1Handle=NULL;//任务1句柄
TaskHandle_t task2Handle=NULL;//任务2句柄
void setup() {
Serial.begin(9600);
while (!Serial);//等待串口连接后执行
xTaskCreate(TaskPrint1, "Print1", 128, NULL, 1, &task1Handle); //创建任务1
xTaskCreate(TaskPrint2, "Print2", 128, NULL, 2, &task2Handle); //创建任务2
vTaskStartScheduler(); //启动任务调度
}
void TaskPrint1(void *param)
{
while (1)
{
Serial.println("TaskPrint1...");
vTaskDelay(1000 / portTICK_PERIOD_MS ); // 等待1秒
vTaskSuspend(task1Handle);//做完一次就自己挂起自己
}
}
void TaskPrint2(void *param)
{
static int i = 0;
while (1)
{
i++;
Serial.println("TaskPrint2...");
vTaskDelay(2000 / portTICK_PERIOD_MS ); // 等待2秒
if(i>5)//每执行5次任务2会释放一次任务1
{
i=0;
vTaskResume(task1Handle);
}
}
}
void loop() {
}