一、任务通知的简介
任务通知是 FreeRTOS 中一种用于任务间通信的机制,它允许一个任务向其他任务发送简单的通知或信号,以实现任务间的同步和协作。任务通知通常用于替代二值信号量或事件标志组,提供了更轻量级的任务间通信方式。
大多数任务间通信方法通过中间对象,如队列、信号量或事件组。发送任务写入通信对象,接收任务从通信对象读取。当使用直接任务通知时,顾名思义,发送任务直接向接收任务发送通知,而无需中间对象。
每个 RTOS 任务都有一个任务通知组,每条通知均独立运行,都有“挂起”或“非挂起”的通知状态,以及一个 32 位通知值。常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 可设置任务通知组中的索引数量。在 FreeRTOS V10.4.0 版本前,任务只有单条任务通知(即只能一对一),没有任务通知组。
向任务发送“任务通知” 会将目标任务通知设为“挂起”状态。 正如任务可以阻塞中间对象 (如等待信号量可用的信号量),任务也可以阻塞任务通知, 以等待通知状态变为“挂起”。向任务发送“任务通知”也可以更新目标通知的值(可选),可使用下列任一方法:
- 覆盖原值,无论接收任务是否已读取被覆盖的值。
- 覆盖原值(仅当接收任务已读取被覆盖的值时)。
- 在值中设置一个或多个位。
- 对值进行增量(添加 1)。
RTOS 任务通知功能默认为启用状态,将configUSE_TASK_NOTIFICATIONS 设为0可以禁用。
二、任务通知相关API函数介绍
任务通知相关函数如下:
函数 |
描述 |
xTaskNotify() |
发送通知,带有通知值 |
xTaskNotifyAndQuery() |
发送通知,带有通知值并且保留接收任务的原通知值 |
xTaskNotifyGive() |
发送通知,不带通知值 |
xTaskNotifyFromISR() |
在中断中发送任务通知 |
xTaskNotifyAndQueryFromISR() | |
vTaskNotifyGiveFromISR() | |
ulTaskNotifyTake() |
获取任务通知,可选退出函数时对通知值清零或减1 |
xTaskNotifyWait() |
获取任务通知,可获取通知值和清除通知值的指定位 |
注意:发送通知有相关ISR函数,接收通知没有ISR函数,不能在ISR中接收任务通知。
三、FreeRTOS 任务通知模拟二值信号量实验
任务名 |
任务功能描述 |
start_task |
用于创建其他任务 |
task1 |
用于扫描按键和发送任务通知 |
task2 |
用于接收任务通知并作相应解释 |
代码
#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
/* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,
MAGENTA, GREEN, CYAN, YELLOW,
BROWN, BRRED, GRAY};
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);
lcd_show_string(10, 47, 220, 24, 24, "Notify Bina Sem", RED);
lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);
lcd_draw_rectangle(5, 130, 234, 314, BLACK); /* 画矩形 */
xTaskCreate((TaskFunction_t )start_task, /* 任务函数 */
(const char* )"start_task", /* 任务名称 */
(uint16_t )START_STK_SIZE, /* 任务堆栈大小 */
(void* )NULL, /* 传入给任务函数的参数 */
(UBaseType_t )START_TASK_PRIO, /* 任务优先级 */
(TaskHandle_t* )&StartTask_Handler); /* 任务句柄 */
vTaskStartScheduler();
}
/**
* @brief start_task
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
/* 创建任务2 */
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/**
* @brief task1
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
if (Task2Task_Handler != NULL)
{
key = key_scan(0);
switch (key)
{
case KEY0_PRES: /* 发送任务通知 */
{
xTaskNotifyGive((TaskHandle_t)Task2Task_Handler); /* 接收任务通知的任务句柄 */
break;
}
default:
{
break;
}
}
}
vTaskDelay(10);
}
}
/**
* @brief task2
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task2(void *pvParameters)
{
uint32_t notify_val = 0;
uint32_t task2_num = 0;
while (1)
{
notify_val = ulTaskNotifyTake((BaseType_t )pdTRUE, /* 通知值在函数退出时清零,类似二值信号量 */
(TickType_t )portMAX_DELAY); /* 等待时间 */
printf("%d\n", notify_val);
if (notify_val != 0)
{
lcd_fill(6, 131, 233, 313, lcd_discolor[++task2_num % 11]); /* LCD区域刷新 */
}
}
}
现象
LCD ID:7789
1
1
1
1
1
1
1
此时按下按键 0,让 task1 任务发送任务通知到 task2 任务,可以看到,LCD 发生了变化,如下图所示:
可以看放 LCD 的区域刷新了颜色,这是因为 task2 任务成功接收到任务通知后执行的操作。
多次按下按键 0,LCD 区域都会刷新颜色,这与预计的实验结果相符。
四、reeRTOS 任务通知模拟计数型信号量实验
任务名 |
任务功能描述 |
start_task |
用于创建其他任务 |
task1 |
用于扫描按键和发送任务通知 |
task2 |
用于接收任务通知并作相应解释 |
代码
#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
/* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,
MAGENTA, GREEN, CYAN, YELLOW,
BROWN, BRRED, GRAY};
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);
lcd_show_string(10, 47, 220, 24, 24, "Notify Count Sem", RED);
lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(54, 111, 200, 16, 16, "Notify Value: 0", BLUE);
lcd_draw_rectangle(5, 110, 234, 314, BLACK);
lcd_draw_line(5, 130, 234, 130, BLACK);
xTaskCreate((TaskFunction_t )start_task, /* 任务函数 */
(const char* )"start_task", /* 任务名称 */
(uint16_t )START_STK_SIZE, /* 任务堆栈大小 */
(void* )NULL, /* 传入给任务函数的参数 */
(UBaseType_t )START_TASK_PRIO, /* 任务优先级 */
(TaskHandle_t* )&StartTask_Handler); /* 任务句柄 */
vTaskStartScheduler();
}
/**
* @brief start_task
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
/* 创建任务2 */
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/**
* @brief task1
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
key = key_scan(0);
if (Task2Task_Handler != NULL)
{
switch (key)
{
case KEY0_PRES: /* 发送任务通知 */
{
xTaskNotifyGive((TaskHandle_t)Task2Task_Handler); /* 接收任务通知的任务句柄 */
break;
}
default:
{
break;
}
}
}
vTaskDelay(10);
}
}
/**
* @brief task2
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task2(void *pvParameters)
{
uint32_t notify_val = 0;
uint32_t task2_num = 0;
while (1)
{
notify_val = ulTaskNotifyTake((BaseType_t )pdFALSE, /* 通知值在函数退出时递减,类似计数型信号量 */
(TickType_t )portMAX_DELAY); /* 等待时间 */
lcd_show_xnum(166, 111, notify_val - 1, 2, 16, 0, BLUE); /* 在LCD上显示任务通知值 */
lcd_fill(6, 131, 233, 313, lcd_discolor[++task2_num % 11]); /* LCD区域刷新 */
vTaskDelay(1000);
}
}
效果
快速地多次按下按键 0,可以看到,任务通知的通知值变大,并且 LCD 区域会自动刷新,每刷新一次任务通知的通知值就减 1,直到任务通知的通知值减到 0 为止,如下图所示:
LCD 区域会已知刷新颜色,直到任务通知的通知值为 0,这与预期的实验结果相符。
五、 FreeRTOS 任务通知模拟消息邮箱实验
任务名 |
任务功能描述 |
start_task |
用于创建其他任务 |
task1 |
用于扫描按键和发送任务通知 |
task2 |
用于接收任务通知并作相应解释 |
代码
#include "freertos_demo.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
/* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,
MAGENTA, GREEN, CYAN, YELLOW,
BROWN, BRRED, GRAY};
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);
lcd_show_string(10, 47, 220, 24, 24, "Notify MailBox", RED);
lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);
lcd_draw_rectangle(5, 125, 234, 314, BLACK); /* 画矩形 */
xTaskCreate((TaskFunction_t )start_task, /* 任务函数 */
(const char* )"start_task", /* 任务名称 */
(uint16_t )START_STK_SIZE, /* 任务堆栈大小 */
(void* )NULL, /* 传入给任务函数的参数 */
(UBaseType_t )START_TASK_PRIO, /* 任务优先级 */
(TaskHandle_t* )&StartTask_Handler); /* 任务句柄 */
vTaskStartScheduler();
}
/**
* @brief start_task
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
/* 创建任务2 */
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/**
* @brief task1
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
key = key_scan(0);
if ((Task2Task_Handler != NULL) && (key != 0))
{
printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n", key);
xTaskNotify((TaskHandle_t )Task2Task_Handler, /* 接收任务通知的任务句柄 */
(uint32_t )key, /* 要更新的bit位 */
(eNotifyAction )eSetValueWithOverwrite); /* 更新方式为覆写 */
}
vTaskDelay(10);
}
}
/**
* @brief task2
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task2(void *pvParameters)
{
uint32_t notify_val = 0;
uint32_t task2_num = 0;
while (1)
{
xTaskNotifyWait((uint32_t )0x00000000, /* 进入函数时,不清除任务通知值 */
(uint32_t )0xFFFFFFFF, /* 函数退出时,清零任务通知值 */
(uint32_t* )¬ify_val, /* 接收到的通知值 */
(TickType_t )portMAX_DELAY); /* 等待时间 */
switch (notify_val)
{
case KEY0_PRES: /* LCD区域刷新 */
{
printf("接收到的通知值为:%d\r\n", notify_val);
lcd_fill(6, 126, 233, 313, lcd_discolor[++task2_num % 11]);
break;
}
case KEY1_PRES: /* LED0闪烁 */
{
printf("接收到的通知值为:%d\r\n", notify_val);
LED0_TOGGLE();
break;
}
default:
{
break;
}
}
}
}
演示
LCD ID:7789
LCD ID:7789
任务通知模拟消息邮箱发送,发送的键值为:1
接收到的通知值为:1
任务通知模拟消息邮箱发送,发送的键值为:1
接收到的通知值为:1
任务通知模拟消息邮箱发送,发送的键值为:2
接收到的通知值为:2
任务通知模拟消息邮箱发送,发送的键值为:2
接收到的通知值为:2
六、FreeRTOS 任务通知模拟事件标志组实验
任务名 |
任务功能描述 |
start_task |
用于创建其他任务 |
task1 |
用于扫描按键和发送任务通知 |
task2 |
用于接收任务通知并作相应解释 |
代码
#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
#define EVENTBIT_0 (1<<0) /* 事件位 */
#define EVENTBIT_1 (1<<1)
#define EVENTBIT_ALL (EVENTBIT_0 | EVENTBIT_1)
/******************************************************************************************************/
/* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,
MAGENTA, GREEN, CYAN, YELLOW,
BROWN, BRRED, GRAY};
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);
lcd_show_string(10, 47, 220, 24, 24, "Notify Event Group", RED);
lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30,110,220,16,16,"Event Group Value: 0",BLUE);
lcd_draw_rectangle(5,130,234,314,BLACK); /* 画矩形 */
xTaskCreate((TaskFunction_t )start_task, /* 任务函数 */
(const char* )"start_task", /* 任务名称 */
(uint16_t )START_STK_SIZE, /* 任务堆栈大小 */
(void* )NULL, /* 传入给任务函数的参数 */
(UBaseType_t )START_TASK_PRIO, /* 任务优先级 */
(TaskHandle_t* )&StartTask_Handler); /* 任务句柄 */
vTaskStartScheduler();
}
/**
* @brief start_task
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
/* 创建任务2 */
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/**
* @brief task1
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
if (Task2Task_Handler != NULL)
{
key = key_scan(0);
switch (key)
{
case KEY0_PRES:
{
xTaskNotify((TaskHandle_t )Task2Task_Handler, /* 接收任务通知的任务句柄 */
(uint32_t )EVENTBIT_0, /* 要更新的bit位 */
(eNotifyAction )eSetBits); /* 更新方式为设置bit位 */
printf("将bit0位置1\r\n");
break;
}
case KEY1_PRES:
{
xTaskNotify((TaskHandle_t )Task2Task_Handler, /* 接收任务通知的任务句柄 */
(uint32_t )EVENTBIT_1, /* 要更新的bit位 */
(eNotifyAction )eSetBits); /* 更新方式为设置bit位 */
printf("将bit1位置1\r\n");
break;
}
default:
{
break;
}
}
}
vTaskDelay(10);
}
}
/**
* @brief task2
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task2(void *pvParameters)
{
uint32_t notify_val = 0;
uint32_t event_val = 0;
uint32_t task2_num = 0;
while (1)
{
xTaskNotifyWait((uint32_t )0x00000000, /* 进入函数时,不清除任务通知值 */
(uint32_t )0xFFFFFFFF, /* 函数退出时,清零任务通知值 */
(uint32_t* )¬ify_val, /* 接收到的通知值 */
(TickType_t )portMAX_DELAY); /* 等待时间 */
if (notify_val & EVENTBIT_0) /* 模拟事件0发生 */
{
event_val |= EVENTBIT_0;
}
else if (notify_val & EVENTBIT_1) /* 模拟事件1发生 */
{
event_val |= EVENTBIT_1;
}
lcd_show_xnum(182, 110, event_val, 1, 16, 0, BLUE); /* LCD上显示事件值 */
if (event_val == EVENTBIT_ALL)
{
LED0_TOGGLE();
printf("任务通知模拟事件标志组接收成功\r\n");
event_val = 0; /* 模拟事件组清零 */
lcd_fill(6, 131, 233, 313, lcd_discolor[++task2_num % 11]);
}
}
}
演示
LCD ID:7789
将bit0位置1
任务通知模拟事件标志组接收成功
将bit1位置1
将bit0位置1
任务通知模拟事件标志组接收成功
将bit1位置1