FreeRTOS教程7 事件组

news2024/11/15 9:39:08

目录

1、准备材料

2、学习目标

3、前提知识

3.1、什么是事件组?

3.1、事件组特征

3.1.1、事件组、事件标志和事件位

3.1.2、EventBits_t 数据类型

3.1.3、多个任务访问

3.2、创建事件组

3.3、操作事件组

3.4、xEventGroupWaitBits() API 函数

3.4.1、uxBitsToWaitFor 和 xWaitForAllBits 参数

3.4.2、xClearOnExit 参数

3.5、xEventGroupSync() API 函数

3.5.1、函数返回值

3.5.2、应用举例

3.5、删除事件组

4、实验一:使用事件组进行事件管理

4.1、实验目标

4.2、CubeMX相关配置

4.3、添加其他必要代码

4.4、烧录验证

4.5、测试 xWaitForAllBits 参数

5、实验二:使用事件组进行任务同步

5.1、实验目标

5.2、CubeMX相关配置

5.3、添加其他必要代码

5.4、烧录验证

6、注释详解

参考资料


1、准备材料

正点原子stm32f407探索者开发板V2.4

STM32CubeMX软件(Version 6.10.0)

Keil µVision5 IDE(MDK-Arm)

野火DAP仿真器

XCOM V2.6串口助手

2、学习目标

本文主要学习 FreeRTOS 事件组的相关知识,包括事件组概述、事件组特征、创建事件组、操作事件组、删除事件组等知识

3、前提知识

3.1、什么是事件组?

事件组(event group)也是FreeRTOS中另外一种进程间通信技术,事件组适用于多个事件触发一个或多个任务运行,可以实现事件的广播,还可以实现多个任务的同步运行,如下所述

  • 事件组允许任务等待一个或多个事件的组合
  • 事件组会解除所有等待同一事件的任务的阻塞状态

3.1、事件组特征

3.1.1、事件组、事件标志和事件位

事件 “标志” 是一个布尔值(1 或 0),用于指示事件是否发生,事件 “组” 是一组事件标志,事件标志只能为 1 或 0 ,允许事件标志的状态存储在单个位中,并且事件组中所有事件标志的状态存储在单个变量中

事件组中每个事件标志的状态由 EventBits_t 类型变量中的单个位表示。因此,事件标志也称为事件 “位” ,如果 EventBits_t 变量中的某个位设置为 1 ,则该位表示的事件已发生,否则如果 EventBits_t 变量中的某个位设置为 0 ,则该位表示的事件尚未发生

如下图所示显示了各个事件标志如何映射到 EventBits_t 类型变量中的各个位 (注释1)

3.1.2、EventBits_t 数据类型

一个事件组对象有一个变量类型为 EventBits_t 的内部变量用于存储事件标志位,该变量可以设置为 16 位或 32 位,具体由参数 configUSE_16_BIT_TICKS 所决定,当参数设置为 1 时,那么每个事件组包含 8 个可用的事件位(包括 8 个保留位),否则设置为 0 时,每个事件组包含 24 个可用的事件位(包括 8 个保留位)

3.1.3、多个任务访问

事件组本身就是对象,任何知道其存在的任务或 ISR 都可以访问它们。任意数量的任务可以在同一事件组中设置位,并且任意数量的任务可以从同一事件组中读取位

3.2、创建事件组

一个事件组在使用之前必须先创建,如下所示为使用动态/静态内存分配创建一个事件组的 API 函数

/**
  * @brief  动态分配内存创建事件组函数
  * @retval 返回成功创建的事件组的句柄,返回NULL表示因内存空间不足创建失败
  */
EventGroupHandle_t xEventGroupCreate(void);
 
/**
  * @brief  静态分配内存创建事件组函数
  * @param  pxEventGroupBuffer:指向StaticEventGroup_t类型的变量,该变量用于存储事件组数据结构体
  * @retval 返回成功创建的事件组的句柄,返回NULL表示因pxEventGroupBuffer空间不足创建失败
  */
EventGroupHandle_t xEventGroupCreateStatic(
								StaticEventGroup_t *pxEventGroupBuffer);

3.3、操作事件组

FreeRTOS 提供了两组 API 来对事件组的某些位进行置位和清零两种操作,具体如下所示

