使用15V电压供电,电机能正常启动,电压提高到16V时就会进入HardFault_Handler
开始怀疑可能是:电压升高后,启动瞬间的相电流/电流尖峰增大,触发了硬件过流保护;随后中断处理或嵌套调度导致真正的CPU HardFault。
16 V启动 → 电流尖峰触发 COMP1/2/4 → TIM1_BRK_M1_IRQHandler → 在Break中断里再次调用 MC_RunMotorControlTasks() → ADC/FOC中断嵌套、栈压力或非法访问 → HardFault_Handler内存窗口中看到的0x0800566B位于工程映射文件中的:
R3_2_CurrentReadingPolarization()附近。因此目前最可疑的路径是:
电流偏置校准 / PWM电流采样 → R3_2_CurrentReadingPolarization() → 电流采样或COMP过流触发 → 内存/栈/中断上下文异常 → HardFault调试发现
SCB->CFSR = 0x00000100 SCB->HFSR = 0x40000000CFSR bit 8 = IBUSERR:指令总线错误;HFSR.FORCED = 1:BusFault升级成了HardFault;PRECISERR = 0、BFARVALID = 0:不是一个可定位到具体数据地址的普通数据访问错误;SCB->BFAR = 0xE000EDF8目前无效,因为BFARVALID没有置位,不能把它当作真实故障地址。
出现新方向:按键干扰,由于
VECTPENDING = 0x29根据该芯片的中断向量表,0x29对应:
EXTI15_10_IRQn也就是PC13启动/停止按键中断当前处于Pending状态。16 V时如果功率级噪声耦合到PC13,可能会产生按键误触发。
改为程序自动触发,可能性排除
系统能够在16-18V稳定启动
星期一13:54
已处理 1m 52s
目前16–18 V能够稳定启动,主要得益于以下修改。
移除Break中断中的重入调度
文件:
[stm32g4xx_mc_it.c]
删除了:
MC_RunMotorControlTasks();原来过流/过压Break中断中会再次调用完整电机状态机,可能与ADC电流环、SysTick任务嵌套,造成栈或异常现场破坏。
缩小问题发生时间和范围
通过已确认稳定的按键或串口启动。不再出现HardFault,问题集中在“上电后立即启动”的时序,而不是电机运行阶段。
当前最可能的链路是:
MC_StartMotor1() → MCI_StartMotor() → 直接进入启动状态 → 中断/初始化时序冲突或异常栈损坏 → 非法PC 0xA7F708F4 → HardFault自动启动命令已经正确执行,故障发生在START启动阶段,而不是按键或MCI句柄初始化阶段。
由于
gHardFaultStackPC = 0x07F6F734 gHardFaultStackLR = 0x07F739CD这两个地址都不在Flash区0x080xxxxx,说明CPU在进入START后发生了非法取指,异常栈中的返回地址已经被破坏。
怀疑栈空间不足
Stack_Size EQU 0x1000即将栈从1 KB增加到4 KB。有一定效果。异常栈已经恢复正常:
gHardFaultCFSR = 0x00000100 gHardFaultStackPC = 0x08004450 gHardFaultStackLR = 0x0800448B gHardFaultStackXPSR = 0x41000000地址映射为:
0x08004450 → MCI_GetSTMState() 0x0800448B → MCI_StartMotor() 内部因此这次不是非法函数指针,也不像栈溢出。当前是:
CFSR = 0x100 → IBUSERR,指令总线错误也就是CPU在Flash中执行MCI_GetSTMState()附近代码时,发生了指令取指错误。BFARVALID没有置位,所以不是数据访问地址错误。
锁定问题:CPU 取指令时发生总线错误,开始查询发生原因
还有一个线索宏定义#define M1_CHARGE_BOOT_CAP_DUTY_CYCLES ((uint32_t)0.000\是自举电容充电占空比,初始值是0,当调大他的数值时系统就不会进入HardFault_Handler,而是稳定触发过流保护:说明问题集中在“启动初期低侧导通/自举充电”阶段,而不是正常运行阶段。
- FOC 已完整执行;
- 高频任务已返回;
- ADC 中断主体已完成;
- PWM 回调指针预检查正常;
ADC 注入转换状态没有及时清除,TIM1 更新中断卡在:
while (ADCDataReg1[...]->JSQR != 0) {}这会阻塞 TIM1 ISR,造成后续中断时序异常,最终出现 HardFault。
FOC、高频任务和 Break ISR 都执行过,但随后仍发生未定义指令。更重要的是:刚才加入的 ADC 超时保护在超时后直接返回,这会跳过后续。如果 ADC 等待确实超时,PWM 更新中断就不会重新配置 ADC 触发,电机自然没有启动意向。也就是说,这次“更稳定但电机不启动”很可能是超时保护把启动链路截断了。
重新测试发现故障不是 ADC 超时,由于发生了未对齐访问。故障地址0x0800968E对应 OPAMP 初始化时的STRD指令,该指令要求目标地址 8 字节对齐,但诊断变量改变了全局变量布局,使hopamp1/hopamp2只满足 4 字节对齐。
后经过设置变量验证,说明不是 OPAMP 句柄未对齐,而是异常栈/返回现场再次损坏。
观察到电机启动后的中断活动导致HAL_GetTick()访问地址异常
- 按键启动后,定时器中断确实继续运行了约
0xD9E次; - FOC 已经完整执行到
0x1E; - 没有检测到回调指针错误;
- ADC 等待没有超时;
- 栈只下降了 8 字节,不像栈溢出;
- 故障每次都落在同一个位置,具有确定性。
故障现场仍然是:
gHardFaultStackPC = 0x080023D2 -> HAL_GetTick() gHardFaultStackLR = 0x08002171 -> HAL_Delay() gHardFaultStackR0 = 0xF8802201 BFAR = 0xF8802201这里不能理解为“HAL_Delay()本身有问题”。真正异常是:执行HAL_GetTick()时,CPU试图访问0xF8802201
查询官方 MCSDK 的启动路径
MC_StartMotor1() → MCI_StartMotor() → SysTick 中的 MC_RunMotorControlTasks() → TSK_MediumFrequencyTaskM1() → IDLE → R3_2_TurnOnLowSides() → CHARGE_BOOT_CAP(约10 ms) → OFFSET_CALIB / START → PWMC_SwitchOnPWM() → TIM1_UP / ADC1_2 中断 → FOC_HighFrequencyTask()加入启动过程快照
gStartTraceStage变量:
1:收到按键启动请求之前的快照 2:MC_StartMotor1() 返回之后 3:进入 IDLE 启动分支,准备低侧导通 4:自举电容充电结束,准备切换 5:调用 PWMC_SwitchOnPWM() 之前 6:PWMC_SwitchOnPWM() 返回之后- 停在
stage = 1:启动请求没有被正常消费。 - 停在
stage = 3:低侧导通或状态机没有继续。 - 停在
stage = 4:自举充电、READY/NFAULT 或定时器状态异常。 - 停在
stage = 5:重点怀疑pFctSwitchOnPwm函数指针或 PWM 句柄。 - 到达
stage = 6后卡住:重点检查首次 TIM1/ADC 中断和电流采样。
经过测试,启动前 / 自举充电 / PWM首次打开等过程已排除,范围缩小到START 状态进入后的 Rev-Up / FOC 高频中断阶段
检查START 状态进入后问题发生位置
加入变量后调试发现
- 按键请求已正常进入状态机;
- 自举充电已完成;
PWMC_SwitchOnPWM()已返回;- READY/NFAULT 没有触发保护;
- PWM 函数指针没有异常。
- FOC 环仍然在运行,但给出的
Iq转矩参考为零或接近零。此时电机可能只得到很小的电压矢量,没有形成足够启动转矩。 - 同时存在异常栈/返回地址破坏
发现StackPC=0x50519178不是有效代码地址,XPSR也不像正常 Cortex-M 异常栈帧。这说明 HardFault 发生时,异常栈或返回地址已经被破坏,不能再把StackPC当作可靠的故障源码位置。
怀疑硬件过流导致出现问题
启动时的实际路径是:
分流电阻电压 ↓ COMP1/2/4 比较器 ↓ TIM1 BKIN ↓ TIM1 Break 中断 ↓ TIMx_BRK_M1_IRQHandler() ↓ PWMC_OCP_Handler() ↓ 关闭 PWM,置位 OverCurrentFlag这一条路径中,COMP 比较器是硬件异步工作的,ADC 软件滤波无法阻止它触发。
因此:
- 软件 ADC 滤波只能改善 FOC 电流采样;
- 不能阻止 COMP 硬件过流;
HAL_Delay()或等待也不能阻止硬件 Break;- 真正要抑制误触发,需要比较器迟滞、消隐、数字滤波、RC 滤波或改善功率级振铃。
先区分“真实硬件过流”和“过流中断后软件崩溃”
在 Break 中断入口、清除 TIM1 标志之前,锁存:
- TIM1
SR/BDTR/AF1/AF2 - COMP1/2/4
CSR - DAC3
DOR1/DOR2 - GPIOE
IDR - 当时的 CFSR/HFSR
- TIM1
增加 OCP 回调指针检查。
如果
pFctSwitchOffPwm地址异常,则:- 不再调用错误函数指针;
- 直接关闭 TIM1 主输出;
- 设置过流标志;
- 记录
gBreakTraceGuardFlags; - 避免在 Break 中断中再次 HardFault。
加固 HardFault 诊断代码。
现在会先验证 MSP、PSP、faultFrame 和 PWM 句柄地址,再读取内容,避免 HardFault 处理函数因读取坏指针造成二次故障。
本次没有直接增加 ADC 软件滤波或大段延时,因为 COMP 过流保护是硬件异步路径,软件滤波无法阻止它,而且可能掩盖真实的功率级振铃问题。
测试发现:不是过流保护触发,而是 CPU 的未对齐访问故障。启动后某个指针、结构体地址或栈数据被破坏,导致未对齐访问;不是 ADC 数值本身直接触发 HardFault。
判断是中频任务执行时出了问题还是电流ADC转换完成中断完成后的高频任务出的冲突
| 变量 | 当前值 | 结论 |
|---|---|---|
gDiagLastContext | 0x1F | 最后记录的是 ADC 转换完成中断正常结束 |
gDiagFOCStage | 0x1E | FOC 高频任务已执行到阶段 30,主体已返回 |
gDiagHFStage | 4 | 高频任务已完成 |
gHardFaultFrameValid | 0 | HardFault 堆栈帧已经损坏或无效 |
gHardFaultStackPointer | 0x10880021 | 不在 SRAM,属于异常指针 |
gBreakTraceCount | 启动前后均为1 | 启动瞬间没有新的 Break/OCP 事件 |
因此:
- 当前没有证据表明是中频任务直接触发。
- ADC 高频任务本体已经执行完成。
- 更像是高频中断返回阶段、异常返回阶段,或此前发生了栈/指针破坏。
- 目前无法排除“中频任务进入前后”的边界问题,因为原有
gDiagLastContext不够细。
加入变量gDiagMFStage:
1 进入中频调度 2 开始处理启动/停止命令 3 启动/停止命令处理完成 4 即将进入 TSK_MediumFrequencyTaskM1 5 中频任务及回调完成 6 即将进入安全任务 7 中频调度完成 0xE1 运行完整性检查失败 0xE2 MCboot 尚未完成不是中频任务出错,而是:
中频任务已经完成,随后 ADC 转换完成中断中的高频 FOC 已完成;故障更可能发生在 ADC 高频中断退出、异常返回,或此前已经发生的栈/指针破坏。
屏蔽高频任务,看看电机启动后中频任务是否有问题
ADC 转换中断仍然运行,但不会调用:
TSK_HighFrequencyTask();
同时会清除:
TIM1->BDTR.MOE
因此这次测试中电机不会真正输出 PWM 旋转,只用于验证中频任务和状态机。重新启用时将:
gDiagDisableHFTask = 0;
屏蔽高频任务后:
- ADC 中断仍在运行。
- 中频任务持续执行并正常返回。
- 中频任务没有引起 HardFault。
- 原来的 HardFault 需要高频 FOC 任务或其与 PWM/ADC 的交互才会出现。
排除:
- 中频任务本身直接触发 HardFault;
- SysTick 中频调度栈溢出;
- 本次测试中的硬件 Break/OCP 触发。
高频任务问题判断
修改代码实现
- 高频 FOC 继续执行;
- ADC 电流采样继续执行;
- FOC 运算继续执行;
- 跳过
PWMC_SetPhaseVoltage(); - 每次强制清除
TIM1->BDTR.MOE,禁止实际 PWM 输出。
测试后发现
- 高频任务确实在执行;
- FOC 高频任务主体能够完成;
- 中频任务也能够完整执行;
- 最后进入
FAULT_OVER,PastFaults=0x0010是MC_START_UP启动失败,不是 HardFault; - 因为 PWM 更新被屏蔽,启动失败是预期结果。
允许 PWM 更新重新测试
执行真实 PWM 后出现了指令总线故障。
从数据看:
gDiagPWMUpdateRunCount = 0x25:已实际执行 37 次PWMC_SetPhaseVoltage()。gDiagPWMUpdateLastError = 0:PWM 计算函数返回正常。gDiagPWMStateBefore = 1:PWM 当时已开启。gDiagPWMBDTRBefore/After = 0x2333BC3B:MOE 未被软件清除。gDiagValpha = -10、gDiagVbeta = 86:初始电压指令很小,没有明显越界。gHardFaultFrameValid = 1、gHardFaultFrameSource = 1:HardFault 堆栈帧有效,来自 MSP。
得到结论:电机启动、PWM 开始输出后,CPU 在取指令时发生 BusFault。
尝试软件上解决问题:滤除窄毛刺、保留真实过流保护、降低 24 V 启动冲击
- COMP1/COMP2/COMP4 增加
10 mV迟滞,减少比较器阈值附近振荡。 - TIM1 实际使用的是
Break1,已把滤波编码从3调整为8。- 当前 170 MHz、CKD=DIV2 下约要求故障持续
0.56 µs才触发。 - 小于此宽度的开关毛刺会被滤除。
- 持续过流和 STSPIN32G4
NFAULT仍会立即关闭 PWM。
- 当前 170 MHz、CKD=DIV2 下约要求故障持续
测试结果:启动后:
gDiagPWMUpdateRunCount=0x22:真实 PWM 更新成功运行了 34 次。gDiagPWMUpdateStage=3:PWMC_SetPhaseVoltage()已正常返回。gDiagPWMUpdateLastError=0:占空比计算没有报告错误。gDiagFOCStage=0x1E:整个 FOC 高频任务已经走到出口。gDiagHFStage=4:高频任务完整返回。gDiagLastContext=0x1F:ADC 中断已经执行到末尾。Valpha=4、Vbeta=11:故障前并没有输出异常大的电压矢量。gBreakTraceCount、TIM1_SR、电机故障字均未变化。gOcpFilterCode=8、COMP CSR=0x00010040:比较器迟滞和 Break 滤波已经生效。
测试后发现:不是比较器过流,且无法通过配置COMP解决,而是物理 PWM 开始输出后,ADC 高频中断完整执行若干次,随后在中断返回或紧邻位置发生非法取指。
阶段性总结
| 问题 | 测试方式 | 结果 |
|---|---|---|
| 按键误触发 | 初始化后直接调用MC_StartMotor1() | 仍可复现 |
| COMP/OPAMP重复初始化 | 在HAL_COMP_Init()等位置断点 | 只在上电初始化进入 |
| 中频任务自身崩溃 | 屏蔽高频任务,只运行中频状态机 | 无 HardFault,只因无电流环进入预期故障状态 |
| ADC/FOC计算本身 | 高频任务运行,但禁止真实 PWM 更新 | 可以持续运行 |
PWMC_SetPhaseVoltage()同步崩溃 | 观察 Stage 和返回值 | Stage=3、Error=0,函数已经返回 |
| 软件电压矢量溢出 | 记录Valpha/Vbeta | 故障前数值很小 |
| 硬件过流 Break | 比较 Break 计数、TIM1_SR、电机故障字 | 启动前后均未变化 |
| 比较器阈值振荡 | 加 10 mV 迟滞和 BKF=8 | 配置已生效,但故障仍存在 |
| 普通栈溢出 | 4 KB 栈、记录最小 MSP、栈保护标志 | 未发现栈越界 |
| 明显空指针/外设实例错误 | 检查 PWM、OPAMP 指针和运行保护标志 | 地址合理、保护标志为 0 |
| SysTick/HAL_GetTick根因 | 屏蔽 PWM 时 SysTick长期运行正常 | 不是独立根因 |
| 确定性的 MCSDK算法错误 | 低电压稳定,同一程序随母线电压升高才出现 | 不符合普通确定性算法错误特征 |
尝试不修改硬件的情况下能通过调整配置解决
目前现象:PWM能够输出几次才发生HardFault。
尝试一:完整执行状态机、ADC、FOC 和 CCR 更新,但禁止 TIM1.MOE,功率管不动作:
结论:24V 上电并启动。电机不转是正常现象。没有进入HardFault。
允许真实 PWM 边沿,但强制输出零电压矢量。
尝试二:允许真实 PWM 边沿,但强制输出零电压矢量。
结论锁定:HardFault 需要“真实功率级开关边沿”才会出现,与 FOC 输出电压大小、启动电流或按键无关。普通“降低FOC占空比”不能可靠解决,因为零矢量虽然三相线电压接近零,但三个桥臂仍以约50%占空比产生完整的24V开关边沿。降低调制量只降低电流,不降低每个边沿的24V幅度。
尝试三:把 STSPIN32G4 栅极驱动 VCC 从当前12V降到8V,保持20kHz和零矢量,观察边沿减缓后是否稳定。
8V测试结果无改善:配置及回读均成功,但仍在约0x22次FOC更新后HardFault,与12V时的0x23基本一致。
尝试四:栅极驱动保持8V。PWM从20kHz降至10kHz。
已确认这次三相零矢量测试仍会 HardFault:
gHardFaultCFSR = 0x1400:非精确 BusFault + 异常入栈失败。- MSP 已损坏为非法地址。
- 10 kHz 只是让故障从约 34 次延后到 40 次 PWM 更新,不能解决问题。
- 因此基本排除 FOC 电压矢量和电机电流本身;故障需要真实开关沿才能产生。
尝试五:仅 U/V 相半桥开关,8 V 栅极电源、10 kHz、零矢量、过流保护均保留。
没有进入HardFault 结论:单桥臂稳定运行0x40AD次,而三桥臂约0x28次就 HardFault,故障与多桥臂同步开关瞬态高度相关,不是过流保护或 FOC 运算错误。
尝试六:改为 U+V 双桥臂测试
直接进入HardFault :双桥臂正常运行时故障
尝试七:双桥臂错沿测试
- U/V 两桥臂开启。
- 两相比较沿相差
128个定时器计数。 - 8 V 栅极电源、10 kHz、零矢量不变。
错沿有效但尚未完全消除问题:
- 原双桥臂同步换相通常约
0x23次就故障。 - 加入 64 tick 错沿后,多次可运行到
0x37F、0x40AD,说明故障概率已经显著下降。 - 偶发 HardFault 的
CFSR=0x00020000是INVSTATE;异常帧中的 PC、LR、xPSR 同时损坏,仍是开关瞬态破坏 CPU 执行状态,不是程序主动跳到某个错误函数。 PastFaults=0x0010是双桥臂/零矢量测试无法建立转速后的预期启动失败,不是过流;Break 计数仍为 0。
尝试八:双桥臂错沿从 64 tick 加大到 128 tick
预期的启动超时:当前零矢量测试不会产生旋转,状态机随后停止 PWM。任然可能触发HardFault,IBUSERR,而且异常帧被破坏;把错沿从 64 增至 128 没有消除故障,因此暂时不再继续盲目增大占空比差。
尝试九:恢复栅极驱动 VCC到12V,PWM到20kHz
问题依旧……