任务通知简介
任务通知在FreeRTOS
中是一个可选的功能,要使用任务通知的话就需要将宏 configUSR_TASK_NOTIFICATIONS
定义为1。
FreeRTOS
的每个任务都有一个32位的通知值,任务控制款中的成员变量ulNotifiedValue
就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:
- 不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没有被处理)。
- 覆盖接收任务的通知值。
- 更新接收任务通知值的一个或多个bit。
- 增加接收任务的通知值。
合理、灵活地使用上面这些更改任务通知值的方法可以在一些场合中替代队列、二值信号量、计数型信号量和时间标志组。使用任务通知来实现二值信号量功能的时候,解除任务阻塞的时间比直接使用二值信号量要快45%(FreeRTOS
官方测试结果,使用v8.1.2版本中的二值信号量,GCC编译器,-O2优化条件下测试的,没有使能断言函数configASSERT()
),并且使用的RAM
更少!
任务通知的发送使用函数xTaskNotify()
或者xTaskNotifyGive()
(还有次函数的中断版本)来完成,这个通知值会一直保存着,直到接收任务调用函数xTaskNotifyWait()
或者ulTaskNotifyTake()
来获取这个通知值。假如接收任务因为等待任务通知而阻塞的话那么在接收到任务通知以后就会解除阻塞态。
任务通知虽然可以提高速度,并且减少RAM的使用,但是任务通知也是由使用限制的:
FreeRTOS
的任务通知只能有一个接收任务,其实大多数的应用都是这种情况。
- 接收任务可以因为接收任务通知而进入阻塞态,但是发送任务不会因为任务通知发送失败而阻塞。
xTaskNotify()
此函数用于发送任务通知,此函数发送任务通知的时候带有通知值
,此函数是个宏
,真正执行的函数xTaskGenericNotify()
,函数原型如下:
1 2 3
| BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction )
|
xTaskGenericNotify()
1 2 3 4 5
| BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t * pulPreviousNotificationValue )
|
- [
xTaskToNotify
] 被通知的任务句柄。
- [
uxIndexToNotify
]
- [
ulValue
] 更新的通知值
- [
eAction
] 枚举类型,指明更新通知值的方法,枚举变量成员以及作用见表1-2所示。
- [
pulPreviousNotificationValue
] 回传未被更新的任务通知值。如果不需要回传未被更新的任务通知值,这里设置为NULL。
表1-2
1 2 3 4 5 6 7 8 9 10 11
| typedef enum { eNoAction = 0, eSetBits, eIncrement, eSetValueWithOverwrite, eSetValueWithoutOverwrite
} eNotifyAction;
|
xTaskNotifyWait
函数原型:
1 2 3 4
| BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
|
- ulBitsToClearOnEntry
- 若调用
xTaskNotifyWait()
前,task没有notification pending
,则ulBitsToClearOnEntry
中所有置1的位,会清0notification value
所对应的位。
ulBitsToClearOnEntry
为0x01时,task的notification value
的第0位置清0; ulBitsToClearOnExit
为0xffffffff(ULONG_MAX)时, tasknotification value
所有位将清零
- ulBitsToClearOnExit
- 若xTaskNotifyWait()因为有
notification pending
而退出时,pulNotificationValue将先保存notification value
内容之后,ulBitsToClearOnExit中置的位,将清空task notification value
中对应的位
- pulNotificationValue
- pulNotificationValue 保存
notification value
被ulBitsToClearOnExit清零任何一位之前的值。
- pulNotificationValue 是可选的,不需要时置为NULL。
- xTicksToWait
pdMS_TO_TICKS()
, portMAX_DELAY
, porPeriod_tick_ms
例子1 eSetBits
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 111 112 113 114 115 116 117 118 119 120 121 122
|
#include "main.h" #include "FreeRTOS.h" #include "semphr.h" #include "task.h" #include "debug_printf.h"
#include "gd32f1x0.h" #include "gd32f1x0r_eval.h" #include <stdio.h> #include <limits.h> #include "key.h"
#define LED1_TASK_PRIO 0 #define LED1_STK_SIZE 50 TaskHandle_t LED1Task_Handler; void led1_task(void *pvParameters);
#define LED2_TASK_PRIO 0 #define LED2_STK_SIZE 50 TaskHandle_t LED2Task_Handler; void led2_task(void *pvParameters);
int main(void) { key_init(); debug_printf_init(EVAL_COM0); gd_eval_led_init(LED1); gd_eval_led_init(LED2); gd_eval_led_init(LED3);
xTaskCreate((TaskFunction_t)led1_task, \ (const char*)"led1_task", \ (uint16_t)LED1_STK_SIZE, \ (void*)NULL, \ (UBaseType_t)LED1_TASK_PRIO, \ (TaskHandle_t*)&LED1Task_Handler); xTaskCreate((TaskFunction_t)led2_task, \ (const char*)"led2_task", \ (uint16_t)LED2_STK_SIZE, \ (void*)NULL, \ (UBaseType_t)LED2_TASK_PRIO, \ (TaskHandle_t*)&LED2Task_Handler); app_main(); vTaskStartScheduler();
while (1) {
} }
void led1_task(void *pvParameters) { uint32_t count = 0; while(1) { gd_eval_led_toggle(LED1); count++; vTaskDelay(1000); if(count >= 10) { xTaskNotify(LED2Task_Handler, (1UL << 8UL), eSetBits); count = 0; } } }
void led2_task(void *pvParameters) { uint32_t ulNotifiedValue; while(1) { xTaskNotifyWait(0x00, ULONG_MAX, &ulNotifiedValue, portMAX_DELAY); if((ulNotifiedValue & 0x100) != 0) { printf("count: 10\n"); }
} }
|
串口打印结果:
例子2 eNoAction
使用 eNoAction
方式发送通知,将不使用通知值,其功能类似于 xTaskNotifyGive()
。
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
|
void led1_task(void *pvParameters) { uint32_t count = 0; uint32_t notify_value = 1; while(1) { gd_eval_led_toggle(LED1); count++; vTaskDelay(1000); if(count >= 10) { xTaskNotify(LED2Task_Handler, notify_value, eNoAction); count = 0; } } }
void led2_task(void *pvParameters) { uint32_t ulNotifiedValue; while(1) { xTaskNotifyWait(0x00, ULONG_MAX, &ulNotifiedValue, portMAX_DELAY); printf("ulNotifiedValue: %d\n", ulNotifiedValue); gd_eval_led_toggle(LED2); } }
|
串口打印结果:
例子3 eIncrement
使用 eIncrement
模式xTaskNotify() 函数,每调用一次任务的通知值就会加一。
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
|
void led1_task(void *pvParameters) { uint32_t count = 0; static uint32_t notify_value = 1; while(1) { gd_eval_led_toggle(LED1); count++; vTaskDelay(1000); if(count >= 2) { xTaskNotify(LED2Task_Handler, 0, eIncrement); xTaskNotify(LED2Task_Handler, 0, eIncrement); count = 0; } } }
void led2_task(void *pvParameters) { uint32_t ulNotifiedValue; while(1) { xTaskNotifyWait(0x00, ULONG_MAX, &ulNotifiedValue, portMAX_DELAY); printf("ulNotifiedValue: %d\n", ulNotifiedValue); gd_eval_led_toggle(LED2); } }
|
xTaskNotifyGive()
发送任务通知,相对于函数xTaskNotify()
,此函数发送任务通知的时候不带有通知值。此函数只是将任务通知值简单加一。此函数是个宏,真正执行的是函数xTaskGenericNotify()
,此函数原型如下:
1
| BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify )
|
参数:
xTaskToNotify
:任务句柄,指定任务通知是发送给哪个任务的。
返回值:
pdPASS
:此函数只会返回pdPASS
。
vTaskNotifyGiveFromISR()
此函数为xTaskNotifyGive()
的中断版本,用在中断服务函数中,函数原型如下:
1 2
| void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken )
|
参数:
xTaskToNotify
:任务句柄,指定任务通知是发送给哪个任务的。
pxHigherPriorityTaskWoken
:退出此函数以后是否进行任务切换,这个变量的值会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE
的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
无。
例子1 xTaskNotifyGive() & ulTaskNotifyTake()
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 111 112 113 114 115 116 117 118 119 120 121 122 123 124
|
#include "main.h" #include "FreeRTOS.h" #include "semphr.h" #include "task.h" #include "debug_printf.h"
#include "gd32f1x0.h" #include "gd32f1x0r_eval.h" #include <stdio.h> #include "key.h"
#define LED1_TASK_PRIO 0 #define LED1_STK_SIZE 50 TaskHandle_t LED1Task_Handler; void led1_task(void *pvParameters);
#define LED2_TASK_PRIO 0 #define LED2_STK_SIZE 50 TaskHandle_t LED2Task_Handler; void led2_task(void *pvParameters);
static char pcWriteBuffer[256] = {0};
int main(void) { key_init(); debug_printf_init(EVAL_COM0); gd_eval_led_init(LED1); gd_eval_led_init(LED2); gd_eval_led_init(LED3);
xTaskCreate((TaskFunction_t)led1_task, \ (const char*)"led1_task", \ (uint16_t)LED1_STK_SIZE, \ (void*)pv_string, \ (UBaseType_t)LED1_TASK_PRIO, \ (TaskHandle_t*)&LED1Task_Handler); xTaskCreate((TaskFunction_t)led2_task, \ (const char*)"led2_task", \ (uint16_t)LED2_STK_SIZE, \ (void*)NULL, \ (UBaseType_t)LED2_TASK_PRIO, \ (TaskHandle_t*)&LED2Task_Handler); app_main(); vTaskStartScheduler();
while (1) {
} }
void led1_task(void *pvParameters) { uint32_t count = 1; while(1) { gd_eval_led_toggle(LED1); vTaskDelay(1000); printf("led1_task(%d)...\n", count); count++; if (count > 10) { count = 1; xTaskNotifyGive(LED2Task_Handler); } } }
void led2_task(void *pvParameters) { while(1) { printf("led2_task...\n"); ulTaskNotifyTake(pdTRUE, portMAX_DELAY); gd_eval_led_toggle(LED2); vTaskDelay(500); } }
void app_main(void) { }
|