/**
  * @brief  将事件组某些位置位
  * @param  xEventGroup:要设置位的事件组
  * @param  uxBitsToSet:指定要在事件组中设置的一个或多个位的按位值,例如设置为0x09表示置位3 和位0
  * @retval 调用 xEventGroupSetBits()返回时事件组的值
  */
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,
							   const EventBits_t uxBitsToSet);
 
/**
  * @brief  将事件组某些位清零
  * @param  xEventGroup:要在其中清除位的事件组
  * @param  uxBitsToSet:表示要在事件组中清除一个或多个位的按位值
  * @retval 返回清除指定位之前的事件组的值
  */
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,
								 const EventBits_t uxBitsToClear);
 
/**
  * @brief  上述两个函数的中断安全版本
  * @param  pxHigherPriorityTaskWoken:用于通知应用程序编写者是否应该执行上下文切换
  * @retval 消息已发送到RTOS守护进程任务,则返回pdPASS,否则将返回pdFAIL
  */
BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,
									 const EventBits_t uxBitsToSet,
									 BaseType_t *pxHigherPriorityTaskWoken);
 
BaseType_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup,
									   const EventBits_t uxBitsToClear);
 
/*example1: 将事件组 EventGroup_Test 的位 1 和 3 置位*/
EventBits_t return_value;
return_value = xEventGroupSetBits(EventGroup_Test, 0x0A);
 
/*example2: 将事件组 EventGroup_Test 的位 0 和 2 清零*/
EventBits_t return_value;
return_value = xEventGroupClearBits(EventGroup_Test, 0x05);

同时 FreeRTOS 也提供了查询事件组当前值的 API 函数,具体如下所示

/**
  * @brief  读取事件组的当前值
  * @param  xEventGroup:正在查询的事件组
  * @retval 返回事件组当前的值
  */
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup);
 
/**
  * @brief  上述函数的中断安全版本
  */
EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup);

3.4、xEventGroupWaitBits() API 函数

FreeRTOS 关于事件组提出了等待事件组和事件组同步两个比较重要的 API 函数,分别对应两种不同的使用场景,等待事件组主要用于使用事件组进行事件的管理,而另外一主要用于使用事件组进行任务间的同步,接下来主要详细介绍两个函数的具体用法

xEventGroupWaitBits() API 函数允许任务读取事件组的值,并且可以选择在阻塞状态下等待事件组中的一个或多个事件位被设置(如果事件位尚未设置),如下所示为其具体的函数声明

/**
  * @brief  等待事件组中多个事件位表示的事件成立
  * @param  xEventGroup:所操作事件组的句柄
  * @param  uxBitsToWaitFor:所等待事件位的掩码,例如设置为0x05表示等待第0位和/或第2位
  * @param  xClearOnExit:pdTRUE表示事件组条件成立退出阻塞状态时将掩码指定的所有位清零;pdFALSE表示事件组条件成立退出阻塞状态时不将掩码指定的所有位清零
  * @param  xWaitForAllBits:pdTRUE表示等待掩码中所有事件位都置1,条件才算成立(逻辑与);pdFALSE表示等待掩码中所有事件位中一个置1,条件就成立(逻辑或)
  * @param  xTicksToWait:任务进入阻塞状态等待时间成立的超时节拍数
  * @retval 返回事件位等待完成设置或阻塞时间过期时的事件组值
  */
EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup,
								const EventBits_t uxBitsToWaitFor,
								const BaseType_t xClearOnExit,
								const BaseType_t xWaitForAllBits,
								TickType_t xTicksToWait);

3.4.1、uxBitsToWaitFor 和 xWaitForAllBits 参数

调度程序用来确定任务是否进入阻塞状态以及任务何时离开阻塞状态的条件称为 “解除阻塞条件” 。解锁条件由 uxBitsToWaitFor 和 xWaitForAllBits 参数值的组合指定:

  • uxBitsToWaitFor 指定要测试事件组中的哪些事件位
  • xWaitForAllBits 指定是使用按位 OR 测试还是按位 AND 测试

如果调用 xEventGroupWaitBits() 时满足解锁条件,任务将不会进入阻塞状态,下表提供了导致任务进入阻塞状态或退出阻塞状态的条件示例。表中列出的值仅显示事件组和 uxBitsToWaitFor 值的最低有效的四个二进制位,其他位均假定为零

