手把手教 – STM32 单片机 EasyFlash 软件包 实现参数持久化存储

文章链接:https://mp.weixin.qq.com/s/-6FD1FFLsrVtwK3IVi5WDQ

一、开发环境

开发板:STM32F407ZG
RT-Thread版本:V4.1.0

二、EasyFlash 软件包介绍

EasyFlash是一款开源的轻量级嵌入式Flash存储器库,方便开发者更加轻松的实现基于Flash存储器的常见应用开发。非常适合智能家居、可穿戴、工控、医疗、物联网等需要断电存储功能的产品,资源占用极低,支持各种 MCU 片上存储器。

该库主要包括 三大实用功能 :
ENV 快速保存产品参数,支持 写平衡(磨损平衡) 及 掉电保护功能
EasyFlash不仅能够实现对产品的 设定参数 或 运行日志 等信息的掉电保存功能,还封装了简洁的 增加、删除、修改及查询 方法, 降低了开发者对产品参数的处理难度,也保证了产品在后期升级时拥有更好的扩展性。
让Flash变为NoSQL(非关系型数据库)模型的小型键值(Key-Value)存储数据库

IAP 在线升级再也不是难事儿
该库封装了IAP(In-Application Programming)功能常用的接口,支持CRC32校验,同时支持Bootloader及Application的升级。

Log 无需文件系统,日志可直接存储在Flash上
非常适合应用在小型的不带文件系统的产品中,方便开发人员快速定位、查找系统发生崩溃或死机的原因。同时配合EasyLogger(我开源的超轻量级、高性能C日志库,它提供与EasyFlash的无缝接口)一起使用,轻松实现C日志的Flash存储功能。

三、移植

基于 RT-Thread 的移植参考。主要基于以下两种底层 Flash 驱动

fal : Flash 抽象层SFUD : 万能 SPI Flash 驱动库
如果 Flash 驱动使用的是上面中的一种,那么移植 EasyFlash 将会非常简单。如果没有使用上面的驱动,请参考 EasyFlash 的 移植文档 进行移植。在 EasyFlash 官方仓库 下有很多 demo,也可以参考。

本文章基于基于 fal : Flash 抽象层进行移植,使用片上flash。

四、配置

4.1 fal 配置和测试

开启 fal
手把手教 - STM32 单片机 EasyFlash 软件包 实现参数持久化存储
手把手教 - STM32 单片机 EasyFlash 软件包 实现参数持久化存储
开启 片上flash
手把手教 - STM32 单片机 EasyFlash 软件包 实现参数持久化存储
fal_cfg.h 文件修改

fal_cfg.h 文件

oardports
目录下。


/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-12-5      SummerGift   first version
 */

#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_

#include <rtthread.h>
#include <board.h>

#ifdef BSP_USING_SPI_FLASH_LITTLEFS
extern struct fal_flash_dev w25q128;
#else
#define FLASH_SIZE_GRANULARITY_16K   (4 * 16 * 1024)
#define FLASH_SIZE_GRANULARITY_64K   (64 * 1024)
#define FLASH_SIZE_GRANULARITY_128K  (7 * 128 * 1024)
#define STM32_FLASH_START_ADRESS_16K  STM32_FLASH_START_ADRESS
#define STM32_FLASH_START_ADRESS_64K  (STM32_FLASH_START_ADRESS_16K + FLASH_SIZE_GRANULARITY_16K)
#define STM32_FLASH_START_ADRESS_128K (STM32_FLASH_START_ADRESS_64K + FLASH_SIZE_GRANULARITY_64K)

extern const struct fal_flash_dev stm32_onchip_flash_16k;
extern const struct fal_flash_dev stm32_onchip_flash_64k;
extern const struct fal_flash_dev stm32_onchip_flash_128k;
#endif


/* flash device table */
#ifdef BSP_USING_SPI_FLASH_LITTLEFS
#define FAL_FLASH_DEV_TABLE                                          
{                                                                    
    &w25q128,                                                     
}
#else
#define FAL_FLASH_DEV_TABLE                                          
{                                                                    
    &stm32_onchip_flash_16k,                                         
    &stm32_onchip_flash_64k,                                         
    &stm32_onchip_flash_128k,                                        
}
#endif

/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG

/* partition table */
#ifdef BSP_USING_SPI_FLASH_LITTLEFS
#define FAL_PART_TABLE                                                                                                     
{                                                                                                                          
    {FAL_PART_MAGIC_WROD, "spiflash0", "W25Q128", 0 , 16 * 1024 * 1024, 0}, 
}
#else
#define FAL_PART_TABLE                                                                                                     
{                                                                                                                          
    {FAL_PART_MAGIC_WROD, "easyflash",        "onchip_flash_128k", 1* 128 * 1024 , 2* 128 * 1024, 0}, 
}
// 2* 128 * 1024 : ENV area size. It's at least one empty sector for GC. So it's definition must more then or equal 2 flash sector size. 
#endif
#endif /* FAL_PART_HAS_TABLE_CFG */
#endif /* _FAL_CFG_H_ */

4.2 easyflash 配置 版本 V4.1.0

开启 easyflash
手把手教 - STM32 单片机 EasyFlash 软件包 实现参数持久化存储

ENV: Environment variables: 是否使能环境变量功能
1.1 Auto update ENV to latest default when current ENV version number is changed.:是否启用环境变量自动更新功能。启动这个功能后,环境变量将在其版本号发生变化时自动更新。
1.2 Setting current ENV version number : 当前环境变量版本号LOG: Save logs on flash:日志功能,可以将日志顺序保存至 Flash 中。还可以配合 EasyLogger 完成产品日志的掉电存储。IAP: In Application Programming:IAP 在线升级功能,开启后将提供一些 IAP 功能里常用的 API 。Erase minimum granularity:擦除的最小粒度,一般 SPI Flash 通常为 4KB,STM32F4 片内 Flash 通常为 128KB。Write minimum granularity:写数据的最小粒度,一般 SPI Flash 通常为 1bit,STM32F4 片内 Flash 通常为 8bit,详见具体选项。Start addr on flash or partition:EasyFlash 的整个存储区相对于 Flash 或者 分区 的偏移地址,视移植代码而定。Enable debug log output:是否使能调试日志输出。开启后将会看到更多调试日志信息。


