imuncle.github.io icon indicating copy to clipboard operation
imuncle.github.io copied to clipboard

串口发送浮点数与C语言结构体字节对齐

Open imuncle opened this issue 6 years ago • 0 comments
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语言结构体字节对齐详解

主控板接收到的数据如下:

image

从中可以看到,发过来的数据其实是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位字节的数据,然而调试结果如下:

image

WTF?!居然是相邻两个通道的数据组合成了一个32位的数据存进来。

现在看来估计跟这字节对齐差不多的道理,所以我把代码修改如下:

uint16_t adc_data[4];

HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_data, 4);

结果如下:

image

nice!问题解决!

imuncle avatar Apr 28 '19 07:04 imuncle