STM32任务通知

news/2024/11/29 5:45:59/

目录

什么是任务通知?

任务通知值的更新方式

任务通知的优势和劣势

任务通知的优势

任务通知的劣势

任务通知相关 API 函数

2. 等待通知

实操

1. 模拟二值信号量

2. 模拟计数型信号量

3. 模拟事件标志组

4. 模拟邮箱


什么是任务通知?

FreeRTOS 从版本 V8.2.0 开始提供任务通知这个功能,每个任务都有一个 32 位的通知值。按照
FreeRTOS 官方的说法,使用消息通知比通过二进制信号量方式解除阻塞任务快 45% , 并且更加
省内存(无需创建队列)。
在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件标志组,可以替代长度为 1
的队列(可以保存一个 32 位整数或指针值),并且任务通知速度更快、使用的 RAM 更少!

任务通知值的更新方式

FreeRTOS 提供以下几种方式发送通知给任务 :
  • 发送消息给任务,如果有通知未读, 不覆盖通知值
  • 发送消息给任务,直接覆盖通知值
  • 发送消息给任务,设置通知值的一个或者多个位
  • 发送消息给任务,递增通知值
  • 通过对以上方式的合理使用,可以在一定场合下替代原本的队列、信号量、事件标志组等。

任务通知的优势和劣势

任务通知的优势

1. 使用任务通知向任务发送事件或数据,比使用队列、事件标志组或信号量快得多。
2. 使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。

任务通知的劣势

1. 只有任务可以等待通知,中断服务函数中不可以,因为中断没有 TCB
2. 通知只能一对一,因为通知必须指定任务。
3. 等待通知的任务可以被阻塞, 但是发送消息的任务,任何情况下都不会被阻塞等待。
4. 任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保
持一个数据。

任务通知相关 API 函数

1. 发送通知
函数
描述
xTaskNotify()
发送通知,带有通知值
xTaskNotifyAndQuery()
发送通知,带有通知值并且保留接收任务的原通知值
xTaskNotifyGive()
发送通知,不带通知值
xTaskNotifyFromISR()
在中断中发送任务通知
xTaskNotifyAndQueryFromISR()
在中断中发送任务通知
vTaskNotifyGiveFromISR()
在中断中发送任务通知
BaseType_t xTaskNotify ( TaskHandle_t xTaskToNotify ,
uint32_t ulValue ,
eNotifyAction eAction );
参数:
xTaskToNotify :需要接收通知的任务句柄;
ulValue :用于更新接收任务通知值, 具体如何更新由形参 eAction 决定;
eAction :一个枚举,代表如何使用任务通知的值;

枚举值
描述
eNoAction
发送通知,但不更新值(参数 ulValue 未使用)
eSetBits
被通知任务的通知值按位或 ulValue 。(某些场景下可代替事 件组,效率更高)
eIncrement
被通知任务的通知值增加 1 (参数 ulValue 未使用),相当于 xTaskNotifyGive
eSetValueWithOverWrite
被通知任务的通知值设置为 ulValue 。(某些场景下可代替 xQueueOverwrite ,效率更高)
eSetValueWithoutOverwrite
如果被通知的任务当前没有通知,则被通知的任务的通知值 设为ulValue
如果被通知任务没有取走上一个通知,又接收到了一个通
知,则这次通知值丢弃,在这种情况下视为调用失败并返回 pdFALSE
(某些场景下可代替 xQueueSend ,效率更高)
返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS
BaseType_t xTaskNotifyAndQuery ( TaskHandle_t xTaskToNotify ,
uint32_t ulValue ,
eNotifyAction eAction ,
uint32_t * pulPreviousNotifyValue );
参数:
xTaskToNotify :需要接收通知的任务句柄; ulValue :用于更新接收任务通知值, 具体如何更新
由形参 eAction 决定; eAction :一个枚举,代表如何使用任务通知的值;
pulPreviousNotifyValue :对象任务的上一个任务通知值,如果为 NULL , 则不需要回传, 这个时
候就等价于函数 xTaskNotify()
返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS
BaseType_t xTaskNotifyGive ( TaskHandle_t xTaskToNotify );
参数:
xTaskToNotify :接收通知的任务句柄, 并让其自身的任务通知值加 1
返回值:
总是返回 pdPASS

2. 等待通知

等待通知 API 函数只能用在任务,不可应用于中断中!
函数
描述
ulTaskNotifyTake()
获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减 一。当任务通知用作二值信号量或者计数信号量的时候,使用此函数来获取信号量。
xTaskNotifyWait()
获取任务通知,比  ulTaskNotifyTak() 更为复杂,可获取通知值和清除通知值的指定位
uint32_t ulTaskNotifyTake ( BaseType_t xClearCountOnExit ,
TickType_t xTicksToWait );
参数:
xClearCountOnExit :指定在成功接收通知后,将通知值清零或减 1 pdTRUE :把通知值清零(二
值信号量); pdFALSE :把通知值减一(计数型信号量); xTicksToWait :阻塞等待任务通知值
的最大时间;
返回值:
0 :接收失败 非 0 :接收成功,返回任务通知的通知值
BaseType_t xTaskNotifyWait ( uint32_t ulBitsToClearOnEntry ,
uint32_t ulBitsToClearOnExit ,
uint32_t * pulNotificationValue ,
TickType_t xTicksToWait );

ulBitsToClearOnEntry :函数执行前清零任务通知值那些位 。
ulBitsToClearOnExit :表示在函数退出前,清零任务通知值那些位,在清 0 前,接收到的任务通知值会先被保存到形参 *pulNotificationValue 中。
pulNotificationValue :用于保存接收到的任务通知值。 如果不需要使 用,则设置为 NULL 即可 。 xTicksToWait :等待消息通知的最大等待时间。

