FreeRTOS教程7 事件組

来源:https://www.cnblogs.com/lc-guo/p/18068515
-Advertisement-
Play Games

本文主要學習 FreeRTOS 事件組的相關知識,包括事件組概述、事件組特征、創建事件組、操作事件組、刪除事件組等知識 ...


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、uxBitsToWaitForxWaitForAllBits 參數

調度程式用來確定任務是否進入阻塞狀態以及任務何時離開阻塞狀態的條件稱為 “解除阻塞條件” 。解鎖條件由 uxBitsToWaitForxWaitForAllBits 參數值的組合指定:

  • uxBitsToWaitFor 指定要測試事件組中的哪些事件位
  • xWaitForAllBits 指定是使用按位 OR 測試還是按位 AND 測試

如果調用 xEventGroupWaitBits() 時滿足解鎖條件,任務將不會進入阻塞狀態,下表提供了導致任務進入阻塞狀態或退出阻塞狀態的條件示例。表中列出的值僅顯示事件組和 uxBitsToWaitFor 值的最低有效的四個二進位位,其他位均假定為零

現有事件組值 uxBitsToWaitFor xWaitForAllBits 導致的結果
0000 0101 pdFALSE 由於事件組中的位 0 或位 2 均未設置,調用任務將進入阻塞狀態,並且當事件組中的位 0 或位 2 被設置時,調用任務將離開阻塞狀態
0100 0101 pdTRUE 調用任務將進入阻塞狀態,因為事件組中的位 0 和位 2 未同時設置,並且當事件組中的位 0 和位 2 均設置時,調用任務將離開阻塞狀態
0100 0110 pdFALSE 調用任務不會進入阻塞狀態,因為 xWaitForAllBits 為 pdFALSE,並且 uxBitsToWaitFor 指定的兩個位之一已在事件組中設置
0100 0110 pdTRUE 調用任務將進入阻塞狀態,因為 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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述:在WPF中使用`WpfAnimatedGif`庫展示GIF動畫,首先確保全裝了該庫。通過XAML設置Image控制項,指定GIF路徑,然後在代碼中使用庫提供的方法實現動畫控制。這簡化了在WPF應用中處理GIF圖的過程,提供了方便的介面來管理動畫播放和暫停。 當使用 WpfAnimatedGif  ...
  • 概述:這個WPF項目通過XAML繪製汽車動態速度表盤,實現了0-300的速度刻度,包括數字、指針,並通過定時器模擬速度變化,展示了動態效果。詳細實現包括界面設計、刻度繪製、指針角度計算等,通過C#代碼與XAML文件結合完成。 新建 WPF 項目: 在 Visual Studio 中創建一個新的 WP ...
  • Next Terminal —— 一個簡單好用安全的開源交互審計系統,支持 RDP、SSH、VNC、Telnet、Kubernetes 協議。 ...
  • 本文主要學習 FreeRTOS 軟體定時器的相關知識,包括軟體定時器回調函數、屬性、狀態、運行原理和常見 API 函數等知識 ...
  • Uboot功能 Uboot(Universal bootloader)就是一個裸機程式,用於啟動內核。不過相較於單片機程式更加複雜 相關知識補充 Uboot:Universal bootloader XIP:CPU可以直接發出地址信號讀取Flash/RAM,執行指令(可以看成直接在Flash/RAM ...
  • ASan介紹 ASan全稱AddressSanitizer,是一種記憶體錯誤檢測工具,目的是幫助開發者檢測和調試記憶體相關的問題,如使用未分配的記憶體、使用已釋放的記憶體、堆記憶體溢出等。ASan是由Google開發的,廣泛用於C、C++等語言的代碼中。 ASan的工作原理是在編譯時將額外的代碼插入到目標程式 ...
  • 軟體介紹: winmail收到新郵件不會提示用戶,這樣用戶就容易錯過消息 下載foxmail可以解決這個問題 一:下載foxmail 1.實體機進入官網https://www.foxmail.com/,點擊下載版本隨意 下載好後拖到虛擬機上 二:配置foxmail 1.開始創建賬號 因為這幾種郵箱都 ...
  • 實驗環境: 一臺安裝好的DNS伺服器,ip為192.168.1.201 一臺郵件伺服器,192.168.1.224 一臺客戶端,192.168.1.249,dnsIP為192.168.1.201 都是wmnet1,使其能互相ping通 一:配置DNS 1.打開DNS伺服器,新建主機 把郵件伺服器的主 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...