結(jié)構(gòu)體對(duì)齊原則在自定義協(xié)議解析時(shí)的妙用之法
掃描二維碼
隨時(shí)隨地手機(jī)看文章
關(guān)于結(jié)構(gòu)體對(duì)齊的設(shè)置,以GCC 32bit編譯為例,我們可以來(lái)看看下面這個(gè)例子:
#include <stdio.h>
//默認(rèn)情況下,結(jié)構(gòu)體一般在內(nèi)存中的自動(dòng)對(duì)齊格式是4個(gè)字節(jié)
//結(jié)構(gòu)體設(shè)置手動(dòng)對(duì)齊
//如果這里是4,那么下面的打印就是8
//如果這里是2,那么下面的打印就是6
//如果這里是1,那么下面的打印就是5
struct mystu
{
char a ;
int b ;
};
#pragma pack(4)
struct mystu0
{
char a ;
int b ;
};
#pragma pack()
#pragma pack(2)
struct mystu1
{
char a ;
int b ;
};
#pragma pack()
#pragma pack(1)
struct mystu2
{
char a ;
int b ;
};
#pragma pack()
int main(void)
{
printf("mystu:%d\n",sizeof(struct mystu));
printf("mystu0:%d\n",sizeof(struct mystu0));
printf("mystu1:%d\n",sizeof(struct mystu1));
printf("mystu2:%d\n",sizeof(struct mystu2));
return 0 ;
}
運(yùn)行結(jié)果:
根據(jù)這樣的原理,在MCU協(xié)議數(shù)據(jù)解析的時(shí)候就很有作用了,比如下面這個(gè)例子,目前在小車(chē)上用:
//結(jié)構(gòu)體,用于存儲(chǔ)解析的數(shù)據(jù)
typedef struct
{
//幀頭(固定解析為FF)
uint8_t frame_top ;
//版本 A1
uint8_t version ;
//方向 01(前進(jìn)) 02(后退) 03(左轉(zhuǎn)) 04(右轉(zhuǎn))
uint8_t car_direction ;
//速度 01(低速檔) 02(中速檔) 03(高速檔)
uint8_t car_speed ;
//炮臺(tái) 00(回到中間) 01(炮臺(tái)左轉(zhuǎn)) 02(炮臺(tái)右轉(zhuǎn))
uint8_t car_fort_status ;
//夾具 00(夾具夾緊) 01(夾具松開(kāi))
uint8_t car_Fix_status ;
//幀尾(固定解析為BB)
uint8_t frame_tail ;
} Protocol;
//以下是串口回調(diào)的處理
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/*自定義協(xié)議*/
/*
幀頭 版本 方向 速度 炮臺(tái) 夾具 幀尾
ff A1
*/
uint8_t Res;
if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET))
{
HAL_UART_Receive(&huart1, &Res, 1, 1000);
if(0xBB != Res)
{
rbBuf[rx_count++] = Res ;
}
else
{
rbBuf[rx_count] = 0xBB ;
//接收到0xBB了,這時(shí)候認(rèn)為已經(jīng)接收到完整的一幀數(shù)據(jù),將接收標(biāo)志置1
Recv_Flag = 1 ;
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
}
//在主函數(shù)中,進(jìn)行數(shù)據(jù)解析
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
while (1)
{
if(1 == Recv_Flag)
{
Recv_Flag = 0 ;
//將接收緩沖區(qū)的數(shù)組強(qiáng)制轉(zhuǎn)換為一個(gè)結(jié)構(gòu)體指針
//通過(guò)結(jié)構(gòu)體指針可訪問(wèn)到每一個(gè)協(xié)議規(guī)格的數(shù)據(jù)
Protocol *Car_Procol = (Protocol *)rbBuf;
printf("幀頭:0x%x\n", Car_Procol->frame_top);
printf("版本:0x%x\n", Car_Procol->version);
printf("方向:0x%x\n", Car_Procol->car_direction);
printf("速度:0x%x\n", Car_Procol->car_speed);
printf("炮臺(tái):0x%x\n", Car_Procol->car_fort_status);
printf("夾具:0x%x\n", Car_Procol->car_Fix_status);
printf("幀尾:0x%x\n", Car_Procol->frame_tail);
rx_count = 0 ;
}
}
}
從這里可以看到,串口接收的數(shù)據(jù)是一個(gè)字節(jié)一個(gè)字節(jié)進(jìn)行接收,所以接收的每個(gè)數(shù)據(jù)類(lèi)型一致,我們就可以直接定義一個(gè)結(jié)構(gòu)體,按照協(xié)議定義的順序,將數(shù)據(jù)緩沖區(qū)中的數(shù)據(jù)依次讀取出來(lái)。
在小熊派上的運(yùn)行結(jié)果:
我在寫(xiě)上位機(jī)涉及到與MCU進(jìn)行協(xié)議通信的時(shí)候,經(jīng)常都是這么干的,這個(gè)方法不得不說(shuō)真的超方便。
案例下載
公眾號(hào)后臺(tái)回復(fù):protocol 即可獲取本節(jié)案例的下載鏈接。
往期精彩
【為宏正名】本應(yīng)寫(xiě)入教科書(shū)的“世界設(shè)定”
【為宏正名】99%人都不知道的"##"里用法
學(xué)習(xí)嵌入式可以帶娃,不信你們看
ESP8266實(shí)戰(zhàn)貼:使用HTTP POST請(qǐng)求上傳數(shù)據(jù)到公有云OneNet
覺(jué)得本次分享的文章對(duì)您有幫助,隨手點(diǎn)[在看]
并轉(zhuǎn)發(fā)分享,也是對(duì)我的支持。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!