STM32G031 Flash页擦除启用操作偶发HardFault问题求助
STM32G031 Flash页擦除偶尔触发HardFault的排查与解决
核心问题定位
你遇到的偶发HardFault,根源是CFGBSY位未清零就尝试写入FLASH_CR寄存器。从你提供的SR值0x40000能看到CFGBSY位被置位,该位表示Flash控制器正在执行配置类操作(比如选项字节加载、时钟相关的Flash配置),此时写入FLASH_CR会触发总线错误,直接引发HardFault。你的代码只等待了BSY1位,完全忽略了CFGBSY位,这是故障偶发的关键原因。
具体修复步骤
1. 等待Flash完全就绪(同时检查BSY1和CFGBSY)
把所有等待Flash就绪的循环,修改为同时等待BSY1和CFGBSY两位清零:
// 替换原有的仅检查BSY1的循环 while ((FLASH->SR & (FLASH_SR_BSY1 | FLASH_SR_CFGBSY)) != 0) { __asm("nop"); }
2. 优化错误标志清除逻辑
原代码逐个判断清除错误位,效率低且易遗漏,直接按手册要求写入错误位掩码即可一次性清除所有错误标志:
// 替换原有的多个if判断 FLASH->SR = FLASH_SR_OPTVERR | FLASH_SR_RDERR | FLASH_SR_FASTERR | FLASH_SR_MISERR | FLASH_SR_PGSERR | FLASH_SR_SIZERR | FLASH_SR_PGAERR | FLASH_SR_WRPERR | FLASH_SR_PROGERR | FLASH_SR_OPERR;
3. 确保解锁操作安全(可选)
在解锁前检查LOCK位,避免重复执行解锁序列(错误的解锁序列会直接锁死FLASH_CR并触发HardFault):
if ((FLASH->CR & FLASH_CR_LOCK) != 0) { FLASH->KEYR = 0x45670123; FLASH->KEYR = 0xCDEF89AB; }
4. 规范页擦除寄存器操作顺序
按RM0444手册步骤,合并寄存器操作减少写入次数,逻辑更清晰:
// 先清除原有PER和PNB位,再同时配置 FLASH->CR &= ~(FLASH_CR_PER | FLASH_CR_PNB); FLASH->CR |= FLASH_CR_PER | (0xF << FLASH_CR_PNB_Pos);
修复后的关键代码片段
void FLASH_WriteValue(uint16_t val) { uint32_t old_primask = __get_PRIMASK(); __disable_irq(); // 等待Flash完全就绪(BSY1 + CFGBSY) while ((FLASH->SR & (FLASH_SR_BSY1 | FLASH_SR_CFGBSY)) != 0) { __asm("nop"); } // 安全解锁Flash if ((FLASH->CR & FLASH_CR_LOCK) != 0) { FLASH->KEYR = 0x45670123; FLASH->KEYR = 0xCDEF89AB; } // 等待Flash就绪 while ((FLASH->SR & (FLASH_SR_BSY1 | FLASH_SR_CFGBSY)) != 0) { __asm("nop"); } // 清除所有错误标志 FLASH->SR = FLASH_SR_OPTVERR | FLASH_SR_RDERR | FLASH_SR_FASTERR | FLASH_SR_MISERR | FLASH_SR_PGSERR | FLASH_SR_SIZERR | FLASH_SR_PGAERR | FLASH_SR_WRPERR | FLASH_SR_PROGERR | FLASH_SR_OPERR; // 等待Flash就绪 while ((FLASH->SR & (FLASH_SR_BSY1 | FLASH_SR_CFGBSY)) != 0) { __asm("nop"); } // 配置页擦除参数 FLASH->CR &= ~(FLASH_CR_PER | FLASH_CR_PNB); FLASH->CR |= FLASH_CR_PER | (0xF << FLASH_CR_PNB_Pos); // 启动擦除 FLASH->CR |= FLASH_CR_STRT; // 等待擦除完成 while ((FLASH->SR & (FLASH_SR_BSY1 | FLASH_SR_CFGBSY)) != 0) { __asm("nop"); } // 关闭页擦除模式 FLASH->CR &= ~FLASH_CR_PER; // 后续编程操作同理,所有等待都要检查BSY1和CFGBSY while ((FLASH->SR & (FLASH_SR_BSY1 | FLASH_SR_CFGBSY)) != 0) { __asm("nop"); } // 清除错误标志 FLASH->SR = FLASH_SR_OPTVERR | FLASH_SR_RDERR | FLASH_SR_FASTERR | FLASH_SR_MISERR | FLASH_SR_PGSERR | FLASH_SR_SIZERR | FLASH_SR_PGAERR | FLASH_SR_WRPERR | FLASH_SR_PROGERR | FLASH_SR_OPERR; while ((FLASH->SR & (FLASH_SR_BSY1 | FLASH_SR_CFGBSY)) != 0) { __asm("nop"); } // 写入数据 uint32_t address = 0x08007800; uint32_t data = val; uint32_t dataPadding = 0; FLASH->CR |= FLASH_CR_PG; *(volatile uint32_t*)address = data; *(volatile uint32_t*)(address + 4) = dataPadding; while ((FLASH->SR & (FLASH_SR_BSY1 | FLASH_SR_CFGBSY)) != 0) { __asm("nop"); } FLASH->CR &= ~FLASH_CR_PG; // 锁定Flash FLASH->CR |= FLASH_CR_LOCK; __set_PRIMASK(old_primask); }
额外注意事项
- 确保系统时钟配置稳定,Flash操作对时钟稳定性要求高,时钟异常可能导致CFGBSY位异常置位
- 避免在Flash操作期间触发任何修改Flash配置的操作(比如选项字节编程、时钟切换)
内容的提问来源于stack exchange,提问作者Henrik Rune Jakobsen
相关产品推荐
相关产品推荐

