FlashDB介绍

基本概念

  • 键值数据库(KVDB):是一种非关系数据库,它将数据存储为键值(Key-Value)对集合,其中键作为唯一标识符。KVDB 操作简洁,可扩展性强。
  • 时序数据(TSDB) :时间序列数据库 (Time Series Database , 简称 TSDB),它将数据按照 时间顺序存储 。TSDB 数据具有时间戳,数据存储量大,插入及查询性能高。
  • 时序记录(TSL) :TSL (Time series log),是 TSDB 中每条记录的简称。
  • Blob :在计算机中,blob 常常是数据库中用来存储二进制文件的字段类型。在 FlashDB 中, KV 和 TSL 都使用 blob 类型来存储,该类型可以兼容任意变量类型。
  • 迭代器(iterator):它可以让用户透过特定的接口巡访容器中的每一个元素,而不用了解底层的实现。 TSDB 和 KVDB 都支持通过迭代器对数据库进行遍历访问。

 

 FlashDB官网:FlashDB官网

 首先从GitHub上面拉取FlashDB源码:Releases · armink/FlashDB · GitHub

 其中src文件夹是FlashDB的源码、inc是FlashDB的头文件。

将src和inc的文件分别拷贝到自己工程中

注意需要将拷贝过来的文件链接到自己的工程中,我这里是通过Makefile来链接的

FlashDB 底层的 Flash 管理及操作依赖于 RT-Thread 的 FAL (Flash Abstraction Layer) Flash 抽象层开源软件包。我这里用的是freertos,所以需要先移植FAL到工程中。

移植前建议先了解下 FAL 功能介绍,详见:软件包 - RT-Thread物联网操作系统

 FlashDB源码目录下有fal的文件。将这里的src和inc拷贝到我们自己的工程中

同样也需要将拷贝进来的文件链接到自己的工程中

首先需要定义FLASH设备,提供flash读写等操作

/*
 * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <string.h>
#include <fal.h>
#include "sys/fdcm.h"
#include "sys/image.h"
#include "image/flash.h"


#define FLASH_ERASE_MIN_SIZE    (4 * 1024)

#define LOCKER_ENABLE
#ifdef LOCKER_ENABLE
#include "kernel/os/os.h"


static OS_Semaphore_t s_lock;

#define LOCK()                                  \
    do {                                        \
        OS_SemaphoreWait(&s_lock, OS_WAIT_FOREVER);  \
    } while(0)

#define UNLOCK()                                \
    do {                                        \
        OS_SemaphoreRelease(&s_lock);           \
    } while(0)
#else
#define LOCK()
#define UNLOCK()
#endif

static int init(void)
{
#ifdef LOCKER_ENABLE
	if (0 == OS_SemaphoreIsValid(&s_lock)) {
        if (OS_OK != OS_SemaphoreCreateBinary(&s_lock)) {
            printf("fal semaphore create fail!!!!!!!!!!!!!.\n");
            return -1;
        } else {
			printf("fal semaphore create suc.\n");
			OS_SemaphoreRelease(&s_lock);
		}
    }
#endif
    return 1;
}

static int read(long offset, uint8_t *buf, size_t size)
{
    //Xassert(size % 4 == 0);

    /* You can add your code under here. */
    int32_t ret;
    uint32_t addr = nor_flash0.addr + offset;

    
    LOCK();
	ret = flash_read(0, addr, buf, size);
    UNLOCK();

    return ret;
}

static int write(long offset, const uint8_t *buf, size_t size)
{
    int32_t ret;
    uint32_t addr = nor_flash0.addr + offset;
    
    LOCK();
    ret = flash_write(0, addr, buf, size);
    UNLOCK();
    
    return ret;
}

static int erase(long offset, size_t size)
{
    int32_t ret;
    uint32_t addr = nor_flash0.addr + offset;

    int32_t erase_size = ((size - 1) / FLASH_ERASE_MIN_SIZE) + 1;

    LOCK();
    ret = flash_erase(0, addr, erase_size * FLASH_ERASE_MIN_SIZE);
    UNLOCK();
    
    return ret;
}

//1.定义 flash 设备

struct fal_flash_dev nor_flash0 =
{
    .name       = "norflash0",
    .addr       = 0x0,
    .len        = 4*1024*1024,
    .blk_size   = FLASH_ERASE_MIN_SIZE,
    .ops        = {init, read, write, erase},
    .write_gran = 1
};

然后是定义Flash设备表和分区表

Flash 设备表定义在 fal_cfg.h 头文件中,定义分区表前需 新建 fal_cfg.h 文件 ,请将该文件统一放在对应 BSP 或工程目录的 port 文件夹下,并将该头文件路径加入到工程。

