个项目中用到了HC32F460的官方USB库使用官方的CDC例程发现收发数据时可能会进入HardFault中断通过在线调试可以确认异常原因是“用法错误非对齐访问”。故障仅发生在开启编译优化时且和编译器有关在我使用的Keil版本中如果切换编译器版本为ARMCC v6.16没有测试其他编译器版本或完全关闭编译优化则无法复现问题。环境芯片平台HC32F460测试程序HC32F460_DDL_Rev3.3.0\projects\ev_hc32f460_lqfp100_v2\applications\usb\usb_dev_cdc编译器版本ARMCC v5.06编译选项-c99 -O3在编译优化配置为O0时无法复现问题。复现条件官方CDC例程是将虚拟串口的操作映射到一个实体串口外设上实现一个USB-USART的透传效果。通过USB连接并打开虚拟串口实体串口外设收到数据时会通过USB发送到主机端此时大概率会触发异常。分析过程在可以在线调试的环境中我们可以直接查看调用堆栈确认异常出现的具体位置。首先通过Keil自带的“Call Stack Locals”窗口我们可以看到进入异常中断前程序正在执行usb_wrpkt函数。更进一步通过查看具体的堆栈信息我们可以确定触发异常的具体汇编命令。首先查看当前SP指针指向的堆栈栈顶地址然后可以通过在线调试直接读取对应的RAM地址观察进入异常前M4内核自动压栈的栈帧。通过其中的PC寄存器我们可以定位到具体发生异常的代码。通过上述步骤可以确定异常发生在一个LDM汇编指令上出错的这行汇编代码执行两件事加载寄存器r1指向的32位数据到r4寄存器然后将寄存器r1的值增加4。此时对照源码可以确定引发异常的代码是WRITE_REG32(*fifo, *pu32Src)源码void usb_wrpkt(LL_USB_TypeDef *USBx, const uint8_t *pu8src, uint8_t ch_ep_num, uint16_t len, uint8_t u8DmaEn){__IO uint32_t *fifo;uint32_t u32Count32b;uint32_t u32Tmp;uint32_t *pu32Src (uint32_t *)(uint32_t)pu8src;if (u8DmaEn 0U) {u32Count32b (len 3UL);u32Count32b u32Count32b 2U;fifo USBx-DFIFO[ch_ep_num];u32Tmp 0UL;while (u32Tmp u32Count32b) {WRITE_REG32(*fifo, *pu32Src);u32Tmp;}}}从源码不难看出该函数对传入的地址pu8Src做了强制类型转换直接通过32位宽度去访问数据当传入的地址不是对齐地址时这行语句必然会触发非对齐访问。但是Cortex-M4内核支持内存非对齐访问为什么这里会触发异常为什么调整编译优化可以规避问题带着问题查阅资料可以发现Cortex-M4内核的内存非对齐访问仅支持单地址操作LDRLDRHSTRSTRH不支持多地址LDMSTM等。与此同时不开启编译优化时问题代码对应是通过LDR实现的。LDR在M4内核上可以正确执行非对齐访问。这就解释了为什么关闭编译优化后无法复现问题。如何解决源码中使用强制类型转换的引诱编译器对该处进行优化使用更高效的LDM命令而不是LDRADD来实现数据加载和递增最终引发悲剧。要根治问题可以选择放弃一点性能换取鲁棒性逐字节从pu8Src指向的内存中读取数据。例如while (u32Tmp u32Count32b) {uint32_t dataTemp ((uint32_t)*pu8Src) | ((uint32_t)*(pu8Src 1)) 8 | ((uint32_t)*(pu8Src 2)) 16 | ((uint32_t)*(pu8Src 3)) 24;WRITE_REG32(*fifo, dataTemp);u32Tmp;pu8Src 4;}