STM32G474程序无法运行
STM32G474 “程序无法运行/PWM无输出” 排查笔记
日期:2026-09-15
芯片:STM32G474RET6
现象链
- TIM6中断能正常触发(回调函数里的计数器
a在自增),一开始误以为一切正常 - 但TIM1的PWM输出(PA8/PA9/PA10)示波器测不到任何波形
- 换成完全不依赖HAL的裸机GPIO翻转程序测试,PA8依然恒定3.3V,无任何跳变 —— 说明问题不在应用层代码
- 暂停调试查看PC寄存器/反汇编窗口,发现程序停在
0x1FFF4xxx地址,而不是Flash地址0x08000xxx - 反汇编显示卡在一个死循环,不断轮询读某寄存器状态位(
LDR → LSLS → BPL循环跳回自身)
走过的弯路(先排除,再定位)
依次排查了下面这些软件层面的东西,虽然部分确实是bug,但都不是本次故障的根本原因:
- NVIC中断优先级/使能 —— 没问题
HAL_TIMEx_PWMN_Start三次都误传了TIM_CHANNEL_1(本该是CH1/CH2/CH3)—— 是个真bug,顺手修了- 缺少
__HAL_TIM_MOE_ENABLE(高级定时器主输出使能,STM32的安全设计,必须手动开)—— 也顺手补了,是必要条件但非本次故障原因 SystemClock_Config里改HSE/HSI来回试 —— 全部无效,因为问题发生在main()执行之前,改任何用户代码都不可能影响它
根本原因
选项字节 nSWBOOT0 被勾选(Checked: BOOT0 taken from PB8/BOOT0 pin),即启动模式由物理PB8引脚电平决定,而PB8被外部电路拉到了3.3V(高电平)。
STM32启动逻辑:BOOT0逻辑值=1 → 芯片从内置System Bootloader(ROM,地址 0x1FFF0000 附近)启动,根本不会执行用户Flash里的任何代码。所以不管怎么改 main.c 都是徒劳 —— 程序连 Reset_Handler 都没走到用户区。
关键教训
测物理PB8引脚电压不能直接判断问题,因为如果 nSWBOOT0 是未勾选的(用选项字节 nBOOT0 位而不是物理引脚决定启动模式),那PB8电压再高也无关。所以另一块"好板子"PB8同样是3.3V却能正常工作,因为它的 nSWBOOT0 配置是未勾选状态。
解决步骤
用 STM32CubeProgrammer(不是Keil,Keil连不上被卡死的芯片时优先用这个工具):
- 连接芯片
- 若连不上,先确认Keil已完全 Stop Debug Session,释放SWD占用
- Reset mode可尝试切换为 Hardware reset,更容易抢到连接时机
- 进入 OB (Option Bytes) → Detailed view
- 取消勾选
nSWBOOT0(改为 “BOOT0 taken from the option bit nBOOT0”) - 确认
nBOOT0= 1(勾选,即从主Flash启动) - 点 Apply
- 断电重新上电(选项字节生效必须重新上电,软件复位不够)
后续注意
之后调试时若发现 nSWBOOT0 又被改回勾选状态,检查Keil的 Debug → Settings → Flash Download 里是否带有旧的Option Bytes配置,每次下载时把设置覆盖回去了,需要同步改掉。
通用诊断方法论
当"改了代码但完全没反应,无论怎么改都一样"时,第一反应应该是确认程序是否真的跑在用户Flash里,而不是钻进业务逻辑排查。
- 调试器暂停,看 PC寄存器 / Disassembly窗口 当前地址:
0x08000000开头 → 用户Flash,正常0x1FFF0000开头 → System Bootloader,说明启动模式配置错了,去查BOOT0/选项字节,不要查应用代码0x20000000开头 → 在RAM,可能是调试配置问题或程序跑飞
- 一旦确认卡在Bootloader地址,直接去查选项字节里的
nSWBOOT0/nBOOT0,而不是纠结晶振、时钟树、外设初始化顺序这些 —— 那些改了也没用,因为程序压根没执行到那里。
涉及的其他知识点(顺带修复的真bug)
1. TIM1互补通道启动错误
// 错误写法(三次都传CHANNEL_1)
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);
// 正确写法
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_3);
2. 高级定时器必须手动使能主输出
STM32高级定时器(TIM1/TIM8)有独立的MOE(Main Output Enable)位,是硬件安全设计,防止意外输出驱动信号,必须手动置位:
__HAL_TIM_MOE_ENABLE(&htim1);
3. TIM6中断触发不代表整个系统正常
TIM6中断能触发只说明TIM6这一个外设的时钟和中断链路是通的,不能作为"系统时钟/启动流程整体正常"的证据。当排查"为什么另一个外设不工作"时,不能因为某个外设正常就排除系统级问题(如启动模式错误)。
最后如果DeBug一直进入这个东西的话,看看外部晶振是不是不起振了,换一下内部晶振(2026.9.19)
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
更多推荐


所有评论(0)