FreeRTOS教程5 信號量

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

1、準備材料 正點原子stm32f407探索者開發板V2.4 STM32CubeMX軟體(Version 6.10.0) Keil µVision5 IDE(MDK-Arm) 野火DAP模擬器 XCOM V2.6串口助手 一個滑動變阻器 2、學習目標 本文主要學習 FreeRTOS 信號量的相關知識 ...


1、準備材料

正點原子stm32f407探索者開發板V2.4

STM32CubeMX軟體(Version 6.10.0

Keil µVision5 IDE(MDK-Arm

野火DAP模擬器

XCOM V2.6串口助手

一個滑動變阻器

2、學習目標

本文主要學習 FreeRTOS 信號量的相關知識,包括創建/刪除信號量、釋放信號量、獲取信號量等知識

3、前提知識

3.1、信號量概述

信號量是進程間用於通信的一種手段,其是基於隊列實現的,信號量更適用於進程間同步,信號量包括二值信號量(Binary Semaphores)和計數信號量(Counting Semaphores)

二值信號量就是只有一個項的隊列,該隊列不為空則為滿(所謂二值),二值信號量就像一個標誌,適和用於進程間同步的通信

舉個例子:ADC 的周期採集中斷負責採集完成後將採集到的 ADC 的值寫入數據緩存區中並且釋放信號量,總是嘗試獲取信號量的數據處理任務在 ADC 採集中斷釋放信號量之後成功獲取,然後退出阻塞狀態對寫入數據緩存區中採集到的 ADC 值進行處理,上述過程如下圖所示:(註釋1)

如下圖所示為使用二值信號量來同步任務和中斷的工作流程 (註釋2)

計數信號量就是有固定長度的隊列,隊列中每個單元都是一個標誌,其通常用於對多個共用資源的訪問進行控制

舉個例子:一家餐館有 4 張可供用餐的桌子,我們創建一個長度為 4 ,初值為 4 的計數信號量來表示當前可供用餐的桌子數量,當有客人進來用餐時會 “獲取” 一張餐桌,這時用來表示可供用餐的桌子數量的計數信號量就會減少一個,當有客人離開時會 “釋放” 一張餐桌,這時用來表示可供用餐的桌子數量的計數信號量就會增加一個,上述過程如下圖所示:

如下圖所示為計數信號量的工作流程

3.2、創建信號量

信號量在使用之前也必須先創建,信號量被創建完之後是無效的,也即為 0 ,而由於信號量分為二值信號量和計數信號量兩種,因此FreeRTOS也提供了不同的API函數,具體如下所述

/**
  * @brief  動態分配記憶體創建二值信號量函數
  * @param  xSemaphore:創建的二值信號量句柄
  * @retval None
  */
void vSemaphoreCreateBinary(SemaphoreHandle_t xSemaphore);

/**
  * @brief  靜態分配記憶體創建二值信號量函數
  * @param  pxSemaphoreBuffer:指向一個StaticSemaphore_t類型的變數,該變數將用於保存信號量的狀態
  * @retval 返回創建成功的信號量句柄,如果返回NULL則表示因為pxSemaphoreBuffer為空無法創建
  */
SemaphoreHandle_t xSemaphoreCreateBinaryStatic(
									StaticSemaphore_t *pxSemaphoreBuffer);

/**
  * @brief  動態分配記憶體創建計數信號量函數
  * @param  uxMaxCount:可以達到的最大計數值
  * @param  uxInitialCount:創建信號量時分配給信號量的計數值
  * @retval 返回創建成功的信號量句柄,如果返回NULL則表示記憶體不足無法創建
  */
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, 
										   UBaseType_t uxInitialCount);

/**
  * @brief  靜態分配記憶體創建計數信號量函數
  * @param  uxMaxCount:可以達到的最大計數值
  * @param  uxInitialCount:創建信號量時分配給信號量的計數值
  * @param  pxSempahoreBuffer:指向StaticSemaphore_t類型的變數,該變數然後用於保存信號量的數據結構體
  * @retval 返回創建成功的信號量句柄,如果返回NULL則表示因為pxSemaphoreBuffer為空無法創建
  */
SemaphoreHandle_t xSemaphoreCreateCountingStatic(
									UBaseType_t uxMaxCount,
									UBaseType_t uxInitialCount,
									StaticSemaphore_t pxSempahoreBuffer);

3.3、釋放信號量

以下兩個函數不僅僅可以用於釋放二值信號量,還可以用於釋放計數信號量和互斥量,具體如下所示

/**
  * @brief  釋放信號量函數
  * @param  xSemaphore:要釋放的信號量的句柄
  * @retval 如果信號量釋放成功,則返回pdTRUE;如果發生錯誤,則返回pdFALSE
  */
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);

