imuncle.github.io
imuncle.github.io copied to clipboard
串口发送浮点数与C语言结构体字节对齐
trafficstars
前段时间搞裁判系统的时候接收过浮点数的数据,今天捣鼓超级电容的时候想到,可以把电容的电压反馈给主控板,这其中就会涉及到串口发送浮点数的问题。
由于浮点数本身存储方式与整形数据存储方式有很大不同,感兴趣的可以看这篇博客(32位单精度浮点数表示法),总之就是比较复杂,所以浮点数的发送我使用指针而不是按位发送。
定义的结构体如下:
struct
{
uint8_t header;
float cap_v;
uint8_t end;
} tx_data;
发送部分如下:
tx_data.header = 0xFF;
tx_data.end = 0xFE;
tx_data.cap_v = 3.1415f;
HAL_UART_Transmit(&huart1, (uint8_t *)&tx_data, sizeof(tx_data), 100);
按理说,tx_data应该是一个6字节大小的结构体,因为uint8_t占1个字节,float占4个字节,于是我的主控板上的接收函数如下:
struct
{
uint8_t header;
float cap_v;
uint8_t end;
} cap_data;
uint8_t data[6];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef * huart)
{
HAL_UART_Receive_IT(&huart3, data, 6);
memcpy(&cap_data, data, 6);
}
结果根本没有接收到数据!
后来我使用一个较长的数组缓存数据,进入调试一看才发现,原来是因为C语言结构体的字节对齐问题。印象中大一自学的时候见到过,一时却没想起来。关于字节对齐的知识点可以看这篇文章:结构体字节对齐,C语言结构体字节对齐详解
主控板接收到的数据如下:

从中可以看到,发过来的数据其实是12位的,所以我的代码应该修改如下:
struct
{
uint8_t header;
float cap_v;
uint8_t end;
} cap_data;
uint8_t data[12];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef * huart)
{
HAL_UART_Receive_IT(&huart3, data, 12);
memcpy(&cap_data, data, 12);
count = 0;
}
进一步的,我想到了之前玩ADC的时候遇到的问题:
首先,使能ADC的函数如下:
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)
可以看到要传入的数据要求是uint32_t *类型的,所以我就直接定义了一个32位的数组:
uint32_t adc_data[4];
我这里需要转换四个通道,按理说我的这个数组里面应当是每一位存储一个12位字节的数据,然而调试结果如下:

WTF?!居然是相邻两个通道的数据组合成了一个32位的数据存进来。
现在看来估计跟这字节对齐差不多的道理,所以我把代码修改如下:
uint16_t adc_data[4];
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_data, 4);
结果如下:

nice!问题解决!