/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-05-17     armink       the first version
 */

#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_

//#include <rtconfig.h>
//#include <board.h>

#define NOR_FLASH_DEV_NAME             "norflash0"
#define FAL_PART_HAS_TABLE_CFG

/* ===================== Flash device Configuration ========================= */
//extern const struct fal_flash_dev stm32f2_onchip_flash;
extern struct fal_flash_dev nor_flash0;

/* flash device table */
#define FAL_FLASH_DEV_TABLE                                          \
{                                                                    \
    &nor_flash0,                                                     \
}
/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE                                                               \
{                                                                                    \
    {FAL_PART_MAGIC_WORD, "fdb_kvdb1",  NOR_FLASH_DEV_NAME,          0x37C000, 48*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */

#endif /* _FAL_CFG_H_ */

我这里不需要完整使用FAL层来抽象flash。只需要对特定的地址分区使用FlashDB。所以只定义了flash中0x37c000地址开始的48K数据来使用

此时移植工作已经基本完毕。可以写demo来测试

#include <stdio.h>
//#include "freertos/FreeRTOS.h"
//#include "freertos/task.h"
//#include "semphr.h"
//#include "esp_system.h"
//#include "esp_spi_flash.h"

#include <flashdb.h>
//#include "os_util.h"
#include "kernel/os/os.h"


#define FDB_LOG_TAG "[main]"

static uint32_t boot_count = 0;
static time_t boot_time[10] = {0, 1, 2, 3};
/* default KV nodes */
static struct fdb_default_kv_node default_kv_table[] = {
        {"username", "armink", 0}, /* string KV */
        {"password", "123456", 0}, /* string KV */
        {"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */
        {"boot_time", &boot_time, sizeof(boot_time)},    /* int array type KV */
};
#if 0		
static struct fdb_default_kv_node default_file_kv_table[] = {
        {"abc", "123", 0}, /* string KV */
        {"qwe", "456", 0}, /* string KV */
        {"f_boot_cnt", &boot_count, sizeof(boot_count)}, /* int type KV */
        {"f_boot_time", &boot_time, sizeof(boot_time)},    /* int array type KV */
};
#endif		
//static struct fdb_kvdb file_kvdb = {{ 0 }};
/* KVDB object */
static struct fdb_kvdb kvdb = {{ 0 }};

static OS_Mutex_t s_lock;

extern void kvdb_basic_sample(fdb_kvdb_t kvdb);
extern void kvdb_type_string_sample(fdb_kvdb_t kvdb);
extern void kvdb_type_blob_sample(fdb_kvdb_t kvdb);
extern void tsdb_sample(fdb_tsdb_t tsdb);
extern void kvdb_type_string_sample_mytest(fdb_kvdb_t kvdb);
static void lock(fdb_db_t db)
{
    //xSemaphoreTake(s_lock, portMAX_DELAY);
    if (OS_OK != (OS_MutexLock(&s_lock, OS_WAIT_FOREVER))) {
		printf("[%s][%d]OS_MutexLock error.\n", __func__, __LINE__);
	}
}

static void unlock(fdb_db_t db)
{
    //xSemaphoreGive(s_lock);
    if (OS_OK != (OS_MutexUnlock(&s_lock))) {
		printf("[%s][%d]OS_MutexUnlock error.\n", __func__, __LINE__);
	}
}

#ifdef FDB_USING_TSDB
/* TSDB object */
struct fdb_tsdb tsdb = {{ 0 }};

/* counts for simulated timestamp */
static int counts = 0;
static fdb_time_t get_time(void)
{
    /* Using the counts instead of timestamp.
     * Please change this function to return RTC time.
     */
    return ++counts;
}
#endif


int flashdb_demo(void)
{
    fdb_err_t result;

    //if (s_lock == NULL) {
    //    s_lock = xSemaphoreCreateCounting(1, 1);
    //    assert(s_lock != NULL);
    //}
	if (OS_OK != (OS_MutexCreate(&s_lock))) {
		printf("[%s][%d]MutexCreate error.\n", __func__, __LINE__);
		return -1;
	}

#ifdef FDB_USING_KVDB
    { /* KVDB Sample */
        struct fdb_default_kv default_kv;

        default_kv.kvs = default_kv_table;
        default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);
        /* set the lock and unlock function if you want */
        fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, lock);
        fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, unlock);

        /* Key-Value database initialization
         *
         *       &kvdb: database object
         *       "env": database name
         * "fdb_kvdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
         *              Please change to YOUR partition name.
         * &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully.
         *        NULL: The user data if you need, now is empty.
         */
        result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, NULL);

        if (result != FDB_NO_ERR) {
			printf("err!!!!!!!!!!!!!!!!!!\n");
            return -1;
        }
		printf("fdb_kv_set_default-------------------------\n");
		fdb_kv_set_default(&kvdb);
		printf("0000-------------------------\n");
		fdb_kv_print(&kvdb);
		printf("0000-------------------------\n");
        /* run basic KV samples */
        kvdb_basic_sample(&kvdb);
        /* run string KV samples */
        kvdb_type_string_sample(&kvdb);
        /* run blob KV samples */
        kvdb_type_blob_sample(&kvdb);

		printf("-------------------------\n");
		kvdb_type_string_sample_mytest(&kvdb);

//		fdb_kv_print(&kvdb);
    }
