【flash系列】带校验、备份的简易存储方案 您所在的位置:网站首页 flash芯片数据读取错误怎么办 【flash系列】带校验、备份的简易存储方案

【flash系列】带校验、备份的简易存储方案

2024-06-17 02:00| 来源: 网络整理| 查看: 265

背景

        在实际项目开发过程中,常常会涉及到一些参数的存储,这些参数占用存储空间不大,但需要具备可读可写的能力,而且这些参数往往扮演着重要的角色。例如,一个温度控制器系统,如果需要存储默认开机恒温参数,假设没有其它安全逻辑进行判断,那么这个恒温参数是万万不能出差错的。如果默认设置为40°C,而由于写入错误或flash块异常,读出参数值为400℃,这个错误是致命的!那么在实际项目开发时,是有必要做一些措施来降低风险。目前针对开源的存储方案如 armink 的FlashDB、easyflash功能可靠,可移植性强,但是针对不同的项目或许并不想占用那么多资源(尽管是一个轻量级嵌入式产品的数据存储方案),也能够对实现的代码知根知底(类似FlashDB开源项目读懂代码需花费一定时间,快速上手出现问题不好定位),那么希望在小型项目,资源较少的MCU中实现存储方案是可靠、简单的。如有兴趣可往下继续阅读探讨。

实现方法

        针对存储方案第一个要求:可靠。这里的理解是数据的可靠性,即我读出的数据是我写入的数据,针对其它存储可靠性本文无法实现。为了保证读出即写入,那么首先想到的是做校验,使用校验,存储的数据中带一个校验,在读出数据时将这个校验与读出的有效数据校验进行对比,以此确定数据的有效性。那么每个参数前面都带了一个校验吗?通常我的做法是直接将要存储的数据放置在一个结构中,然后在结构体的头部定义一个校验,这个校验是针对多个参数,读出时也是多个参数的一个校验比对。现在增加校验可以让数据可靠性得到提高,但是还想到另外一个问题,如果这个扇区出现异常了该怎么办?在加入校验的基础上我们加入备份区,通过1~2个的备份区,当一个扇区出现异常时我们去获取备份区的数据,即使扇区失效我们也能得救,增强鲁棒性。

        那么针对第二个要求:简单。看到实现的代码仅两百多行,可读性是非常强的,在stm32和GD32上已实现功能,其它MCU应该类似。

代码实现(stm32)

bsp_flash.c文件