实操

1. 模拟二值信号量

void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){xTaskNotifyGive(TaskReceiveHandle);printf("二值信号量放入成功\r\n");}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}osDelay(10);}/* USER CODE END StartTaskSend */
}/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t rev=0;/* Infinite loop */for(;;){if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){rev=ulTaskNotifyTake(pdTRUE, portMAX_DELAY);if(rev!=0)printf("二值信号量获取成功\r\n");}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(10);}/* USER CODE END StartTaskReceive */
}/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application *//* USER CODE END Application */

2. 模拟计数型信号量

/* USER CODE END Header_StartTaskSend */
void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){xTaskNotifyGive(TaskReceiveHandle);printf("计数信号量放入成功\r\n");}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}osDelay(10);}/* USER CODE END StartTaskSend */
}/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t rev=0;/* Infinite loop */for(;;){if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){rev=ulTaskNotifyTake(pdFALSE, portMAX_DELAY);if(rev!=0)printf("计数信号量获取成功 %d\r\n",rev);}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(10);}/* USER CODE END StartTaskReceive */
}/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application *//* USER CODE END Application */

3. 模拟事件标志组

/* USER CODE END Header_StartTaskSend */
void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){printf("将bit0位置1\r\n");xTaskNotify(TaskReceiveHandle, 0x01, eSetBits);}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){printf("将bit1位置1\r\n");xTaskNotify(TaskReceiveHandle, 0x02, eSetBits);}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(10);}/* USER CODE END StartTaskSend */
}/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t notify_val = 0, event_bit = 0;/* Infinite loop */for(;;){xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);if (notify_val & 0x01)event_bit |= 0x01;if (notify_val & 0x02)event_bit |= 0x02;if (event_bit == (0x01 | 0x02)){printf("任务通知模拟事件标志组接收成功!\r\n");event_bit = 0;}osDelay(1);}/* USER CODE END StartTaskReceive */
}/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application *//* USER CODE END Application */

4. 模拟邮箱

/* USER CODE END Header_StartTaskSend */
void StartTaskSend(void const * argument)
{/* USER CODE BEGIN StartTaskSend *//* Infinite loop */for(;;){if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){printf("按键1按下\r\n");xTaskNotify(TaskReceiveHandle, 1, eSetValueWithoutOverwrite);}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){printf("按键2按下\r\n");xTaskNotify(TaskReceiveHandle, 2, eSetValueWithoutOverwrite);}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(10);}/* USER CODE END StartTaskSend */
}/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{/* USER CODE BEGIN StartTaskReceive */uint32_t notify_val = 0, event_bit = 0;/* Infinite loop */for(;;){xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);printf("接收到的通知值为:%d\r\n", notify_val);osDelay(1);}/* USER CODE END StartTaskReceive */
}/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application *//* USER CODE END Application */


http://www.ppmy.cn/news/429205.html

相关文章

使用ACR及MIUI自带通话录音实现通话录音读取及上传

虽然在Android5.0之前在AudioRecord开放给第三方的初始化方法中VOICE_CALL音频源是可以使用的,但是在厂商的适配过程中很多还是不能用,因此在低版本中一直是通过AudioRecord读取MIC录音源实现。 很多厂商在做系统优化时,使用MIC仍然无法通话…

Android 通话录音功能

通话录音功能因为涉及隐私问题,Android 6.0上就移除官方的通话录音接口,只能通过其他方式去获取调用。 录音时需要设置音频类型,系统中定义以下几种 (MediaRecorder.AudioSource) CAMCORDER 录音来源于同方向的相机麦…

android实现通话录音获取上传实现过程记录。

项目里提了一个需求,需要通话录音功能(录制双方的声音),并上传到后台。(软件是内部人员工作使用不涉及个人隐私) 首先想到的肯定是用APP来进行录音,可控性比较高,测试了android自带的MediaRecorder与AudioRecord结果发现都只能录…

uniapp 移动端通话录音上传

记录一下近半个月的一个项目解决思路 需求是这样的 1,通话双向录音 2,监听移动端通话状态 3,挂断后上传通话录音到后端进行统计 先说下第一点 通话双向录音,这个在android 6之前还有办法实现,但是现在基本都是单向的&a…

通话录音

当我拿到Demo时候,就觉得是个很简单的功能。然而问题就在测试时候完全暴露出来了,不同手机不同机型由于厂家修改系统而导致的一堆奇葩问题接踵而至,手机通话状态监听不到、通话状态接收多次、通话状态混乱等问题。 下面就是根据测试的手机机…

分享!!前端也需要了解的一些技术!!

目录 1、临时路径共享使用 2、关闭指定端口号的进程 1、临时路径共享使用 场景:自己开发中的pc、移动端、app等,由于还在开发中,没有上线,此时有的人需要暂时使用它么,那么就可以创建一个临时路径,共享给…

【Docker】技术架构演进

基本概念 应用(Application) / 系统(System) ​ 为了完成一整套服务的一个程序或者一组相互配合的程序群。生活例子类比:为了完成一项任务,而搭建的由一个人或者一群相互配的人组成的团队。 模块&#xff0…

物理内存与虚拟内存--问答

1.请简述Linux内核在理想情况下页面分配器(page allocator)是如何分配出连续物理页面的。 2.在页面分配器中,如何从分配掩码(gfp_mask)中确定可以从哪些zone中分配内存? 3&#xff0…