You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cortex-M0汇编延迟函数多次调用触发HardFault问题排查

Cortex-M0汇编延迟函数触发HardFault问题排查

我为Cortex-M0 CPU和Keil uVision 5.38编写了如下汇编延迟函数:

static __INLINE __ASM void _asm_delay10us(unsigned int num)
{
    /* R0 contains "num"*/

    PUSH {R4, LR}

    /* R4 will contain number of loops for a single 10us delay (158 loops) */
    MOVS R4, #158

    /* Calculate total number of loops as R4 = 158*value */
    MULS R4, R0, R4 //

loop    
    SUBS  R4, #1  
    BEQ   exit  
    B loop  
exit
    POP {R4, pc}
}

通过C代码调用该函数:

void delay1ms(unsigned int u32Cnt)
{
        _asm_delay10us(100 * u32Cnt);
}

运行几次后出现挂死,后续排查发现:

  • 更新1:第二次调用函数时触发HardFault
  • 更新2:编译器生成的反汇编代码片段如下:

汇编函数反汇编:

__asm___5_hal_c_0731ac3b___asm_delay10us
0x08000154:    b510        ..      PUSH     {r4,lr}
0x08000156:    249e        .$      MOVS     r4,#0x9e
0x08000158:    4344        DC      MULS     r4,r0,r4
0x0800015a:    3c01        .<      SUBS     r4,#1
0x0800015c:    d000        ..      BEQ      0x8000160 ; 
__asm___5_hal_c_0731ac3b___asm_delay10us + 12
0x0800015e:    e7fc        ..      B        0x800015a ; 
__asm___5_hal_c_0731ac3b___asm_delay10us + 6
0x08000160:    bd10        ..      POP      {r4,pc}
0x08000162:    0000        ..      MOVS     r0,r0

调用函数及相关代码反汇编:

.text
delay1ms
0x080026bc:    b510        ..      PUSH     {r4,lr}
0x080026be:    4604        .F      MOV      r4,r0
0x080026c0:    2164        d!      MOVS     r1,#0x64
0x080026c2:    4361        aC      MULS     r1,r4,r1
0x080026c4:    4608        .F      MOV      r0,r1
0x080026c6:    f7fdfd45    ..E.    BL       __asm___5_hal_c_0731ac3b___asm_delay10us ; 0x8000154
0x080026ca:    bd10        ..      POP      {r4,pc}
hal_init
0x080026cc:    b570        p.      PUSH     {r4-r6,lr}
0x080026ce:    f7fdfe57    ..W.    BL       SystemCoreClockUpdate ; 0x8000380
0x080026d2:    48a2        .H      LDR      r0,[pc,#648] ; [0x800295c] = 0x9c4
0x080026d4:    f7fffff2    ....    BL       delay1ms ; 0x80026bc
0x080026d8:    2164        d!      MOVS     r1,#0x64

问题根源

Cortex-M0的MULS指令是16x16乘法,结果仅保留低16位存入目标寄存器。当num较大时,158 * num的结果会超出16位范围(>65535),导致R4被截断为低16位数值:

  • 若截断后数值为0,循环直接退出,延迟失效
  • 若截断后数值为负数(SUBS指令会影响标志位),循环会进入异常死循环,破坏上下文触发HardFault

第一次调用可能刚好数值在16位范围内,第二次调用时参数超出阈值,触发故障。


修复方案

方案1:拆分循环避免乘法溢出

把延迟拆分为外层循环(处理num次)和内层循环(固定158次实现10us),彻底规避乘法操作:

static __INLINE __ASM void _asm_delay10us(unsigned int num)
{
    /* R0 = num */
    PUSH {R4, LR}

outer_loop:
    CBZ R0, exit     ; num为0直接退出

    MOVS R4, #158    ; 单10us循环次数
inner_loop:
    SUBS R4, #1
    BNE inner_loop

    SUBS R0, #1      ; num减1
    B outer_loop

exit:
    POP {R4, PC}
}

方案2:模拟32位乘法(纯汇编)

如果必须保留乘法逻辑,用循环累加模拟32位乘法,避免数值截断:

static __INLINE __ASM void _asm_delay10us(unsigned int num)
{
    /* R0 = num */
    PUSH {R4, R5, LR}

    MOVS R4, #158    ; 单10us循环次数
    MOVS R5, #0      ; 32位结果低16位
    MOVS R3, #0      ; 32位结果高16位

mult_loop:
    ADDS R5, R0      ; 累加num到低16位
    ADC R3, #0       ; 处理进位到高16位
    SUBS R4, #1      ; 乘法循环计数减1
    BNE mult_loop    ; 完成乘法

    MOVS R4, R5
    ORRS R4, R3      ; 检查结果是否为0
    BEQ exit

loop:
    SUBS R4, #1
    BEQ exit
    B loop

exit:
    POP {R4, R5, PC}
}

方案3:C语言实现延迟(更可靠)

放弃手动汇编,用C语言编写延迟函数,开启Keil优化后会生成高效且安全的汇编代码:

static inline void delay10us(unsigned int num)
{
    while(num--)
    {
        volatile unsigned int i = 158;
        while(i--);
    }
}

验证建议

  • 调用delay1ms(1000)测试1秒延迟,确认能正常退出
  • 查看反汇编代码,确认循环逻辑无数值截断
  • 用Keil调试功能查看HardFault状态寄存器,验证故障已解决

内容的提问来源于stack exchange,提问作者Vladimir T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 14:25:12