STM32G473VET6 FlashDB数据库移植(裸机、片内Flash)
此文档也适用于STM32G070
源码下载
此处使用FlashDB官方最新源码
FlashDB: 一款支持 KV 数据和时序数据的超轻量级数据库 (gitee.com)
克隆源码后目录如下
红框中几个为移植必要文件与参考
根据FlashDB官方文档可知,FlashDB底层依赖于RT-Thread的FAL组件,所以需要先移植FAL
FlashDB源码中port目录下即为FAL组件源码
移植参考文档
开始移植
添加FAL组件源码
准备一个HAL库STM32G473工程,配置好LOG打印串口把printf重定向至串口,细节此处略过
添加FAL与FlashDB源码至准备好的工程
如下图所示,目录可自行更改无需与本文档一致,此处仅为演示所用
红框中为FAL源码部分,位于FlashDB源码的port/fal目录下的inc与src文件夹中,此处我把头文件与源文件存放在了一个文件夹中
同时复制一份官方原源码中的移植参考代码重命名后放入此文件夹中,参考代码目录如下图
代码位于port/fal/samples/porting目录下
添加FlashDB源码
添加过程同上,需添加文件如下
以上文件位于FlashDB源码的inc与src目录下。
所有文件添加完成后keil目录如下,不要忘记添加头文件路径,此处详细过程省略。
移植FAL
移植FAL很简单,仅需实现对flash的读写擦除三个函数即可,可根据参考例程修改。
此次移植修改如下
STM32G473VE的Flash为512k,根据数据手册可知,此flash为双bank每个bank有128个页每页2k单次擦除最小为一页所以写入是要注意bank与页的计算
同时需注意G473与G070的flash写最小粒度皆为双字
修改后代码如下(fal_flash_stm32_port.c)
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <fal.h>
#include <stm32g4xx_hal.h>
#define PAGE_SIZE FLASH_PAGE_SIZE
/*
STM32F1会因容量不同而不同
小容量和中容量产品主存储块128KB以下, 每页1KB。
大容量和互联型产品主存储块256KB以上, 每页2KB。
GD32 会因容量不同而不同
1. Low-density Products Flash容量从 16KB到 32KB的产品
2. Medium-density Products Flash容量从 64KB到 128KB的产品
全是1K
3. High-density Products Flash容量从256KB到 512KB的产品
全是2K
4. XL-density Products Flash容量从768KB到3072KB的产品
<512K 是2K
>512K 是4K
雅特力
全是2K
STM32F4
STM32F4的flash页尺寸不一样,低地址16KB,高地址32KB或128KB.
*/
static uint32_t GetPage(uint32_t Addr)
{
uint32_t page = 0;
page = (Addr-FLASH_BASE) / FLASH_PAGE_SIZE;
return page;
}
static int init(void)
{
/* do nothing now */
return 1;
}
static int ef_err_port_cnt = 0;
int on_ic_read_cnt = 0;
int on_ic_write_cnt = 0;
void feed_dog(void)
{
}
static int read(long offset, uint8_t *buf, size_t size)
{
size_t i;
uint32_t addr = stm32_onchip_flash.addr + offset;
if( addr%8 != 0)
ef_err_port_cnt++;
for (i = 0; i < size; i++, addr++, buf++)
{
*buf = *(uint8_t *) addr;
}
on_ic_read_cnt++;
return size;
}
static int write(long offset, const uint8_t *buf, size_t size)
{
size_t i;
uint32_t addr = stm32_onchip_flash.addr + offset;
__ALIGN_BEGIN uint64_t write_data __ALIGN_END;
__ALIGN_BEGIN uint64_t read_data __ALIGN_END;
if(addr%8 != 0)
ef_err_port_cnt++;
/*
if((int)buf%4 != 0)
ef_err_port_cnt++;
*/
HAL_FLASH_Unlock();
for (i = 0; i < size; i += 8, buf += 8, addr += 8) {
memcpy(&write_data, buf, 8); //用以保证HAL_FLASH_Program的第三个参数是内存首地址对齐
HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, write_data);
read_data = *(uint64_t *)addr;
if (read_data != write_data) {
HAL_FLASH_Lock();
return -1;
}
else {
feed_dog();
}
}
HAL_FLASH_Lock();
on_ic_write_cnt++;
return size;
}
static int erase(long offset, size_t size)
{
uint32_t addr = stm32_onchip_flash.addr + offset;
HAL_StatusTypeDef flash_status;
size_t erase_pages, i;
uint32_t PAGEError = 0;
erase_pages = size / PAGE_SIZE;//页数量
if (size % PAGE_SIZE != 0) {
erase_pages++;
}
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.NbPages = 1; //一次擦出一个扇区, 以执行一次喂狗,防止超时
HAL_FLASH_Unlock();
for (i = 0; i < erase_pages; i++) {
if(addr + (FLASH_PAGE_SIZE * i)<=0x0803ffff) {//判断当前页所处bank
EraseInitStruct.Banks = FLASH_BANK_1;
} else {
EraseInitStruct.Banks = FLASH_BANK_2;
}
EraseInitStruct.Page = GetPage(addr + (FLASH_PAGE_SIZE * i));//计算当前所处页
flash_status = HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
if (flash_status != HAL_OK) {
HAL_FLASH_Lock();
return -1;
}
else {
//FLash操作可能非常耗时,如果有看门狗需要喂狗,以下代码由用户实现
feed_dog();
}
}
HAL_FLASH_Lock();
return size;
}
/*
"stm32_onchip" : Flash 设备的名字。
0x08000000: 对 Flash 操作的起始地址。
1024*1024:Flash 的总大小(1MB)。
128*1024:Flash 块/扇区大小(因为 STM32F2 各块大小不均匀,所以擦除粒度为最大块的大小:128K)。
{init, read, write, erase} :Flash 的操作函数。 如果没有 init 初始化过程,第一个操作函数位置可以置空。
8 : 设置写粒度,单位 bit, 0 表示未生效(默认值为 0 ),该成员是 fal 版本大于 0.4.0 的新增成员。各个 flash 写入粒度不尽相同,可通过该成员进行设置,以下列举几种常见 Flash 写粒度:
nor flash: 1 bit
stm32f2/f4: 8 bit
stm32f1: 32 bit
stm32l4: 64 bit
*/
//1.定义 flash 设备
const struct fal_flash_dev stm32_onchip_flash =
{
.name = "stm32_onchip",
.addr = 0x08000000,
.len = 512*1024,
.blk_size = 2*1024,
.ops = {init, read, write, erase},
.write_gran = 64
};
修改配置头文件(fal_cfg.h)
头文件无需做很大修改,配置一下分区表即可,分区表具体设置细节可参考FlashDB官方文档
此处修改完成后如下
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_
#define FAL_DEBUG 1
#define FAL_PART_HAS_TABLE_CFG
/* ===================== Flash device Configuration ========================= */
extern const struct fal_flash_dev stm32_onchip_flash;
/* flash device table */
#define FAL_FLASH_DEV_TABLE \
{ \
&stm32_onchip_flash, \
}
/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WORD, "fdb_kvdb1", "stm32_onchip", 230*1024, 16*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
#endif /* _FAL_CFG_H_ */
若无需打印LOG信息可把宏#define FAL_DEBUG 1进行注释
FlashDB移植
因为FlashDB依赖于FAL,所以移植完成FAL之后FlashDB的移植工作基本就算完成90%了,
下面仅需修改一下FlashDB的配置文件即可完成移植
修改fdb_cfg.h
把宏#define FDB_USING_TSDB进行注释,因为FlashDB的TSDB还不支持双字写入粒度的flash
添加宏#define FDB_USING_FAL_MODE
修改宏#define FDB_WRITE_GRAN为64(双字写入粒度)
修改完成后如下
/*
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief configuration file
*/
#ifndef _FDB_CFG_H_
#define _FDB_CFG_H_
#define FDB_USING_FAL_MODE
/* using KVDB feature */
#define FDB_USING_KVDB
#ifdef FDB_USING_KVDB
/* Auto update KV to latest default when current KVDB version number is changed. @see fdb_kvdb.ver_num */
/* #define FDB_KV_AUTO_UPDATE */
#endif
/* using TSDB (Time series database) feature */
//#define FDB_USING_TSDB
/* the flash write granularity, unit: bit
* only support 1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1) */
#define FDB_WRITE_GRAN 64
/* MCU Endian Configuration, default is Little Endian Order. */
/* #define FDB_BIG_ENDIAN */
/* log print macro. default EF_PRINT macro is printf() */
/* #define FDB_PRINT(...) my_printf(__VA_ARGS__) */
/* print debug information */
#define FDB_DEBUG_ENABLE
#endif /* _FDB_CFG_H_ */
测试
跑一跑FlashDB的demo,demo可从FlashDB源码例程里面复制(懒得自己写了)
void kvdb_basic_sample(fdb_kvdb_t kvdb)
{
struct fdb_blob blob;
uint8_t boot_count = 0;
debug_print("==================== kvdb_basic_sample ====================\n");
{ /* GET the KV value */
/* get the "boot_count" KV value */
fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
/* the blob.saved.len is more than 0 when get the value successful */
if (blob.saved.len > 0) {
debug_print("get the 'boot_count' value is %d\n", boot_count);
} else {
debug_print("get the 'boot_count' failed\n");
}
}
{ /* CHANGE the KV value */
/* increase the boot count */
boot_count += 10;
/* change the "boot_count" KV's value */
fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
debug_print("set the 'boot_count' value to %d\n", boot_count);
}
debug_print("===========================================================\n");
}
可以看到FAL分区信息正常,FlashDB每次复位之后键对应的值都会+10,存储成功,移植完成。