RTOS中的任务通知
2026/9/1 17:00:45 网站建设 项目流程

任务通知

之前我们使用队列、信号量、事件组等方法来控制任务,在这个过程当中我们都需要实现创建对应的结构体,双方通过结构体来进行交流。

同时,我们还可以使用任务通知的方式,来进行控制。

优势:

  • 效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组都有大的优势。
  • 更节省内存:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构

缺点:

  • 不能发送数据给 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
取出通知ulTaskNotifyTakexTaskNotifyWait

简化版

其中,简化版当中发出通知可以使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); }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询