即拿即用:STM32后备寄存器(BKP)的另类用法(无需开启RTC)
即拿即用:STM32后备寄存器(BKP)的另类用法(无需开启RTC)
【关键词】 STM32、后备寄存器、BKP、复位、状态保持、无需RTC、看门狗
打破一个误解
很多开发者以为,要使用STM32的后备寄存器,必须先开启RTC(实时时钟)。
其实不然。
F1系列拥有20个16位后备寄存器,F4系列则有32个32位后备寄存器。它们虽然属于RTC后备域,但完全可以独立于RTC单独使用。
今天分享一个我在实际项目中使用的技巧:用后备寄存器记录设备状态,让意外重启变成“无感恢复”。
一个真实的应用案例
我设计的一款PID电加热控制板,主要有三种工作状态:
-
正在自整定
-
正在加热
-
待机
控制板通过固态继电器驱动负载。但麻烦的是,加热或整定过程中,固态继电器的过零干扰偶尔会导致死机,触发看门狗复位。复位之后所有状态丢失,整个工艺流程就断了。
解决办法很简单:在每一个状态切换的节点,把当前状态写入后备寄存器。系统重启时,程序入口处读取后备寄存器,恢复到重启前的状态继续跑。外部完全察觉不到控制器曾发生过复位,生产节奏丝毫不受影响。
实现逻辑
-
开始PID自整定时,将“自整定”标记写入后备寄存器,整定完成时清除。
-
开始加热时,将“加热”标记写入后备寄存器,停止加热时清除。
就这么简单。即使看门狗超时引发了复位,程序启动后先读一下后备寄存器,是什么标记就干什么事,逻辑清晰又可靠。
代码实战
后备寄存器的操作并不复杂,也无需开启RTC。以下是可直接复制使用的读写函数:
// 写后备寄存器
static void BKP_Write(volatile uint32_t *reg, uint32_t val)
{
__HAL_RCC_PWR_CLK_ENABLE(); // 使能电源接口时钟
HAL_PWR_EnableBkUpAccess(); // 允许访问后备域
*reg = val;
HAL_PWR_DisableBkUpAccess(); // 禁止访问后备域
}
// 读后备寄存器
static uint32_t BKP_Read(volatile uint32_t *reg)
{
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
uint32_t dat = *reg;
HAL_PWR_DisableBkUpAccess();
return dat;
}
验证一下写入是否可靠。向4个寄存器写入测试值,然后软件复位:
void SaveStateToBKP(void)
{
BKP_Write(&RTC->BKP1R, 0x11223344);
BKP_Write(&RTC->BKP2R, 0x33445566);
BKP_Write(&RTC->BKP3R, 0x778899AA);
BKP_Write(&RTC->BKP4R, 0xAABBCCDD);
HAL_NVIC_SystemReset(); // 软件复位,验证写入的数据不丢失
}
复位后读出来验证:
uint32_t DataFromBKP1;
uint32_t DataFromBKP2;
uint32_t DataFromBKP3;
uint32_t DataFromBKP4;
void ReadDataFromBKP(void)
{
DataFromBKP1 = BKP_Read(&RTC->BKP1R);
DataFromBKP2 = BKP_Read(&RTC->BKP2R);
DataFromBKP3 = BKP_Read(&RTC->BKP3R);
DataFromBKP4 = BKP_Read(&RTC->BKP4R);
}
一个关键技巧:魔数校验
写入状态标记时, 务必额外写入一个校验用的“魔数” 。
上电时后备寄存器里的内容可能是随机值。必须用一个魔数来确认:这确实是我上次亲手写进去的数据。
这个魔数可以是约定好的特殊值,比如 0xAABBCCDD,也可以是前面数据的CRC校验值。
重要原则:魔数必须在所有有效数据写完之后再写。
如果先写魔数,万一后续数据写到一半时发生复位,就会出现“魔数对但数据不全”的情况,系统可能做出错误判断。
代码示例如下:
// 写入状态和魔数(魔数最后写)
void SaveStateWithMagic(void)
{
BKP_Write(&RTC->BKP1R, STATE_TUNING); // 先写状态标记
BKP_Write(&RTC->BKP2R, 0xAABBCCDD); // 最后写魔数
}
// 读取时先校验魔数
uint8_t ReadStateWithMagic(void)
{
uint32_t magic = BKP_Read(&RTC->BKP2R);
if (magic == 0xAABBCCDD)
{
return (uint8_t)BKP_Read(&RTC->BKP1R); // 魔数正确,数据可信
}
return STATE_UNKNOWN; // 魔数错误,数据不可信
}
额外提醒
后备寄存器的数据由 VBAT引脚 供电维持。
如果VBAT端没有接纽扣电池或其他备用电源,主电源一断,寄存器数据就丢了。
因此,对于需要经历断电的工况,建议给VBAT接上电池或超级电容,才能真正发挥后备寄存器的最大价值。如果只有上电复位没有断电的场景,则无需额外处理。
更多推荐


所有评论(0)