/**
  * @brief  釋放信號量的中斷安全版本函數
  * @param  pxHigherPriorityTaskWoken:用於通知應用程式編寫者是否應該執行上下文切換
  * @retval 如果成功給出信號量,則返回pdTRUE,否則errQUEUE_FULL
  */
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, 
								 BaseType_t *pxHigherPriorityTaskWoken);

3.4、獲取信號量

以下兩個函數不僅僅可以用於獲取二值信號量,還可以用於獲取計數信號量和互斥量,具體如下所示

/**
  * @brief  獲取信號量函數
  * @param  xSemaphore:正在獲取的信號量的句柄
  * @param  xTicksToWait:等待信號量變為可用的時間
  * @retval 成功獲得信號量則返回pdTRUE;如果xTicksToWait過期,信號量不可用,則返回pdFALSE
  */
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
/**
  * @brief  獲取信號量的中斷安全版本函數
  * @param  xSemaphore:正在獲取的信號量的句柄
  * @param  pxHigherPriorityTaskWoken:用於通知應用程式編寫者是否應該執行上下文切換
  * @retval 成功獲取則返回pdTRUE,未成功獲取則返回pdFALSE
  */
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, 
								 signed BaseType_t *pxHigherPriorityTaskWoken);

3.5、刪除信號量

/**
  * @brief  刪除信號量,包括互斥鎖型信號量和遞歸信號量
  * @param  xSemaphore:被刪除的信號量的句柄
  * @retval None
  */
void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);

3.6、工具函數

/**
  * @brief  獲取信號量計數
  * @param  xSemaphore:正在查詢的信號量的句柄
  * @retval 如果信號量是計數信號量,則返回信號量的當前計數值。如果信號量是二進位信號量,則當信號量可用時,返回1,當信號量不可用時,返回 0
  */
UBaseType_t uxSemaphoreGetCount(SemaphoreHandle_t xSemaphore);

4、實驗一:二值信號量的應用

4.1、實驗目標

  1. 創建一個二值信號量 BinarySem_ADC
  2. 配置 ADC1 IN5 在 500ms 的定時器驅動下周期採集 ADC 值,採集完成後將採集值寫入緩存數組,然後釋放二值信號量 BinarySem_ADC
  3. 創建一個任務 TASK_ADC,該任務總是嘗試獲取二值信號量 BinarySem_ADC,獲取成功之後,將寫入緩存數組的 ADC 採集值進行轉換,然後通過 USART1 輸出給用戶

4.2、CubeMX相關配置

首先讀者應按照 "FreeRTOS教程1 基礎知識" 章節配置一個可以正常編譯通過的 FreeRTOS 空工程,然後在此空工程的基礎上增加本實驗所提出的要求

本實驗需要初始化 USART1 作為輸出信息渠道,具體配置步驟請閱讀“STM32CubeMX教程9 USART/UART 非同步通信”,如下圖所示

本實驗需要設置 TIM3 作為 ADC1 IN5 觸發源的單通道 ADC 採集,採集周期為 500ms ,因此需要配置 ADC1 和 TIM3,感興趣讀者可以閱讀”STM32CubeMX教程13 ADC - 單通道轉換“實驗,如下圖所示

由於我們將要在 ADC 採集完成中斷中使用 FreeRTOS 的釋放二值信號量的函數,因此需要將其優先順序設置在 15~5 之間,在這裡設置為 7,具體如下圖所示

單擊 Middleware and Software Packs/FREERTOS,在 Configuration 中單擊 Tasks and Queues 選項卡雙擊預設任務修改其參數,具體如下圖所示

然後在 Configuration 中單擊 Timers and Semaphores ,在 Binary Semaphores 中單擊 Add 按鈕新增加一個名為 BinarySem_ADC 的二值信號量,具體如下圖所示

配置 Clock Configuration 和 Project Manager 兩個頁面,接下來直接單擊 GENERATE CODE 按鈕生成工程代碼即可

4.3、添加其他必要代碼

按照 “STM32CubeMX教程9 USART/UART 非同步通信” 實驗 “6、串口printf重定向” 小節增加串口 printf 重定向代碼,具體不再贅述

首先應該在 freertos.c 中添加信號量的頭文件,如下所述

/*freertos.c中添加頭文件*/
#include "semphr.h"

然後在該文件中重新實現 ADC 採集完成中斷回調函數,在該函數中獲取採集完成的 ADC 值,將其保存在全局變數 adc_value 中,然後釋放二值信號量 BinarySem_ADC ,如下所述

