從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);//如果需要的話進行一次任務切換}
}