STM32-FreeRTOS操作系統-二值信號量與計數信號量

引言

在嵌入式開發領域,任務同步與通信是系統穩定運行的核心。STM32配合FreeRTOS操作系統,為開發者提供了強大的工具支持。其中,二值信號量和計數信號量作為FreeRTOS的關鍵同步機制,分別用于任務間的簡單同步和資源計數控制。二值信號量如同一個開關,用于控制任務的進入與退出;而計數信號量則用于管理有限資源的訪問,確保資源的合理分配。本文將深入剖析這兩種信號量的工作原理、使用方法及在STM32平臺上的應用實例,助力開發者精準掌握其精髓,提升系統開發效率與穩定性。

什么是二值信號量?

二值信號量是一種同步機制,用于在多任務環境中協調任務的執行。它本質上是一個只能取兩個值(通常是0和1)的變量,類似于一個開關。當信號量的值為1時,表示資源可用;當值為0時,表示資源已被占用。在FreeRTOS中,二值信號量主要用于任務間的同步,而不是用于互斥。它的工作原理如下:當一個任務需要資源時,它會嘗試獲取信號量。如果信號量的值為1,任務會將其值減1并繼續執行;如果信號量的值為0,任務會進入阻塞狀態,等待信號量變為1。當另一個任務釋放資源時,它會將信號量的值加1,從而喚醒等待的任務。

什么是計數信號量?

計數信號量是一種用于同步和資源管理的機制,它維護一個非負整數值,表示可用資源的數量。在FreeRTOS中,計數信號量通常用于管理有限數量的資源,例如緩沖區、硬件設備等。計數信號量的工作原理如下:當一個任務需要使用資源時,它會嘗試獲取信號量。如果信號量的值大于0,任務會將其值減1并繼續執行,表示占用了一個資源;如果信號量的值為0,任務會進入阻塞狀態,等待資源變為可用。當任務釋放資源時,它會將信號量的值加1,從而表示資源數量增加,可能會喚醒等待的任務。

二值信號量

創建二值信號量句柄

步驟都跟前面的創建任務差不多,只不過在創建任務的基礎上添加了二值信號量的相關函數,從本文章開始就不再細說怎么創建任務,直接開始講二值信號量的創建及使用。

首先定義一下二值信號量的句柄并為其賦初始值為NULL:

SemaphoreHandle_t BinarySem_Handle =NULL; //二值信號量句柄

然后是xSemaphoreCreateBinary,它是FreeRTOS操作系統提供的一個函數,用于創建一個二值信號量,前面也說了,二值信號量是一個特殊的信號量,它的值只能是0或1,通常用于任務間的同步。其函數原型為:

SemaphoreHandle_t xSemaphoreCreateBinary(void);

這個函數沒有參數,但有一個返回值,通常返回的是一個二值信號量的句柄,如果創建成功就返回相應的值,失敗就返回NULL。

發送二值信號量

也叫釋放二值信號量,其函數為xSemaphoreGive(),該函數原型為:

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore)

參數xSemaphore為需要發送或釋放的信號量句柄,這里我們填入上面定義好的二值信號量句柄。同時,該函數還有一個返回值,返回的類型是BaseType_t,通常是一個整數類型。如果返回值為pdTRUE(在freertos中通常定義為1),則表示信號量發送或釋放成功。如果返回值為pdFALSH(在freertos中通常定義為0),則表示發送或釋放失敗。

獲取二值信號量

有發送就有獲取,其函數為xSemaphoreTake(),該函數原型為:

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);

參數xSemaphore跟上面的一樣,填入定義好的句柄即可。參數xTicksToWait為任務等待信號量的最大時間,可根據需要選擇,通常為portMAX_DELAY。該函數還有一個返回值,跟上面一樣,這里就跳過了。

二值信號量示例代碼