/*轉換完成中斷回調*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    /*定時器中斷啟動單通道轉換*/
    if(hadc->Instance == ADC1)
    {
        adc_value = HAL_ADC_GetValue(hadc);
		BaseType_t highTaskWoken = pdFALSE;
		if(BinarySem_ADCHandle != NULL)
		{
			xSemaphoreGiveFromISR(BinarySem_ADCHandle, &highTaskWoken);
			portYIELD_FROM_ISR(highTaskWoken);
		}
    }
}

接下來仍然在該文件中實現任務 TASK_ADC 的函數體內容,該任務函數總是嘗試獲取二值信號量,一旦獲取成功表示 ADC 轉換完成,就將 ADC 轉換完成的值變為電壓值,然後通過 USART1 輸出給用戶顯示,如下所述

/*ADC任務函數*/
void TASK_ADC(void *argument)
{
	/* USER CODE BEGIN TASK_ADC */
	/* Infinite loop */
	for(;;)
	{
		if(xSemaphoreTake(BinarySem_ADCHandle, portMAX_DELAY) == pdTRUE)
		{
			uint32_t Volt = (3300 * adc_value)>>12;
			printf("val:%d, Volt:%d\r\n", adc_value, Volt);
		}
	}
	/* USER CODE END TASK_ADC */
}

最後在 main.c 文件主函數 main() 中以中斷方式啟動 ADC 轉換即可,如下所述

//以中斷方式啟動ADC1
HAL_ADC_Start_IT(&hadc1);
//啟動ADC1觸發源定時器TIM3
HAL_TIM_Base_Start(&htim3);

4.4、燒錄驗證

燒錄程式,打開串口助手,可以發現每隔一段時間就會輸出當前 ADC1 IN5 通道採集到的 ADC 的值,將其接入一個滑動變阻器,當滑動變阻器從一端滑動到另一端時,串口輸出的採集值也在從 0 逐漸變為最大值 4095 ,整個過程串口輸出信息如下圖所示

5、實驗二:計數信號量的應用

5.1、實驗目標

  1. 創建一個計數信號量 CountingSem_Tables ,設置最大值為 5 ,初始值設為 5 ,表示飯店內初始有 5 張桌子
  2. 啟動 RTC 周期喚醒中斷,喚醒周期為 3s ,在 RTC 喚醒中斷中釋放信號量,模擬有客人離開飯店
  3. 創建任務 TASK_KEY2 ,當按鍵 KEY2 按下時嘗試獲取信號量,模擬客人進店,同時調用獲取信號量計數查詢函數,查詢當前可用餐桌個數

5.2、CubeMX相關配置

首先讀者應按照 "FreeRTOS教程1 基礎知識" 章節配置一個可以正常編譯通過的 FreeRTOS 空工程,然後在此空工程的基礎上增加本實驗所提出的要求

本實驗需要初始化開發板上 KEY2 用戶按鍵做普通輸入,具體配置步驟請閱讀 “STM32CubeMX教程3 GPIO輸入 - 按鍵響應” ,註意雖開發板不同但配置原理一致,如下圖所示

本實驗需要初始化 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 選項卡,雙擊預設任務修改其參數,具體如下圖所示

然後在 Configuration 中單擊 Timers and Semaphores 選項卡,在最下方的 Counting Semaphores 中單擊右下角的 Add 按鈕增加一個計數信號量,具體如下圖所示

配置 Clock Configuration 和 Project Manager 兩個頁面,接下來直接單擊 GENERATE CODE 按鈕生成工程代碼即可

5.3、添加其他必要代碼

按照 “STM32CubeMX教程9 USART/UART 非同步通信” 實驗 “6、串口printf重定向” 小節增加串口 printf 重定向代碼,具體不再贅述

首先應該在 freertos.c 中添加信號量的頭文件,如下所述

/*freertos.c中添加頭文件*/
#include "stdio.h"
#include "semphr.h"

然後在該文件中重新實現周期喚醒回調函數,該函數用於周期釋放計數信號量,具體如下所示

/*周期喚醒回調函數*/
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
	if(CountingSem_TablesHandle != NULL)
	{
		BaseType_t highTaskWoken = pdFALSE;
		//釋放計數信號量
		xSemaphoreGiveFromISR(CountingSem_TablesHandle, &highTaskWoken);
		portYIELD_FROM_ISR(highTaskWoken);
	}
}

