STM32L4xx系列:如何通过嵌入式代码禁用写保护及检查状态?
嘿,我来帮你搞定STM32L4xx系列的写保护问题,完全用嵌入式代码实现,不用依赖ST-Link Utility工具。下面分两部分给你讲清楚:检查写保护状态,以及禁用写保护的具体代码实现。
一、检查用户地址是否处于写保护状态
STM32L4的FLASH写保护(WRP)是通过选项字节来配置的,每个WRP区域对应一段FLASH地址范围。要检查某个地址是否被保护,我们需要读取选项字节里的WRP配置,然后判断目标地址是否在保护范围内。
这里给你一个裸机寄存器操作的示例函数,直接读取硬件寄存器,不需要依赖HAL库:
#include "stm32l4xx.h" /** * @brief 检查指定地址是否处于FLASH写保护状态 * @param address 要检查的用户地址(必须在FLASH用户区:0x08000000 ~ 0x081FFFFF,具体范围看你的芯片型号) * @return 1:地址处于写保护;0:地址未被写保护/非法地址 */ uint8_t CheckFlashWriteProtection(uint32_t address) { // 先校验地址合法性 if(address < FLASH_BASE || address > (FLASH_BASE + FLASH_SIZE - 1)) { return 0; // 非法地址,这里默认返回未保护,你也可以改成报错 } // 读取选项字节的WRP配置寄存器 uint32_t optcr = FLASH->OPTCR; // 解析WRP1A区域的起始和结束偏移(STM32L4默认第一个WRP区域) uint32_t wrp1a_start = (optcr & FLASH_OPTCR_WRP1A_STRT) >> FLASH_OPTCR_WRP1A_STRT_Pos; uint32_t wrp1a_end = (optcr & FLASH_OPTCR_WRP1A_END) >> FLASH_OPTCR_WRP1A_END_Pos; // 转换为实际的FLASH地址范围:每个WRP单元对应1KB(0x400)的FLASH uint32_t wrp_start_addr = FLASH_BASE + wrp1a_start * 0x400; uint32_t wrp_end_addr = FLASH_BASE + (wrp1a_end + 1) * 0x400 - 1; // 检查地址是否在WRP1A区域内,且WRP使能位已置位 if((address >= wrp_start_addr && address <= wrp_end_addr) && (optcr & FLASH_OPTCR_WRPEN)) { return 1; } // 如果你需要检查其他WRP区域(比如WRP1B、WRP2A),可以参考上面的逻辑重复判断 // 具体区域数量和范围请参考你的STM32L4型号参考手册(RM0351) return 0; }
二、禁用FLASH写保护的代码实现
要禁用写保护,必须先解锁FLASH控制器和选项字节,然后修改WRP配置,最后保存配置并重新锁定(可选)。下面是完整的示例函数:
/** * @brief 禁用FLASH的写保护(以WRP1A区域为例,可扩展到其他区域) * @return 0:操作成功;1:操作失败 */ uint8_t DisableFlashWriteProtection(void) { // 临时关闭全局中断,避免干扰FLASH操作 __disable_irq(); // 1. 解锁FLASH_CR寄存器(默认是锁定状态) if(FLASH->CR & FLASH_CR_LOCK) { FLASH->KEYR = FLASH_KEY1; // 写入解锁密钥1 FLASH->KEYR = FLASH_KEY2; // 写入解锁密钥2 if(FLASH->CR & FLASH_CR_LOCK) { __enable_irq(); return 1; // FLASH解锁失败 } } // 2. 解锁选项字节寄存器OPTCR if(FLASH->CR & FLASH_CR_OPTLOCK) { FLASH->OPTKEYR = FLASH_OPT_KEY1; // 选项字节解锁密钥1 FLASH->OPTKEYR = FLASH_OPT_KEY2; // 选项字节解锁密钥2 if(FLASH->CR & FLASH_CR_OPTLOCK) { __enable_irq(); return 1; // 选项字节解锁失败 } } // 3. 修改WRP配置:清除WRP使能位,设置WRP1A区域为无保护 // 无保护配置:WRP1A_STRT设为0x1F,WRP1A_END设为0x00(参考STM32L4手册) FLASH->OPTCR &= ~(FLASH_OPTCR_WRPEN | FLASH_OPTCR_WRP1A_STRT | FLASH_OPTCR_WRP1A_END); FLASH->OPTCR |= (0x1F << FLASH_OPTCR_WRP1A_STRT_Pos) | (0x00 << FLASH_OPTCR_WRP1A_END_Pos); // 如果要禁用其他WRP区域,这里继续修改对应的WRPx_STRT和WRPx_END即可 // 4. 触发选项字节编程,保存配置 FLASH->CR |= FLASH_CR_OPTSTRT; // 等待操作完成(BUSY位清零) while(FLASH->SR & FLASH_SR_BSY); // 5. 检查是否有错误发生 if(FLASH->SR & (FLASH_SR_WRPERR | FLASH_SR_OPTVERR)) { // 清除错误标志 FLASH->SR |= FLASH_SR_WRPERR | FLASH_SR_OPTVERR; __enable_irq(); return 1; } // 6. 重新锁定FLASH和选项字节(可选,根据你的需求决定是否保持解锁状态) FLASH->CR |= FLASH_CR_LOCK | FLASH_CR_OPTLOCK; // 恢复全局中断 __enable_irq(); return 0; }
几个关键注意事项
- 供电要求:操作选项字节时,芯片供电必须稳定,电压要在2.0V~3.6V范围内(STM32L4的规范),否则可能导致配置失败甚至硬件损坏。
- 中断处理:操作过程中关闭全局中断是个好习惯,避免中断触发影响FLASH的编程流程。
- 芯片差异:不同STM32L4型号的FLASH大小、WRP区域数量可能不同,比如L476有4个WRP区域,L432可能只有2个。具体请参考对应型号的参考手册(RM0351)。
- 密钥值:上面代码里的
FLASH_KEY1、FLASH_KEY2、FLASH_OPT_KEY1、FLASH_OPT_KEY2都是STM32标准定义的宏,如果你用的是标准库或HAL库,已经包含在头文件里了;如果是纯裸机,你可以直接写数值:- FLASH_KEY1 = 0x45670123;
- FLASH_KEY2 = 0xCDEF89AB;
- FLASH_OPT_KEY1 = 0x08192A3B;
- FLASH_OPT_KEY2 = 0x4C5D6E7F;
内容的提问来源于stack exchange,提问作者Hoa.N




