Env 小型KV数据库,支持 写平衡(磨损平衡) 及掉电保护模式
让Flash变为NoSQL(非关系型数据库)模型的小型键值(Key-Value)存储数据库。在产品上,能够更加简捷的实现 设定参数 或掉电保存的功能。
功能:
简易设置KEY和VAL,自动垃圾回收。至少需要占用两页FLASH空间。
平衡flash读写,提高flash擦写寿命
资源占用
RAM 16 字节 ROM 582字节
支持平台
各类单片机
函数简洁
void kv_gc_env(void);
void kv_gc_check(void);
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);
```c
/************************************FLASH**************************************/
// // ======================= // // // Default: 0x800 (2K byte) #define BS_FLASH_PAGE_SIZE (0x800U) // // Default: 0x40000 (256K byte) #define BS_FLASH_MAX_SIZE (0x40000U) // // Default: 0 #define BS_FLASH_START_ADDR (0x8000000) // // Default: 0x3800 (14K byte) #define BS_FLASH_BOOT_SIZE (0x3800) // // Default: 2 #define BS_FLASH_KV_PAGE (2) // // Default: 32 #define BS_FLASH_KV_ONE_PAGE_BYTE (32) // // <0=> KV_SYS // <1=> FDS #define BS_FLASH_INFO_TYPE (0) //
/********************************[FLASH INFO]*********************************/
#define BS_FLASH_APP_ADDR (BS_FLASH_START_ADDR + BS_FLASH_BOOT_SIZE)
#define BS_FLASH_APP_SIZE (BS_FLASH_MAX_SIZE - BS_FLASH_BOOT_SIZE - (BS_FLASH_KV_PAGE * BS_FLASH_PAGE_SIZE))
#define BS_FLASH_OTA_ADDR (BS_FLASH_APP_ADDR + BS_FLASH_APP_SIZE / 2)
#define BS_FLASH_END_ADDR (BS_FLASH_START_ADDR + BS_FLASH_MAX_SIZE)
#define BS_KV_BASE_ADDR (BS_FLASH_START_ADDR + BS_FLASH_APP_SIZE)
#define BS_KV_BACK_ADDR (BS_KV_BASE_ADDR + (BS_FLASH_KV_PAGE - 1) * BS_FLASH_PAGE_SIZE)
/********************************************************************************
* @file kv_sys.c
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-11-03
* @brief KV键值最小系统 https://lisun.blog.csdn.net/article/details/121140849
********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include #include #include #include #include "bsp_flash.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) 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 = NULL; 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 ------------------------------------------------*/ /** * @brief FLASH垃圾回收 */ void kv_gc_env(void) { bsp_flash_erase_page(BS_KV_BACK_ADDR, 1); while(bsp_flash_is_busy()); kv_sys_t *kv = NULL; uint8_t sum = 0; uint8_t kv_page_tick = 0; uint8_t back_tick = 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->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); } while(bsp_flash_is_busy()); } /** * @brief [上电调用] 检测当前KV键值是否异常 如:掉电导致异常,则进行数据恢复 */ void kv_gc_check(void) { kv_sys_t *kv = NULL; // 得到空白块 kv = find_blank_addr(); // 如果数据满了,则进行垃圾回收处理 if(kv == NULL) { kv_gc_env(); } else { // 判断备份区是否有残余 kv = (kv_sys_t *)BS_KV_BACK_ADDR; if (kv->key_id != 0xFF) { for (uint8_t i = 0; i < KV_BACK_SUM_NUM; i++) { kv = (kv_sys_t *)(BS_KV_BACK_ADDR + i * BS_FLASH_KV_ONE_PAGE_BYTE); kv_set_env(kv->key_id, kv->buff, kv->len);
上一篇:[单片机][USB_HID] USB问题汇总
下一篇:C语言中使用 #pragma pack 和 __attribute(aligned(n)) 【非常有用的字节对齐用法说明】
推荐阅读最新更新时间:2024-11-11 11:35