【STM32 FreeRTOS】任务通知

任务通知简介

任务通知:用来通知任务的,任务控制块中的结构体成员变量ulNotifiedValue(32位)就是这个通知值。

c 复制代码
	#if( configUSE_TASK_NOTIFICATIONS == 1 )
		volatile uint32_t ulNotifiedValue;
		volatile uint8_t ucNotifyState;
	#endif

任务控制块TCB里有两个成员变量,一个uint32_t的表示通知值,一个uint8_t的用来表示通知状态。

  • 使用队列、信号量、事件标志组时都需要另外创建一个结构体,通过中间的结构体进行间接通信。
  • 使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的通知。

任务通知值的更新方式:

  • 不覆盖接收任务的通知值
  • 覆盖接收任务的通知值
  • 更新接收任务通知值的一个或多个bit
  • 增加接收任务的通知值

只要合理,灵活的利用任务通知的特点,可以在一些场合中替代队列、信号量和事件标志组。

任务通知的优势:

  • 效率更高:使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多。
  • 使用内存更小:使用任务通知时无需额外创建结构体

任务通知的劣势:

  • 无法发送数据给ISR:ISR没有任务结构体,所以无法给ISR发送数据。但是ISR可以使用任务通知发数据给任务。
  • 无法广播给多个任务:任务通知只能是被指定的一个任务接收并处理。
  • 无法缓存多个数据:任务结构体中只有一个任务通知值,只能保存一个数据。
  • 发送受阻不支持阻塞:发送发无法进入阻塞状态等待。

任务通知状态:

c 复制代码
#define taskNOT_WAITING_NOTIFICATION	( ( uint8_t ) 0 )
#define taskWAITING_NOTIFICATION		( ( uint8_t ) 1 )
#define taskNOTIFICATION_RECEIVED		( ( uint8_t ) 2 )
  • 任务未等待通知,任务通知默认的初始化状态
  • 等待通知,接收方已经准备好了(调用了接收任务通知函数),等待发送方发给个通知
  • 等待接收,发送方已经发送出去(调用了发送任务通知函数),等待接收方接收

任务通知相关API函数介绍

发送通知

函数 描述
xTaskNotify 发送通知,带有通知值
xTaskNotifyAndQuery 发送通知,带有通知值并且保留接收任务的原有通知值
xTaskNotifyGive 发送通知,不带通知值
xTaskNotifyFromISR
xTaskNotifyAndQueryFromISR
vTaskNotifyGiveFromISR

接收通知

函数 描述
ulTaskNotifyTake 获取任务通知,可以设置在退出该函数的时候将任务通知值清零或者减一。
xTaskNotifyWait 获取任务通知,可获取通知值和清除通知值的指定位
  • 当任务通知用作于信号量时,使用ulTaskNotifyTake函数获取信号量。
  • 当任务用作于事件标志组或队列时,使用xTaskNotifyWait函数来获取。

模拟二值信号量和计数信号量

c 复制代码
void StartMyTask1(void *argument)
{
	  printf("StartMyTask1\r\n");

	  uint32_t notify_value = 0;
	  for(;;)
	  {
		  //这里实际是取通知值,只要通知值大于0的时候就不会阻塞,可以执行到下面
		  //第一个参数设置为true,表示执行完该函数后,会将通知值设置为0,
		  //这意味着如果再没有接收通知,那么会一直阻塞在这里。
		  //函数返回值,表示通知值再设置为0之前的值(一般情况下为1)
		  notify_value = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
		  if(0 != notify_value)
			  printf("task1 notify_value=%d\r\n",notify_value);

		  vTaskDelay(pdMS_TO_TICKS(500));//500ms
	  }
}

