当前位置: 首页 > news >正文

HC32F460 USB CDC通信异常:非对齐访问异常排查

一个项目中用到了HC32F460的官方USB库,使用官方的CDC例程时发现收发数据时可能会进入HardFault中断,通过在线调试可以发现异常原因为“用法错误,非对齐访问”。

fault_report

故障仅发生在开启编译优化时,且和编译器有关,在我使用的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函数。

call_stack

更进一步,通过查看具体的堆栈信息我们可以确定触发异常的具体汇编命令。

首先查看当前SP指针指向的堆栈栈顶地址,然后可以通过在线调试直接读取对应RAM地址,观察进入异常前一M4内核自动压栈的栈帧。通过其中的PC寄存器我们可以定位到具体发生异常的代码。

stack_top

stack_frame

通过上述步骤可以确定异常发生在一个LDM汇编指令上:

ldm_with_r1

出错的这行汇编代码执行两件事,加载寄存器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内核的内存非对齐访问仅支持单地址操作(LDR,LDRH,STR,STRH),不支持多地址(LDM,STM等)。

m4_unaligend_limit

m4_unaligend_limit2

与此同时,不开启编译优化时问题代码对应的汇编代码是通过LDR实现,在M4内核上可以正确执行非对齐访问。这一情况解释了为什么关闭编译优化后无法复现问题。

asm_with_o0

如何解决?

源码中使用强制类型转换的引诱编译器对该处进行优化,使用更高效的LDM命令而不是LDR+ADD来实现数据加载和递增,最终引发悲剧。要根治问题,可以选择放弃一点性能换取鲁棒性,逐字节从pu8Src指向的内存中读取数据。例如:

while (u32Tmp < u32Count32b) {uint32_t dataTemp = (*pu8Src) | (*(pu8Src + 1)) << 8 | (*(pu8Src + 2)) << 16 | (*(pu8Src + 3)) << 24;WRITE_REG32(*fifo, dataTemp);u32Tmp++;pu8Src += 4;
}

参考资料

[1] ARM Ltd. "Arm Cortex-M4 Processor Technical Reference Manual".
[2] Joseph Yiu. "The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors". 3rd Edition
[3] Ryy. "ARM Cortex-M3/M4的异常/中断处理流程" https://www.renyunyang.cn/archives/arm01.html

http://www.jsqmd.com/news/647225/

相关文章:

  • MySQL升级导致排序规则变化怎么处理_更新Collation配置
  • 别再手动配环境了!用pyproject.toml统一管理你的Python项目(附Poetry/Flit/Hatch对比)
  • mrpack-install如何解决Minecraft服务器模组包部署:面向开发者的自动化部署方案
  • 从训练到部署全链路压缩提速4.6倍:SITS2026专家实测TensorRT-LLM+OpenVINO双栈协同压缩方案
  • CSS如何让Bootstrap列表项整齐排列_利用display grid实现
  • Java的ForeignFunctionAPI与ProjectPanama在本地内存访问中的突破
  • 工业自动化调试的革命:ModbusTool如何通过三合一协议支持重塑设备通信测试
  • 【ESP8266】巧用内部EEPROM,构建WiFi配置的持久化记忆
  • EtherCAT 转Profinet 极片生产数据全程追溯工业物联网
  • 从‘软’到‘硬’:手把手解析铜凸点如何解决焊料凸点的塌陷与短路难题
  • 借助爱毕业(aibiye),用户可以轻松完成数学建模论文的复现与智能排版优化
  • 低成本玩转宇树机器狗Go2:Gazebo仿真+Velodyne雷达实战教程
  • 2026毕业季生存指南:实测5款降ai工具,亲测有效
  • 如何快速上手GSE:魔兽世界高级宏编辑器的终极指南
  • Step3-VL-10B轻量级多模态模型教程:10B参数下GPU显存占用实测(24GB)
  • 2个高星CLAUDE.md范例,直接复制能用(Claude Code实用指南)
  • 十年信任崩塌:Backblaze为何悄悄停止备份你的云端数据?
  • CSS如何使得下拉选择框不受外层容器的overflow裁剪_只能将下拉框放在body下并使用JS结合绝对定位计算位置
  • 伯明翰大学发布诗歌生成新标准:AI能否成为下一个莎士比亚?
  • Linux 性能分析:CPU/内存/IO/网络,一套工具全搞定
  • LPC1114 PWM呼吸灯进阶:如何用MR3寄存器精准控制频率与平滑度?
  • 终极ComfyUI-Crystools完全指南:20+强大工具节点提升AI绘画工作流效率
  • 利用ArcGIS实现SHP文件边界坐标批量导出为TXT格式
  • 2026广州注册公司代办机构实测测评|4家靠谱机构对比,避坑指南+首选推荐 - 企业推荐官【官方】
  • Windows乱码终结者:3步学会用Locale Emulator运行多语言软件
  • [Tools] Laragon 本地集成开发环境
  • 6.2 组合优化:考虑换手、成本、约束下的均值-方差优化
  • 2026年想找专业长沙美缝施工团队?哪家才是你的最佳之选? - 企业推荐官【官方】
  • 在Ubuntu 22.04上,用Python3和pysoem库搞定EtherCAT电机回零与位置控制的保姆级避坑指南
  • 对齐不准、融合失焦、推理崩塌?多模态大模型上线前必须完成的7项融合健康检查,漏一项即致A/B测试失败