IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 【flash系列】带校验、备份的简易存储方案 -> 正文阅读

[嵌入式]【flash系列】带校验、备份的简易存储方案

背景

????????在实际项目开发过程中,常常会涉及到一些参数的存储,这些参数占用存储空间不大,但需要具备可读可写的能力,而且这些参数旺旺扮演着重要的角色。例如,一个温度控制器系统,如果需要存储默认开机恒温参数,假设没有其它安全逻辑进行判断,那么这个恒温参数是万万不能出差错的。如果默认设置为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: flash_read_word
 * @param :   uint16_t read_addr: EEPROM 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_read_word(uint32_t read_addr, uint32_t *buffer, 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: flash_write_word
 * @param :   uint16_t write_addr: EEPROM 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(uint32_t write_addr, const uint32_t *buffer, 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(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 i = 0; i < wr_len; i++) //与原有数据进行对比
            {
                if (fr_pinfo[i + head_len] != fw_pinfo[i + 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 i = 0; i < wr_len; i++)
			{
				((uint32_t *)flash_info)[head_len + i] = fr_pinfo[head_len + i];
			}
            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);
}

注意?

  1. 代码对可移植性还是存在限制,但是看懂代码后用到自己的MCU上,是可以很快实现的,上文实现是stm32的code,如需要GD32的code可私聊
  2. 在使用上有部分局限性:扇区大小需一致
  3. 代码虽已在工程中使用,但使用时不能保证绝对可靠,需自行测试
  4. 本文使用的flash为MCU的内部flash


版权声明:本文为CSDN博主「Hi,Mr.Wang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/qq_34672688/article/details/118635537

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


  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-07-11 16:46:50  更:2021-07-11 16:47:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 13:22:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码