博客專欄

EEPW首頁 > 博客 > 聯(lián)合體原來是這么用的!

聯(lián)合體原來是這么用的!

發(fā)布人:魚鷹談單片機(jī) 時間:2022-06-22 來源:工程師 發(fā)布文章

不管是線程間、還是設(shè)備間通信,都需制定一個通信協(xié)議,規(guī)定數(shù)據(jù)的格式、內(nèi)容等。

線程間通信因?yàn)樵谛酒瑑?nèi)部傳輸,基本可以排除數(shù)據(jù)干擾導(dǎo)致的異常,所以通常會設(shè)計的比較簡單,但是設(shè)備間的通信(不管是無線還是有線)就會復(fù)雜一些,一般都包含幀頭、校驗(yàn)位之類的,因此魚鷹在一篇文章中介紹了一個基本的串口協(xié)議框架《如何寫一個健壯且高效的串口接收程序?》。

因?yàn)楫?dāng)時剛畢業(yè)沒多久,所以雖然從大的方向介紹了基本協(xié)議內(nèi)容,但在細(xì)節(jié)處理上還不夠好,比如可維護(hù)性、可讀性等方面。

后來,魚鷹在學(xué)習(xí)開源的飛控源碼時,發(fā)現(xiàn)里面使用了聯(lián)合體+結(jié)構(gòu)體的方式,大大提高了程序的可維護(hù)性和可讀性。

比如,我們的協(xié)議中有這樣三條命令,心跳包、獲取固件版本號、獲取序列號。

初級版本:

直接使用基本的類型聲明所需要的數(shù)據(jù)結(jié)構(gòu)。

uint8_t heartbeat;char version[6]; 
// "2.0.4"
char sn[7]; // "654321"

一般這樣寫的,大概率是工作一兩年,當(dāng)然也不排除工作好多年的也可能這樣寫。看似簡單,但可維護(hù)性、可讀性都非常差。

中級版本:

使用結(jié)構(gòu)體的形式聲明各種消息內(nèi)容。

typedef struct {  uint8_t heart_nbr;}msg_heartbeat_def;
typedef struct {  char version[sizeof("2.0.4") + 1];}msg_version_def;
typedef struct {  char sn[sizeof("123456") + 1]; }msg_serial_number_def;

這種方式,一般是工作兩三年以上的,開始使用結(jié)構(gòu)體容納數(shù)據(jù)內(nèi)容,可擴(kuò)展性比較強(qiáng),可維護(hù)性、可讀性也不錯。比如假設(shè)后面版本號這個消息里面希望同時獲取編譯時間,那直接在里面增加即可。

typedef struct {  char version[sizeof("2.0.4") + 1];   
char compile_time[sizeof("2022-12-12, 12:00:00") + 1]; 
}msg_version_def;

如果其它代碼寫的比較好,甚至不需要多大改動,即可完成一次擴(kuò)展。

高級版本:

在中級版本的基礎(chǔ)上,使用聯(lián)合體容納前面的所有消息類型。

typedef union {  msg_heartbeat_def      heartbeat;     // 心跳包  msg_version_def        version_nbr;   // 版本號  msg_serial_number_def  serial_number; // 產(chǎn)品序列號}msg_data_def;

當(dāng)你需要發(fā)送消息的時候,可以這樣發(fā)送:


typedef enum {  MSG_ID_HEARTBEAT,  MSG_ID_VERSION,  MSG_ID_SN,}msg_id_def;
void msg_uart_send(msg_id_def id, msg_data_def *msg_data, uint32_t size){// 這里加入幀頭、消息ID、校驗(yàn)之類的再發(fā)送出去#define FRAME_FIX_SIZE_MIN  10  // 組成一幀數(shù)據(jù)的最小空間,包含幀頭之類的  uint8_t send_buff[sizeof(msg_data_def) + FRAME_FIX_SIZE_MIN]; }void msg_send_vesion(void){   msg_data_def data;    strcpy(data.version_nbr, "1.0.1");         msg_uart_send(MSG_ID_VERSION, &data, sizeof(data.version_nbr));}

因?yàn)楝F(xiàn)在大部分 IDE 都有代碼提示功能,所以當(dāng)你需要發(fā)送數(shù)據(jù)的時候,可以根據(jù)提示選擇你需要的消息進(jìn)行發(fā)送,相當(dāng)方便快捷,也不容易出錯。


接收消息時,可以使用 switch(id) 之類的解析對應(yīng)數(shù)據(jù)。


在這個例子中,我們利用聯(lián)合體的特性,將所有消息類型集成在一起,這樣當(dāng)你需要發(fā)送一條消息時,很容易就能找到想要的數(shù)據(jù)類型。并且在空間占用上也是非常合理的。當(dāng)需要開辟緩存容納數(shù)據(jù)幀格式,利用了聯(lián)合體空間占用特性(找最大),這樣你開辟的空間一定是剛剛好,不多也不少,節(jié)省了空間使用。


所以,當(dāng)我們需要傳輸一類消息,但這些消息不會同時存在時,不如使用聯(lián)合體吧。


*博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點(diǎn),如有侵權(quán)請聯(lián)系工作人員刪除。



關(guān)鍵詞: 單片機(jī)

相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