从v8.2.0版本开始,FreeRTOS新增了任务通知这个功能,可以使用任务通知来代替信号量、消息队列、事件标志组等这些东西,使用任务通知的话效率会更高。
任务通知在FreeRTOS是一个可选的选项,要使用任务通知的话就需要将宏configUSE_TASK_NOTIFICATIONS定义为1 。FreeRTOS的每个任务都有一个32位的通知值,任务控制块中的成员变量ulNotifiedValue就是这个通知值。任务通知是一个事件,可以提高速度,减少RAM的使用,但是任务通知也是有限的,FreeRTOS的任务通知只能有一个接收任务,接收任务可以因为任务通知而进入阻塞态,但是发送任务不会因为任务通知发送失败而阻塞。
0x01 发送任务通知
任务通知函数有6个
- xTaskNotify
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
- TaskHandle_t xTaskToNotify: 任务句柄,指定任务通知是发送给那个任务的
- uint32_t ulValue:任务通知值
- eNotifyAction eAction:任务通知更新的方法,是一个枚举类型
typedef enum
{eNoAction = 0, /* Notify the task without updating its notify value. */eSetBits, /* Set bits in the task's notification value.更新指定的bit */eIncrement, /* Increment the task's notification value.通知值加1 */eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. 覆写的方式更新通知值*/eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task.不覆写通知值 */
} eNotifyAction;
- xTaskNotifyFromISR
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );
- BaseType_t *pxHigherPriorityTaskWoken:退出此函数是否要进行任务切换
- xTaskNotifyGive
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
- TaskHandle_t xTaskToNotify:指定通知是发送给那个任务的
- vTaskNotifyGiveFromISR
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
- xTaskNotifyAndQuery
xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t*pulPreviousNotificationValue);
- TaskHandle_t xTaskToNotify:任务句柄,指定任务通知是发送给那个任务的
- uint32_t ulValue:任务通知值
- eNotifyAction eAction:任务通知更新方法
- uint32_t* pulPreviousNotificationValue:用来保存更新前的任务通知值
- xTaskNotifyAndQueryFromISR
xTaskNotifyAndQueryFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t*pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken);
0x02 获取任务通知
获取任务通知的函数有两个
- ulTaskNotifyTake():获取任务通知,可以设置在退出此函数的时候将任务通知值清理或者减1.当任务通知用作二值信号量或者计数信号量的时候使用此函数来获取信号量。
- xTaskNotifyWait():等待任务通知,比ulTaskNotifyTake更为强大,全功能版任务通知获取函数
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
- BaseType_t xClearCountOnExit:为pdFALSE的话,在退出函数的时候,任务通知值减1,类似计数型信号量,当为pdTRUE的话,在退出函数的时候任务通知清零,类似二值信号量
- TickType_t xTicksToWait:阻塞时间
返回值:任务通知值减少值或者清零之前的值
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )
- uint32_t ulBitsToClearOnEntry:当没有接收到任务通知的时候,将任务通知与此参数的取反值进行按位与运算,当此参数为0xffffffff或者ULONG_MAX的时候就会将任务通知值清零
- uint32_t ulBitsToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将任务通知与此参数的取反值进行按位与运算,当此参数为0xffffffff或者ULONG_MAX的时候就会将任务通知值清零
- uint32_t *pulNotificationValue:此参数来保存任务通知值
- TickType_t xTicksToWait :阻塞时间
返回值:
pdTRUE:获取了任务通知
pdFALSE:任务通知获取失败
实验 任务通知模拟二值信号量
获取任务通知值,如果获取到的话,则进行逻辑处理
//DataProcess_task函数
void DataProcess_task(void *pvParameters)
{u8 len=0;u8 CommandValue=COMMANDERR;u32 NotifyValue;u8 *CommandStr;POINT_COLOR=BLUE;while(1){NotifyValue=ulTaskNotifyTake(pdTRUE,portMAX_DELAY); //获取任务通知if(NotifyValue==1) //清零之前的任务通知值为1,说明任务通知有效{len=USART_RX_STA&0x3fff; //得到此次接收到的数据长度CommandStr=mymalloc(SRAMIN,len+1); //申请内存sprintf((char*)CommandStr,"%s",USART_RX_BUF);CommandStr[len]='\0'; //加上字符串结尾符号LowerToCap(CommandStr,len); //将字符串转换为大写 CommandValue=CommandProcess(CommandStr); //命令解析if(CommandValue!=COMMANDERR){LCD_Fill(10,90,210,110,WHITE); //清除显示区域LCD_ShowString(10,90,200,16,16,CommandStr); //在LCD上显示命令printf("命令为:%s\r\n",CommandStr);switch(CommandValue) //处理命令{case LED1ON: LED1=0;break;case LED1OFF:LED1=1;break;case BEEPON:BEEP=0;break;case BEEPOFF:BEEP=1;break;}}else{printf("无效的命令,请重新输入!!\r\n");}USART_RX_STA=0;memset(USART_RX_BUF,0,USART_REC_LEN); //串口接收缓冲区清零myfree(SRAMIN,CommandStr); //释放内存}else {vTaskDelay(10); //延时10ms,也就是10个时钟节拍 }}
}
接收到了数据,发送任务通知,vTaskNotifyGiveFromISR的第一个参数是DataProcess_task任务的任务句柄DataProcess_Handler
//串口1中断服务程序
void USART1_IRQHandler(void)
{ u32 timeout=0;u32 maxDelay=0x1FFFF;BaseType_t xHigherPriorityTaskWoken;HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数timeout=0;while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//等待就绪{timeout++;超时处理if(timeout>maxDelay) break; }timeout=0;while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1{timeout++; //超时处理if(timeout>maxDelay) break; }//发送任务通知if((USART_RX_STA&0x8000)&&(DataProcess_Handler!=NULL))//接收到数据,并且接收任务通知的任务有效{vTaskNotifyGiveFromISR(DataProcess_Handler,&xHigherPriorityTaskWoken);//发送任务通知portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换}
}