💌 所屬專欄:【BES2500x系列】
😀 作??者:我是夜闌的狗🐶
🚀 個人簡介:一個正在努力學技術的CV工程師,專注基礎和實戰分享 ,歡迎咨詢!
💖 歡迎大家:這里是CSDN,我總結知識的地方,喜歡的話請三連,有問題請私信 😘 😘 😘
您的點贊、關注、收藏、評論,是對我最大的激勵和支持!!!🤩 🤩 🤩
文章目錄
- 前言
- 1 介紹
- 2 功能特性
- 3 同步與通信
- 3.1 通信
- 4 同步與通信
- 4.1 消息隊列
- 4.1.1 定義
- 4.1.2 創建
- 4.1.3發送消息
- 4.2 郵箱處理
- 4.2.1 定義
- 4.2.2 創建
- 4.2.3 發送/釋放郵件
- 4.2.4 獲取郵件
- 總結
前言
??大家好,又見面了,我是夜闌的狗🐶,本文是專欄【BES2500x系列】專欄的第4篇文章;
??今天開始學習BES2500x系列的一天💖💖💖,開啟新的征程,記錄最美好的時刻🎉,每天進步一點點。
??專欄地址:【BES2500x系列】, 此專欄是我是夜闌的狗對BES2500x系列開發過程的總結,希望能夠加深自己的印象,以及幫助到其他的小伙伴😉😉。
??如果文章有什么需要改進的地方還請大佬不吝賜教👏👏。
1 介紹
??在嵌入式系統中,同步和通信是確保系統內各個組件協調工作的兩個核心概念。它們對于實現高效、可靠的嵌入式應用至關重要。前面已經講過同步概念了,接下來對通信概念進行簡要說明。話不多說,那接下來就學習 RTX
系統中通信機制都有哪些吧,讓我們原文再續,書接上回吧。😉
2 功能特性
??在實時操作系統(RTOS)中,任務管理和同步通信是關鍵組件,它們確保系統的高效和有序執行。本文將探討這些概念,特別是線程管理、信號量、互斥鎖、消息隊列和郵箱處理。
- 任務管理:RTX提供任務創建、調度和優先級管理,確保任務按照優先級及時執行。
- 同步與通信:包括信號量、互斥鎖、消息隊列和郵箱,促進任務間的同步和數據交換。
- 內存管理:內存池和動態內存分配,有效管理有限的系統資源。
- 定時器服務:虛擬和硬件定時器,支持周期性任務和一次性事件觸發。
- 中斷處理:保證中斷服務的快速響應,同時保持任務的上下文安全。
- 線程安全:通過內核級保護機制,防止多線程環境下的數據競爭和死鎖。
3 同步與通信
3.1 通信
??通信是指嵌入式系統中不同組件或任務之間交換信息的過程。有效的通信機制對于分布式系統和多處理器系統尤為重要。嵌入式系統中常用的通信方式包括:
序號 | 方法 | 說明 |
---|---|---|
1 | 消息隊列(Message Queues) | 任務間通過發送和接收帶有數據的消息來通信,支持異步通信。 |
2 | 管道(Pipes) | 一種半雙工的數據傳輸方式,常用于進程間的通信。 |
3 | 共享內存(Shared Memory) | 多個任務可以直接讀寫同一塊內存區域,效率高但需要同步機制來避免沖突。 |
4 | 總線(Buses) | 如 I2C 、SPI 、UART 等硬件接口,用于設備間的數據傳輸。 |
5 | 遠程過程調用(RPC) | 允許程序調用網絡中另一臺計算機上的子程序,模擬本地調用。 |
6 | 中斷(Interrupts) | 硬件觸發的事件,用于通知 CPU 處理緊急或外部事件,是一種快速的通信方式。 |
??同步和通信機制的選擇取決于嵌入式系統的具體需求,包括實時性、資源限制、復雜度以及系統的可靠性要求。合理設計同步和通信策略,是保證嵌入式系統高效穩定運行的關鍵。
4 同步與通信
4.1 消息隊列
??消息隊列允許線程間安全地傳遞固定大小的消息,提供了異步通信的方式。
4.1.1 定義
??消息隊列允許線程安全地發送和接收固定大小的數據塊。隊列維護發送和接收的順序。一般在文件開頭會看到這樣的定義:osMessageQDef
。
- 代碼
// 定義一個名為app_test1_queue的消息隊列,可存儲128個uint32_t類型的元素。
osMessageQDef(app_test1_queue, 128, uint32_t);// 聲明一個osMessageQId類型的變量app_test1_queue_id,用于保存消息隊列的句柄。
// 在程序運行時,需要通過調用相關API初始化并獲取有效的句柄值。
osMessageQId app_test1_queue_id = NULL;
??這段代碼首先使用 osMessageQDef
宏定義了一個名為 app_test1_queue
的消息隊列,它可以存儲128個32位無符號整數。然后聲明了一個變量 app_test1_queue_id
,用于存儲消息隊列的標識符(句柄),初始值設為 NULL
。在實際應用中,需要通過操作系統提供的API來初始化這個消息隊列,并將返回的句柄賦值給app_test1_queue_id
。
/*** 定義一個消息隊列。* * 這個宏用于靜態定義一個消息隊列,它會創建一個靜態隊列控制塊和一個用于存儲消息的數據緩沖區。* * @param name 消息隊列的名稱。* @param queue_sz 隊列中能容納的消息數量。* @param type 消息隊列中每條消息的數據類型。*/
#define osMessageQDef(name, queue_sz, type) \
static StaticQueue_t os_mq_cb_##name; \
static uint32_t os_mq_data_##name[(queue_sz) * sizeof(type)]; \
const osMessageQDef_t os_messageQ_def_##name = \
{ (queue_sz), \{ NULL, 0U, (&os_mq_cb_##name), sizeof(StaticQueue_t), \(&os_mq_data_##name), sizeof(os_mq_data_##name) } }
??此宏定義了三個靜態變量:一個靜態隊列控制塊,一個消息數據數組,和一個用于OS的消息隊列定義結構體。這個結構體包含了隊列的大小、指針到靜態隊列控制塊和消息數據數組的地址,以及這些數組的大小。這使得在系統運行時能夠直接使用這個消息隊列而無需動態分配內存。
- 參數/函數講解
序號 | 參數/函數 | 說明 |
---|---|---|
1 | osMessageQId | 聲明變量,用于存儲消息隊列的標識符(句柄),初始值設為NULL。 |
2 | osMessageQDef | 定義了靜態變量:靜態隊列控制塊,消息數據數組和用于OS的消息隊列定義結構體 |
4.1.2 創建
??通過 osMessageQueueCreate()
函數創建消息隊列,指定隊列容量和消息大小。
- 代碼
/*** 初始化app_test1_queue消息隊列。** 這個函數負責創建名為app_test1_queue的消息隊列,并將成功創建的句柄保存到全局變量app_test1_queue_id。* 如果消息隊列創建失敗,它會記錄錯誤信息并返回-1。** @return* - 0: 消息隊列創建成功。* - -1: 創建消息隊列失敗。*/
static int32_t app_test1_queue_init(void)
{// 使用osMessageCreate函數創建消息隊列,并將句柄保存到全局變量app_test1_queue_id = osMessageCreate(osMessageQ(app_test1_queue), NULL);// 檢查創建是否成功,如果失敗則打印錯誤信息并返回-1if (app_test1_queue_id == NULL) {TRACE(0, "Failed to Create app_test_thread1_queue");return -1;}// 創建成功,返回0return 0;
}
??這段代碼定義了一個名為 app_test1_queue_init
的靜態函數,用于初始化之前定義的消息隊列app_test1_queue
。它通過調用o sMessageCreate
函數創建消息隊列,并檢查返回的句柄是否有效。如果創建失敗,它會記錄錯誤信息并返回 -1 ;否則,返回 0 表示成功。
- 參數/函數講解
序號 | 參數/函數 | 說明 |
---|---|---|
1 | osMessageCreate | 創建消息隊列 |
4.1.3發送消息
??使用 osMessageQueueSend()
或 osMessageQueuePut()
函數向隊列發送消息。
- 代碼
/*** 嘗試向app_test1_queue中發送消息。** 此函數檢查消息隊列是否有足夠的空間接收至少6條消息。如果隊列有足夠空間,* 它將向隊列中放入一個值為0xFF的消息,不設置優先級。** 注意:這個函數沒有處理消息隊列滿的情況,因此在隊列滿時不會阻塞。*/
void app_test1_queue_put(void)
{// 檢查消息隊列是否有超過5個空閑槽位if (osMessageGetSpace(app_test1_queue_id) > 5) {// 向消息隊列app_test1_queue_id中插入一個值為0xFF的消息,優先級設為0osMessagePut(app_test1_queue_id, 0xFF, 0);}
}
??這個函數 app_test1_queue_put
嘗試將一個值為 0xFF
的消息放入名為 app_test1_queue
的消息隊列中。首先,它檢查隊列是否有足夠的空間容納至少6個新消息。如果滿足條件,就調用 osMessagePut
將消息放入隊列,否則不做任何操作。注意,這個函數沒有處理隊列已滿的情況,所以如果隊列已滿,消息將不會被發送,也不會阻塞調用線程。
- 參數/函數講解
序號 | 參數/函數 | 說明 |
---|---|---|
1 | osMessageGetSpace | 檢查消息隊列的空閑槽位 |
2 | osMessagePut | 將消息放入隊列 |
4.2 郵箱處理
??郵箱是用于線程間交換結構化數據的對象池。每個郵箱包含一組預先分配的內存塊,線程可以申請、發送和接收這些內存塊。
4.2.1 定義
??一般在文件開頭會看到這樣的定義:osMailQDef
。
- 代碼
osMailQDef (app_test1_mailbox, APP_TEST1_MAX_MAILBOX, APP_TEST1_MAIL);
/*** app_test1_mailbox: 郵箱隊列定義** 使用osMailQDef宏定義一個名為'app_test1_mailbox'的郵箱隊列,最大郵件數為APP_TEST1_MAX_MAILBOX,* 郵件類型為APP_TEST1_MAIL。*/// 郵箱隊列ID,用于后續操作
static osMailQId app_test1_mailbox_id = NULL;/*** app_test1_mail_alloc - 分配并初始化一個APP_TEST1_MAIL類型的郵件** @param mail 指向郵件指針的指針,用于存放新分配的郵件地址。** 返回值: 成功分配時返回0,失敗則返回非0值。** 此函數為內部使用,負責從'app_test1_mailbox'郵箱隊列中分配一個新的郵件,并將其地址存儲在* 輸入參數'mail'指向的變量中。具體實現省略。*/
static int app_test1_mail_alloc(APP_TEST1_MAIL** mail)
{// ...
}
??osMailQDef
定義了一個名為 app_test1_mailbox
的郵箱隊列,用于存儲 APP_TEST1_MAIL
類型的數據。APP_TEST1_MAX_MAILBOX
定義了郵箱隊列可容納的最大郵件數量。這個郵箱隊列可以用于多線程或任務之間的數據通信,確保數據安全地傳遞。
/*** 定義一個郵箱隊列。* * 該宏用于靜態定義一個郵箱隊列以及相關的OS郵箱隊列結構體。它為指定的郵箱隊列分配內存,* 并初始化OS郵箱隊列結構體。* * @param name 郵箱隊列的名稱。* @param queue_sz 郵箱隊列中郵件的最大數量。* @param type 郵件中元素的類型。*/
#define osMailQDef(name, queue_sz, type) \
static uint32_t os_mailQ_m_##name[3+((sizeof(type)+3)/4)*(queue_sz)]; \
osMailQDef_t os_mailQ_def_##name = \
{ {(queue_sz), sizeof(type), (os_mailQ_m_##name)}, NULL, {NULL} }
??在上述宏定義中:
??(1) 第一部分定義了一個靜態數組 os_mailQ_m_##name
,用于存儲郵箱隊列中的郵件。數組大小根據郵件類型 type
的大小和隊列大小 queue_sz
動態計算得出。
??(2) 第二部分定義了一個 osMailQDef_t
類型的結構體 os_mailQ_def_##name
,其中包含了郵箱隊列的配置信息,如隊列大小、郵件類型大小以及郵件存儲區的指針。
- 參數/函數講解
序號 | 參數/函數 | 說明 |
---|---|---|
1 | osMailQDef | 定義了的郵箱隊列,用于存儲 APP_TEST1_MAIL 類型的數據 |
2 | app_test1_mailbox_id | 是一個全局變量,用于存儲郵箱隊列的標識符,方便后續操作 |
3 | app_test1_mail_alloc | 用于從 app_test1_mailbox 中分配一個新的郵件,并將分配的郵件地址通過參數 mail 返回 |
4 | os_mailQ_m_##name | 用于存儲郵箱隊列中的郵件 |
5 | osMailQDef_t | 定義結構體,其中包含了郵箱隊列的配置信息 |
4.2.2 創建
??通過 osMailQCreate()
函數創建郵箱,指定郵箱的大小和數據類型。
- 代碼
/*** app_test1_mailbox_init - 初始化app_test1_mailbox郵箱隊列** @return: 成功初始化時返回0,失敗則返回-1。** 此函數用于初始化之前定義的'app_test1_mailbox'郵箱隊列。它調用osMailCreate函數創建郵箱隊列,* 并將返回的郵箱ID存儲在全局變量'app_test1_mailbox_id'中。如果創建失敗,函數會輸出錯誤信息* "Failed to Create app_test_thread1_mailbox",并返回-1表示初始化失敗。*/
static int32_t app_test1_mailbox_init(void)
{app_test1_mailbox_id = osMailCreate(osMailQ(app_test1_mailbox), NULL);if (app_test1_mailbox_id == NULL) {TRACE(0, "Failed to Create app_test_thread1_mailbox");return -1;}return 0;
}
??這個函數 app_test1_mailbox_init
負責初始化之前通過 osMailQDef
宏定義的 app_test1_mailbox
郵箱隊列。如果初始化成功,它將返回0;如果失敗(即無法創建郵箱隊列),它會打印錯誤信息并返回-1。
- 參數/函數講解
序號 | 參數/函數 | 說明 |
---|---|---|
1 | osMailCreate | 創建郵箱隊列 |
4.2.3 發送/釋放郵件
??使用 osMailQAlloc()
分配郵箱中的空間,然后用 osMailPut()
發送郵件。
- 代碼
/*** app_test1_mail_send - 發送一個APP_TEST1_MAIL類型的郵件到app_test1_mailbox** @param mail 需要發送的郵件對象指針。** 返回值: 成功發送時返回0,失敗則返回非0值。** 此函數用于將一個APP_TEST1_MAIL類型的郵件對象發送到'app_test1_mailbox'郵箱隊列中。* 具體實現省略,可能涉及到郵箱隊列的同步原語以保證線程安全。*/
static int app_test1_mail_send(APP_TEST1_MAIL* mail)
{// ...
}/*** app_test1_mail_free - 釋放app_test1_mailbox中的一個郵件對象** @param mail_p 已分配的郵件對象指針。** 返回值: 成功釋放時返回0,失敗則返回非0值。** 此函數用于釋放'app_test1_mailbox'郵箱隊列中已分配的一個郵件對象,以便于后續再使用。* 具體實現省略,可能涉及郵箱隊列的同步原語以保證線程安全。*/
static int app_test1_mail_free(APP_TEST1_MAIL* mail_p)
{// ...
}
- 參數/函數講解
序號 | 參數/函數 | 說明 |
---|---|---|
1 | app_test1_mail_send | 用于向 app_test1_mailbox 郵箱隊列中發送郵件 |
2 | app_test1_mail_free | 用于向 app_test1_mailbox 郵箱隊列中釋放已分配的郵件 |
4.2.4 獲取郵件
??線程通過 osMailGet()
函數獲取郵件,可以選擇等待或立即返回。
- 代碼
/*** @brief 獲取應用測試1的郵件對象* * @description 該函數從內部數據結構中獲取一個`APP_TEST1_MAIL`類型的郵件對象。* 如果郵件可用,它將分配內存并填充郵件內容,然后將其指針返回。* * @param[out] mail_p 指向接收`APP_TEST1_MAIL`結構體指針的指針。* 如果成功獲取郵件,此參數將被設置為有效郵件對象的指針。* * @return 成功獲取郵件對象返回0,否則返回非0錯誤代碼:* -1:郵件隊列為空* -2:內存分配失敗* 其他值:可能表示其他錯誤情況** @note 實現應考慮線程安全,可能需要加鎖來保護數據結構。* 如果隊列為空,可以選擇阻塞等待,直到有新郵件到達。*/
static int app_test1_mail_get(APP_TEST1_MAIL** mail_p)
{// 實現獲取郵件對象的邏輯,包括檢查隊列、分配內存、填充郵件內容等// ...if (/* 郵件隊列為空或分配內存失敗等錯誤條件 */) {return -1; // 或者 -2}return 0; // 成功獲取郵件
}
??app_test1_mail_get
函數用于從 app_test1_mailbox
郵箱隊列中取出一個郵件對象。當郵箱隊列為空時,函數可能阻塞等待,直到有新的郵件可供消費。函數返回0表示成功獲取郵件,非0值表示隊列為空或出現錯誤。具體實現細節被省略,實際操作中可能需要考慮線程同步問題。
- 參數/函數講解
序號 | 參數/函數 | 說明 |
---|---|---|
1 | app_test1_mail_get | 用于從郵箱隊列中取出一個郵件對象 |
總結
??感謝觀看,這里就是 同步與通信篇 – 消息隊列和郵箱處理,如果覺得有幫助,請給文章點個贊吧,讓更多的人看到。🌹 🌹 🌹
??也歡迎你,關注我。👍 👍 👍
??原創不易,還希望各位大佬支持一下,你們的點贊、收藏和留言對我真的很重要!!!💕 💕 💕 最后,本文仍有許多不足之處,歡迎各位認真讀完文章的小伙伴們隨時私信交流、批評指正!下期再見。🎉
更多專欄訂閱:
😀 【LeetCode題解(持續更新中)】
🥇 【恒玄BES】
🌼 【鴻蒙系統】
💎 【藍牙協議棧】
🎃 【死機分析】
👑 【Python腳本筆記】
🚝 【Java Web項目構建過程】
💛 【微信小程序開發教程】
? 【JavaScript隨手筆記】
🤩 【大數據學習筆記(華為云)】
🦄 【程序錯誤解決方法(建議收藏)】
🔐 【Git 學習筆記】
🚀 【軟件安裝教程】
訂閱更多,你們將會看到更多的優質內容!!