现有事件组值uxBitsToWaitForxWaitForAllBits导致的结果
00000101pdFALSE由于事件组中的位 0 或位 2 均未设置,调用任务将进入阻塞状态,并且当事件组中的位 0 或位 2 被设置时,调用任务将离开阻塞状态
01000101pdTRUE调用任务将进入阻塞状态,因为事件组中的位 0 和位 2 未同时设置,并且当事件组中的位 0 和位 2 均设置时,调用任务将离开阻塞状态
01000110pdFALSE调用任务不会进入阻塞状态,因为 xWaitForAllBits 为 pdFALSE,并且 uxBitsToWaitFor 指定的两个位之一已在事件组中设置
01000110pdTRUE调用任务将进入阻塞状态,因为 xWaitForAllBits 为pdTRUE,并且事件组中仅已设置 uxBitsToWaitFor 指定的两个位之一。 当事件组中的位 2 和位 3 均被设置时,任务将离开阻塞状态

3.4.2、xClearOnExit 参数

调用任务使用 uxBitsToWaitFor 参数指定要测试的位,并且调用任务可能需要在满足其解锁条件后将这些位清零。可以使用 xEventGroupClearBits() API 函数清除事件位,但使用该函数手动清除事件位将导致应用程序代码中出现竞争条件

因此提供 xClearOnExit 参数就是为了避免这些潜在的竞争条件。如果 xClearOnExit 设置为 pdTRUE,则事件位的测试和清除对于调用任务来说是一个原子操作(不能被其他任务或中断中断),简单来说就是如果 xClearOnExit 设置为 pdTRUE,则调用任务退出后会将事件组所有位清零,否则不清零

如果 xEventGroupWaitBits() 由于满足调用任务的解锁条件而返回,则返回值是满足调用任务的解锁条件时事件组的值(如果 xClearOnExit 为 pdTRUE,则在自动清除任何位之前),在这种情况下,返回值也将满足解锁条件。如果 xEventGroupWaitBits() 因为 xTicksToWait 参数指定的退出阻塞时间到期而返回,则返回值为退出阻塞时间到期时事件组的值,在这种情况下,返回值将不满足解锁条件

3.5、xEventGroupSync() API 函数

提供 xEventGroupSync() 是为了允许两个或多个任务使用事件组来相互同步。该函数允许任务设置事件组中的一个或多个事件位,然后等待同一事件组中指定的事件位组合被设置

如下所示为 xEventGroupSync() API 函数的具体声明

/**
  * @brief  事件组同步
  * @param  uxBitsToSet:设置和测试位的事件组
  * @param  uxBitsToWaitFor:指定事件组中要测试的一个或多个事件位的按位值
  * @param  xTicksToWait:任务进入阻塞状态等待时间成立的超时节拍数
  * @retval 返回函数退出时事件组的值
  */
EventBits_t xEventGroupSync(EventGroupHandle_t xEventGroup,
							const EventBits_t uxBitsToSet,
							const EventBits_t uxBitsToWaitFor,
							TickType_t xTicksToWait);
 

3.5.1、函数返回值

xEventGroupSync() 函数返回函数退出时事件组的值,可能有以下两种情况

  1. xEventGroupSync() 函数的 uxBitsToWaitFor 参数指定了调用任务的解锁条件,如果该函数由于满足解锁条件而返回,则 uxBitsToWaitFor 指定的事件位将在 xEventGroupSync() 返回之前清回零,并且在自动清为零之前会将事件组的值作为函数返回值返回

  2. 如果 xEventGroupSync() 由于 xTicksToWait 参数指定的阻塞时间到期而返回,则返回值为阻塞时间到期时事件组的值,在这种情况下,返回值将不满足调用任务的解锁条件

3.5.2、应用举例

举个简单的例子就容易理解:

假设目前有两个任务,分别为 TASK1 和 TASK2 ,如果 TASK1 被执行过程中因为延时等原因先于 TASK2 调用了 xEventGroupSync() 函数,参数 uxBitsToSet 被设置为 0x01(0000 0001),参数 uxBitsToWaitFor 被设置为 0x05(0000 0101),则 TASK1 执行到该函数时会将事件组中位 0 的值置 1 ,然后进入阻塞状态,等待位 2 和位 0 同时被置 1 ;