#endif /* FDB_USING_KVDB */

#if 1
#ifdef FDB_USING_TSDB
    { /* TSDB Sample */
        /* set the lock and unlock function if you want */
        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, lock);
        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, unlock);
        /* Time series database initialization
         *
         *       &tsdb: database object
         *       "log": database name
         * "fdb_tsdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
         *              Please change to YOUR partition name.
         *    get_time: The get current timestamp function.
         *         128: maximum length of each log
         *        NULL: The user data if you need, now is empty.
         */
        result = fdb_tsdb_init(&tsdb, "log", "fdb_tsdb1", get_time, 128, NULL);
        /* read last saved time for simulated timestamp */
        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_GET_LAST_TIME, &counts);

        if (result != FDB_NO_ERR) {
            return -1;
        }

        /* run TSDB sample */
        tsdb_sample(&tsdb);
    }
#endif /* FDB_USING_TSDB */

#ifdef FDB_USING_FILE_MODE
	{
		//struct fdb_default_kv default_kv;

		//default_kv.kvs = default_file_kv_table;
		//default_kv.num = sizeof(default_file_kv_table) / sizeof(default_file_kv_table[0]);
		/* set the lock and unlock function if you want */
		fdb_kvdb_control(&file_kvdb, FDB_KVDB_CTRL_SET_LOCK, lock);
		fdb_kvdb_control(&file_kvdb, FDB_KVDB_CTRL_SET_UNLOCK, unlock);

		bool file_mode = true;
		//bool not_formatable = true;
		uint32_t sec_size = 4096, db_size = PRJCONF_LITTLE_FS_SIZE-(4*4096);
		/* set the sector and database max size */
		fdb_kvdb_control(&file_kvdb, FDB_KVDB_CTRL_SET_SEC_SIZE, &sec_size);
		fdb_kvdb_control(&file_kvdb, FDB_KVDB_CTRL_SET_MAX_SIZE, &db_size);
		//fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_NOT_FORMAT, &not_formatable);
		/* enable file mode */
		fdb_kvdb_control(&file_kvdb, FDB_KVDB_CTRL_SET_FILE_MODE, &file_mode);

		
		uint32_t lfs_free_spcae_size = 0;
		lfs_free_spcae(&lfs_free_spcae_size);
		printf("lfs free spce = %d	LINE %d\n",lfs_free_spcae_size, __LINE__);

		/*struct lfs_info info;
		if (LFS_ERR_OK != lfs_stat(app_lfs_flash_get_handle(), "fdb_lfs", &info)) {
			if (LFS_ERR_OK != lfs_mkdir(app_lfs_flash_get_handle(), "fdb_lfs")) {
				printf("lfs_mkdir error.\n");
				return -1;
			}
		} else {
			printf("fdb_lfs dir exist.\n");
		}

		
		lfs_free_spcae(&lfs_free_spcae_size);
		printf("lfs free spce = %d	LINE %d\n",lfs_free_spcae_size, __LINE__);*/
		/* Key-Value database initialization
		 *
		 *		 &kvdb: database object
		 *		 "env": database name
		 * "fdb_kvdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
		 *				Please change to YOUR partition name.
		 * &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully.
		 *		  NULL: The user data if you need, now is empty.
		 */
		//result = fdb_kvdb_init(&file_kvdb, "lfs", "fdb_lfs", &default_kv, NULL);
		result = fdb_kvdb_init(&file_kvdb, "lfs", "/", NULL, NULL);

		if (result != FDB_NO_ERR) {
			return -1;
		}

		/* run basic KV samples */
		kvdb_basic_sample(&file_kvdb);
		/* run string KV samples */
		kvdb_type_string_sample(&file_kvdb);
		/* run blob KV samples */
		kvdb_type_blob_sample(&file_kvdb);
	}
#endif
#endif
    return 0;
}

编译烧录到板子上面运行结果:

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