0x01 任务通知介绍
简单举个例子:
例如任务一用于获取数据,任务二用于处理数据。
但这其中存在一个先后关系:必须等到任务一获取完了数据,任务二才能开始工作,你会如何选择何种实现方式呢?
未引入操作系统时,轮询系统会按照代码顺序执行,触发中断时,你可以通过设置一个获取原始数据的函数完成的全局变量作为标志位是否为1来判断是否完成,如果完成则开始执行数据处理任务,否则就等下一轮。
引入了操作系统后,可以通过二进制信号量的方式进行PV原子操作,这样一来便可以确保 任务一完成后,任务二才能执行。
用两份伪代码的方式来描述一下:
任务1-获取数据:
······
······
while(数据没获取完)
获取数据;
V(muttex)//数据获取完了,释放信号量
······
······
任务2-处理数据:
······
······
while(P(muttex))//获取不到信号量会被阻塞
静静等待;
处理数据//得到信号量了跳出循环,可以开始工作
······
······
很通俗易懂,是一个比较简单的生产消费者模型。
这个和轮询系统的区别就是,引入操作系统可以提高系统的实时性。在轮询系统中,MCU需要不断的去检查标志位的值是否满足条件,这会导致浪费一些资源在这个步骤上。而通过PV操作来获取信号量消耗的资源较少,并且此时任务还是处在被挂起的状态,相比起来是不怎么耗费资源的。相比使用全局变量标志位的方式而言,使用二进制信号量所耗费的资源往往可以接受,并且在获取到信号量的瞬间任务便可以开始工作,而在轮询系统中还需要等到其他代码执行完轮到函数2了,才可以开始执行。
0x02 示例代码
下面使用示例代码说明,使用FreeRTOS的软件定时器来为其发送任务通知,促使LED闪烁。
在收到任务通知时,LED会闪烁一下随后继续等待任务通知到来,方可继续闪烁,否则将被阻塞进程。
main.c
#include "tasks.h"
#include "stdio.h"
// 等待任务通知的任务
void vHandlerTask( void *pvParameters )
{
while(1)
{
if(ulTaskNotifyTake(pdTRUE, xMaxExpectedBlockTime));//未收到任务通知会被阻塞
LED_Blink;
}
}
// 软件定时器回调函数
void vTimerCallback(TimerHandle_t xTimer)
{
if (xHandlerTaskHandle != NULL) {
// 发送任务通知到vHandlerTask
xTaskNotifyGive(xHandlerTaskHandle);
}
}
// 设置并启动软件定时器
void vStartTimer(void)
{
const TickType_t xTimerPeriod = pdMS_TO_TICKS(1000); // 定时器周期为1000ms(1秒)
// 创建定时器
TimerHandle_t xTimer = xTimerCreate("Timer",
xTimerPeriod,
pdTRUE,
(void *) 0,
vTimerCallback);
}
以上演示的是在"任务对任务"通知,是否可以在中断里面对通知任务,让其开始工作呢?答案当然是可以的
下面是一个任务,在等待串口发来信息将其唤醒,随后判断字符有效性
void TaskBoardSelect(void *Params)
{
BaseType_t xReturn = pdTRUE;
uint32_t GetBoard;
while(1)
{
//获取任务通知 ,没获取到则一直等待
xReturn=xTaskNotifyWait(0x0,0xFFFF,(uint32_t *)&GetBoard,portMAX_DELAY); //阻塞时间
if ( pdTRUE == xReturn )
{
char BoardID = ((char *)GetBoard)[0];
printf("TaskBoardSelect 任务通知为 %s \n",GetBoard);
if(BoardID >= '0' && BoardID <= '9')
{
OSstate++;// 系统状态机进入下一状态
}
else if(BoardID == 'q' || BoardID == 'Q')
{
OSstate--
}
else
{
printf("输入有误");
}
}
vTaskDelay(50);
}
}
那么在中断中应该如何唤醒这个任务呢?在串口中断中使用以下API即可
xTaskNotifyFromISR( TaskBoardSelect_Handle,
(uint32_t)&USART0RecvPacket.RxData,
eSetValueWithOverwrite);