让Flash变为NoSQL(非关系型数据库)模型的小型键值(Key-Value)存储数据库。在产品上,能够更加简捷的实现 设定参数 或掉电保存的功能。
Code (inc. data) | RO Data | RW Data | ZI Data | Debug | Object Name|
|--|--|--|--|--|--|
| 498 | 16 | 0 | 0 | 33 | 3252 | kv_sys.o|
RAM 16 字节 ROM 约500字节
各类单片机、MCU,通用性强
void *kv_get_env(uint8_t key_id);
bool kv_del_env(uint8_t key_id);
bool kv_set_env(uint8_t key_id, void *data, uint8_t len);
// 存入
kv_set_env(BS_KV_KEY_TEMP_INFO, "ABCDAD", strlen("ABCDAD"));
// 读取
uint8_t *p;
uint8_t buff[10];
p = kv_get_env(BS_KV_KEY_TEMP_INFO);
if (p != NULL)
{
memcpy(buff, p, 10);
}
/********************************************************************************
* @file kv_sys.c
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-11-03
* @brief KV键值最小系统
********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "bsp_flash.h"
#include "log.h"
/* Private Includes ----------------------------------------------------------*/
#include "kv_sys.h"
#include "business_function.h"
/* Private Define ------------------------------------------------------------*/
// KV系统总共可以使用N字节
#define KV_SUM_SIZE (BS_FLASH_PAGE_SIZE * (BS_FLASH_KV_PAGE - 1))
// KV系统总共使用键值数量
#define KV_SUM_NUM (KV_SUM_SIZE / BS_FLASH_KV_ONE_PAGE_BYTE)
// KV系统备份区使用键值数量
#define KV_BACK_SUM_NUM (BS_FLASH_PAGE_SIZE / BS_FLASH_KV_ONE_PAGE_BYTE)
// KV系统中buff最大长度值
#define KV_BUFF_MAX_SIZE (BS_FLASH_KV_ONE_PAGE_BYTE - 4)
#if BOOT_SUPPORT
#include "os_api.h"
#define delay os_delay
#else
#include "main.h"
#define delay delay_ms
#endif
kv_sys_t kv_sys_temp = {0};
bool kv_set_state = false; // flash--free true--bus
/* Private Function Prototypes -----------------------------------------------*/
static uint8_t compute_checksum(uint8_t *data, uint8_t len)
{
uint16_t sum = 0;
for (uint8_t i = 0; i < len; i++)
{
sum += *(data + i);
}
return (uint8_t)(sum & 0x00FF);
}
static void *find_kv_addr(uint8_t key_id)
{
kv_sys_t *kv;
uint8_t sum = 0;
for (uint8_t i = 0; i < KV_SUM_NUM; i++)
{
kv = (kv_sys_t *)(BS_KV_BASE_ADDR + BS_FLASH_KV_ONE_PAGE_BYTE * i);
if (kv->key_id != key_id)
{
continue;
}
if (kv->is_enabled != 0xFF)
{
continue;
}
sum = compute_checksum((uint8_t *)kv, sizeof(kv_sys_t) - 1);
if (kv->sum == sum)
{
return (void *)kv;
}
}
return NULL;
}
static void *find_blank_addr(void)
{
kv_sys_t *kv;
for (uint8_t i = 0; i < KV_SUM_NUM; i++)
{
kv = (kv_sys_t *)(BS_KV_BASE_ADDR + BS_FLASH_KV_ONE_PAGE_BYTE * i);
if (kv->key_id == 0xFF && kv->is_enabled == 0xFF)
{
return (void *)kv;
}
}
return NULL;
}
/* Public Function Prototypes ------------------------------------------------*/
void kv_gc_env(void)
{
bsp_flash_erase_page(BS_KV_BACK_ADDR, 1);
delay(50);
kv_sys_t *kv;
uint8_t sum = 0;
uint8_t kv_page_tick = 0;
uint8_t back_tick = 0; // KV_BACK_SUM_NUM
for (uint8_t i = 0; i < KV_SUM_NUM; i++)
{
kv = (kv_sys_t *)(BS_KV_BASE_ADDR + BS_FLASH_KV_ONE_PAGE_BYTE * i);
if (kv->is_enabled != 0xFF)
{
continue;
}
// 判断数据的完整性
sum = compute_checksum((uint8_t *)kv, sizeof(kv_sys_t) - 1);
if (kv->sum != sum)
{
continue;
}
// 搬运有效数据
bsp_flash_write_nbyte_s(BS_KV_BACK_ADDR + back_tick * BS_FLASH_KV_ONE_PAGE_BYTE, (uint8_t *)kv, sizeof(kv_sys_t));
back_tick ++;
if (back_tick == KV_BACK_SUM_NUM)
{
bsp_flash_carry(BS_KV_BASE_ADDR + kv_page_tick * BS_FLASH_PAGE_SIZE, BS_KV_BACK_ADDR, BS_FLASH_PAGE_SIZE);
kv_page_tick ++;
back_tick = 0;
}
}
if (back_tick != 0)
{
bsp_flash_carry(BS_KV_BASE_ADDR + kv_page_tick * BS_FLASH_PAGE_SIZE, BS_KV_BACK_ADDR, BS_FLASH_PAGE_SIZE);
kv_page_tick ++;
back_tick = 0;
}
// 清理未使用的空间
for (uint8_t i = kv_page_tick; i < BS_FLASH_KV_PAGE - 1; i++)
{
bsp_flash_erase_page(BS_KV_BASE_ADDR + kv_page_tick * BS_FLASH_PAGE_SIZE, 1);
}
delay(50);
}
/**
* @brief 从FLASH中获取KV值
* @param key_id: KEY ID
* @retval 数据指针
*/
void *kv_get_env(uint8_t key_id)
{
if (key_id == 0 || key_id == 255)
{
return NULL;
}
kv_sys_t *kv = (kv_sys_t *)find_kv_addr(key_id);
if (kv != NULL)
{
return kv->buff;
}
return NULL;
}
bool kv_del_env(uint8_t key_id)
{
bool state;
kv_sys_t *kv = (kv_sys_t *)find_kv_addr(key_id);
uint32_t temp_addr = (uint32_t)kv + (uint8_t)BS_FLASH_KV_ONE_PAGE_BYTE -2;
if (kv != NULL)
{
// 将之前值标记为无效
state = bsp_flash_write_byte(temp_addr, 0x00);
}
return state;
}
/**
* @brief KV值写入Flash
* @param key_id: KEY ID
* @param *data: 数组指针
* @param len: 数据长度
*/
bool kv_set_env(uint8_t key_id, void *data, uint8_t len)
{
// 检测ID是否异常
if (key_id == 0 || key_id == 255)
{
return false;
}
// 检测参数和当前状态是否异常
if ((len > KV_BUFF_MAX_SIZE) || (kv_set_state) || (KV_SUM_SIZE == 0))
{
return false;
}
kv_set_state = true;
// 检测KEY_ID是否存在
kv_sys_t *kv = NULL;
// 判断数据是否相同
uint8_t *old = kv_get_env(key_id);
if(old != NULL)
{
if (memcmp(data, old, len) == 0)
{
// 数据一致,直接返回
kv_set_state = false;
return true;
}
}
kv_del_env(key_id);
// 得到空白块
kv = find_blank_addr();
// 如果数据满了,则进行垃圾回收处理
if(kv == NULL)
{
kv_gc_env();
kv = find_blank_addr();
}
// 填充数据
kv_sys_temp.key_id = key_id;
memset(kv_sys_temp.buff, 0, KV_BUFF_MAX_SIZE);
memcpy(kv_sys_temp.buff, data, len);
kv_sys_temp.len = len;
kv_sys_temp.is_enabled = 0xFF;
kv_sys_temp.sum = compute_checksum((uint8_t *)&kv_sys_temp, sizeof(kv_sys_t) - 1);
bsp_flash_write_nbyte_s((uint32_t)kv, (uint8_t *)&kv_sys_temp, sizeof(kv_sys_t));
kv_set_state = false;
return true;
}
/********************************************************************************
* @file kv_sys.h
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-11-03
* @brief KV键值系统
********************************************************************************/
#ifndef __KV_SYS_H__
#define __KV_SYS_H__
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include "business_function.h"
/* Public Struct -------------------------------------------------------------*/
typedef struct
{
uint8_t key_id; // KEY ID [1,254] 0和255不能使用
uint8_t buff[BS_FLASH_KV_ONE_PAGE_BYTE - 4]; // 实际数据
uint8_t len; // 实际长度
uint8_t is_enabled; // 是否有效 0--无效 FF--有效
uint8_t sum; // 校验和
} kv_sys_t;
/* Public Function Prototypes -----------------------------------------------*/
void *kv_get_env(uint8_t key_id);
bool kv_del_env(uint8_t key_id);
bool kv_set_env(uint8_t key_id, void *data, uint8_t len);
#endif