问题描述
之前的开发一直是基于STM32F429片子进行的,因为我比较懒,秉承着能少写就少些的原则,在对类型不匹配的变量进行赋值的时候,我比较偏向于使用类型强制转换,比如:
uint8_t tmp[2]={0,0};
uint16_t tmp_value=0xABCD;
此时如果要将tmp_value的值赋值给tmp,但是两者的类型又不相同的时候,一般有以下两种方法:
/* 方法1 */
tmp[0] = (uint8_t)tmp_value;
tmp[1] = (uint8_t)(tmp_value>>8);
/* 方法2 */
*(uint16_t *)tmp = tmp_value;
由于方法2能少写一句,所以,一直以来我就是按照方法2进行编写的,也没出现过问题,但是最近由于需要开发一个新的项目,功能相对简单一些,不需要很高性能的片子,选择了最近ST比较热门的芯片STM32G071,当程序开发完成以后,发现片子不能够正常运行,进入调试模式发现程序老是在执行下面这句话的时候进入HardFault;
*((uint16_t *)(&(txPacket.payload[2]))) = Data_From;
其中 txPacket.payload 是一个 uint8_t 类型的数组
先开始找不到出问题的原因,因为之前对于F429、F409一直都是采用的这样的写法,后来通过memory调试查看,发现了可能是因为地址问题,这个片子不支持非字节对齐访问,因为 txPacket.payload[2] 的地址为一个奇数,不能够被2整除,为了印证我的猜测,我将上述代码改为:
*((uint16_t *)(&(txPacket.payload[3]))) = Data_From;
结果程序可以正常跳过,为了印证我的猜想,查看了Cortex-M0+手册,发现M0+内核并不支持字节非对其访问(差评)。关于非对其访问的解释:
单片机中,经常使用到的数据类型有:
short、int、long、char、float、double
它们的大小分别为:
2 Byte、4 Byte、4 Byte、1 Byte、4 Byte、8 Byte
当定义一个变量的时候,它们在内存中的地址和大小有关系的,也就是地址能够被大小整除。
比如一个char类型的变量,它的大小为 1 Byte,那么它在内存中的地址的最后一位一定是以 0~F中的一个 ;
再比如一个short类型的变量,它的大小为 2 Byte,那么它在内存中的地址的最后一位一定是 0、2、4、6 、8 、A、 C、E中的一个;
以此类推int类型的变量,它在内存中的地址的最后一位一定是0、4、8、c中的一个;
当我们直接强制到一个变量地址中去取一个和该地址不匹配的数据类型,比如 0x20000001 去访问一个short类型的数据,如果能够成功访问,那么就证明能够进行非对其访问,反之,则证明不能进行非对其访问。
解决方法
最终为了秉承能少写一句就少写一句的原则,我想起来可以使用标准库函数里的memcpy(),于是我将代码改为:
memcpy((void *)&txPacket.payload[2], (void *)&Data_Form, 2);
编译下载,no problem!