0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

写flash意外断电的处理方法

冬至子 来源:小陈学不停 作者:小陈学不停 2023-07-21 16:33 次阅读

1 写flash意外断电
在写flash时突然断电可能会造成数据丢失,为了避免这种情况发生,我们可以加一层数据保护,在上电时检查数据是否正确,如果不正确则使用备份的数据

2 内部flash还是以STM32F103ZET6为例可在ST官网下载文档:PM0075
(STM32F10xxx Flash memory microcontrollers)

图片

FLASH的最小擦除单位是扇区,扇区大小为2K

3 实现数据恢复
3.1 实现原理
-在保存数据时,对当前数据进行CRC校验,把校验结果一起写入FLASH,同时再拷贝一份作为备份数据
-在上电加载参数时,对当前数据进行CRC校验,对比校验结果是否正确,如果不正确则使用备份数据,正确则不处理
3.1.1 测试数据
假设需要存储的数据是这样的:

typedef struct
{
  uint32_t times_clean;
  uint32_t times_error;
  uint8_t name[8];
  uint32_t crc32;
}test_data_t;

利用影子变量,每隔一定时间来检查参数是否发生变化,如果变化了就把最新的数据写入FLASH

if  (0  !=  rt_memcmp(&test_data,&test_data_shadow,sizeof(test_data_t)))
{
    uint32_t get_crc = crc32_customized(&test_data_shadow,sizeof(test_data_t)-4);
    test_data_shadow.crc32 = get_crc;         
    stm32_flash_erase(CONFIG_ADDRESS_TEST_DATA,sizeof(test_data_t)*2);
    stm32_flash_write(CONFIG_ADDRESS_TEST_DATA,&test_data_shadow,sizeof(test_data_t));         
    stm32_flash_write(CONFIG_ADDRESS_TEST_DATA+sizeof(test_data_t),&test_data_shadow,sizeof(test_data_t));   
    rt_memcpy(&test_data,&test_data_shadow,sizeof(test_data_t));
}

此时FLASH中的数据应该是这个样子的:

图片

3.2 实现代码
3.2.1 需要被存储的数据相关定义

#define CONFIG_ADDRESS_TEST_DATA        0x0807F800


#define CONFIG_HEAT_PARAMETER_DEFAULT   
{                                       
    .times_clean = 0,                   
    .times_error = 0,                   
    .name = "test",                     
};
test_data_t test_data =  CONFIG_HEAT_PARAMETER_DEFAULT;
test_data_t test_data_shadow = CONFIG_HEAT_PARAMETER_DEFAULT;
test_data_t test_data_bak = CONFIG_HEAT_PARAMETER_DEFAULT;

3.2.2 CRC32校验API,与STM32的硬件CRC结果相同

#define CONFIG_CRC32_POLY              0x04C11DB7
#define CONFIG_CRC32_INIT_VALUE        0xFFFFFFFF
#define CONFIG_CRC32_OUT_XOR          0x00000000 


uint32_t crc32_stm32_hardware(uint8_t *source,uint32_t length)
{
    uint32_t crc_value = CONFIG_CRC32_INIT_VALUE;


    for  (int i =0; i < length; i++)
    {
        for  (int j = 0; j < 8; j++)
        {
            uint8_t get_bit_value = ((source[i] > > (7 - j) & 1) == 1);
            uint8_t get_value = ((crc_value > > 31 & 1) == 1);
            crc_value < <= 1;
            if  (get_value ^ get_bit_value)
            {
                crc_value ^= CONFIG_CRC32_POLY;
            }
        }
    }


    crc_value &= 0xFFFFFFFF;


    return (crc_value ^= CONFIG_CRC32_OUT_XOR);
}

3.2.3 上电加载参数,检查数据是否出错,出错则使用备份数据

