一、简介
1 、基本概念
FreeRTOS 从 V8.2.0 版本开始提供任务通知这个功能,每个任务都有 一个 32 位 的通知值,在大多数情况下,任务通知可以 替代二值信号量、计数信号量、事件组,也可以替代长度为 1 的队列(可以保存一个 32 位整数或指针值)。
相对于以前使用 FreeRTOS 内核通信的资源,必须创建队列、二进制信号量、计数信号量或事件组的情况,使用任务通知显然更灵活。按照 FreeRTOS 官方的说法,使用任务通知比通过信号量等 ICP 通信方式解除阻塞的任务要快 45%,并且更加省 RAM 内存空间(使用 GCC 编译器,-o2 优化级别),任务通知的使用无需创建队列。 想要使用任务通知,必须将 FreeRTOSConfig.h 中的宏定义 configUSE_TASK_NOTIFICATIONS 设置为 1,其实FreeRTOS 默认是为 1 的,所以任务通知是默认使能的。
FreeRTOS 提供以下几种方式发送通知给任务 :
- 发送通知给任务, 如果有通知未读,不覆盖通知值。
- 发送通知给任务,直接覆盖通知值。
- 发送通知给任务,设置通知值的一个或者多个位 ,可以当做事件组来使用。
- 发送通知给任务,递增通知值,可以当做计数信号量使用。
- 通过对以上任务通知方式的合理使用,可以在一定场合下替代 FreeRTOS 的信号量,队列、事件组等。
当然,凡是都有利弊,不然的话 FreeRTOS 还要内核的 IPC 通信机制干嘛,消息通知虽然处理更快,RAM 开销更小,但也有以下限制 :
- 只能有一个任务接收通知消息,因为必须指定接收通知的任务。
- 只有等待通知的任务可以被阻塞,发送通知的任务,在任何情况下都不会因为发送失败而进入阻塞态。
- 任务通知只能通知任务,不能通知中断(isr)
2、 运作机制
由于任务通知的数据结构包含在任务控制块中,只要任务存在,任务通知数据结构就已经创建完毕,可以直接使用,所以使用的时候很是方便。
任务通知可以在任务中向指定任务发送通知,也可以在中断中向指定任务发送通知,FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue 就是这个通知值。只有在任务中可以等待通知,而不允许在中断中等待通知。如果任务在等待的通知暂时无效,任务会根据用户指定的阻塞超时时间进入阻塞状态,我们可以将等待通知的任务看作是消费者;其它任务和中断可以向等待通知的任务发送通知,发送通知的任务和中断服务函数可以看作是生产者,当其他任务或者中断向这个任务发送任务通知,任务获得通知以后,该任务就会从阻塞态中解除,这与 FreeRTOS 中内核的其他通信机制一致。
二、STM32CubeMX设置
1、配置RCC、USART1、时钟72M
2、配置SYS,将Timebase Source修改为除滴答定时器外的其他定时器。
3、初始化LED的两个引脚
4、开启FreeRTOS,v1与v2版本不同,一般选用v1即可
5、创建三个线程:任务3用作发送,LED1,LED2用作接收。
以上步骤可参考:STM32CubeMX学习笔记22---FreeRTOS(任务创建和删除)-CSDN博客
6、创建任务Task
要想使用任务通知必须在 Config parameters
中把 USE_TASK_NOTIFICATIONS
选择 Enabled
来使能。
7、生成代码
三、相关API函数
1、向指定的任务发送一个任务通知:osSignalSet
向指定的任务发送一个任务通知,带有通知值并且用户可以指定通知值的发送方式。该函数可以在中断函数中使用。
函数 | int32_t osSignalSet (osThreadId thread_id, int32_t signal) |
---|---|
参数 | thread_id: 接收通知的任务ID signal: 任务通知值,一般按位操作数字,一个事件用一个类似这样的值表示(即0x0001,0x0002,0x0004,0x0008,0x0010…) |
返回值 | 错误码 |
2、等待任务通知:osSignalWait
用于实现等待任务通知,根据用户指定的参数的不同,可以灵活的用于实现轻量级的消息队列队列、二值信号量、计数信号量和事件组功能,并带有超时等待。函数不允许在中断函数中使用。
四、程序编程
1、任务三编写发送任务通知,分别给任务1和任务2发送任务通知
void StartTask3(void const * argument)
{
/* USER CODE BEGIN StartTask3 */
/* Infinite loop */
char i=0;
for(;;)
{
printf("task3 run %d\r\n",i++);
if(i++==5)
{
//往任务LED1发送任务通知(数值1)
osSignalSet(LED1Handle,1);
}
if(i==10)
{
i=0;
//往任务LED1发送任务通知(数值3)
osSignalSet(LED2Handle,3);
}
osDelay(300);
}
/* USER CODE END StartTask3 */
}
2、任务1等待接收
void LED1_Task1(void const * argument)
{
/* USER CODE BEGIN LED1_Task1 */
/* Infinite loop */
osEvent event;
char i=0;
for(;;)
{
event = osSignalWait( 1, /* 接收任务感兴趣的事件 */
osWaitForever); /* 指定超时事件,一直等 */
if(event.status == osEventSignal) //如果接收到通知
{
if(event.value.signals & 1)//接收的通知为KEY1_EVENT
{
printf("接收到通知,数值为%d \n",event.value.signals);
}
}
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5); //LED1状态每500s翻转一次
printf("LED1 run %d\r\n",i);
osDelay(300);
}
/* USER CODE END LED1_Task1 */
}
3、任务2等待通知
void LED2_Task03(void const * argument)
{
/* USER CODE BEGIN LED2_Task03 */
/* Infinite loop */
osEvent event;
char i=0;
for(;;)
{
event = osSignalWait( 3, /* 接收任务感兴趣的事件 */
osWaitForever); /* 指定超时事件,一直等 */
if(event.status == osEventSignal) //如果接收到通知
{
if(event.value.signals & 3)//接收的通知为KEY1_EVENT
{
printf("接收到通知,数值为%d \n",event.value.signals);
}
}
printf("LED2 run %d\r\n",i++);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5); //LED1状态每500s翻转一次
osDelay(300);
}
/* USER CODE END LED2_Task03 */
}
下载验证:
程序编译无误后下载到板子上可以看到任务3运行到5给任务1发送任务通知之后任务1才运行,任务2也是等待任务3发送任务通知后才运行,其他情况都不运行。
五、参考文献
韦东山freeRTOS系列教程之【第九章】任务通知(Task Notifications)_通知播报 task-CSDN博客
STM32CubeMX学习笔记(34)——FreeRTOS实时操作系统使用(任务通知)_ossignalset-CSDN博客
stm32cubemx hal学习记录:FreeRTOS任务管理_ossignalset-CSDN博客
FreeRTOS任务通知 基于STM32_smt32标准库 串口收发 freertos-CSDN博客