如果 TASK2 与 TASK1 一样,只不过落后于 TASK1 执行 xEventGroupSync() 函数,并且参数 uxBitsToSet 被设置为 0x04(0000 0100),当 TASK2 执行该函数时会将事件组中位 2 的值置 1 ,此时满足解锁条件,所以 TASK2 不会进入阻塞状态,同时 TASK1 也满足解锁条件,从阻塞状态中退出,这时候假设任务优先级一致,则 TASK1 和 TASK2 会同时从同步点开始运行后续的程序代码,从而达到同步的目的

3.5、删除事件组

/**
  * @brief  删除事件组
  * @param  xEventGroup:要删除事件组的句柄
  * @retval None
  */
void vEventGroupDelete(EventGroupHandle_t xEventGroup);

4、实验一:使用事件组进行事件管理

4.1、实验目标

Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf 手册第 “8.3” 小节最后介绍了关于事件组的事件管理示例 22 ,这里我们来复现一下

  1. 创建一个用于演示本实验的事件组 xEventGroup
  2. 创建一个负责将事件组 xEventGroup 位 0 和位 1 置位的任务 Task_SetBits
  3. 启动 RTC 1s 周期唤醒,在 RTC 周期唤醒回调函数中负责将事件组 xEventGroup 位 0 置位
  4. 创建一个负责等待事件组位 0 或位 1 或位 2 满足条件的任务 Task_ReadBits

4.2、CubeMX相关配置

首先读者应按照 "FreeRTOS教程1 基础知识" 章节配置一个可以正常编译通过的 FreeRTOS 空工程,然后在此空工程的基础上增加本实验所提出的要求

本实验需要初始化 USART1 作为输出信息渠道,具体配置步骤请阅读 “STM32CubeMX教程9 USART/UART 异步通信” ,如下图所示

本实验需要配置 RTC 周期唤醒中断,具体配置步骤和参数介绍读者可阅读”STM32CubeMX教程10 RTC 实时时钟 - 周期唤醒、闹钟A/B事件和备份寄存器“实验,此处不再赘述,这里参数、时钟配置如下图所示

由于需要在 RTC 周期唤醒中断中使用 FreeRTOS 的 API 函数,因此 RTC 周期唤醒中断的优先级应该设置在 15~5 之间,此处设置为 7 ,具体如下图所示

单击 Middleware and Software Packs/FREERTOS,在 Configuration 中单击 Tasks and Queues 选项卡,双击默认任务按任务 Task_SetBits 修改其参数,然后增加另外一个 Task_ReadBits 任务,具体如下图所示

然后在 Configuration 中单击 Events 选项卡,单击右下角的 Add 按钮增加一个事件组 xEventGroup ,具体如下图所示

配置 Clock Configuration 和 Project Manager 两个页面,接下来直接单击 GENERATE CODE 按钮生成工程代码即可

4.3、添加其他必要代码

按照 “STM32CubeMX教程9 USART/UART 异步通信” 实验 “6、串口printf重定向” 小节增加串口 printf 重定向代码,具体不再赘述

首先应该在 freertos.c 中添加信号量的头文件和定义需要用到的事件组位的宏定义,如下所述

/*freertos.c中添加头文件*/
#include "stdio.h"
#include "event_groups.h"
 
/*事件组位宏定义*/
#define mainFIRST_TASK_BIT 	( 1UL << 0UL ) /* 事件组位 0 */
#define mainSECOND_TASK_BIT ( 1UL << 1UL ) /* 事件组位 1 */
#define mainISR_BIT         ( 1UL << 2UL ) /* 事件组位 2 */

然后在该文件中重新实现周期唤醒回调函数,该函数用于 1s 周期将事件组 xEventGroup 的位 2 置 1 ,具体如下所示

/*周期唤醒回调函数*/
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
	/* 输出信息提示 */
	printf("Bit setting ISR -\t about to set bit 2.\r\n");
	/* 从中断中设置事件组位 2 为 1 */
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	xEventGroupSetBitsFromISR(xEventGroupHandle, mainISR_BIT, &xHigherPriorityTaskWoken);
	portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

