经过前期将SFUD+FlashDB移植过来,然后验证是否OK,需要编写点demo或应用程序
目录
1. 设计DB表格格式
一般来说用DB,主要key-value比较多,因此你要设计好需要多少个,类型怎么样,初始值是多少。数据库格式要先设计好,换成代码如下:
static uint16_t s_HexFileLen = 0; /* Hex文件大小,默认0,表示无文件 */
static uint16_t s_BinFileLen = 0; /* Bin文件大小,默认0,表示无文件 */
/* default KV nodes */ /* 这张表要配置好,根据实际情况输入 */
static struct fdb_default_kv_node default_kv_table[] =
{
{ "soft_ver", "V1.0.02 b06", 0 },
{ "hex_file_len", &s_HexFileLen, sizeof(s_HexFileLen) },
{ "bin_file_len", &s_BinFileLen, sizeof(s_BinFileLen) }
};
/* 字符串+数值型 */
这些都是应用层代码,建议单独一个模块
2. DB初始化代码
要做内容:选择一个 锁(Mutex),开启初始化功能
/* KVDB object */
static struct fdb_kvdb s_Kvdb = { 0 };
/* TSDB object */
static SemaphoreHandle_t s_MutexFDB_kvdb;
/* 初始化标记 */
static uint8_t s_Inited = 0;
//
// 注意看注释掉的代码,本demo使用Freertos里机制。也可以用HAL的mutex,也可以irq
static void lock(fdb_db_t db)
{
xSemaphoreTake(s_MutexFDB_kvdb, portMAX_DELAY);
//osMutexWait(FDBMutex_idHandle, osWaitForever);
//__disable_irq();
}
static void unlock(fdb_db_t db)
{
xSemaphoreGive(s_MutexFDB_kvdb);
//osMutexRelease(FDBMutex_idHandle);
//__enable_irq();
}
void DBInit(void)
{
if(s_Inited)
{
return;
}
/* KVDB Sample */
struct fdb_default_kv default_kv;
fdb_err_t result;
//这个必须保证创建好,否则会卡死的
s_MutexFDB_kvdb = xSemaphoreCreateMutex();
//mutex_id = osMutexCreate();
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(&s_Kvdb, FDB_KVDB_CTRL_SET_LOCK, (void *)lock);
fdb_kvdb_control(&s_Kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void *)unlock);
s_Kvdb.ver_num = 1; //这个会第一次将默认值写入,否则表中若没有的话,读取的时候会失败
///"fdb_kvdb1" 这个不能随意修改,要跟 fal_cfg.h 里的一致
result = fdb_kvdb_init(&s_Kvdb, "env", "fdb_kvdb1", &default_kv, NULL);
if (result != FDB_NO_ERR) {
printf("KVDB init failed. resoult = %d\n",result);
return;
}
s_Inited = 1;
}
注意点,看代码注释,已经很保姆了。
如果不出意外的话,在main中,rtos init之前,调用DBInit,会初始化成功!!
3. FlashDB 读写代码
往数据库里写入数据
// 写入字符串key,eg: DBSetValueBuf("soft_ver","v12.22233.DBG");
void DBSetValueBuf(const char *key,char *buf)
{
if (!s_Inited)
{
DBInit();
}
fdb_kv_set(&s_Kvdb, key, buf);
}
// 写入U16 key,eg: DBSetValueU16("hex_file_len",23232);
void DBSetValueU16(const char *key,uint16_t value)
{
if (!s_Inited)
{
DBInit();
}
struct fdb_blob blob;
fdb_kv_set_blob(&s_Kvdb, key, fdb_blob_make(&blob, &value, sizeof(value)));
}
往数据库里读取数据
// eg:
// char version[20] = {0};
// DBGetValueBuf("soft_ver",version,16);
//
bool DBGetValueBuf(const char *key,char *buf, uint8_t bufSize)
{
if (!s_Inited)
{
DBInit();
}
char *returnValue;
returnValue = fdb_kv_get(&s_Kvdb, key);
if (returnValue == NULL)
{
printf("[%s] get failed\n",key);
return false;
}
strncpy(buf, returnValue, bufSize);
return true;
}
4. SFUD 读写代码
sfud读写通过sufd.h 提供接口进行访问,首先,得调用sfud_init
然后确认好flash起始地址、以及待处理长度
读的话比较简单
sfud_flash *sfud_dev = NULL;
sfud_err ret;
uint16_t readLen = bufSize;
sfud_dev = sfud_get_device(SFUD_W25Q128_DEVICE_INDEX);
if(sfud_dev == NULL)
{
/* device error */
errorf("SFUD: get device failed.");
return 0;
}
/// startReadAdr 开始地址,readLen 要读取的长度,buf是读取后存放地方
ret = sfud_read(sfud_dev, startReadAdr, readLen, buf);
写也简单
sfud_flash *sfud_dev = NULL;
sfud_err ret;
uint16_t writeLen = bufSize;
sfud_dev = sfud_get_device(SFUD_W25Q128_DEVICE_INDEX);
if(sfud_dev == NULL)
{
/* device error */
errorf("LoaderWriteData error. get device failed.");
return 0;
}
// startWriteAdr 开始写起始地址;writeLen 待写入长度,buf 待写入的内容
ret = sfud_write(sfud_dev, startWriteAdr, writeLen, buf);
sfud_write 是不带擦除的,如果没有擦除过,会写失败的;
一种办法:
针对批量写入:先擦除待写入的所有区域
sfud_erase(sfud_dev,startWriteAdr,len);
另外一种办法:
调用 sfud_erase_write 接口,这个效率低,但针对“打补丁”比较适合
注意:nor flash擦除次数限制,避免同一个地方经常擦除!!
5. 其他注意事项
地址规划
一般来说flash DB会规划一块区域,其他区域不应该有交叉,否则很乱。实在不行提高flash容量更划算点
Task的stack_size
这个task是指Freertos task的堆栈空间,通过CubeMX配置task默认比较小,稍微多一些逻辑就会超过,问题的关键,不小心超过,有时候未必发现得了,(往往经过多轮run之后会卡死)
默认的是128 words,是很小的。特别是一些针对db业务代码;
如果剩余不足的话,有时候很难发现异常。
仿真时的JTAG时钟配置
如下图,这个地方,默认是10MHz,但实际很不稳定,老连接失败之类,改成2MHz,稳定性加强了很多,这个据说是连接线的质量问题(或供电问题),很奇怪!