关于结构体对齐的设置,以GCC 32bit编译为例,我们可以来看看下面这个例子:
#include <stdio.h> //默认情况下,结构体一般在内存中的自动对齐格式是4个字节 //结构体设置手动对齐 //如果这里是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 ; }
运行结果:
根据这样的原理,在MCU协议数据解析的时候就很有作用了,比如下面这个例子,目前在小车上用:
//结构体,用于存储解析的数据 typedef struct { //帧头(固定解析为FF) uint8_t frame_top ; //版本 A1 uint8_t version ; //方向 01(前进) 02(后退) 03(左转) 04(右转) uint8_t car_direction ; //速度 01(低速档) 02(中速档) 03(高速档) uint8_t car_speed ; //炮台 00(回到中间) 01(炮台左转) 02(炮台右转) uint8_t car_fort_status ; //夹具 00(夹具夹紧) 01(夹具松开) uint8_t car_Fix_status ; //帧尾(固定解析为BB) uint8_t frame_tail ; } Protocol; //以下是串口回调的处理 /** * @brief This function handles USART1 global interrupt. */ void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ /*自定义协议*/ /* 帧头 版本 方向 速度 炮台 夹具 帧尾 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了,这时候认为已经接收到完整的一帧数据,将接收标志置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 */ } //在主函数中,进行数据解析 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); while (1) { if(1 == Recv_Flag) { Recv_Flag = 0 ; //将接收缓冲区的数组强制转换为一个结构体指针 //通过结构体指针可访问到每一个协议规格的数据 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("炮台: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 ; } } }
从这里可以看到,串口接收的数据是一个字节一个字节进行接收,所以接收的每个数据类型一致,我们就可以直接定义一个结构体,按照协议定义的顺序,将数据缓冲区中的数据依次读取出来。
在小熊派上的运行结果:
我在写上位机涉及到与MCU进行协议通信的时候,经常都是这么干的,这个方法不得不说真的超方便。
案例下载
公众号后台回复:protocol 即可获取本节案例的下载链接。