在各类通信中,常使用memcpy将接收到的字节数组(如:unsigned char buffer[100]),直接复制到结构体当中,无须解码,直接使用结构体中的变量就完事了。

但,使用MDK5作为IDE, 对STM32芯片进行编程完成类似操作时,确并不如意。

相关代码如下:

// 如果有一些似曾相识的感觉,那应该是上一篇讲STM32用中断实现串口通信的文章
#include "usermain.h"
#include "usart.h"
#include "Angela.h"
#include "AngelaDecode.h"

unsigned char value;
unsigned char AngelaRx[128]={0x00};
short AngelaRxCNT = 0;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart->Instance==USART1)
  {				
		AngelaRx[AngelaRxCNT] = value;
		AngelaRxCNT++;
		HAL_UART_Receive_IT(&huart1, &value,1);		
		if(AngelaRxCNT==128){		
			AngelaCmdDecode();
			AngelaRxCNT = 0;
		}
  }
}

void AngelaCmdDecode(void){
    int i = 0;
    memcpy((unsigned char *)&AngelaCmd, (unsigned char *)AngelaRx, sizeof(AngelaRx));
	// [重点关注这一句]
		if(AngelaCmd.bHeader[0]==0xAA && AngelaCmd.bHeader[1]==0x55){
				AngelaCmdExecute();
		}else{
				return;
		}
}

void AngelaCmdExecute(void){
	AngelaRxCNT = 0;
}

AngelaCmd是个结构体,其定义如下

struct AngelaCmdstruct AngelaCmd;

AngelaCmdstruct的声明如下

struct AngelaCmdstruct
{
    unsigned char bHeader[2];				// 2    2
	unsigned char bCmdID;
	unsigned char bReserved1;				// 2    4
         
    union AngelaCmdPara{
		unsigned char	b[8];
        unsigned short	s[4];
        int				i[2];
        float	   		f[2];
        double			d;
    }CmdPara[15];							// 120  124
	
    unsigned char	bCrcCheck;
    unsigned char	bSumCheck;
    unsigned char	bTail[2];				// 2    128
};

一切的一切看似都很正常, 之前用CCS写DSP的时候,也用过类似的操作,完全没问题。

但,在STM32中,就出问题了。接收到字节数组AngelaRx[128]数据没问题,完全正常。

但,使用

memcpy((unsigned char *)&AngelaCmd, (unsigned char *)AngelaRx, sizeof(AngelaRx));

之后, AngelaCmd里面的数据,就是不对。

已经确认,并且写程序测试过(用sizeof),在STM32中

unsigned char		// 占1字节
unsigned short		// 占2字节
int					// 占4字节
float				// 占4字节
double				// 占8字节

AngelaCmdstruct它肯定是128字节,断点调试也看了,AngelaRx的数据也是准确无误,且长度为128字节。

好了,揭晓谜底。

#pragma pack (1)
struct AngelaCmdstruct
{
    unsigned char bHeader[2];				// 2    2
	unsigned char bCmdID;
	unsigned char bReserved1;				// 2    4
         
    union AngelaCmdPara{
		unsigned char	b[8];
        unsigned short	s[4];
        int				i[2];
        float	   		f[2];
        double			d;
    }CmdPara[15];							// 120  124
	
    unsigned char	bCrcCheck;
    unsigned char	bSumCheck;
    unsigned char	bTail[2];				// 2    128
};
#pragma pack()

在结构体struct AngelaCmdstruct的声明的前后,用#pragma pack (1) 和 #pragma pack()就能解决这个问题。

#pragma pack (1)的意思是

设置结构体的边界对齐为1个字节,也就是所有数据在内存中是连续存储的。

#pragma pack ()的意思是

取消指定对齐,恢复缺省对齐,等价于#pragma pack(pop)

请看下面这个结构体

struct s {
    char ch;
    int i;
};

如果主函数中写一句:printf("%d", sizeof(struct s));

我们知道,char型占用1个字节,int型占4个字节,那么输出的结果是5吗? 答案是否定的。你可以自己试一下,输出结果为8。

为什么会这样呢?这是因为编译器为了让程序跑得跟快,减少CPU读取数据的指令周期,对结构体的存储进行了优化。实际上第一个char型成员虽然本来只有1个字节,但实际上却占用掉了4个字节,为的是让第二个int型成员的地址能够被4整除。因此实际占用的是8个字节。

而#pragma pack(1)让编译器将结构体数据强制连续排列,这样的话,sizeof(struct s)输出的结果就是5了。