最後仍然在該文件中實現任務 TASK_KEY2 ,該任務負責當按鍵 KEY2 按下時嘗試獲取計數信號量,當無任何按鍵按下時不斷輸出當前計數信號量可用數量,具體如下所示

void TASK_KEY2(void *argument)
{
  /* USER CODE BEGIN TASK_KEY2 */
  /* Infinite loop */
  for(;;)
  {
	if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin) == GPIO_PIN_RESET)
	{
		//獲取計數信號量
		BaseType_t result = xSemaphoreTake(CountingSem_TablesHandle, pdMS_TO_TICKS(100));
		if(result == pdTRUE) printf("Check In OK\r\n");
		else printf("Check In Fail\r\n");
		//按鍵消抖
		osDelay(pdMS_TO_TICKS(300));
	}
	else
	{
		UBaseType_t AvailableTables = uxSemaphoreGetCount(CountingSem_TablesHandle);
		printf("Now AvailableTables is : %d\r\n", (uint16_t)AvailableTables);
		osDelay(pdMS_TO_TICKS(10));	
	}
  }
  /* USER CODE END TASK_KEY2 */
}

5.4、燒錄驗證

燒錄程式,打開串口助手,發現不斷輸出當前可用計數信號量數量,當按住按鍵 KEY2 不鬆開,連續模擬客人進店,可以發現在模擬 3 個客人進店之後,剩餘可用計數信號量的數量變為了 2 個,然後每隔一段時間計算信號量的數量慢慢增加直到最大值 5 ,接著按住按鍵 KEY2 不鬆開,連續模擬 5 個客人進店,當 5 個客人進店之後,再次按下 KEY2 按鍵可以發現串口輸出 ”Check In Fail“,表示當前已無剩餘可用計數信號量,上述整個過程如下圖所示

6、註釋詳解

註釋1:圖片來源於 STM32Cube高效開發教程(高級篇) 第五章

註釋2:圖片來源於 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
更多相關文章
  • 程式能運行,就是告訴你RZ10012,然後各種提示沒有了。 清理解決方案、電腦重啟了都沒有用,後來搜索到github,解決了,記一下: 關閉vs 刪除文件夾 .vs,bin,object 打開vs,重新生成解決方案 也是醉了。 文字少的博文不允許投稿到該網站分類? 知道什麼叫短小精悍嗎?知道什麼叫短 ...
  • 11.1.0-beta1 Avalonia 是 dotnet 的跨平臺 UI 框架,提供靈活的樣式系統,支持 Windows、macOS、Linux、iOS、Android 和 WebAssembly 等多種平臺。 Avalonia 已經成熟並已做好生產準備,已被 Schneider Electri ...
  • System.Diagnostics.DiagnosticSource 可以豐富地記錄程式中地日誌,包括不可序列化的類型(例如 HttpResponseMessage 或 HttpContext)。 System.Diagnostics.DiagnosticSource 通過訂閱發佈模式運行,我們可 ...
  • 開源項目地址:https://gitee.com/lowcodexaf/rules-engine-editor 前言 本項目是基於XAFBlazor的規則引擎編輯器,規則引擎採用的是微軟開源的RulesEngine RulesEngine項目地址:https://github.com/microso ...
  • 開閉原則(Open-Closed Principle, OCP)是面向對象設計的五大SOLID原則之一。這個原則主張“軟體實體(類、模塊、函數等)應該對擴展開放,對修改關閉”。也就是說,軟體的設計應該允許在不修改原有代碼的情況下增加新的功能。這樣的設計有助於降低代碼的複雜性和維護成本,同時提高系統的 ...
  • 上傳附件判斷word、excel、txt等文檔中是否含有敏感詞如身份證號,手機號等,其它檢測如PDF,圖片(OCR)等可以自行擴展。 互聯網項目中,展示的數據中不能包含個人信息等敏感信息。判斷word中是否包含手機號,word正文中是否包含身份證號等敏感信息,通過正則表達式判斷匹配手機號,身份證號, ...
  • 單一職責原則(Single Responsibility Principle, SRP)是面向對象編程和設計的五大SOLID原則之一。它強調一個類、方法或模塊應該只有一個職責,即只做一件事情。如果一個類承擔的職責過多,那麼它的可維護性、可讀性和可擴展性都會受到影響。當需要修改類的某個職責時,可能會影 ...
  • 由於同一臺電腦可以安裝多個版本的.NET Core SDK。 當安裝了許多不同版本的.NET Core SDK 之後,要如何才能使用舊版dotnet 命令,執行dotnet new 或dotnet build 之類的命令? 這部分其實並不困難,只要設定global.json 即可。 首先要查詢目前電 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...