最后仍然在该文件中实现任务 Task_SetBits 和任务 Task_ReadBits 两个任务函数体即可,具体如下所示

/*事件组置位任务*/
void AppTask_SetBits(void *argument)
{
  /* USER CODE BEGIN AppTask_SetBits */
	/* 400ms延时变量 */
	const TickType_t xDelay400ms = pdMS_TO_TICKS(400UL);
	/* Infinite loop */
	for(;;)
	{
		/* 在下次循环开始之前短延时 */
		vTaskDelay(xDelay400ms);
		/* 输出事件组位 0 被置位任务置 1 信息 */
		printf("Bit setting task -\t about to set bit 0.\r\n");
		xEventGroupSetBits(xEventGroupHandle, mainFIRST_TASK_BIT);
		/* 在置位下一位之前短延时 */
		vTaskDelay(xDelay400ms);
		/* 输出事件组位 1 被置位任务置 1 信息 */
		printf("Bit setting task -\t about to set bit 1.\r\n");
		xEventGroupSetBits(xEventGroupHandle, mainSECOND_TASK_BIT);
	}
	/* USER CODE END AppTask_SetBits */
}
 
/*事件组读取任务*/
void AppTask_ReadBits(void *argument)
{
	/* USER CODE BEGIN AppTask_ReadBits */
	/* 创建事件组 */
	EventBits_t xEventGroupValue;
	/* 设置要测试的位 */
	const EventBits_t xBitsToWaitFor = (mainFIRST_TASK_BIT |
										mainSECOND_TASK_BIT | 
										mainISR_BIT);
	/* Infinite loop */
	for(;;)
	{
	xEventGroupValue = xEventGroupWaitBits( 
							/* 被读的事件组 */
							xEventGroupHandle,
							/* 要测试的位 */
							xBitsToWaitFor,
							/* 阻塞条件满足退出时清除所有事件位 */
							pdTRUE,
							/* 不等待所有位. */
							pdFALSE,
							/* 永远等待,不会超时 */
							portMAX_DELAY);
		/* 位 0 被置 1 */
		if((xEventGroupValue & mainFIRST_TASK_BIT) != 0)
		{
			printf("Bit reading task -\t Event bit 0 was set\r\n");
		}
		/* 位 1 被置 1 */
		if((xEventGroupValue & mainSECOND_TASK_BIT ) != 0 )
		{
			printf("Bit reading task -\t Event bit 1 was set\r\n");
		}
		/* 位 2 被置 1 */
		if((xEventGroupValue & mainISR_BIT ) != 0 )
		{
			printf("Bit reading task -\t Event bit 2 was set\r\n");
		}
	}
	/* USER CODE END AppTask_ReadBits */
}
 

4.4、烧录验证

烧录程序,在 xEventGroupWaitBits() 函数 xWaitForAllBits 参数设置为 pdFALSE 的情况下串口产生的输出信息如下图所示

从图中可可以看出,因为对 xEventGroupWaitBits() 的调用中的 xWaitForAllBits 参数设置为 pdFALSE, 每次设置任何事件位时,从事件组读取的任务都会离开阻塞状态并立即执行

4.5、测试 xWaitForAllBits 参数

将任务 AppTask_ReadBits() 调用的 xEventGroupWaitBits() 函数 xWaitForAllBits 参数设置为 pdTRUE,表示需要等待所有事件组测试位满足才能离开阻塞状态,这种情况下串口产生的输出如下图所示

在上图中可以看出,由于 xWaitForAllBits 参数设置为 pdTRUE,从事件组读取的任务仅在所有三个事件位均置 1 后才可以离开阻塞状态

5、实验二:使用事件组进行任务同步

5.1、实验目标

  1. 创建一个用于演示本实验的事件组 xEventGroup
  2. 创建三个任务通过延时模拟不同时间到达任务同步点

5.2、CubeMX相关配置

首先读者应按照 "FreeRTOS教程1 基础知识" 章节配置一个可以正常编译通过的 FreeRTOS 空工程,然后在此空工程的基础上增加本实验所提出的要求

本实验需要初始化 USART1 作为输出信息渠道,具体配置步骤请阅读 “STM32CubeMX教程9 USART/UART 异步通信” ,如下图所示

单击 Middleware and Software Packs/FREERTOS,在 Configuration 中单击 Tasks and Queues 选项卡,双击默认任务修改其参数,具体如下图所示