void g_check_data(void)
{
    stm32_flash_read(CONFIG_ADDRESS_TEST_DATA,&test_data_shadow,sizeof(test_data_t));
    stm32_flash_read(CONFIG_ADDRESS_TEST_DATA+sizeof(test_data_t),&test_data_bak,sizeof(test_data_t));
    uint32_t crc_value_cal = crc32_stm32_hardware(&test_data_shadow,sizeof(test_data_t)-4);


    rt_kprintf("crc_value_cal[%x], crc_old[%x]rn",crc_value_cal,test_data_shadow.crc32);

    if  (crc_value_cal != test_data_shadow.crc32)
    {
        rt_kprintf("test data is invalidrn");

        rt_memcpy(&test_data_shadow,&test_data_bak,sizeof(test_data_t)-4);

        uint32_t crc_value_bak = crc32_stm32_hardware(&test_data_bak,sizeof(test_data_t)-4);

        test_data_shadow.crc32 = crc_value_bak;

        rt_memcpy(&test_data_shadow,&test_data_bak,sizeof(test_data_t)-4);
    }

    rt_memcpy(&test_data,&test_data_shadow,sizeof(test_data_t)); 
}

3.2.4 完整的测试代码

int main(void)
{
    uint32_t get_crc_first = crc32_stm32_hardware(&test_data_shadow,sizeof(test_data_t)-4);

    test_data_shadow.crc32 = get_crc_first;
    test_data.crc32 = get_crc_first;

    g_check_data();

    while (1)
    {
         if (0 != rt_memcmp(&test_data,&test_data_shadow,sizeof(test_data_t)))
         {
             uint32_t get_crc = crc32_stm32_hardware(&test_data_shadow,sizeof(test_data_t)-4);

             test_data_shadow.crc32 = get_crc;

             rt_base_t level;

             level = rt_hw_interrupt_disable();

             stm32_flash_erase(CONFIG_ADDRESS_TEST_DATA,sizeof(test_data_t)*2);

             stm32_flash_write(CONFIG_ADDRESS_TEST_DATA,&test_data_shadow,sizeof(test_data_t));

             stm32_flash_write(CONFIG_ADDRESS_TEST_DATA+sizeof(test_data_t),&test_data_shadow,sizeof(test_data_t));

             rt_hw_interrupt_enable(level);

             rt_memcpy(&test_data,&test_data_shadow,sizeof(test_data_t));
         }
         
         rt_thread_mdelay(1000);
    }
}


int cmd_flash_protect_test(int argc, char **argv)
{
    if (2 == argc)
    {
        uint32_t get_type = atoi(argv[1]);


        if (0 == get_type)
        {
            g_check_data();
        }
        else if (1 == get_type)
        {
            test_data_shadow.times_clean++;
        }
    }

    return 0;
}
MSH_CMD_EXPORT_ALIAS(cmd_flash_protect_test,flash_protect,flash_protect [val]);

4 测试效果

| /
RT -     Thread Operating System
 / |      4.1.1 build Jul  1 2023 21:37:26
 2006 - 2022 Copyright by RT-Thread team
crc_value_cal[95663ff9], crc_old[95663ff9]
msh / >flash_protect 1
msh / >need write flash crc[ba0600aa]
old_data: times_clean:[6] times_error:[0] name[test] crc:[95663ff9] old_data end
new_data: times_clean:[7] times_error:[0] name[test] crc:[ba0600aa] new_data end
msh / >flash_protect 0
crc_value_cal[ba0600aa], crc_old[ba0600aa]

5 总结
这个方法不适合存储的数据超过一个扇区大小,还需要根据实际情况来调整写入和加载参数的方式
我们虽不能保证自己的软件完全没有BUG,但可以先写一份软件测试用例,将需要测试的每一个功能列成TODOLIST,再按照这个清单去自测,这样就能在自测试发现并及时修正错误,反复测试多次后,我们再把软件提交给测试可能会更好一些,工作中遇到困难是让我们进步的,是提醒我们该优化自己的工作方法了。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储器
    +关注

    关注

    38

    文章

    7151

    浏览量

    162001
  • CRC校验
    +关注

    关注

    0

    文章

    81

    浏览量

    15032
  • RT-Thread
    +关注

    关注

    31

    文章

    1149

    浏览量

    38896
  • Flash单片机
    +关注

    关注

    0

    文章

    111

    浏览量

    9294
  • STM32F103ZET6
    +关注

    关注

    9

    文章

    67

    浏览量

    20773
