STM32中如何将UART接收的uint8_t缓冲区转换为Mesaj结构体?
STM32 UART DMA接收数据转Mesaj结构体实现方案
针对你的需求,在验证接收缓冲区长度为15字节后,可通过两种可靠方式完成uint8_t数组到Mesaj结构体的转换,同时需要注意内存对齐和字节序的问题:
前提准备:修正结构体内存布局
STM32默认的编译器对齐规则会给结构体成员添加填充字节,导致Mesaj结构体总大小超过15字节,因此必须给结构体添加打包属性,确保内存布局与接收字节流完全对应:
// GCC/Clang 使用 __attribute__((packed)),Keil 可替换为 __packed typedef struct __attribute__((packed)) { uint8_t baslangic; uint16_t emirNo; uint8_t birimNo; uint32_t emirAdresi; uint8_t komut; uint8_t dataTipi; uint32_t data; uint8_t bitis; } Mesaj;
方法一:手动逐成员赋值(推荐,兼容性强)
这种方式不依赖内存拷贝,可灵活处理字节序差异,适合对数据可靠性要求高的场景。假设串口发送端采用小端字节序(与STM32默认字节序一致),转换代码如下:
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart->Instance == USART1) { HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RxBuffer, RxBufferSize); __HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); for (int i = Size; i < RxBufferSize; i++) { RxBuffer[i] = 0; } if (Size == 15) { Mesaj alinanMesaj; // 提取单字节成员 alinanMesaj.baslangic = RxBuffer[0]; alinanMesaj.birimNo = RxBuffer[3]; alinanMesaj.komut = RxBuffer[8]; alinanMesaj.dataTipi = RxBuffer[9]; alinanMesaj.bitis = RxBuffer[14]; // 提取16位成员(小端模式:低字节在前) alinanMesaj.emirNo = (uint16_t)RxBuffer[1] | ((uint16_t)RxBuffer[2] << 8); // 提取32位成员(小端模式:低字节在前) alinanMesaj.emirAdresi = (uint32_t)RxBuffer[4] | ((uint32_t)RxBuffer[5] << 8) | ((uint32_t)RxBuffer[6] << 16) | ((uint32_t)RxBuffer[7] << 24); alinanMesaj.data = (uint32_t)RxBuffer[10] | ((uint32_t)RxBuffer[11] << 8) | ((uint32_t)RxBuffer[12] << 16) | ((uint32_t)RxBuffer[13] << 24); // 可选:验证帧头帧尾(根据协议要求) if (alinanMesaj.baslangic != 0xAA || alinanMesaj.bitis != 0x55) { // 处理帧格式错误 return; } // 此处添加后续处理逻辑,比如使用alinanMesaj } else { // 处理长度不符情况,比如重置接收、记录错误等 } } }
若发送端采用大端字节序,只需反转多字节成员的字节顺序即可,以emirNo为例:
alinanMesaj.emirNo = (uint16_t)RxBuffer[2] | ((uint16_t)RxBuffer[1] << 8);
方法二:直接内存拷贝(简洁高效)
在结构体已添加打包属性的前提下,可直接通过memcpy完成转换,前提是发送端字节序与STM32一致:
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart->Instance == USART1) { HAL_UARTEx_ReceiveToIdle_DMA(&huart1, RxBuffer, RxBufferSize); __HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); for (int i = Size; i < RxBufferSize; i++) { RxBuffer[i] = 0; } if (Size == 15) { Mesaj alinanMesaj; // 直接拷贝15字节到结构体(sizeof(Mesaj)此时为15) memcpy(&alinanMesaj, RxBuffer, sizeof(Mesaj)); // 可选:帧头帧尾验证 if (alinanMesaj.baslangic != 0xAA || alinanMesaj.bitis != 0x55) { // 处理格式错误 return; } // 后续处理逻辑 } else { // 长度不符处理 } } }
注意事项
- 务必确认发送端与STM32的字节序一致,否则多字节成员会出现数据错误;
- 帧头帧尾的验证可有效过滤无效数据,建议根据通信协议添加;
- 若使用Keil编译器,结构体打包属性需替换为
__packed。
内容的提问来源于stack exchange,提问作者Hasan Altay
相关产品推荐
相关产品推荐