#include "bsp_flash.h" flash_appinfo_t flash_app_info; /** * @description: crc32_chksum_get, CRC32 get chksum * @param const uint32_t* buff: Expected check data * const uint16_t length: Expected check data len * uint32_t* result: check result * @return success: 0 | fail: 1 */ static int crc32_chksum_get(const uint32_t *buff, const uint16_t length, uint32_t *result) { uint32_t chksum = 0xFFFFFFFF; if (buff == NULL || result == NULL) { return -1; } for (int i = 0; i < length; i++) { chksum += buff[i]; } *result = chksum; return 0; } /** * @description: read flash content by word * @param : uint16_t read_addr: addr(first addr: ) [input] * uint8_t *buffer: memery cache [output] * uint16_t length: len (unit: BYTE) [input] * @return: success: 0 | fail: 1 */ static int flash_read_word(const uint32_t read_addr, uint32_t *buffer, const uint16_t length) { if (buffer == NULL || ((read_addr % 4) != 0)) { return -1; } if ((read_addr) < FLASH_STORE_BASE_ADDR || ((read_addr + length * 4) >= (FLASH_STORE_BASE_ADDR + STM_SECTOR_SIZE * STM32_FLASH_SIZE))) { return -1; } HAL_FLASH_Unlock(); __disable_irq(); for (int i = 0; i < length; i++) { buffer[i] = *(__IO uint32_t *)(read_addr); read_addr += 4; } __enable_irq(); HAL_FLASH_Lock(); return 0; } /** * @description: write flash content by word * @param : uint16_t write_addr: addr(first addr: ) [input] * uint8_t *buffer: Temporary storage cache [output] * uint16_t length: len (unit: BYTE) [input] * @return: success: 0 | fail: 1 */ static int flash_write_word(const uint32_t write_addr, const uint32_t *buffer, const uint16_t length) { uint16_t i = 0; uint32_t *tempBuff = NULL; HAL_StatusTypeDef hal_status = HAL_ERROR; if (buffer == NULL || ((write_addr % 4) != 0)) { return -1; } if ((write_addr) < FLASH_STORE_BASE_ADDR || ((write_addr + length * 4) >= (FLASH_STORE_BASE_ADDR + STM_SECTOR_SIZE * STM32_FLASH_SIZE))) { return -1; } tempBuff = (uint32_t *)buffer; __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_WRPERR); //clear flash->SR WPRERR flag HAL_FLASH_Unlock(); __disable_irq(); for (i = 0; i < length; i++) { hal_status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, write_addr + 4 * i, *(tempBuff + i)); if (hal_status != HAL_OK) { __enable_irq(); return -1; } } __enable_irq(); HAL_FLASH_Lock(); return 0; } /** * @description: flash_Write_bytes_check * @param : * @return: success: 0 | fail: 1 */ static int flashex_write_check(const flash_appinfo_t *flash_info) { uint8_t head_len = 1; uint16_t wr_len = sizeof(flash_appinfo_t) / 4 - head_len; flash_appinfo_t fw_info, fr_info; uint32_t *fw_pinfo = (uint32_t *)flash_info, *fr_pinfo = (uint32_t *)(&fr_info); // pflash_info: if (flash_info == NULL) { return -1; } if (flash_info->addr < FLASH_STORE_BASE_ADDR || (flash_info->addr >= (FLASH_STORE_BASE_ADDR + STM_SECTOR_SIZE * STM32_FLASH_SIZE))) { return -1; } // 获取校验和, 并保存在结构体中 if (crc32_chksum_get(&fw_pinfo[head_len + 1], wr_len - 1, &flash_info->crc32) != 0) { return -1; } // 开始写入... for (int i = 0; i < BACKUP_BLOCK_NUMS; ++i) { // 擦除块扇区 FLASH_EraseInitTypeDef EraseInitStruct; uint32_t SECTORError = 0; EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.PageAddress = (flash_info->addr - (flash_info->addr % STM_SECTOR_SIZE)) + i * STM_SECTOR_SIZE; EraseInitStruct.NbPages = 1; HAL_FLASH_Unlock(); //解锁 HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError); HAL_FLASH_Lock(); // 写入内容: 校验和 + 数据 if (flash_write_word(flash_info->addr + i * STM_SECTOR_SIZE, &fw_pinfo[head_len], wr_len) != 0) { return -1; } // 读取地址 数据并进行校验,读取内容: 校验和 + 数据 if (flash_read_word(flash_info->addr + i * STM_SECTOR_SIZE, &fr_pinfo[head_len], wr_len) != 0) { return -1; } // 校验 if (crc32_chksum_get(&fr_pinfo[head_len + 1], wr_len - 1, &fr_info.crc32) != 0) //计算读出的校验和 { return -1; } if (fr_info.crc32 == flash_info->crc32) //与原有校验和比较 { for (int j = 0; j < wr_len; ++j) //与原有数据进行对比 { if (fr_pinfo[j + head_len] != fw_pinfo[j + head_len]) { return -1; } } } } return 0; } /** * @description: flash_Write_bytes_check * @param : * @return: success: 0 | fail: 1 */ static int flashex_read_check(flash_appinfo_t *flash_info) { uint8_t head_len = 1; uint16_t wr_len = sizeof(flash_appinfo_t) / 4 - head_len; flash_appinfo_t fr_info = {0}; uint32_t *fr_pinfo = (uint32_t *)(&fr_info); // pflash_info: if (flash_info == NULL) { return -1; } if (flash_info->addr < FLASH_STORE_BASE_ADDR || (flash_info->addr >= (FLASH_STORE_BASE_ADDR + STM_SECTOR_SIZE * STM32_FLASH_SIZE))) { return -1; } // 读取 flash 数据 for (int i = 0; i < BACKUP_BLOCK_NUMS; ++i) { // 读取数据: 校验和 + 数据 if (flash_read_word(flash_info->addr + i * STM_SECTOR_SIZE, &fr_pinfo[head_len], wr_len) != 0) { continue; } //校验数据 uint32_t chksum = 0; if (crc32_chksum_get(&fr_pinfo[head_len + 1], wr_len - 1, &chksum) != 0) { continue; } //读取出来的校验和 与 外部计算校验数据做比较 if (chksum == fr_info.crc32) { for(int j = 0; j < wr_len; ++j) { ((uint32_t *)flash_info)[head_len + j] = fr_pinfo[head_len + j]; } return 0; } } return -1; } /** * @description: flash_app_store_init, init app store area data but not write flash * @param flash_appinfo_t *flash_info * @return 0 | -1 */ int32_t flash_app_store_init(flash_appinfo_t *flash_info) { flash_info->addr = FLASH_STORE_BASE_ADDR; /* 参数初始化 */ // 默认值初始化...放置代码 //如 flash_info->count = 999; return 0; } /** * @description: flash_app_store_read, read app store area data * @param flash_appinfo_t *flash_info * @return 0 | -1 */ int32_t flash_app_store_read(flash_appinfo_t *flash_info) { return flashex_read_check(flash_info); } /** * @description: flash_app_store_write ,write app store area data * @param flash_appinfo_t *flash_info * @return 0 | -1 */ int32_t flash_app_store_write(flash_appinfo_t *flash_info) { return flashex_write_check(flash_info); }

