打印
[其他ST产品]

操作中的掉电保护机制

[复制链接]
45|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xiyaoko2365|  楼主 | 2024-9-30 18:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在嵌入式系统中使用 STM32 的 Flash 存储时,如果在写入操作过程中发生掉电,可能会导致数据丢失或损坏。这在存储关键配置数据或日志信息时是不可接受的。为了解决这一问题,我们可以引入掉电保护机制,确保在掉电或系统重启时不会发生数据损坏。

下面讨论几种常见的掉电保护技术及其在 STM32 Flash 写入过程中的实现方法。

使用特权

评论回复
沙发
xiyaoko2365|  楼主 | 2024-9-30 18:20 | 只看该作者
问题分析
STM32 的 Flash 写操作分为两个主要步骤:

页擦除:Flash 写入必须在页被擦除的情况下进行,一旦擦除操作开始,页内的数据全部清除。如果在此过程中掉电,整个页的数据会丢失。
数据写入:擦除完成后,才可以执行写操作,写入过程会依次将数据写入 Flash 中。如果写入过程中掉电,可能会导致部分字写入成功,而部分字未写入,这会导致数据不完整或损坏。
因此,掉电保护机制主要是防止在这两个过程中发生掉电时导致数据损坏。

使用特权

评论回复
板凳
xiyaoko2365|  楼主 | 2024-9-30 18:20 | 只看该作者
掉电保护的设计原则
为了在 Flash 写入过程中实现掉电保护,常见的设计策略包括:

双缓冲区机制:使用两个 Flash 页交替存储数据,当其中一个写入失败时,系统仍然可以从另一个页读取完整的数据。
数据校验与回滚:使用校验和机制验证数据完整性,如果写入失败或不完整,回滚到上一个有效的写入状态。
掉电检测和大容量电容:通过硬件电路(如检测掉电信号)并使用大容量电容,在系统掉电后仍能维持足够时间完成写操作。
日志型写入(Wear Leveling):将数据写入新的位置,保证原始数据不会被覆盖,直到新数据完全写入成功。

使用特权

评论回复
地板
xiyaoko2365|  楼主 | 2024-9-30 18:20 | 只看该作者
双缓冲区机制
双缓冲区机制是最常用的掉电保护方法之一,尤其适用于需要频繁更新的数据存储场景。它的原理是使用两个 Flash 页作为备份,在写入新数据之前保留旧数据,如果写入失败,可以回滚到旧数据。

使用特权

评论回复
5
xiyaoko2365|  楼主 | 2024-9-30 18:21 | 只看该作者
实现步骤:
定义两个缓冲区页:设定两个 Flash 页(如 PageA 和 PageB)用于交替存储数据。
读取操作:系统启动时,从 PageA 和 PageB 中读取最近一次成功写入的数据。通常可以通过标志位或校验和来判断哪个页面包含有效数据。
写入操作:
写入数据到空闲页。
写入完成后,通过校验和验证写入是否成功。
成功后标记该页为有效。
失败回滚:如果写入失败或校验不通过,则保持原有页的有效性,回滚到旧数据。

使用特权

评论回复
6
xiyaoko2365|  楼主 | 2024-9-30 18:21 | 只看该作者
示例代码:
c

#define PAGE_A_ADDRESS 0x08020000  // 假设 PageA 的 Flash 地址
#define PAGE_B_ADDRESS 0x08020400  // 假设 PageB 的 Flash 地址

// 检查某一页是否包含有效数据(例如通过校验和)
bool Check_Page_Valid(uint32_t PageAddress)
{
    // 读取页面数据,校验数据的完整性和标志位(具体实现视应用而定)
    return Is_Checksum_Valid(PageAddress);
}

// 读取数据
uint32_t Flash_Read_Protected(uint32_t* data)
{
    if (Check_Page_Valid(PAGE_A_ADDRESS))
    {
        *data = Flash_Read(PAGE_A_ADDRESS);
        return PAGE_A_ADDRESS;
    }
    else if (Check_Page_Valid(PAGE_B_ADDRESS))
    {
        *data = Flash_Read(PAGE_B_ADDRESS);
        return PAGE_B_ADDRESS;
    }
    else
    {
        // 错误处理,可能两页都无效
        return 0;
    }
}

// 写入数据,使用双缓冲区
void Flash_Write_Protected(uint32_t new_data)
{
    uint32_t valid_page = Flash_Read_Protected(&new_data);

    // 如果 PageA 有效,则写入 PageB,反之亦然
    uint32_t write_page = (valid_page == PAGE_A_ADDRESS) ? PAGE_B_ADDRESS : PAGE_A_ADDRESS;

    Flash_Erase(write_page);    // 擦除空闲页
    Flash_Write(write_page, new_data);  // 写入新数据

    // 写入完成后,验证数据是否正确
    if (Check_Page_Valid(write_page))
    {
        // 写入成功
    }
    else
    {
        // 写入失败,回滚到旧页
    }
}