ef_fal_port.c 文件修改如下:


/* EasyFlash partition name on FAL partition table */
#define FAL_EF_PART_NAME               "easyflash"

五、代码和测试

测试代码


#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <stdlib.h>
#include <fal.h>
#include <easyflash.h>

/* defined the LED0 pin: PF9 */
#define LED0_PIN    GET_PIN(C, 3)

/**
 * EasyFlash核心测试函数
 */
static void easyflash_test(void)
{
    EfErrCode err;
    char str_value[127]={0};
    int int_val = 0;
    float float_val = 0.0f;

    // 1. 初始化EasyFlash
    err = easyflash_init();
    if (err != EF_NO_ERR)
    {
        rt_kprintf("EasyFlash int err,code is:%d
", err);
        return;
    }
    rt_kprintf("EasyFlash int ok!
");
    
    rt_kprintf("
******************************
");
    rt_kprintf("ef_print_env 1
");
    ef_print_env();
    rt_kprintf("
******************************
");
    // 2. 测试字符串类型KV
    const char *kv_str_key = "user_name";
    const char *kv_str_def = "rt-thread";
    char *kv_str_value = RT_NULL;
    if ( (kv_str_value=ef_get_env(kv_str_key)) == NULL)
    {
        rt_kprintf("KV[%s],write: %s
", kv_str_key, kv_str_def);
        ef_set_env_blob(kv_str_key, kv_str_def,rt_strlen(kv_str_def));
    }
    else
    {
        rt_kprintf("read KV[%s]: %s
", kv_str_key, kv_str_value);
    }

    // 3. 测试整数类型KV
    const char *kv_int_key = "device_id";
    int kv_int_def = 123456;
    
    rt_memset(str_value,0,sizeof(str_value));
    if( ef_get_env_blob(kv_int_key, str_value, sizeof(str_value) , NULL) == 0)
    {
        rt_kprintf("KV[%s],write: %d
", kv_int_key, kv_int_def);
        rt_sprintf(str_value,"%d",kv_int_def);
        ef_set_env_blob(kv_int_key, str_value, rt_strlen(str_value) );
    }
    else
    {
        rt_kprintf("read KV[%s]:%d
", kv_int_key, int_val=atoi(str_value));
        // 修改并重新写入
        int_val += 1;
        rt_kprintf("wirte KV[%s]: %d
", kv_int_key, int_val);
        
        rt_memset(str_value,0,sizeof(str_value));
        rt_sprintf(str_value,"%d",int_val);
        ef_set_env_blob(kv_int_key, str_value, rt_strlen(str_value) );
    }

    // 4. 测试浮点类型KV
    const char *kv_float_key = "temperature";
    float kv_float_def = 3.1f;
    
    rt_memset(str_value,0,sizeof(str_value));
    if (  ef_get_env_blob(kv_float_key, str_value, sizeof(str_value) , NULL) == 0)
    {
        rt_kprintf("KV[%s],write: 3.14
", kv_float_key);
        
        rt_sprintf(str_value,"%d.%d",((int)(kv_float_def*100))/100,((int)(kv_float_def*100))%100);
        ef_set_env_blob(kv_float_key, str_value, rt_strlen(str_value) );
    }
    else
    {
        float_val=atof(str_value);
        rt_kprintf("read KV[%s]: %d.%d
", kv_float_key,((int)(float_val*100))/100,((int)(float_val*100))%100);
        // 修改并重新写入
        float_val += 0.1f;
        rt_kprintf("write KV[%s]: %d.%d
", kv_float_key,((int)(float_val*100))/100,((int)(float_val*100))%100);
        
        rt_memset(str_value,0,sizeof(str_value));
        rt_sprintf(str_value,"%d.%d",((int)(float_val*100))/100,((int)(float_val*100))%100);
        ef_set_env_blob(kv_float_key, str_value, rt_strlen(str_value) );
    }
    rt_kprintf("
******************************
");
    rt_kprintf("ef_print_env 2
");
    ef_print_env();
    rt_kprintf("
******************************
");
    
//    // 5. 测试删除KV(可选,注释掉,需测试时打开)
//    err = ef_del_env(kv_str_key);
//    if (err == EF_NO_ERR) 
//        rt_kprintf("del KV[%s] ok
", kv_str_key);
//    
//    rt_kprintf("
******************************
");
//    rt_kprintf("ef_print_env 3
");
//    ef_print_env();
//    rt_kprintf("
******************************
");
    
    rt_kprintf("EasyFlash test ok
");
}

int main(void)
{
    /* set LED0 pin mode to output */
    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
    
    fal_init();
    easyflash_test();

    while (1)
    {
        rt_pin_write(LED0_PIN, PIN_HIGH);
        rt_thread_mdelay(500);
        rt_pin_write(LED0_PIN, PIN_LOW);
        rt_thread_mdelay(500);
    }
}

手把手教 - STM32 单片机 EasyFlash 软件包 实现参数持久化存储
重启设备
手把手教 - STM32 单片机 EasyFlash 软件包 实现参数持久化存储

参考

https://gitee.com/RT-Thread-Mirror/EasyFlash
https://gitee.com/RT-Thread-Mirror/EasyFlash/blob/master/docs/zh/api.md

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...