然后在 Configuration 中单击 Events 选项卡,单击右下角的 Add 按钮增加一个事件组 xEventGroup ,具体如下图所示

配置 Clock Configuration 和 Project Manager 两个页面,接下来直接单击 GENERATE CODE 按钮生成工程代码即可

5.3、添加其他必要代码

按照 “STM32CubeMX教程9 USART/UART 异步通信” 实验 “6、串口printf重定向” 小节增加串口 printf 重定向代码,具体不再赘述

首先应该在 freertos.c 中添加信号量的头文件和定义需要用到的事件组位的宏定义,如下所述

/*头文件*/
#include "stdio.h"
#include "stdlib.h"
#include "event_groups.h"
 
/*事件组位宏定义*/
#define mainFIRST_TASK_BIT 	( 1UL << 0UL ) /* 事件组位 0 */
#define mainSECOND_TASK_BIT ( 1UL << 1UL ) /* 事件组位 1 */
#define mainTHIRD_TASK_BIT  ( 1UL << 2UL ) /* 事件组位 2 */

修改 MX_FREERTOS_Init() 函数,将默认生成的创建一个任务程序注释掉,然后利用一个任务回调函数通过不同的参数创建三个不同的任务,部分注释已经删除,具体如下所示

void MX_FREERTOS_Init(void) {
  /* Create the thread(s) */
  /* creation of Task_Syncing */
  //Task_SyncingHandle = osThreadNew(AppTask_Syncing, NULL, &Task_Syncing_attributes);
 
  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
	xTaskCreate(AppTask_Syncing, "Task 1", 1000, (void*)mainFIRST_TASK_BIT, 24, NULL);
	xTaskCreate(AppTask_Syncing, "Task 2", 1000, (void*)mainSECOND_TASK_BIT, 24, NULL);
	xTaskCreate(AppTask_Syncing, "Task 3", 1000, (void*)mainTHIRD_TASK_BIT, 24, NULL);
  /* USER CODE END RTOS_THREADS */
 
  /* Create the event(s) */
  /* creation of xEventGroup */
  xEventGroupHandle = osEventFlagsNew(&xEventGroup_attributes);
 
  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */
}

最后实现任务入口函数 AppTask_Syncing() 的函数体即可,具体如下所述

/*事件组同步任务函数*/
void AppTask_Syncing(void *argument)
{
	/* USER CODE BEGIN AppTask_Syncing */
	/* 创建两个延时用于合成随机延时时间 */
	const TickType_t xMaxDelay = pdMS_TO_TICKS(4000UL);
	const TickType_t xMinDelay = pdMS_TO_TICKS(200UL);
	/* 延时时间 */
	TickType_t xDelayTime;
	/* 任务要设置的事件组的位 */
	EventBits_t uxThisTasksSyncBit;
	/* 任务要等待的事件组的所有位 */
	const EventBits_t uxAllSyncBits = ( mainFIRST_TASK_BIT |
										mainSECOND_TASK_BIT |
										mainTHIRD_TASK_BIT );
	
	uxThisTasksSyncBit = ( EventBits_t )argument;
	/* Infinite loop */
	for(;;)
	{
		/* 合成随机延时时间,模拟三个任务不同时间到达同步点 */
		xDelayTime = (rand() % xMaxDelay) + xMinDelay;
		vTaskDelay(xDelayTime);
		printf("%s reached sync point\r\n", pcTaskGetTaskName(NULL));
		xEventGroupSync(/* 被读的事件组 */
										xEventGroupHandle,
										/* 测试的位 */
										uxThisTasksSyncBit,
										/* 需要等待的所有位 */
										uxAllSyncBits,
										/* 永远等待,不会超时 */
										portMAX_DELAY);
		/* 任务会同时退出同步点,串口输出需要时间,所以通过临界段保护串口输出 */
		taskENTER_CRITICAL();
		printf("%s exited sync point\r\n", pcTaskGetTaskName(NULL));
		taskEXIT_CRITICAL();
	}
	/* USER CODE END AppTask_Syncing */
}

5.4、烧录验证