收藏 人收藏

    评论

    相关推荐

    Flash基本操作——Flash基础(1)#多媒体技术

    FlaSh
    未来加油dz
    发布于 :2023年05月24日 10:43:53

    Flash与DSP的硬件接口设计和Flash程序

    ;nbsp;Flash; DSP; 接口; 打包在现代数字信号处理系统中,采用Flash做为DSP程序加载和引导是一种常用的方法,它为用户对那些将来
    发表于 10-07 11:01

    关于LF2407A的FLASH问题说明

    setup.exe。 二、进入cc中,在tools图标下有烧工具; 1、关于FLASH时钟的选择,此烧工具默认最高频率进行FLASH的操作。根据目标系统的工作主频重新要进行PLL设
    发表于 04-07 09:11

    意外断电之后如何避免不稳定的电冲击击坏电器

    的电源插头,使之在意外断电之后,其内部出现一个断电保护,当电路中再次通了电,须经确认电路稳定之后由人工关闭插线板或者插头上的断电保护开关,有点类似于闸刀开关和漏电开关等。虽然本人对电
    发表于 06-18 16:04

    *** Flash 成功烧 断电不运行

    dsp *** 编写的串口程序,用ccs4.1.2 成功调通,烧写过程没有问题,但是断电后程序没有正常运行。烧后进行了复位,也没有反应
    发表于 03-01 16:02

    DSP烧FLASH

    请问,DSP28335第二次烧FLASH用擦除吗?应该怎么擦除?烧FLASH正确,为什么不出结果?请问大神们为什么?谢谢
    发表于 09-03 22:50

    FLASH锁死

    写入了密码。如果在烧的的过程中,受到干扰就有机会导致FLASH锁死,如果试了下面的方法,仍旧没有办法解锁,那么没只能够更换芯片。在C2000烧写过程中(clear)-->erase-->
    发表于 08-30 10:56

    28035烧FLash出现错误

    之前烧写过很多次,也没事,今天烧FLash的时候突然出现这个问题:Flash API Error #24:The Erase operation failed the pre-compaction step
    发表于 11-09 14:21

    flash的步骤解析

    步骤已经初步实现了boot的启动,为什么说是初步呢,是因为我正确烧flash后,拔掉仿真器,按复位按钮,程序可以正常启动,这说明,bootload和flash已经正常工作,但问题
    发表于 07-28 08:18

    如何去处理意外中断的情况

    一、列举一个指针跑飞的例子:1、 意外中断。是否打开了某个中断,但是没有响应和清除中端标志,导致程序一直进入中断,造成死机假象。2. 中断变量处理不妥。若定义某些会在中断中修改的全局变量,这时要注意
    发表于 07-15 06:37

    ESP32C3FN4片内Flash批量烧方法是怎样?

    1. ESP32C3 量产测试方法是否和ESP32一样?2. ESP32C3FN4片内Flash批量烧方法是怎样?
    发表于 03-13 09:41

    断相保护工作原理_断电保护方法

    本文首先介绍了断电保护是什么,然后解释了断电保护的工作原理,最后说明了断电保护的方法
    发表于 08-13 10:46 9049次阅读

    HCC推出故障安全 防止意外复位或断电的exFAT解决方案

    HCC推出故障安全,防止意外复位或断电的exFAT解决方案
    的头像 发表于 02-25 16:11 2016次阅读

    AN038 Keil环境中关于IEC60730 Flash自检的CRC校验批处理添加方法

    AN038 Keil环境中关于IEC60730 Flash自检的CRC校验批处理添加方法
    发表于 02-23 19:10 3次下载
    AN038 Keil环境中关于IEC60730 <b class='flag-5'>Flash</b>自检的CRC校验批<b class='flag-5'>处理</b>添加<b class='flag-5'>方法</b>

    AN039 eclipse环境中关于IEC60730 Flash自检的CRC校验批处理添加方法

    AN039 eclipse环境中关于IEC60730 Flash自检的CRC校验批处理添加方法
    发表于 02-27 18:18 0次下载
    AN039 eclipse环境中关于IEC60730 <b class='flag-5'>Flash</b>自检的CRC校验批<b class='flag-5'>处理</b>添加<b class='flag-5'>方法</b>