void NotifyMyTask1FromISR()
{
	printf("NotifyMyTask1FromISR\r\n");
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;

	//这里是在中断服务函数中调用的
	vTaskNotifyGiveFromISR(myTask1Handle,&xHigherPriorityTaskWoken);
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
NotifyMyTask1FromISR
task1 notify_value=2
NotifyMyTask1FromISR
NotifyMyTask1FromISR
task1 notify_value=2
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
NotifyMyTask1FromISR
NotifyMyTask1FromISR
task1 notify_value=3
NotifyMyTask1FromISR
NotifyMyTask1FromISR
NotifyMyTask1FromISR
task1 notify_value=3

可以看到,正常情况下,执行一次NotifyMyTask1FromISR 就会执行一次ulTaskNotifyTake 。但是如果NotifyMyTask1FromISR 函数执行的频率很快,ulTaskNotifyTake 函数执行不过来,所以通知值不是1(执行通知函数的时候实际上是将通知值加一),ulTaskNotifyTake 函数执行一次后还是会阻塞,因为执行一次后将通知值设置为0了。如果将ulTaskNotifyTake函数的第一个参数设置为pdFALSE,表示执行完后将通知值减一,这样其实就是模拟计数信号量了。

c 复制代码
void StartMyTask1(void *argument)
{
	  printf("StartMyTask1\r\n");

	  uint32_t notify_value = 0;
	  for(;;)
	  {
		  //这里实际是取通知值,只要通知值大于0的时候就不会阻塞,可以执行到下面
		  //第一个参数设置为false,表示执行完该函数后,会将通知值减1,
		  //函数返回值,表示通知值在减1之前的值
		  notify_value = ulTaskNotifyTake(pdFALSE,portMAX_DELAY);
		  if(0 != notify_value)
			  printf("task1 notify_value=%d\r\n",notify_value);

		  vTaskDelay(pdMS_TO_TICKS(500));//500ms
	  }
}
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
NotifyMyTask1FromISR
NotifyMyTask1FromISR
task1 notify_value=3
task1 notify_value=2
task1 notify_value=1

模拟消息邮箱

c 复制代码
void NotifyMyTask1FromISR(uint32_t notify_value)
{
	printf("NotifyMyTask1FromISR\r\n");
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;

	//这里是在中断服务函数中调用的
	//将参数值当作通知值发送给任务
	xTaskNotifyFromISR(myTask1Handle,notify_value,
			eSetValueWithOverwrite,&xHigherPriorityTaskWoken);
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

void StartMyTask1(void *argument)
{
	  printf("StartMyTask1\r\n");

	  uint32_t notify_value = 0;
	  for(;;)
	  {
		  //第一个参数表示接收前不清零通知值的bit
		  //第二个参数表示接收后清零通知值的所有bit
		  xTaskNotifyWait(0x00,0xFFFFFFFF,&notify_value,
									  portMAX_DELAY);
		  printf("task1 notify_value=%d\r\n",notify_value);

		  vTaskDelay(pdMS_TO_TICKS(500));//500ms
	  }
}
NotifyMyTask1FromISR
task1 notify_value=1
NotifyMyTask1FromISR
task1 notify_value=2
NotifyMyTask1FromISR
task1 notify_value=3
NotifyMyTask1FromISR
task1 notify_value=4

可以将参数作为通知值传递给任务。

模拟事件标志组

c 复制代码
void NotifyMyTask1FromISR(uint32_t notify_value)
{
	//notify_value表示第几位设置为1,其实可以一次设置多位,这里不演示
	//notify_value == 2,则表示第二位置1,那么通知值 = (1<<2)
	printf("NotifyMyTask1FromISR\r\n");
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;

	//模拟事件标志组其实就是利用通知值的每一位作为事件的Flag,
	//发送和接收的时候,都只是修改和判断对应bit
	xTaskNotifyFromISR(myTask1Handle,1<<notify_value,
			eSetBits,&xHigherPriorityTaskWoken);
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

void StartMyTask1(void *argument)
{
	  printf("StartMyTask1\r\n");

	  uint32_t notify_value = 0;
	  for(;;)
	  {
		  //第一个参数表示接收前不清零通知值的bit
		  //第二个参数表示接收后清零通知值的所有bit
		  xTaskNotifyWait(0x00,0xFFFFFFFF,&notify_value,
									  portMAX_DELAY);
		  printf("task1 notify_value=%d\r\n",notify_value);
		  if((notify_value & (1<<0)) != 0)
		  {
			  printf("bit0 event... \r\n");
		  }
		  else if((notify_value & (1<<1)) != 0)
		  {
			  printf("bit1 event... \r\n");
		  }
		  else if((notify_value & (1<<2)) != 0)
		  {
		  	  printf("bit2 event... \r\n");
		  }
		  vTaskDelay(pdMS_TO_TICKS(500));//500ms
	  }
}
NotifyMyTask1FromISR
task1 notify_value=4
bit2 event... 
NotifyMyTask1FromISR
task1 notify_value=2
bit1 event... 
NotifyMyTask1FromISR
task1 notify_value=1
bit0 event... 
NotifyMyTask1FromISR
task1 notify_value=4
bit2 event... 

其实,关于xTaskNotifyWait函数的第二个参数,准确来说不是执行完xTaskNotifyWait函数后清零比特位,而是在下次给通知值赋值的时候吧???

相关推荐
追梦少年时20 分钟前
STM32-Flash闪存
stm32·单片机·嵌入式硬件·51单片机
weixin_452600691 小时前
《青牛科技 GC6125:驱动芯片中的璀璨之星,点亮 IPcamera 和云台控制(替代 BU24025/ROHM)》
人工智能·科技·单片机·嵌入式硬件·新能源充电桩·智能充电枪
weixin_452600693 小时前
【青牛科技】14W 高保真音频放大电路——D2030
科技·单片机·嵌入式硬件·音视频·电动工具·智能电表
小刘同学-很乖10 小时前
MQTT从入门到精通之MQTT Dashboard
spring boot·stm32·物联网·iot
YuCaiH10 小时前
【STM32】USART串口数据包
笔记·stm32·单片机·嵌入式硬件
Kasen's experience12 小时前
STM32 GPIO 配置
stm32·单片机·嵌入式硬件
知行电子-12 小时前
Proteus中数码管动态扫描显示不全(已解决)
单片机·proteus·嵌入式
学习路上_write13 小时前
FPGA/Verilog,Quartus环境下if-else语句和case语句RT视图对比/学习记录
单片机·嵌入式硬件·qt·学习·fpga开发·github·硬件工程
非概念13 小时前
stm32学习笔记----51单片机和stm32单片机的区别
笔记·stm32·单片机·学习·51单片机
jjjxxxhhh12315 小时前
FPGA,使用场景,相比于单片机的优势
单片机·嵌入式硬件·fpga开发