烧录程序,打开串口助手,通过串口助手输出的信息可以发现,三个任务在不同的(伪随机)时间到达任务点,但是当其中最后一个任务到达同步点之后,三个任务会同时退出同步点,具体的串口输出信息如下图所示

6、注释详解

注释1:图片来源于 Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

参考资料

STM32Cube高效开发教程(基础篇)

Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1527760.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

主机与windows虚拟机远程桌面实现方法

目录 一、虚拟机相关配置1. 配置虚拟机网络2. 打开虚拟机远程桌面功能3. 配置虚拟机用户与分组 二、主机相关配置 当无法通过共享文件夹实现主机与windows虚拟机文件共享时&#xff0c;可以通过主机与虚拟机远程桌面的方法实现文件的共享传输。本文主要介绍主机与虚拟机远程桌面…

【接口防重复提交】⭐️基于RedisLockRegistry 分布式锁管理器实现

目录 前言 思路 实现方式 实践 1.引入相关依赖 2.aop注解 3.切面类代码 4.由于启动时报错找不到对应的RedisLockRegistry bean&#xff0c;选择通过配置类手动注入&#xff0c;配置类代码如下 测试 章末 前言 项目中有个用户根据二维码绑定身份的接口&#xff0c;由于用户在…

诺视科技完成亿元Pre-A2轮融资,加速Micro-LED微显示芯片商业化落地

近日&#xff0c;Micro-LED微显示芯片研发商诺视科技&#xff08;苏州&#xff09;有限公司&#xff08;以下简称“诺视科技”&#xff09;宣布完成亿元Pre-A2轮融资&#xff0c;本轮融资由力合资本领投&#xff0c;老股东盛景嘉成、汕韩基金以及九合创投持续加码&#xff0c;这…

YOLOv8改进 | 图像去雾 | MB-TaylorFormer改善YOLOv8高分辨率和图像去雾检测(ICCV,全网独家首发)

一、本文介绍 本文给大家带来的改进机制是图像去雾MB-TaylorFormer,其发布于2023年的国际计算机视觉会议(ICCV)上,可以算是一遍比较权威的图像去雾网络, MB-TaylorFormer是一种为图像去雾设计的多分支高效Transformer网络,它通过应用泰勒公式展开的方式来近似softmax-at…

华为openEuler系统安装openjdk并配置环境变量

华为openEuler系统安装openjdk并配置环境变量 1、安装JDK软件包 执行dnf list installed | grep jdk 查询JDK软件是否已安装。 $ dnf list installed | grep jdk查看命令打印信息&#xff0c;若打印信息中包含“jdk”&#xff0c;表示该软件已经安装了&#xff0c;则不需要再…

堆排序(向下调整法,向上调整法详解)

目录 一、 二叉树的顺序结构 二、 堆的概念及结构 三、数组存储、顺序存储的规律 此处可能会有疑问&#xff0c;左右孩子的父节点计算为什么可以归纳为一个结论了&#xff1f; 四、大小堆解释 五、大小堆的实现&#xff08;向上和向下调整法&#xff09; 5.11向上调整法…

docxTemplater——从word模板生成docx文件

官网文档&#xff1a;Get Started (Browser) | docxtemplater 官网在线演示&#xff1a;Demo of Docxtemplater with all modules active | docxtemplater 源码&#xff1a;https://github.com/open-xml-templating/docxtemplater 不仅可以处理word&#xff08;免费&#xf…

【深度学习实践】面部表情识别,深度学习分类模型,mmpretrain用于分类的实用教程,多任务网络头

文章目录 数据集数据集的进一步处理转换training.csv转换validation.csv 剔除无法使用的图片数据选择mmpretrain框架来训练配置四个文件改写base model改写base datasetsschedulesdefault_runtime 总配置开始训练训练分析考虑在网络上增加facial_landmarks回归head考虑是否可以…

论文解析:V3D: Video Diffusion Models are Effective 3DGenerators

摘要&#xff1a; 自动三维生成最近引起了广泛关注。最近的方法大大加快了生成速度&#xff0c;但由于模型容量有限或三维数据&#xff0c;生成的物体通常不够精细。在视频扩散模型最新进展的推动下&#xff0c;我们引入了 V3D&#xff0c;利用预训练视频扩散模型的世界模拟能…

【已解决】MySQL:常用的除法运算+精度处理+除数为0处理