bap_flash.h文件

#ifndef __BSP_FLASH_H #define __BSP_FLASH_H #include "main.h" /* 确定自己的芯片,仅供参考 */ #define STM32_FLASH_SIZE (256) #define STM_SECTOR_SIZE (2048) /* 备份区数量和存储起始地址,按扇区为单位 */ #define BACKUP_BLOCK_NUMS (3) #define FLASH_STORE_BASE_ADDR (0x0807E800) #pragma pack(4) typedef struct { uint32_t addr; uint32_t crc32; /* ... 定义自己的参数 */ }flash_appinfo_t; #pragma pack() extern flash_appinfo_t flash_app_info; int32_t flash_app_store_init(flash_appinfo_t *flash_info); int32_t flash_app_store_read(flash_appinfo_t *flash_info); int32_t flash_app_store_write(flash_appinfo_t *flash_info); #endif

app.c文件

void board_init(void) { flash_app_info.addr = FLASH_STORE_BASE_ADDR ; if(flash_app_store_read(&flash_app_info) != 0) { PRINTD(" init flash info. \r\n"); flash_app_store_init(&flash_app_info); flash_app_store_write(&flash_app_info); } /* 读写测试,在应用时使用这两个函数即可*/ flash_app_store_write(&flash_app_info); flash_app_store_read(&flash_app_info); } 注意  代码对可移植性还是存在限制,但是看懂代码后用到自己的MCU上,是可以很快实现的,上文实现是stm32的code,如需要GD32的code可私聊在使用上有部分局限性:扇区大小需一致代码虽已在工程中使用,但使用时不能保证绝对可靠,需自行测试本文使用的flash为MCU的内部flash

版权声明:本文为CSDN博主「Hi,Mr.Wang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:【flash系列】带校验、备份的简易存储方案_flash校验和_Hi,Mr.Wang的博客-CSDN博客

分享不易,点个赞再走吧☺☺☺



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有