:任务通知详解)
目录一、什么是任务通知二、任务通知值的更新方式三、任务通知的优势和劣势3-1 任务通知的优势3-2 任务通知的劣势四、任务通知相关 API 函数4-1 发送通知4-2 等待通知五、测试程序一、什么是任务通知FreeRTOS 从版本 V8.2.0 开始提供任务通知这个功能每个任务都有一个 32 位的通知值。按照 FreeRTOS官方的说法使用消息通知比通过二进制信号量方式解除阻塞任务快 45% 并且更加省内存无需创建队列。在大多数情况下任务通知可以替代二值信号量、计数信号量、事件标志组可以替代长度为 1 的队列可以保存一个 32 位整数或指针值并且任务通知速度更快、使用的RAM更少二、任务通知值的更新方式FreeRTOS提供以下几种方式发送通知给任务 1.发送消息给任务如果有通知未读 不覆盖通知值2.发送消息给任务直接覆盖通知值3.发送消息给任务设置通知值的一个或者多个位4.发送消息给任务递增通知值通过对以上方式的合理使用可以在一定场合下替代原本的队列、信号量、事件标志组等三、任务通知的优势和劣势3-1 任务通知的优势1. 使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多。2. 使用其他方法时都要先创建对应的结构体使用任务通知时无需额外创建结构体。3-2 任务通知的劣势1. 只有任务可以等待通知中断服务函数中不可以因为中断没有 TCB 。2. 通知只能一对一因为通知必须指定任务。3. 等待通知的任务可以被阻塞 但是发送消息的任务任何情况下都不会被阻塞等待。4. 任务通知是通过更新任务通知值来发送数据的任务结构体中只有一个任务通知值只能保持一个数据。四、任务通知相关API函数4-1发送通知/* * -------------函数---------------------------------------描述------------------- *| xTaskNotify() | 发送通知带有通知值 *|------------------------------|---------------------------------------------- *| xTaskNotifyAndQuery() | 发送通知带有通知值并且保留接收任务的原通知值 *|------------------------------|---------------------------------------------- *| xTaskNotifyGive() | 发送通知不带通知值 *|------------------------------|---------------------------------------------- *| xTaskNotifyFromISR() | 在中断中发送任务通知 *|------------------------------|---------------------------------------------- *| xTaskNotifyAndQueryFromISR() | 在中断中发送任务通知 *|------------------------------|---------------------------------------------- *| vTaskNotifyGiveFromISR() | 在中断中发送任务通知 *|------------------------------|---------------------------------------------- * * *//* * 参数 * xTaskToNotify需要接收通知的任务句柄 * ulValue用于更新接收任务通知值 具体如何更新由形参 eAction 决定 * eAction一个枚举代表如何使用任务通知的值 * * * -------------枚举值-------------------------------描述------------------------ * | eNoAction | 发送通知但不更新值参数ulValue未使用 * |------------------------------|---------------------------------------------- * | eSetBits | 被通知任务的通知值按位或ulValue。某些场景下可代替事件组效率更高 * |------------------------------|---------------------------------------------- * | eIncrement | 被通知任务的通知值增加1参数ulValue未使用相当于xTaskNotifyGive * |------------------------------|---------------------------------------------- * | eSetValueWithOverWrite | 被通知任务的通知值设置为 ulValue。某些场景下可代替xQueueOverwrite 效率更高 * |------------------------------|---------------------------------------------- * | eSetValueWithoutOverwrite | 如果被通知的任务当前没有通知则被通知的任务的通知值设为ulValue。如果被通知任务没有取走上一个通知又接收到了一个通知则这次通知值丢弃在这种情况下视为调用失败并返回 pdFALSE某些场景下可代替 xQueueSend 效率更高 * |------------------------------|---------------------------------------------- * * 返回值: * 如果被通知任务还没取走上一个通知又接收了一个通知则这次通知值未能更新并返回 pdFALSE 而其他情况均返回pdPASS。 * * * * BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction ); * * *//* * 参数 * xTaskToNotify需要接收通知的任务句柄 * ulValue用于更新接收任务通知值 具体如何更新由形参 eAction 决定 * eAction一个枚举代表如何使用任务通知的值 * pulPreviousNotifyValue对象任务的上一个任务通知值如果为 NULL 则不需要回传 这个时候就等价于函数 xTaskNotify()。 * * 返回值 * 如果被通知任务还没取走上一个通知又接收了一个通知则这次通知值未能更新并返回 pdFALSE 而其他 * 情况均返回pdPASS。 * * * BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,uint32_t *pulPreviousNotifyValue ); * * *//* * 参数 * xTaskToNotify接收通知的任务句柄 并让其自身的任务通知值加 1。 * 返回值 * 总是返回 pdPASS。 * * BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); * * */4-2等待通知等待通知API函数只能用在任务不可应用于中断中/* * -------------函数-------------------------------描述------------------------ * | ulTaskNotifyTake() | 获取任务通知可以设置在退出此函数的时候将任务通知值清零或者减一。当任务通知用作二值信号量或者计数信号量的时候使用此函数来获取信号量。 * |------------------------------|---------------------------------------------- * | xTaskNotifyWait() | 获取任务通知比 ulTaskNotifyTak()更为复杂可获取通知值和清除通知值的指定位 * |------------------------------|---------------------------------------------- * * *//* * 参数 * xClearCountOnExit指定在成功接收通知后将通知值清零或减 1pdTRUE把通知值清零二值信号量pdFALSE把通知值减一计数型信号量 * xTicksToWait阻塞等待任务通知值的最大时间 * * 返回值 * 0接收失败 * 非0接收成功返回任务通知的通知值 * * * uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,TickType_t xTicksToWait ); * * * *//* * BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t *pulNotificationValue,TickType_t xTicksToWait ); * * * ulBitsToClearOnEntry函数执行前清零任务通知值那些位 。 * ulBitsToClearOnExit表示在函数退出前清零任务通知值那些位在清 0 前接收到的任务通知值会先被 * 保存到形参*pulNotificationValue 中。 * pulNotificationValue用于保存接收到的任务通知值。 如果 不需要使用则设置为 NULL 即可 。 * xTicksToWait等待消息通知的最大等待时间。 * * * */五、测试程序#include wu_task.h /* * FreeRTOS中任务共存在4种状态: 1Running 运行态 --------------------------当任务处于实际运行状态称之为运行态即CPU的使用权被这个任务占用同一时间仅一个任务处于运行态。 2Ready 就绪态 --------------------------处于就绪态的任务是指那些能够运行没有被阻塞和挂起但是当前没有运行的任务因为同优先级或更高优先级的任务正在运行。 3Blocked 阻塞态 --------------------------如果一个任务因延时或等待信号量、消息队列、事件标志组等而处于的状态被称之为阻塞态。 4Suspended 挂起态 --------------------------类似暂停通过调用函数 vTaskSuspend() 对指定任务进行挂起挂起后这个任务将不被执行只有调用函数 xTaskResume() 才可以将这个任务从挂起态恢复。 * * 常用的一些函数用法: * osThreadSuspend(WUTask02Handle);//挂起态 暂停任务 * osThreadResume(WUTask02Handle);//就绪态 恢复任务 * osThreadTerminate(defaultTaskHandle);//删除任务 终止一个线程的执行并将其从活动线程中移除 * * * */ extern osMessageQId WUQueue01Handle;//声明一个外部的信号量队列消息队列 extern osSemaphoreId WUBinarySem01Handle;//声明一个外部的二进制信号量句柄 extern osSemaphoreId WUCountingSem01Handle;//声明一个外部的计数信号量句柄 extern osMutexId WUMutex01Handle;//声明一个外部的互斥锁互斥量 EventGroupHandle_t eventgroup_handle;// 声明事件组句柄 //WUBinarySem01Handle xSemaphoreCreateBinary(); u8 wu_cs5; u8 d1,d2; uint16_t buf 100; // 声明一个事件位变量初始值为0 EventBits_t wu_bit 0; void WU_Task_1(viod) { BaseType_t status; if(click()1) { wu_cs;buf123; // status xQueueSend(WUQueue01Handle, buf, 0);//写入队列 // if (status pdTRUE) // d11; // else // d10; // if (xSemaphoreGive(WUBinarySem01Handle) pdTRUE) // d11; // else // d10; // if (xSemaphoreGive(WUCountingSem01Handle) pdTRUE) // d11; // else // d10; // xEventGroupSetBits(eventgroup_handle, 0x01);// KEY1 按下 xTaskNotifyGive(WUBinarySem01Handle);//任务通知模拟二值信号量发送成功 Seria1_Printf(TXD:任务通知模拟二值信号量发送成功); } // xSemaphoreTake(WUMutex01Handle, portMAX_DELAY); // Seria1_Printf(TaskH: ON! \r\n); // HAL_Delay(1000); // //osDelay(1000); // Seria1_Printf(TaskH: OK! \r\n); // xSemaphoreGive(WUMutex01Handle); // osDelay(1000); } void WU_Task_2(viod) { LED_GREEN_TogglePin; osDelay(1000); // Seria1_Printf(TaskM: CPU-STOP!!! \r\n); // osDelay(1000); // wu_bit xEventGroupWaitBits(eventgroup_handle, 0x01 | 0x02, pdTRUE, pdFALSE, portMAX_DELAY); // Seria1_Printf(wu_bit:%#x\r\n, wu_bit); } void WU_Task_3(viod) { BaseType_t status; uint32_t ydd 0; if(click_2()1) { wu_cs--; // status xQueueReceive(WUQueue01Handle, buf, 0);//读取队列 // if (status pdTRUE) // d21; // else // d20; // if(xSemaphoreTake(WUBinarySem01Handle, portMAX_DELAY ) pdTRUE) // d21; // else // d20; // if (xSemaphoreTake(WUCountingSem01Handle, 0 ) pdTRUE) // d21; // else // d20; // xEventGroupSetBits(eventgroup_handle, 0x02);// KEY2 按下 ydd ulTaskNotifyTake(pdTRUE, portMAX_DELAY); if(ydd ! 0) Seria1_Printf(RXD:任务通知模拟二值信号量接收成功\r\n); } // xSemaphoreTake(WUMutex01Handle, portMAX_DELAY); // Seria1_Printf(TaskL: ON! \r\n); // HAL_Delay(3000); // //osDelay(3000); // Seria1_Printf(TaskL: OK! \r\n); // xSemaphoreGive(WUMutex01Handle); // osDelay(1000); } void WU_Task_4(viod) { //Seria1_Printf(WU); //Wu_xsp_Proc(); //Wu_Mai_Proc(); Wu_Led_Proc(); Wu_Key_Proc(); WU_OLED_U8G2_ClearBuffer(); WU_OLED_U8G2_SetFont(u8g2_font_wqy13_t_gb2312a);//选择字库 // WU_OLED_U8G2_Printf(0,15,CHINA,温度:%d℃,16); // WU_OLED_U8G2_Printf(0,30,CHINA,湿度:%d,58); // // d1?WU_OLED_U8G2_Printf(0,45,CHINA,写入队列值:%d,buf):WU_OLED_U8G2_Printf(0,45,CHINA,写入队列失败); // d2?WU_OLED_U8G2_Printf(0,60,CHINA,读取队列值:%d,buf):WU_OLED_U8G2_Printf(0,60,CHINA,读取队列失败); WU_OLED_U8G2_SendBuffer(); } void StartTask02(void const * argument) { while(1) { WU_Task_2(); } }参考资料1. FreeRTOS官方文档https://www.freertos.org/2. STM32 HAL库开发手册本文为个人学习笔记整理仅供学习交流使用。