任务通知
之前我们使用队列、信号量、事件组等方法来控制任务,在这个过程当中我们都需要实现创建对应的结构体,双方通过结构体来进行交流。
同时,我们还可以使用任务通知的方式,来进行控制。
优势:
- 效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组都有大的优势。
更节省内存:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构
缺点:
- 不能发送数据给 ISR,因为ISR没有任务结构体,但是ISR可以使用任务通知的功能
- 数据只能该任务独享
- 无法缓冲数据,任务结构体只有一个任务通知值,只能保持一个数据
- 无法广播给多个任务,任务通知可以多对一,但不能一对多
- 如果发送受阻,发送方不会进入阻塞状态等待
对应代码
已知任务都有对应的结构体TCB,里面的两个数据:
- 一个是 uint8_t 类型,用来表示通知状态
- 一个是 uint32_t 类型,用来表示通知值
typedef struct tskTaskControlBlock { ...... /* configTASK_NOTIFICATION_ARRAY_ENTRIES = 1 */ volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; ...... } tskTCB;其中的ucNotifyState有3种取值:
- taskNOT_WAITING_NOTIFICATION:任务没有在等待通知
- taskWAITING_NOTIFICATION:任务在等待通知
- taskNOTIFICATION_RECEIVED:任务接收到了通知,也被称为 pending(有数据了,待处理)
任务通知的使用
任务通知函数主要分为简化版和专业版:
| 简化版 | 专业版 | |
| 发出通知 | xTaskNotifyGive vTaskNotifyGiveFromISR | xTaskNotify xTaskNotifyFromISR |
| 取出通知 | ulTaskNotifyTake | xTaskNotifyWait |
简化版
其中,简化版当中发出通知可以使TCB当中的val加一,并使通知状态变为pending,取出通知则需要判断val的值,如果为0,则阻塞,大于0,则从阻塞态进入就绪态。
函数原型
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTa skWoken ); uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );主要讲解一下Take函数中参数的含义:
| 参数 | 说明 |
| xClearCountOnExit | 函数返回前是否清零: pdTRUE:把通知值清零 pdFALSE:如果通知值大于 0,则把通知值减一 |
| xTicksToWait | 任务进入阻塞态的超时时间,它在等待通知值大于 0。 0:不等待,即刻返回; portMAX_DELAY:一直等待,直到通知值大于 0; 其他值:Tick Count,可以用 pdMS_TO_TICKS()把 ms 转换为 Tick Count |
| 返回值 | 函数返回之前,在清零或减一之前的通知值。 如果 xTicksToWait 非 0,则返回值有 2 种情况: 1. 大于 0:在超时前,通知值被增加了 2. 等于 0:一直没有其他任务增加通知值,最后超时返 回 0 |
专业版
xTaskNotify 函数功能更强大,可以使用不同参数实现各类功能。
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyActio n eAction ); BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken ); BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );案例
整体思路:先让小车1动,然后到达终点后同时2和3,分别使用xTaskNotifyGive 和 xTaskNotify。
小车1
static void Car1Task (void *params) { struct car *car = (struct car *) params; //直接右移小汽车 while(1) { if(car -> x < g_xres - CAR_LENGTH) { hideCar(car); car->x += 1; if (car->x > g_xres - CAR_LENGTH) { car->x = g_xres - CAR_LENGTH; } showCar(car); } vTaskDelay(50); if(car ->x >= g_xres - CAR_LENGTH) { /* 发送事件 */ //xEventGroupSetBits(g_xEventCar, 1<<0); //发出通知 xTaskNotifyGive(g_TaskHandleCar2); xTaskNotify(g_TaskHandleCar3, 100, eSetValueWithOverwrite); vTaskDelete(NULL); } } }小车2
static void Car2Task (void *params) { struct car *car = (struct car *) params; //等待事件信号 //xEventGroupWaitBits(g_xEventCar, 1 << 0, pdTRUE, pdFALSE, portMAX_DELAY); //等待通知 ulTaskNotifyTake(pdTRUE, portMAX_DELAY); //直接右移小汽车 while(1) { if(car -> x < g_xres - CAR_LENGTH) { hideCar(car); car->x += 1; if (car->x > g_xres - CAR_LENGTH) { car->x = g_xres - CAR_LENGTH; } showCar(car); } vTaskDelay(50); if(car ->x >= g_xres - CAR_LENGTH) { /* 释放信号量 */ // xSemaphoreGive(g_xSemTicks); vTaskDelete(NULL); } } }完整代码
/* * Project: N|Watch * Author: Zak Kemble, contact@zakkemble.co.uk * Copyright: (C) 2013 by Zak Kemble * License: GNU GPL v3 (see License.txt) * Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/ */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "cmsis_os.h" #include "FreeRTOS.h" // ARM.FreeRTOS::RTOS:Core #include "task.h" // ARM.FreeRTOS::RTOS:Core #include "event_groups.h" // ARM.FreeRTOS::RTOS:Event Groups #include "semphr.h" // ARM.FreeRTOS::RTOS:Core #include "draw.h" #include "resources.h" #include "driver_lcd.h" #include "driver_ir_receiver.h" #include "driver_rotary_encoder.h" #include "driver_mpu6050.h" #define CAR_COUNT 3 #define CAR_WIDTH 12 #define CAR_LENGTH 15 #define ROAD_SPEED 6 #define NOINVERT false #define INVERT true #define key1 0 #define key2 1 #define key3 2 static uint32_t g_xres, g_yres, g_bpp; static uint8_t *g_framebuffer; static SemaphoreHandle_t g_xSemTicks; static EventGroupHandle_t g_xEventCar; static TaskHandle_t g_TaskHandleCar2; static TaskHandle_t g_TaskHandleCar3; struct car{ int x; int y; int control_key; }; struct car cars[3] = { {0, 0, key1}, {0, 17, key2}, {0, 34, key3} }; static const byte carImg[] PROGMEM ={ 0x40,0xF8,0xEC,0x2C,0x2C,0x38,0xF0,0x10,0xD0,0x30,0xE8,0x4C,0x4C,0x9C,0xF0, 0x02,0x1F,0x37,0x34,0x34,0x1C,0x0F,0x08,0x0B,0x0C,0x17,0x32,0x32,0x39,0x0F, }; static const byte roadMarking[] PROGMEM ={ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, }; static const byte clearImg[30] ={0}; static void showCar(struct car *car) { draw_bitmap(car->x, car->y, carImg, 15, 16, NOINVERT, 0); draw_flushArea(car->x, car->y, 15, 16); } static void hideCar(struct car *car) { draw_bitmap(car->x, car->y, clearImg, 15, 16, NOINVERT, 0); draw_flushArea(car->x, car->y, 15, 16); } # if 0 static void CarTask(void *params) { struct car *car = (struct car *)params; //获得信号量 xSemaphoreTake(g_xSemTicks, portMAX_DELAY); while(1) { if(car->x < g_xres - CAR_LENGTH) { hideCar(car); car->x += 1; if (car->x > g_xres - CAR_LENGTH) { car->x = g_xres - CAR_LENGTH; } showCar(car); } vTaskDelay(50); if(car->x >= g_xres - CAR_LENGTH) { /* 释放信号量 */ xSemaphoreGive(g_xSemTicks); vTaskDelete(NULL); } } } #endif static void Car1Task (void *params) { struct car *car = (struct car *) params; //直接右移小汽车 while(1) { if(car -> x < g_xres - CAR_LENGTH) { hideCar(car); car->x += 1; if (car->x > g_xres - CAR_LENGTH) { car->x = g_xres - CAR_LENGTH; } showCar(car); } vTaskDelay(50); if(car ->x >= g_xres - CAR_LENGTH) { /* 发送事件 */ //xEventGroupSetBits(g_xEventCar, 1<<0); //发出通知 xTaskNotifyGive(g_TaskHandleCar2); xTaskNotify(g_TaskHandleCar3, 100, eSetValueWithOverwrite); vTaskDelete(NULL); } } } static void Car2Task (void *params) { struct car *car = (struct car *) params; //等待事件信号 //xEventGroupWaitBits(g_xEventCar, 1 << 0, pdTRUE, pdFALSE, portMAX_DELAY); //等待通知 ulTaskNotifyTake(pdTRUE, portMAX_DELAY); //直接右移小汽车 while(1) { if(car -> x < g_xres - CAR_LENGTH) { hideCar(car); car->x += 1; if (car->x > g_xres - CAR_LENGTH) { car->x = g_xres - CAR_LENGTH; } showCar(car); } vTaskDelay(50); if(car ->x >= g_xres - CAR_LENGTH) { /* 释放信号量 */ // xSemaphoreGive(g_xSemTicks); vTaskDelete(NULL); } } } static void Car3Task (void *params) { struct car *car = (struct car *) params; //等待事件信号 //xEventGroupWaitBits(g_xEventCar, 1 << 0, pdTRUE, pdFALSE, portMAX_DELAY); int val; xTaskNotifyWait(~0, ~0, &val, portMAX_DELAY); //直接右移小汽车 while(1) { if(car -> x < g_xres - CAR_LENGTH) { hideCar(car); car->x += 1; if (car->x > g_xres - CAR_LENGTH) { car->x = g_xres - CAR_LENGTH; } showCar(car); } vTaskDelay(50); if(car ->x >= g_xres - CAR_LENGTH) { /* 释放信号量 */ //xSemaphoreGive(g_xSemTicks); vTaskDelete(NULL); } } } void car_test(void) { g_framebuffer = LCD_GetFrameBuffer(&g_xres, &g_yres, &g_bpp); draw_init(); draw_end(); //绘制路标 for(int i = 0; i < 3; i++) { for(int j = 0; j < 8; j++) { draw_bitmap(16 * j, 16 + i * 17, roadMarking, 8, 1, NOINVERT, 0); draw_flushArea(16 * j, 16 + i * 17, 8, 1); } } //测试显示三辆汽车 for(int i = 0; i < 3; i++) { showCar(&cars[i]); } /* g_xSemTicks = xSemaphoreCreateCounting(3, 2); xTaskCreate(CarTask, "Car1", 128, &cars[0], osPriorityNormal, NULL); xTaskCreate(CarTask, "Car2", 128, &cars[1], osPriorityNormal, NULL); xTaskCreate(CarTask, "Car3", 128, &cars[2], osPriorityNormal, NULL); */ //创建事件组 //g_xEventCar = xEventGroupCreate(); xTaskCreate(Car1Task, "car1", 128, &cars[0], osPriorityNormal, NULL); xTaskCreate(Car2Task, "car2", 128, &cars[1], osPriorityNormal+2, &g_TaskHandleCar2); xTaskCreate(Car3Task, "car3", 128, &cars[2], osPriorityNormal+2, &g_TaskHandleCar3); }