• 【FreeRTOS】任务通知的使用


    ❤️作者主页:凉开水白菜
    ❤️作者简介:共同学习,互相监督,热于分享,多加讨论,一起进步!
    ❤️专栏资料:https://pan.baidu.com/s/1nc1rfyLiMyw6ZhxiZ1Cumg?pwd=free
    ❤️点赞 👍 收藏 ⭐再看,养成习惯

    订阅的粉丝可通过PC端左侧加我微信,可对文章的内容进行一对一答疑!



    前言

    任务通知和信号量、队列、事件等用法相同,在任务之间进行数据传输或者事件传输对比队列和信号量等效率更高,因为任务通知的变量是跟随任务创建时创建的无需像队列事件一样创建结构体可以更节省内存;
    在这里插入图片描述

    发送任务通知

    发送通知,带有通知值并且不保留接收任务原通知值;

    BaseType_t xTaskNotify(TaskHandle_t xTaskToNotify,
    						uint32_t ulValue
    						eNotifyAction eAction)
    
    • 1
    • 2
    • 3

    TaskHandle_t xTaskToNotify:指定发送的任务句柄

    uint32_t ulValue:任务通知值

    eNotifyAction eAction:任务通知更新类型

    typedef enum
    {
    	eNoAction=0,
    	eSetBits,						//更新指定的bit
    	eIncrement,						//通知值加一
    	eSetValueWithOverwrite,			//覆写的方式更新通知值
    	eSetValueWithoutOverwrite		//不覆写通知值
    }eNotifyAction;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    发送通知,不带通知值并且不保留接收任务的通知值,此函数会将接收任务的通知值加一;

    BaseType_txTaskNotifyGive(TaskHandle_t xTaskToNotify);
    
    • 1

    发送通知,带有通知值并且保留接收任务的原通知值;

    BaseType_txTaskNotifyAndQuery(TaskHandle_t xTaskToNotify,
    							uint32_t ulValue,
    							eNotifyAction eAction,
    							uint32_t*pulPreviousNotificationValue);
    
    • 1
    • 2
    • 3
    • 4

    TaskHandle_t xTaskToNotify:指定发送的任务句柄
    uint32_t ulValue:任务通知值

    eNotifyAction eAction:任务通知更新类型

    uint32_t*pulPreviousNotificationValue:用来保存更新前的通知值

    上述的三个函数都是在任务之间使用的,他们的中断版本分别是xTaskNotifyFromISR()、vTaskNotifyGiveFromISR()、XTaskNotiryAndQueryFromISR() ;

    获取任务通知

    获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减一。当任务通知用作二值信号量或者计数信号量的时候使用此函数来获取信号量。

    uint32_tulTaskNotifyTake(BaseType_t xClearCountOnExit,
    						TickType_t xTicksToWait);
    
    • 1
    • 2

    BaseType_t xClearCountOnExit:参数为pdFALSE的话在退出函数ulTaskNotifyTake()的时候任务通知值减一,类似计数型信号量。当此参数为pdTRUE的话在退出函数的时候任务任务通知值清零,类似二值信号量。

    TickType_t xTicksToWait:可设置为portMAX_DELAY:一定等到成功才返回,可以设置为期望的Tick Count,一般用pdMS_TO_TICKS()把ms转换为Tick Count

    返回值:任务通知值减少或者清零之前的值


    此函数也是用来获取任务通知的,不过此函数比ulTaskNotifyTake()更为强大,不管任务通知用作二值信号量、计数型信号量、队列和事件标志组中的哪一种,都可以使用此函数来获取任务通知。但是当任务通知用作二值信号量和计数型信号量的时候推荐使用函数ulTaskNotifyTake()。此函数原型如下:

    BaseType_txTaskNotifyWait(uint32_tulBitsToClearOnEntry,
    						uint32_t ulBitsToClearOnExit,
    						uint32_t*pulNotificationValue,
    						TickType_t xTicksToWait);
    
    • 1
    • 2
    • 3
    • 4

    uint32_tulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取反值进行按位与运算,当此参数为0xffffffff或者ULONG_MAX的时候就会将任务通知值清零。

    uint32_t ulBitsToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将任务通知值与此参数的取反值进行按位与运算,当此参数为0xffffffff或者ULONG_MAX的时候就会将任务通知值清零。

    uint32_t*pulNotificationValue:此参数用来保存任务通知值。

    返回值:pdTRUE:获取到了任务通知。pdFALSE:任务通知获取失败。

    示例

    FreeRTOSConfig文件中对应配置宏configUSE_TASK_NOTIFICATIONS

    static TaskHandle_t send_task_handle = NULL;
    static TaskHandle_t revc_task_handle = NULL;
    
    #define NOTIFICATIONS1 0 /* 模拟二进制信号量 */
    #define NOTIFICATIONS2 0 /* 模拟计数信号量 */
    #define NOTIFICATIONS3 0 /* 模拟邮箱 */
    #define NOTIFICATIONS4 1 /* 模拟事件组 */
    
    #define NOTIFICATION_EVENT1  (1 << 0) /* 事件1 */
    #define NOTIFICATION_EVENT2  (1 << 1) /* 事件2 */
    #define NOTIFICATION_EVENT3  (1 << 2) /* 事件3 */
    
    static void send_task(void *par)
    {
    		uint8_t count = 0;
        while(1)
        {
            #if(NOTIFICATIONS1 == 1)
    				xTaskNotifyGive(revc_task_handle);
    				vTaskDelay(200);   /* 延时200个tick */
    			
    				#elif(NOTIFICATIONS2 == 1)
    				if(count++ <= 100)
    				{
    					xTaskNotifyGive(revc_task_handle);
    				}
    				vTaskDelay(200);   /* 延时200个tick */
    			
    				#elif(NOTIFICATIONS3 == 1)
    				xTaskNotify(revc_task_handle,count++,eSetValueWithOverwrite);
    				vTaskDelay(200);   /* 延时200个tick */
    			
    				#elif(NOTIFICATIONS4 == 1)
    			
    				if(count++ == 5)
    				{ 
    					// 因为是模拟事件标志组的,所以这里发送的值就是将任务通知值的 bit 置 1。
    					xTaskNotify((TaskHandle_t )revc_task_handle,NOTIFICATION_EVENT1,eSetBits);
    				}
    				
    				if(count == 10)
    				{
    					xTaskNotify((TaskHandle_t )revc_task_handle,NOTIFICATION_EVENT2,eSetBits);
    				}
    				
    				if(count == 20)
    				{
    					xTaskNotify((TaskHandle_t )revc_task_handle,NOTIFICATION_EVENT3,eSetBits);
    				}
    				
    				vTaskDelay(100);
    				#endif
    				
        }
    }
    
    static void revc_task(void *par)
    {
    		uint32_t notify_count = 0;
        while(1)
        {
            #if(NOTIFICATIONS1 == 1)
    			  //其中,为pdTRUE的话就是每获取take一次notify,value值复位到0相当于二值信号量
    				notify_count = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    				// 第一次打印是第一次通知,当时值为一, 然后进入阻塞态1000tick那send任务发送五个通知所以下次打印清零前的通知5
    				printf("receive notify! notify_count:%d \r\n",notify_count); 
    				vTaskDelay(1000); // 等待发送一些通知
    			
    				#elif(NOTIFICATIONS2 == 1)
    				//其中,为pdTRUE的话就是每获取take一次notify,value值-1相当于计数信号量
    				notify_count = ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
    				// 打印的是当前剩余的通知数量
    				printf("receive notify! notify_count:%d \r\n",notify_count-1);
    				// 如果任务二阻塞时间比任务一短则当前值永远为0,如果执行比任务一慢则任务值递增
    				// 这里任务2运行一次通知-1,任务一运行两次通知+2,所以notify_count每次+1
    				vTaskDelay(400); 
    			
    				#elif(NOTIFICATIONS3 == 1)
    				xTaskNotifyWait(0,							// 进入函数前不清空
    												0xffffffff,			// 退出函数后清空所有位
    												&notify_count,	// 得到notify
    												portMAX_DELAY);	// 永远等待
    				printf("receive notify! notify_count:%d \r\n",notify_count);
    				
    				#elif(NOTIFICATIONS4 == 1)
    				
    				xTaskNotifyWait(0,							// 进入函数前不清空
    												0xffffffff,			// 退出函数后清空所有位
    												&notify_count,	// 得到notify
    												portMAX_DELAY);	// 永远等待
    				
    				
    				if((notify_count & NOTIFICATION_EVENT1) != 0)
    				{
    					printf("事件1执行!\r\n");
    				}
    				
    				else if((notify_count & NOTIFICATION_EVENT2) != 0)
    				{
    					printf("事件2执行!\r\n");
    				}
    				
    				else if((notify_count & NOTIFICATION_EVENT3) != 0)
    				{
    					printf("事件3执行!\r\n");
    				}
    				
    				#endif
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    模拟事件
    在这里插入图片描述模拟邮箱发送值
    在这里插入图片描述
    模拟计数型信号量
    在这里插入图片描述模拟二进制信号量
    在这里插入图片描述

    结尾

    我是凉开水白菜,我们下文见~

  • 相关阅读:
    【Android Gradle 插件】Gradle 构建机制 ④ ( Gradle 构建生命周期 | 初始阶段 | 配置阶段 | 执行阶段 )
    2023.10.19 关于设计模式 —— 单例模式
    高通平台Android 蓝牙调试和配置手册-- SCO Audio Quality Issues
    Web前端:JavaScript的未来——发展趋势和预测
    nvidia-docker安装指南
    浅析Redis大Key
    。?,放假吗啡快递不好
    消火栓地下水管网压力监测智能化解决方案
    433. 最小基因变化
    python开发数字人助理版
  • 原文地址:https://blog.csdn.net/qq_43581670/article/details/127688318