目录 问题现象&#xff1a; 问题分析&#xff1a; 拓展&#xff1a; 1、除法运算&#xff1a; 拓展&#xff1a;MySQL中常用的几种除法运算 1、取整除法 2、浮点数除法 3、取余除法 4、向上取整除法 5、向下取整除法 2、运算结果的精度处理 1.1、浮点数 1.2、总位数 1.3、…

蓝桥杯练习题——健身大调查

在浏览器中预览 index.html 页面效果如下&#xff1a; 目标 完成 js/index.js 中的 formSubmit 函数&#xff0c;用户填写表单信息后&#xff0c;点击蓝色提交按钮&#xff0c;表单项隐藏&#xff0c;页面显示用户提交的表单信息&#xff08;在 id 为 result 的元素显示&#…

2024年敏捷产品负责人CSPO认证培训

课程名称&#xff1a;Scrum Product Owner CSPO产品负责人认证 课程类型&#xff1a;经理级 课程简介&#xff1a; Scrum Product Owner产品负责人在Scrum产品开发当中扮演“舵手”的角色&#xff0c;他决定产品的愿景、路线图以及投资回报&#xff0c;他需要回答为什么做&am…

【ZooKeeper】2、安装

本文基于 Apache ZooKeeper Release 3.7.0 版本书写 作于 2022年3月6日 14:22:11 转载请声明 下载zookeeper安装包 wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7.0-bin.tar.gz解压 tar -zxvf apache-zookeeper-3.7.0-b…

OpenCV Steger算法提取条纹中心线

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 Steger 算法是一种常用的图像边缘检测算法,可以用于提取图像中的中心线或边缘信息。它的理论假设是:条纹的亮度是按照高斯分布呈现的,即中心亮两侧渐暗。 其计算过程如下所述: 1、首先,我们需要计算每个点Hess…

汽车制造业供应商管理会面临哪些问题?要如何解决?

汽车行业的供应链是及其复杂的&#xff0c;并且呈全球化分布&#xff0c;企业在知识产权方面的优势很可能是阶段性的。企业需要持续保持领先&#xff0c;将面临巨大的挑战&#xff0c;尽快地将产品推向市场是保持领先的唯一途径。然而&#xff0c;如果没有正确的方式去实现安全…

Flutter 运行 flutter doctor 命令长时间未响应

由于 Flutter 运行 flutter doctor 命令&#xff0c;会从 pub(https://pub.dev/ 类似于 Node.js 的 npm) 上进行资源的下载&#xff0c;如果没有配置国内镜像&#xff0c;可能会由于其服务器在国外导致资源下载慢或者下载不下来&#xff0c;所以出现了运行 flutter doctor 命令…

中国银行信息系统应用架构发展历程

概述&#xff1a; 从 20 世纪 80 年代开始至今&#xff0c;我国银行业信息化历程已 有四十年历史。虽然相对于发达国家来讲&#xff0c;我国银行业务信 息化起步较晚&#xff0c;但发展速度很快&#xff0c; 目前我国一些大型商业银行的信息化程度已经处于全球领先水平。 “银行…

【IC设计】Verilog线性序列机点灯案例(四)(小梅哥课程)

文章目录 该系列目录&#xff1a;设计环境设计目标设计思路RTL及Testbench代码RTL代码Testbenchxdc约束 仿真结果 声明&#xff1a;案例和代码来自小梅哥课程&#xff0c;本人仅对知识点做做笔记&#xff0c;如有学习需要请支持官方正版。 该系列目录&#xff1a; Verilog线性…

使用Composer安装Laravel框架

使用Composer安装Laravel框架&#xff0c;不指定版本则安装当下最新版本 composer create-project laravel/laravel laravel-demo 至此&#xff0c;安装框架完成&#xff0c;这里安装的是Laravel11.0.7版本的 进入项目根目录&#xff0c;运行项目 cd laravel.11.0.7 // 进…

maven一点通

1.maven简介 Maven是一个基于Java的工程构建工具&#xff0c;用于管理和构建项目的依赖关系。它提供了一种标准的项目结构和一组约定&#xff0c;使得项目的开发、构建、部署和文档化更加容易和可靠。 Maven的主要功能包括&#xff1a; 依赖管理&#xff1a;Maven可以自动下载…