使用特权

评论回复
7
xiyaoko2365|  楼主 | 2024-9-30 18:21 | 只看该作者
数据校验与回滚机制
数据校验与回滚机制可以确保写入操作的原子性。如果在写入过程中数据损坏,可以根据校验结果判断写入是否成功,并在失败时回滚到之前的有效数据。通常的做法是为写入的数据添加一个校验和或 CRC,写入完成后立即进行校验。

使用特权

评论回复
8
xiyaoko2365|  楼主 | 2024-9-30 18:21 | 只看该作者
校验和示例:
cuint32_t Calculate_Checksum(uint32_t data)
{
    // 简单的校验和计算方法
    return data ^ 0xFFFFFFFF;
}

void Flash_Write_With_Checksum(uint32_t Address, uint32_t Data)
{
    uint32_t checksum = Calculate_Checksum(Data);
   
    HAL_FLASH_Unlock();
   
    // 写入数据
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data);
   
    // 写入校验和
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address + 4, checksum);
   
    HAL_FLASH_Lock();
}

bool Flash_Read_With_Checksum(uint32_t Address, uint32_t* Data)
{
    *Data = Flash_Read(Address);
    uint32_t stored_checksum = Flash_Read(Address + 4);
   
    // 校验
    if (stored_checksum == Calculate_Checksum(*Data))
    {
        return true;  // 数据正确
    }
    else
    {
        return false; // 校验失败
    }
}

使用特权

评论回复
9
xiyaoko2365|  楼主 | 2024-9-30 18:21 | 只看该作者
在这种机制下,如果校验和不匹配,意味着写入过程中发生了错误,系统可以决定重新擦除并写入数据,或回滚到上一次的有效状态。

使用特权

评论回复
10
xiyaoko2365|  楼主 | 2024-9-30 18:21 | 只看该作者
硬件掉电检测与延时电源
如果系统允许添加硬件设计,掉电检测电路可以作为一种有效的保护措施。当电源即将掉电时,MCU 通过硬件信号提前检测到,并利用电容等延迟电源断开的技术维持足够的时间完成当前的 Flash 写入操作。

使用特权

评论回复
11
xiyaoko2365|  楼主 | 2024-9-30 18:21 | 只看该作者
硬件设计示例:
电压检测器:使用电压检测芯片(如 STM32 的内置 PVD 预警电压检测器)监控电源电压,当电压降到某个阈值时触发中断,MCU 可以提前保存重要数据。
大电容:在电源线上接入足够容量的电容,提供短暂的掉电延迟时间,使 MCU 在掉电后仍有足够时间完成 Flash 写入。

使用特权

评论回复
12
xiyaoko2365|  楼主 | 2024-9-30 18:21 | 只看该作者
STM32 内部的 PVD(Programmable Voltage Detector)可以检测电压变化,并在电压低于设定的阈值时产生中断。通过配置 PVD 中断,系统可以及时响应掉电信号。

使用特权

评论回复
13
xiyaoko2365|  楼主 | 2024-9-30 18:21 | 只看该作者
PVD 中断配置示例:
cvoid PVD_Config(void)
{
    PWR_PVDTypeDef sConfigPVD;

    // 配置 PVD 阈值
    sConfigPVD.PVDLevel = PWR_PVDLEVEL_5;  // 例如 2.9V
    sConfigPVD.Mode = PWR_PVD_MODE_IT_RISING;  // 上升沿触发中断

    HAL_PWR_ConfigPVD(&sConfigPVD);
    HAL_PWR_EnablePVD();

    // 启用 PVD 中断
    HAL_NVIC_SetPriority(PVD_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(PVD_IRQn);
}

void PVD_IRQHandler(void)
{
    // 检测到电压过低,保存关键数据
    HAL_PWR_PVD_IRQHandler();
}

使用特权

评论回复
14
xiyaoko2365|  楼主 | 2024-9-30 18:22 | 只看该作者
在掉电检测到时,及时保存当前操作状态到 RAM 或立即执行 Flash 写入。

使用特权

评论回复
15
xiyaoko2365|  楼主 | 2024-9-30 18:22 | 只看该作者
日志型写入与分散存储
日志型写入(Log-based Write)或称分散存储(Wear Leveling)是通过避免在同一位置重复写入,减少擦除操作次数并提高 Flash 的耐久性。这种方式通常与掉电保护结合使用,在掉电时即使某一部分数据写入失败,依然能够读取到上一次完整的写入数据。

具体实现上,通常采用循环队列或在 Flash 的不同区域依次写入数据,每次写入成功后更新指针指向最新的区域。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

32

主题

403

帖子

0

粉丝