谷动谷力

 找回密码
 立即注册
查看: 1174|回复: 0
打印 上一主题 下一主题
收起左侧

单片机写flash意外断电处理

[复制链接]
跳转到指定楼层
楼主
发表于 2023-8-24 22:12:48 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 sunsili 于 2023-8-24 22:19 编辑

单片机写flash意外断电处理


1 写flash意外断电

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

2 内部flash

还是以STM32F103ZET6为例可在ST官网下载文档:PM0075
(STM32F10xxx Flash memory microcontrollers)
FLASH的最小擦除单位是扇区,扇区大小为2K

3 实现数据恢复


3.1 实现原理

-在保存数据时,对当前数据进行CRC校验,把校验结果一起写入FLASH,同时再拷贝一份作为备份数据
-在上电加载参数时,对当前数据进行CRC校验,对比校验结果是否正确,如果不正确则使用备份数据,正确则不处理

3.1.1 测试数据

假设需要存储的数据是这样的:
  1. typedef struct
  2. {
  3.   uint32_t times_clean;
  4.   uint32_t times_error;
  5.   uint8_t name[8];
  6.   uint32_t crc32;
  7. }test_data_t;
复制代码

利用影子变量,每隔一定时间来检查参数是否发生变化,如果变化了就把最新的数据写入FLASH
  1. if  (0  !=  rt_memcmp(&test_data,&test_data_shadow,sizeof(test_data_t)))
  2. {
  3.     uint32_t get_crc = crc32_customized(&test_data_shadow,sizeof(test_data_t)-4);
  4.     test_data_shadow.crc32 = get_crc;         
  5.     stm32_flash_erase(CONFIG_ADDRESS_TEST_DATA,sizeof(test_data_t)*2);
  6.     stm32_flash_write(CONFIG_ADDRESS_TEST_DATA,&test_data_shadow,sizeof(test_data_t));         
  7.     stm32_flash_write(CONFIG_ADDRESS_TEST_DATA+sizeof(test_data_t),&test_data_shadow,sizeof(test_data_t));   
  8.     rt_memcpy(&test_data,&test_data_shadow,sizeof(test_data_t));
  9. }
复制代码

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


3.2 实现代码
3.2.1 需要被存储的数据相关定义
  1. #define CONFIG_ADDRESS_TEST_DATA        0x0807F800

  2. #define CONFIG_HEAT_PARAMETER_DEFAULT   \
  3. {                                       \
  4.     .times_clean = 0,                   \
  5.     .times_error = 0,                   \
  6.     .name = "test",                     \
  7. };
  8. test_data_t test_data =  CONFIG_HEAT_PARAMETER_DEFAULT;
  9. test_data_t test_data_shadow = CONFIG_HEAT_PARAMETER_DEFAULT;
  10. test_data_t test_data_bak = CONFIG_HEAT_PARAMETER_DEFAULT;
复制代码

3.2.2 CRC32校验API,与STM32的硬件CRC结果相同
  1. #define CONFIG_CRC32_POLY              0x04C11DB7
  2. #define CONFIG_CRC32_INIT_VALUE        0xFFFFFFFF
  3. #define CONFIG_CRC32_OUT_XOR          0x00000000

  4. uint32_t crc32_stm32_hardware(uint8_t *source,uint32_t length)
  5. {
  6.     uint32_t crc_value = CONFIG_CRC32_INIT_VALUE;

  7.     for  (int i =0; i < length; i++)
  8.     {
  9.         for  (int j = 0; j < 8; j++)
  10.         {
  11.             uint8_t get_bit_value = ((source >> (7 - j) & 1) == 1);
  12.             uint8_t get_value = ((crc_value >> 31 & 1) == 1);
  13.             crc_value <<= 1;
  14.             if  (get_value ^ get_bit_value)
  15.             {
  16.                 crc_value ^= CONFIG_CRC32_POLY;
  17.             }
  18.         }
  19.     }

  20.     crc_value &= 0xFFFFFFFF;

  21.     return (crc_value ^= CONFIG_CRC32_OUT_XOR);
  22. }
复制代码

