谷动谷力
标题: 单片机写flash意外断电处理 [打印本页]
作者: sunsili    时间: 2023-8-24 22:12
标题: 单片机写flash意外断电处理
 本帖最后由 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 测试数据
假设需要存储的数据是这样的:- 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 >> (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]\r\n",crc_value_cal,test_data_shadow.crc32);
 
-   
 
-     if  (crc_value_cal != test_data_shadow.crc32)
 
-     {
 
-         rt_kprintf("test data is invalid\r\n");
 
-       
 
-         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,再按照这个清单去自测,这样就能在自测试发现并及时修正错误,反复测试多次后,我们再把软件提交给测试可能会更好一些,工作中遇到困难是让我们进步的,是提醒我们该优化自己的工作方法了。
| 欢迎光临 谷动谷力 (http://bbs.sunsili.com/) | Powered by Discuz! X3.2 |