#include "myfreertos.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"
#include "Usart.h"
#include "oled.h"
#include "Task.h"
#include "led.h"
#include "key.h"TaskHandle_t MyTaskHandler;//任務句柄TaskHandle_t MyTask1Handler;//任務1句柄TaskHandle_t SendTask_Handler;  //發送消息句柄TaskHandle_t ReceiveTask_Handler;//接收消息句柄void MyTask(void *pvParameters);    //聲明啟動函數void MyTask1(void *pvParameters);   //聲明任務1函數void Send_task(void *pvParameters);  //聲明發送消息函數void Receive_task(void *pvParameters);  //聲明接收消息函數SemaphoreHandle_t BinarySem_Handle =NULL; //二值信號量句柄void Start_Task(void)
{xTaskCreate(MyTask,"MyTask",128,NULL,1,&MyTaskHandler);//動態方法創建任務vTaskStartScheduler();//啟動任務調動		
}void MyTask(void *arg)            //開始創建任務函數
{taskENTER_CRITICAL();           //進入臨界區	/* 創建二值信號量 BinarySem */BinarySem_Handle = xSemaphoreCreateBinary();	xTaskCreate(MyTask1,"MyTask1",50,NULL,2,&MyTask1Handler);//動態方法創建任務1xTaskCreate(Receive_task,"Receive_task",50,NULL,3,&ReceiveTask_Handler);//創建接收消息任務xTaskCreate(Send_task,"Send_task",50,NULL,4,&SendTask_Handler);  //創建發送消息任務vTaskDelete(MyTaskHandler);    //刪除開始任務taskEXIT_CRITICAL();           //退出臨界區
}void MyTask1(void *arg)     //任務1函數體
{while(1){OLED_ShowString(1,1,"Runing Task Led");GPIO_ResetBits(GPIOC,GPIO_Pin_13);vTaskDelay(300);GPIO_SetBits(GPIOC,GPIO_Pin_13);vTaskDelay(900);}	
}//接收任務函數
void Receive_task(void *pvParameters)
{BaseType_t xReturn = pdPASS;/* 定義一個創建信息返回值,默認為pdPASS */while(1){		      //獲取二值信號量 xSemaphore,沒獲取到則一直等待xReturn = xSemaphoreTake(BinarySem_Handle,100); /* 等待時間 */if(pdTRUE == xReturn)OLED_ShowString(3,1,"Receive_OK");elseOLED_ShowString(3,1,"Receive_NO");vTaskDelay(200);}
}//發送任務函數
void Send_task(void *pvParameters)
{BaseType_t xReturn = pdPASS;/* 定義一個創建信息返回值,默認為pdPASS */while(1){if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==1){xReturn = xSemaphoreGive( BinarySem_Handle );//給出二值信號量if( xReturn == pdTRUE )OLED_ShowString(2,1,"Send_OK");}else{OLED_ShowString(2,1,"Send_NO");}vTaskDelay(200);}
}

計數信號量

創建計數信號量

跟二值信號量一樣,先定義一個計數信號量句柄并給其賦初值為NULL:

SemaphoreHandle_t CountSem_Handle =NULL; //計數信號量句柄

xSemaphoreCreateCounting()是FreeRTOS中用于創建計數信號量的函數,其函數原型為:

SemaphoreHandle_t xSemaphoreCreateCounting(
UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount
)

參數uxMaxCount是計數信號量的最大計數值,當信號量值達此值時,不能再通過xSemaphoreGive增加其值。參數uxInitialCount為信號量的初始計數值。該函數還有一個返回值,如果成功創建信號量,就返回信號量的句柄,失敗則返回NULL。

發送計數信號量與獲取計數信號量

步驟都跟二值信號量大致相同,這里就不過多論述。

計數信號量示例代碼

#include "myfreertos.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"
#include "Usart.h"
#include "oled.h"
#include "Task.h"
#include "led.h"
#include "key.h"TaskHandle_t MyTaskHandler;//任務句柄TaskHandle_t MyTask1Handler;//任務1句柄TaskHandle_t SendTask_Handler;  //發送消息句柄TaskHandle_t ReceiveTask_Handler;//接收消息句柄void MyTask(void *pvParameters);    //聲明啟動函數void MyTask1(void *pvParameters);   //聲明任務1函數void Send_task(void *pvParameters);  //聲明發送消息函數void Receive_task(void *pvParameters);  //聲明接收消息函數SemaphoreHandle_t CountSem_Handle =NULL; //計數信號量句柄void Start_Task(void)
{xTaskCreate(MyTask,"MyTask",128,NULL,1,&MyTaskHandler);//動態方法創建任務vTaskStartScheduler();//啟動任務調動		
}void MyTask(void *arg)            //開始創建任務函數
{taskENTER_CRITICAL();           //進入臨界區	/* 創建 CountSem */CountSem_Handle = xSemaphoreCreateCounting(5,5); 	xTaskCreate(MyTask1,"MyTask1",50,NULL,2,&MyTask1Handler);//動態方法創建任務1xTaskCreate(Receive_task,"Receive_task",50,NULL,3,&ReceiveTask_Handler);//創建接收消息任務xTaskCreate(Send_task,"Send_task",50,NULL,4,&SendTask_Handler);  //創建發送消息任務vTaskDelete(MyTaskHandler);    //刪除開始任務taskEXIT_CRITICAL();           //退出臨界區
}void MyTask1(void *arg)     //任務1函數體
{while(1){OLED_ShowString(1,1,"Runing Task Led");GPIO_ResetBits(GPIOC,GPIO_Pin_13);vTaskDelay(300);GPIO_SetBits(GPIOC,GPIO_Pin_13);vTaskDelay(900);}	
}//接收任務函數
void Receive_task(void *pvParameters)
{BaseType_t xReturn = pdPASS;/* 定義一個創建信息返回值,默認為pdPASS */while(1){		      //獲取計數信號量 xSemaphore,沒獲取到則一直等待xReturn = xSemaphoreTake(CountSem_Handle,100); /* 等待時間 */if(pdTRUE == xReturn)OLED_ShowString(3,1,"Receive_OK");//elseOLED_ShowString(3,1,"Receive_NO");vTaskDelay(200);}
}//發送任務函數
void Send_task(void *pvParameters)
{BaseType_t xReturn = pdPASS;/* 定義一個創建信息返回值,默認為pdPASS */while(1){if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==1){xReturn = xSemaphoreGive( CountSem_Handle );//給出計數信號量if( xReturn == pdTRUE )OLED_ShowString(2,1,"Send_OK");}else{OLED_ShowString(2,1,"Send_NO");}vTaskDelay(200);}
}

二值信號量與計數信號量的區別

二值信號量只有兩種狀態(0和1),通常用于控制互斥訪問,一次只允許一個進程進入臨界區。而計數信號量可以有多個值,用于表示資源的數量,允許多個進程同時訪問有限數量的資源。

講解一下上面兩個代碼的思路

二值信號量

這個我主要是通過按下按鍵然后去釋放二值信號量,按下按鍵釋放二值信號量,然后被接收二值信號量任務接收,該任務就會執行里面的程序,執行完以后退出,二值信號量的信息返回值有由1變0,等待下一次信號的到來。值得注意的是,二值信號量是單次事件,如果需要執行多個事件,執行事件的順序就從高優先級向低優先級執行,不能同時執行。

計數信號量

跟二值信號量的思路邏輯是一樣的,但對于計數信號量而言,它能同時執行多個任務,提高了資源的利用率。

總結

以上是我的個人看法,如有不足,歡迎指出!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/96957.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/96957.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/96957.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MarTech營銷技術全景解析:概念、圖譜與最新實踐案例

一、引言:為什么企業越來越依賴MarTech?在數字化浪潮下,企業營銷環境正發生深刻變化:客戶觸點增加:從官網、社交媒體到短視頻、展會,信息渠道呈指數級增長。決策鏈條復雜:B2B客戶通常需要多輪調…

服務器 - 從一臺服務器切換至另一臺服務器(損失數十條訪客記錄)

服務器 - 從一臺服務器切換至另一臺服務器(損失數十條PV記錄為代價) 看著四年的服務器正式到期,沒什么轟轟烈烈的告別,就像目送老朋友轉身走遠,只默默記下:哦,原來它陪了我這么久啊。 前言 一臺陪伴了我4年的服務器昨…

《云原生邊緣與AI訓練場景:2類高頻隱蔽Bug的深度排查與架構修復》

在云原生技術向邊緣計算與AI訓練場景滲透的過程中,基礎設施層的問題往往會被場景特性放大——邊緣環境的弱網絡、異構硬件,AI訓練的高資源依賴、分布式協作,都可能讓原本隱藏的Bug以“業務故障”的形式爆發。這些問題大多不具備直觀的報錯信息…

【51單片機】【protues仿真】基于51單片機數控直流穩壓電源系統

目錄 一、主要功能 二、使用步驟 三、硬件資源 四、軟件設計 五、實驗現象 一、主要功能 1、數碼管顯示輸出電壓值 2、滑動電阻調節輸出電壓 3、電壓輸出范圍0-15V,步進值1 二、使用步驟 基于51單片機的數控直流穩壓電源是一種通過數字控制實現電壓調節的智…

xtuoj Rectangle

題目思路將矩形間的相交情況通過投影轉化為x、y兩個方向下的線段是否相交,即前面的題目,判斷兩個區間是否相交,x投影的每個區間的左端點是每個矩形x的min,右端點是每個矩形的x的max,y投影情況同理,只要x軸的…

【深度學習踩坑實錄】從 Checkpoint 報錯到 TrainingArguments 精通:QNLI 任務微調全流程復盤

作為一名深度學習初學者,最近在基于 Hugging Face Transformers 微調 BERT 模型做 QNLI 任務時,被Checkpoint 保存和TrainingArguments 配置這兩個知識點卡了整整兩天。從磁盤爆滿、權重文件加載報錯,到不知道如何控制 Checkpoint 數量&#…

Java面試小冊(3)

21【Q】: 什么是Java的SPI機制?【A】:SPI 是一種插件機制,用于在運行時動態加載服務的實現。它通過定義接口(服務接口)并提供一種可擴展的方式來讓服務的提供著(實現類)在運行時注入&#xff0c…

P1150 Peter 的煙

記錄20#include <bits/stdc.h> using namespace std; int main(){int n,k;cin>>n>>k;int cnt0;while(n>k){cntk;nn-k1;}cntn;cout<<cnt;return 0; }突破口每吸完一根煙就把煙蒂保存起來&#xff0c;k&#xff08;k>1&#xff09;個煙蒂可以換一個…

Cursor和Hbuilder用5分鐘開發微信小程序

分享一個5分鐘搞定微信小程序開發的技能&#xff0c;需要用到兩個工具&#xff1a;Cursor和Hbuilder。 第1步、下載HBuilder。Hbuilder可以實現一套代碼直接生成安卓、蘋果、鴻蒙各個平臺APP。訪問Hbuilder的官方網站&#xff0c;HBuilderX-高效極客技巧&#xff0c;選擇適合…

k8s的dashboard

找一個裝有docker的機器&#xff0c;在一個rocky linux的虛擬機里弄拉取一個rancher鏡像建立一個目錄&#xff0c;目的&#xff1a;和里面數據做持久化關聯后臺運行&#xff0c;讓他有權限&#xff0c;8080端口和容器80端口映射&#xff0c;443和443做映射查看一下刪掉&#xf…

橋接模式,打造靈活可擴展的日志系統C++

一、為什么用橋接模式在企業開發中&#xff0c;日志系統幾乎是標配。常見需求&#xff1a;日志有多種類型&#xff08;Info、Warning、Error 等&#xff09;&#xff1b;日志需要支持多種輸出方式&#xff08;控制臺輸出、寫文件、遠程上傳、數據庫存儲等&#xff09;。如果把這…

kafka--基礎知識點--5.3--producer事務

1 事務簡介 Kafka事務是Apache Kafka在流處理場景中實現Exactly-Once語義的核心機制。它允許生產者在跨多個分區和主題的操作中&#xff0c;以原子性&#xff08;Atomicity&#xff09;的方式提交或回滾消息&#xff0c;確保數據處理的最終一致性。例如&#xff0c;在流處理中…

利用DeepSeek實現服務器客戶端模式的DuckDB原型

在網上看到韓國公司開發的一款GooseDB&#xff0c;DuckDB? 的功能擴展分支&#xff0c;具有服務器/客戶端、多會話和并發寫入支持&#xff0c;使用 PostgreSQL 有線協議&#xff0c;但它是Freeware而不是開源&#xff0c;所以讓DeepSeek實現之。 首先把readme頁面發給他翻譯&a…

麥當勞APP逆向

版本 V 7.0.17.0反調試 梆梆企業加固 frida反調試部分代碼 headers {"biz_scenario": "500","biz_from": "1004","User-Agent": "mcdonald_Android/7.0.17.0 (Android)","ct": "102","…

大數據畢業設計選題推薦-基于大數據的結核病數據可視化分析系統-Hadoop-Spark-數據可視化-BigData

?作者主頁&#xff1a;IT畢設夢工廠? 個人簡介&#xff1a;曾從事計算機專業培訓教學&#xff0c;擅長Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等項目實戰。接項目定制開發、代碼講解、答辯教學、文檔編寫、降重等。 ?文末獲取源碼? 精彩專欄推薦?…

Vue3 視頻播放器完整指南 – @videojs-player/vue 從入門到精通

前言 在 Vue 3 生態中&#xff0c;視頻播放功能是許多應用的核心需求。videojs-player/vue 是一個專門為 Vue 3 設計的視頻播放器組件&#xff0c;基于成熟的 Video.js 庫構建&#xff0c;提供了簡單而強大的視頻播放解決方案。 主要特性 Vue 3 組件化&#xff1a;原生 Vue …

【靶場練習】--DVWA第一關Brute Force(暴力破解)全難度分析

注意&#xff0c;這一關必須要使用Burpsuite來抓包 目錄Low1.抓包2.發送到爆破模塊3.選擇爆破模式爆破模式介紹4.添加載荷5.添加字典6.爆破查看查看源碼Medium查看源碼High1.抓包2.在bp的extensions中找到CSRF Token Tracker&#xff0c;并安裝3.構造字典4.成功爆破查看源碼Imp…

Java語言——排序算法

一、基本概念排序&#xff1a;將n個數字按一定順序排列&#xff08;比如&#xff1a;升序&#xff0c;或者降序&#xff09; ^內部排序 &#xff1a;若整個排序過程不需要訪問外存便能完成&#xff0c;則稱此類排序問題為內部排序 ^外部排序&#xff1a;若參加排序的記錄數量很…

【Linux】人事檔案——用戶及組管理

目錄 1 用戶及組管理 2?用戶及用戶組管理命令 2.1 useradd&#xff1a;建立用戶 useradd命令用于建立用戶&#xff0c;該 2.2 passwd&#xff1a;更改用戶密碼 2.3 usermod&#xff1a;更改用戶信息 2.4 groupadd&#xff1a;建立用戶組 2.5 finger&#xff1a;查找并顯…

給定一個有序的正數數組arr和一個正數range,如果可以自由選擇arr中的數字,想累加得 到 1~range 范圍上所有的數,返回arr最少還缺幾個數。

給定一個有序的正數數組arr和一個正數range&#xff0c;如果可以自由選擇arr中的數字&#xff0c;想累加得 到 1~range 范圍上所有的數&#xff0c;返回arr最少還缺幾個數。 #include <iostream> #include <vector>using namespace std;void func1(std::vector<…