3.2.3 上电加载参数,检查数据是否出错,出错则使用备份数据
  1. void g_check_data(void)
  2. {
  3.     stm32_flash_read(CONFIG_ADDRESS_TEST_DATA,&test_data_shadow,sizeof(test_data_t));
  4.     stm32_flash_read(CONFIG_ADDRESS_TEST_DATA+sizeof(test_data_t),&test_data_bak,sizeof(test_data_t));
  5.     uint32_t crc_value_cal = crc32_stm32_hardware(&test_data_shadow,sizeof(test_data_t)-4);

  6.     rt_kprintf("crc_value_cal[%x], crc_old[%x]\r\n",crc_value_cal,test_data_shadow.crc32);
  7.   
  8.     if  (crc_value_cal != test_data_shadow.crc32)
  9.     {
  10.         rt_kprintf("test data is invalid\r\n");
  11.       
  12.         rt_memcpy(&test_data_shadow,&test_data_bak,sizeof(test_data_t)-4);
  13.       
  14.         uint32_t crc_value_bak = crc32_stm32_hardware(&test_data_bak,sizeof(test_data_t)-4);
  15.         
  16.         test_data_shadow.crc32 = crc_value_bak;
  17.       
  18.         rt_memcpy(&test_data_shadow,&test_data_bak,sizeof(test_data_t)-4);
  19.     }
  20.      
  21.     rt_memcpy(&test_data,&test_data_shadow,sizeof(test_data_t));
  22. }
复制代码

3.2.4 完整的测试代码
  1. int main(void)
  2. {
  3.     uint32_t get_crc_first = crc32_stm32_hardware(&test_data_shadow,sizeof(test_data_t)-4);
  4.    
  5.     test_data_shadow.crc32 = get_crc_first;
  6.     test_data.crc32 = get_crc_first;
  7.   
  8.     g_check_data();
  9.   
  10.     while (1)
  11.     {
  12.          if (0 != rt_memcmp(&test_data,&test_data_shadow,sizeof(test_data_t)))
  13.          {
  14.              uint32_t get_crc = crc32_stm32_hardware(&test_data_shadow,sizeof(test_data_t)-4);
  15.            
  16.              test_data_shadow.crc32 = get_crc;
  17.            
  18.              rt_base_t level;
  19.    
  20.              level = rt_hw_interrupt_disable();
  21.            
  22.              stm32_flash_erase(CONFIG_ADDRESS_TEST_DATA,sizeof(test_data_t)*2);
  23.            
  24.              stm32_flash_write(CONFIG_ADDRESS_TEST_DATA,&test_data_shadow,sizeof(test_data_t));
  25.             
  26.              stm32_flash_write(CONFIG_ADDRESS_TEST_DATA+sizeof(test_data_t),&test_data_shadow,sizeof(test_data_t));
  27.             
  28.              rt_hw_interrupt_enable(level);
  29.            
  30.              rt_memcpy(&test_data,&test_data_shadow,sizeof(test_data_t));
  31.          }
  32.          
  33.          rt_thread_mdelay(1000);
  34.     }
  35. }

  36. int cmd_flash_protect_test(int argc, char **argv)
  37. {
  38.     if (2 == argc)
  39.     {
  40.         uint32_t get_type = atoi(argv[1]);

  41.         if (0 == get_type)
  42.         {
  43.             g_check_data();
  44.         }
  45.         else if (1 == get_type)
  46.         {
  47.             test_data_shadow.times_clean++;
  48.         }
  49.     }
  50.    
  51.     return 0;
  52. }
  53. MSH_CMD_EXPORT_ALIAS(cmd_flash_protect_test,flash_protect,flash_protect [val]);
复制代码


4 测试效果
  1. \ | /
  2. - RT -     Thread Operating System
  3. / | \     4.1.1 build Jul  1 2023 21:37:26
  4. 2006 - 2022 Copyright by RT-Thread team
  5. crc_value_cal[95663ff9], crc_old[95663ff9]
  6. msh />flash_protect 1
  7. msh />need write flash crc[ba0600aa]
  8. old_data: times_clean:[6] times_error:[0] name[test] crc:[95663ff9] old_data end
  9. new_data: times_clean:[7] times_error:[0] name[test] crc:[ba0600aa] new_data end
  10. msh />flash_protect 0
  11. crc_value_cal[ba0600aa], crc_old[ba0600aa]
复制代码

5 总结

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

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|深圳市光明谷科技有限公司|光明谷商城|Sunshine Silicon Corpporation ( 粤ICP备14060730号|Sitemap

GMT+8, 2024-11-24 12:38 , Processed in 0.150659 second(s), 42 queries .

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表