FreeRTOS 通知学习
概述
FreeRTOS 通知提供了一种机制,使得任务之间可以发送和接收简单的数据项(通常是整数)。通知可以作为一种轻量级的消息传递或任务间同步的方式。它们特别适用于在任务之间发送简单信号或少量数据,而无需使用队列或信号量的开销。
通知类型
1. 二值信号量
二值信号量用于指示事件的发生。一个任务可以发送通知,这会设置一个标志,另一个任务可以接收通知,这会清除该标志。
实现
void Send_Task(void *pvParameters)
{
while (1)
{
if (Key_Scan(GPIOA, GPIO_PIN_10) == 1)
{
BaseType_t xReturn = xTaskNotifyGive(Receive1_Task_Handle); // 发送通知
if (xReturn == pdPASS)
{
printf("Send_Task Notify to Receive1_Task\r\n");
}
}
if (Key_Scan(GPIOB, GPIO_PIN_4) == 1)
{
BaseType_t xReturn = xTaskNotifyGive(Receive2_Task_Handle); // 发送通知
if (xReturn == pdPASS)
{
printf("Send_Task Notify to Receive2_Task\r\n");
}
}
vTaskDelay(pdMS_TO_TICKS(100)); // 延时100毫秒
}
}
void Receive1_Task(void *pvParameters)
{
while (1)
{
printf("Receive1_Task Waiting for Notify...\r\n");
// 等待通知,清除通知值
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 等待通知
printf("Receive1_Task Notify\r\n");
}
}
void Receive2_Task(void *pvParameters)
{
while (1)
{
printf("Receive2_Task Waiting for Notify...\r\n"); // 示例通知值
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 等待通知
printf("Receive2_Task Notify\r\n");
}
}
实验结果
FreeRTOS STM32F1xx Example
Receive1_Task Waiting for Notify...
Send_Task Notify to Receive2_Task
Receive2_Task Notify
Receive2_Task Waiting for Notify...
Send_Task Notify to Receive2_Task
Receive2_Task Notify
Receive2_Task Waiting for Notify...
Send_Task Notify to Receive1_Task
Receive1_Task Notify
Receive1_Task Waiting for Notify...
Send_Task Notify to Receive2_Task
Receive2_Task Notify
Receive2_Task Waiting for Notify...
Send_Task Notify to Receive1_Task
Receive1_Task Notify
Receive1_Task Waiting for Notify...
2. 计数信号量
计数信号量允许一个任务发送多个通知,另一个任务可以接收这些通知,每次接收时减少计数。
实现
void GIVE_Task(void *pvParameters)
{
while (1)
{
if (Key_Scan(GPIOA, GPIO_PIN_10) == 1)
{
BaseType_t xReturn = xTaskNotifyGive(TAKE_Task_Handle); // 发送通知
if (xReturn == pdPASS)
{
printf("GIVE_Task Notify to TAKE_Task\r\n");
}
}
vTaskDelay(pdMS_TO_TICKS(100)); // 延时100毫秒
}
}
void TAKE_Task(void *pvParameters)
{
while (1)
{
uint32_t r_num = ulTaskNotifyTake(pdFALSE, 0); // 等待通知
printf("TAKE_Task Notify to Receive2_Task, current counting is %d\r\n", r_num);
vTaskDelay(pdMS_TO_TICKS(100)); // 延时100毫秒
}
}
实验结果
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
TAKE_Task Notify to Receive2_Task, current counting is 8
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
TAKE_Task Notify to Receive2_Task, current counting is 15
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
GIVE_Task Notify to TAKE_Task
TAKE_Task Notify to Receive2_Task, current counting is 22
TAKE_Task Notify to Receive2_Task, current counting is 21
TAKE_Task Notify to Receive2_Task, current counting is 20
TAKE_Task Notify to Receive2_Task, current counting is 19
TAKE_Task Notify to Receive2_Task, current counting is 18
TAKE_Task Notify to Receive2_Task, current counting is 17
TAKE_Task Notify to Receive2_Task, current counting is 16
TAKE_Task Notify to Receive2_Task, current counting is 15
TAKE_Task Notify to Receive2_Task, current counting is 14
TAKE_Task Notify to Receive2_Task, current counting is 13
TAKE_Task Notify to Receive2_Task, current counting is 12
TAKE_Task Notify to Receive2_Task, current counting is 11
TAKE_Task Notify to Receive2_Task, current counting is 10
TAKE_Task Notify to Receive2_Task, current counting is 9
TAKE_Task Notify to Receive2_Task, current counting is 8
TAKE_Task Notify to Receive2_Task, current counting is 7
TAKE_Task Notify to Receive2_Task, current counting is 6
TAKE_Task Notify to Receive2_Task, current counting is 5
TAKE_Task Notify to Receive2_Task, current counting is 4
TAKE_Task Notify to Receive2_Task, current counting is 3
TAKE_Task Notify to Receive2_Task, current counting is 2
TAKE_Task Notify to Receive2_Task, current counting is 1
TAKE_Task Notify to Receive2_Task, current counting is 0
3. 数值通知
数值通知允许一个任务向另一个任务发送一个整数值。接收任务可以检索这个值。
实现
void Send_Task(void *pvParameters)
{
while (1)
{
if (Key_Scan(GPIOA, GPIO_PIN_10) == 1)
{
BaseType_t xReturn = xTaskNotify(Receive1_Task_Handle, 11111, eSetValueWithOverwrite); // 发送通知
if (xReturn == pdPASS)
{
printf("Send_Task Notify to Receive1_Task\r\n");
}
}
if (Key_Scan(GPIOB, GPIO_PIN_4) == 1)
{
BaseType_t xReturn = xTaskNotify(Receive2_Task_Handle, 22222, eSetValueWithOverwrite); // 发送通知
if (xReturn == pdPASS)
{
printf("Send_Task Notify to Receive2_Task\r\n");
}
}
vTaskDelay(pdMS_TO_TICKS(100)); // 延时100毫秒
}
}
void Receive1_Task(void *pvParameters)
{
while (1)
{
uint32_t ulValue = 0;
printf("Receive1_Task Waiting for Notify...\r\n");
// 等待通知,清除通知值
BaseType_t xReturn = xTaskNotifyWait(0, 0, &ulValue, portMAX_DELAY); // 等待通知
printf("Receive1_Task Notify: %d\r\n", ulValue);
}
}
void Receive2_Task(void *pvParameters)
{
while (1)
{
uint32_t ulValue = 0;
printf("Receive2_Task Waiting for Notify...\r\n"); // 示例通知值
BaseType_t xReturn = xTaskNotifyWait(0, 0, &ulValue, portMAX_DELAY); // 等待通知
printf("Receive2_Task Notify: %d\r\n", ulValue);
}
}
实验结果
Send_Task Notify to Receive1_Task
Receive1_Task Notify: 11111
Receive1_Task Waiting for Notify...
Send_Task Notify to Receive1_Task
Receive1_Task Notify: 11111
Receive1_Task Waiting for Notify...
Send_Task Notify to Receive2_Task
Receive2_Task Notify: 22222
Receive2_Task Waiting for Notify...
Send_Task Notify to Receive1_Task
Receive2_Task Notify: 22222
Receive2_Task Waiting for Notify...
Send_Task Notify to Receive1_Task
Receive1_Task Notify: 11111
Receive1_Task Waiting for Notify...
Send_Task Notify to Receive1_Task
Receive1_Task Notify: 11111
Receive1_Task Waiting for Notify...
4. 事件组
事件组允许多个任务同时等待多个事件。每个事件由一个32位值中的一个位表示。
实现
void Key_Task(void *pvParameters)
{
while (1)
{
if (Key_Scan(GPIOA, GPIO_PIN_10) == 1)
{
xTaskNotify(Event_Task_Handle, KEY1_EVENT, eSetBits); // 通知Event_Task
printf("Key1_Task Notify to Event_Task\r\n");
}
if (Key_Scan(GPIOB, GPIO_PIN_4) == 1)
{
xTaskNotify(Event_Task_Handle, KEY2_EVENT, eSetBits); // 通知Event_Task
printf("Key2_Task Notify to Event_Task\r\n");
}
vTaskDelay(pdMS_TO_TICKS(100)); // 延时100毫秒
}
}
void Event_Task(void *pvParameters)
{
uint32_t last_notifiedValue = 0; // 上次通知的值
while (1)
{
uint32_t notifiedValue;
BaseType_t x_Return = xTaskNotifyWait(0, 0xffffffff, ¬ifiedValue, portMAX_DELAY); // 等待通知
if (x_Return == pdTRUE)
{
last_notifiedValue |= notifiedValue; // 将当前通知值与上次通知值进行或运算
if (last_notifiedValue == (KEY1_EVENT | KEY2_EVENT))
{
// 如果当前通知值与上次通知值相同,或者两个事件都发生了,则处理事件
printf("All events received---------\r\n");
last_notifiedValue = 0; // 重置上次通知的值
}
}
}
}
实验结果
FreeRTOS STM32F1xx Example
Key1_Task Notify to Event_Task
Key2_Task Notify to Event_Task
All events received---------
Key1_Task Notify to Event_Task
Key2_Task Notify to Event_Task
All events received---------
Key1_Task Notify to Event_Task
Key2_Task Notify to Event_Task
All events received---------
Key1_Task Notify to Event_Task
Key2_Task Notify to Event_Task
All events received---------
关键函数
xTaskNotifyGive(TaskHandle_t xTaskToNotify)
- 用途: 向任务发送通知,增加任务的通知计数。
- 参数:
xTaskToNotify
: 要通知的任务的句柄。
ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait)
- 用途: 等待来自其他任务的通知。
- 参数:
xClearCountOnExit
: 如果为pdTRUE
,则任务的通知计数会被清零。xTicksToWait
: 最大等待时间。
xTaskNotify(TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction)
- 用途: 向任务发送通知,可选地设置或添加一个值。
- 参数:
xTaskToNotify
: 要通知的任务的句柄。ulValue
: 要发送的值。eAction
: 执行的动作 (eSetValueWithOverwrite
,eSetValueWithoutOverwrite
,eIncrement
,eNoAction
)。
xTaskNotifyWait(uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait)
- 用途: 等待来自其他任务的通知,可选地清除任务的通知值中的位。
- 参数:
ulBitsToClearOnEntry
: 进入时要清除的位。ulBitsToClearOnExit
: 退出时要清除的位。pulNotificationValue
: 存储接收到的通知值的指针。xTicksToWait
: 最大等待时间。
最佳实践
- 使用通知进行简单信号传递: 通知最适合用于任务间的简单信号传递。对于更复杂的数据交换,考虑使用队列或信号量。
- 避免无限期阻塞: 总是为
ulTaskNotifyTake
或xTaskNotifyWait
指定超时时间,以避免无限期阻塞。 - 检查返回值: 总是检查通知函数的返回值,以便适当地处理错误。
- 文档化通知使用: 清晰地记录通知的目的和使用方式,以提高代码的可读性和可维护性。
结论
FreeRTOS 通知提供了一种轻量级且高效的机制,用于任务间通信。通过理解不同类型的通知及其使用场景,你可以有效地使用它们来同步和通信任务之间的活动。