聯(lián)合體原來是這么用的!
不管是線程間、還是設(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)系工作人員刪除。