恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

[单片机框架] [kv_sys] 实现一个简易KV键值系统

  • 首页
  • 资讯中心
  • /
  • [单片机框架] [kv_sys] 实现一个简易KV键值系统

相关资讯

[嵌入式方案][cx32l003] 手持电风扇 (风扇模式+手电筒100lm+充电宝+电量百分比显示+超长续航) 2026/9/15 13:00:26
Windmill PostgreSQL 脚本编写实战指南:从参数绑定到 S3 流式导出 2026/9/14 11:08:37
Wasp 类型安全链接(Type-Safe Links)实战指南:用 `@wasp/router` 告别手写字符串路由 2026/9/15 13:00:26

最新资讯

ArduPilot 视频流信息脚本:基于 Lua 脚本与 AP_Camera 库下发 VIDEO_STREAM_INFORMATION 消息的完整指南
代码管理工具选型:发布对账能力决定交付底线
CMakeTestCCompiler.cmake 报错原因与排查方法详解
构建高效标准工时库:vioovi ECRS工时分析软件的实践与应用
AI耳机如何成为运动感知新终端:耳道多模态生理信号融合技术解析
如何用 wire show 查看 Provider Set 的输入、输出与依赖的 Provider Set

今日推荐

GDPR下大数据架构重构与隐私保护实践
多组学数据平台架构设计与优化实践
企业主数据管理系统架构设计与实施全解析

本周热门

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化
Flutter应用改名全指南:从Android到iOS的配置与工具实践

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

[单片机框架] [kv_sys] 实现一个简易KV键值系统

发布时间:2026/9/15 13:00:28
[单片机框架] [kv_sys] 实现一个简易KV键值系统 文章目录Env 小型KV数据库支持 写平衡磨损平衡 及掉电保护模式一、功能二、介绍1. 资源占用2. 与直接读写 Flash 的对比3. 支持平台4. 磨损平衡机制5. 性能与寿命测试6. 函数简洁7. 使用方式三、 常见错误与排查1. 禁止在中断服务中使用Env 小型KV数据库支持 写平衡磨损平衡 及掉电保护模式让Flash变为NoSQL非关系型数据库模型的小型键值Key-Value存储数据库。在产品上能够更加简捷的实现 设定参数 或掉电保存的功能。一、功能简易设置KEY和VAL自动垃圾回收。至少需要占用两页FLASH空间。平衡flash读写提高flash擦写寿命二、介绍1. 资源占用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字节2. 与直接读写 Flash 的对比指标直接读写 Flash本 KV 系统擦写次数每次更新都擦写同一页寿命短页轮换 垃圾回收各页擦写均匀寿命长RAM 占用几乎为 0约 16 字节ROM 占用几乎为 0约 500 字节写入耗时快直接擦写略慢需查空白页满时触发 GC数据管理需自行维护地址与有效性按 KEY 自动管理支持删除与校验简要说明直接读写 Flash 虽然省资源、速度快但每次更新都擦写同一页会迅速耗尽该页寿命本 KV 系统以约 16 字节 RAM 和 500 字节 ROM 的代价换来磨损平衡与自动垃圾回收显著延长 Flash 使用寿命适合频繁保存参数的场景。3. 支持平台4. 磨损平衡机制Flash 的擦写寿命有限若总是写同一页会很快损坏。本系统通过「垃圾回收 页轮换」实现磨损平衡写入时优先找空白页写满后触发kv_gc_env()垃圾回收把有效数据搬运到备份区再擦除旧页并轮换使用使各页擦写次数趋于均匀从而延长 Flash 寿命。是否写入新 KV存在空白页?写入空白页垃圾回收: 搬运有效数据到备份区擦除旧页并轮换各类单片机、MCU,通用性强5. 性能与寿命测试以常见的 SPI NOR Flash 型号 W25Q64容量 8MB页大小 256 字节扇区 4KB擦写寿命约 10 万次为例对比直接读写与本 KV 系统的寿命与性能表现。指标直接读写 Flash本 KV 系统单次写入耗时约 0.5~3 ms页编程约 0.5~3 ms页编程 查空白页开销微秒级触发垃圾回收时单次写入耗时无约 60~120 ms含擦除 4KB 扇区 搬运有效数据垃圾回收触发频率无与 KV 区页数成反比页数越多触发越少擦写寿命单页约 10 万次即报废页轮换后各页均匀磨损整体寿命可提升数倍至数十倍典型寿命估算每秒写 1 次约 27 小时耗尽单页按 8 页轮换估算约 9 天以上估算说明W25Q64 单页擦写寿命约 10 万次。直接读写若固定写同一页每秒写 1 次约 27 小时即耗尽该页本 KV 系统通过页轮换把写入分散到多页假设 KV 区配置 8 页则等效寿命约为单页的 8 倍且垃圾回收仅在空白页耗尽时触发频率远低于每次写入整体寿命显著延长。6. 函数简洁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);7. 使用方式// 存入kv_set_env(BS_KV_KEY_TEMP_INFO,ABCDAD,strlen(ABCDAD));// 读取uint8_t*p;uint8_tbuff[10];pkv_get_env(BS_KV_KEY_TEMP_INFO);if(p!NULL){memcpy(buff,p,10);}三、 常见错误与排查现象可能原因解决方法与调试建议kv_set_env返回falsekey_id为 0 或 255超出合法范围 [1,254]检查传入的key_id宏定义确保在 1~254 之间可在调用前打印key_id定位kv_set_env返回falselen超过KV_BUFF_MAX_SIZE即BS_FLASH_KV_ONE_PAGE_BYTE - 4核对写入长度必要时拆分存储或增大单页字节数调试时打印len与KV_BUFF_MAX_SIZEkv_set_env返回false上次写入未完成kv_set_state仍为true如被中断打断确认未在中断中调用若怀疑状态残留可复位后重试或增加超时保护kv_get_env返回NULLkey_id为 0 或 255同上检查key_id合法性kv_get_env返回NULL该 KEY 从未写入或已被kv_del_env删除先调用kv_set_env写入再读取调试时遍历打印各 KEY 的is_enabled状态kv_get_env返回NULLFlash 中数据校验和sum不匹配数据损坏检查 Flash 驱动写入是否完整、地址是否越界可重新写入该 KEY 覆盖旧数据Flash 写入失败 / 数据丢失写入时 Flash 忙、电压不稳或地址越界确认bsp_flash_write_nbyte_s返回值检查BS_KV_BASE_ADDR与页大小配置是否匹配频繁写入后系统变慢空白页耗尽频繁触发kv_gc_env()垃圾回收适当增大 KV 区页数减少高频写入或对相同数据先比较再写入系统已内置该优化调试建议在kv_set_env与kv_get_env入口处打印key_id、len与返回值结合kv_sys_t结构体中的is_enabled与sum字段可快速定位是参数问题、数据损坏还是 Flash 驱动问题。1. 禁止在中断服务中使用/******************************************************************************** * file kv_sys.c * author jianqiang.xue * version V1.0.0 * date 2021-11-03 * brief KV键值最小系统 ********************************************************************************//* Includes ------------------------------------------------------------------*/#includestdint.h#includestdio.h#includestring.h#includestdbool.h#includebsp_flash.h#includelog.h/* Private Includes ----------------------------------------------------------*/#includekv_sys.h#includebusiness_function.h/* Private Define ------------------------------------------------------------*/// KV系统总共可以使用N字节#defineKV_SUM_SIZE(BS_FLASH_PAGE_SIZE*(BS_FLASH_KV_PAGE-1))// KV系统总共使用键值数量#defineKV_SUM_NUM(KV_SUM_SIZE/BS_FLASH_KV_ONE_PAGE_BYTE)// KV系统备份区使用键值数量#defineKV_BACK_SUM_NUM(BS_FLASH_PAGE_SIZE/BS_FLASH_KV_ONE_PAGE_BYTE)// KV系统中buff最大长度值#defineKV_BUFF_MAX_SIZE(BS_FLASH_KV_ONE_PAGE_BYTE-4)#ifBOOT_SUPPORT#includeos_api.h#definedelayos_delay#else#includemain.h#definedelaydelay_ms#endifkv_sys_tkv_sys_temp{0};bool kv_set_statefalse;// flash--free true--bus/* Private Function Prototypes -----------------------------------------------*/staticuint8_tcompute_checksum(uint8_t*data,uint8_tlen){uint16_tsum0;for(uint8_ti0;ilen;i){sum*(datai);}return(uint8_t)(sum0x00FF);}staticvoid*find_kv_addr(uint8_tkey_id){kv_sys_t*kv;uint8_tsum0;for(uint8_ti0;iKV_SUM_NUM;i){kv(kv_sys_t*)(BS_KV_BASE_ADDRBS_FLASH_KV_ONE_PAGE_BYTE*i);if(kv-key_id!key_id){continue;}if(kv-is_enabled!0xFF){continue;}sumcompute_checksum((uint8_t*)kv,sizeof(kv_sys_t)-1);if(kv-sumsum){return(void*)kv;}}returnNULL;}staticvoid*find_blank_addr(void){kv_sys_t*kv;for(uint8_ti0;iKV_SUM_NUM;i){kv(kv_sys_t*)(BS_KV_BASE_ADDRBS_FLASH_KV_ONE_PAGE_BYTE*i);if(kv-key_id0xFFkv-is_enabled0xFF){return(void*)kv;}}returnNULL;}/* Public Function Prototypes ------------------------------------------------*/voidkv_gc_env(void){bsp_flash_erase_page(BS_KV_BACK_ADDR,1);delay(50);kv_sys_t*kv;uint8_tsum0;uint8_tkv_page_tick0;uint8_tback_tick0;// KV_BACK_SUM_NUMfor(uint8_ti0;iKV_SUM_NUM;i){kv(kv_sys_t*)(BS_KV_BASE_ADDRBS_FLASH_KV_ONE_PAGE_BYTE*i);if(kv-is_enabled!0xFF){continue;}// 判断数据的完整性sumcompute_checksum((uint8_t*)kv,sizeof(kv_sys_t)-1);if(kv-sum!sum){continue;}// 搬运有效数据bsp_flash_write_nbyte_s(BS_KV_BACK_ADDRback_tick*BS_FLASH_KV_ONE_PAGE_BYTE,(uint8_t*)kv,sizeof(kv_sys_t));back_tick;if(back_tickKV_BACK_SUM_NUM){bsp_flash_carry(BS_KV_BASE_ADDRkv_page_tick*BS_FLASH_PAGE_SIZE,BS_KV_BACK_ADDR,BS_FLASH_PAGE_SIZE);kv_page_tick;back_tick0;}}if(back_tick!0){bsp_flash_carry(BS_KV_BASE_ADDRkv_page_tick*BS_FLASH_PAGE_SIZE,BS_KV_BACK_ADDR,BS_FLASH_PAGE_SIZE);kv_page_tick;back_tick0;}// 清理未使用的空间for(uint8_tikv_page_tick;iBS_FLASH_KV_PAGE-1;i){bsp_flash_erase_page(BS_KV_BASE_ADDRkv_page_tick*BS_FLASH_PAGE_SIZE,1);}delay(50);}/** * brief 从FLASH中获取KV值 * param key_id: KEY ID * retval 数据指针 */void*kv_get_env(uint8_tkey_id){if(key_id0||key_id255){returnNULL;}kv_sys_t*kv(kv_sys_t*)find_kv_addr(key_id);if(kv!NULL){returnkv-buff;}returnNULL;}boolkv_del_env(uint8_tkey_id){bool state;kv_sys_t*kv(kv_sys_t*)find_kv_addr(key_id);uint32_ttemp_addr(uint32_t)kv(uint8_t)BS_FLASH_KV_ONE_PAGE_BYTE-2;if(kv!NULL){// 将之前值标记为无效statebsp_flash_write_byte(temp_addr,0x00);}returnstate;}/** * brief KV值写入Flash * param key_id: KEY ID * param *data: 数组指针 * param len: 数据长度 */boolkv_set_env(uint8_tkey_id,void*data,uint8_tlen){// 检测ID是否异常if(key_id0||key_id255){returnfalse;}// 检测参数和当前状态是否异常if((lenKV_BUFF_MAX_SIZE)||(kv_set_state)||(KV_SUM_SIZE0)){returnfalse;}kv_set_statetrue;// 检测KEY_ID是否存在kv_sys_t*kvNULL;// 判断数据是否相同uint8_t*oldkv_get_env(key_id);if(old!NULL){if(memcmp(data,old,len)0){// 数据一致直接返回kv_set_statefalse;returntrue;}}kv_del_env(key_id);// 得到空白块kvfind_blank_addr();// 如果数据满了则进行垃圾回收处理if(kvNULL){kv_gc_env();kvfind_blank_addr();}// 填充数据kv_sys_temp.key_idkey_id;memset(kv_sys_temp.buff,0,KV_BUFF_MAX_SIZE);memcpy(kv_sys_temp.buff,data,len);kv_sys_temp.lenlen;kv_sys_temp.is_enabled0xFF;kv_sys_temp.sumcompute_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_statefalse;returntrue;}/******************************************************************************** * 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 ------------------------------------------------------------------*/#includestdint.h#includestdbool.h#includebusiness_function.h/* Public Struct -------------------------------------------------------------*/typedefstruct{uint8_tkey_id;// KEY ID [1,254] 0和255不能使用uint8_tbuff[BS_FLASH_KV_ONE_PAGE_BYTE-4];// 实际数据uint8_tlen;// 实际长度uint8_tis_enabled;// 是否有效 0--无效 FF--有效uint8_tsum;// 校验和}kv_sys_t;/* Public Function Prototypes -----------------------------------------------*/void*kv_get_env(uint8_tkey_id);boolkv_del_env(uint8_tkey_id);boolkv_set_env(uint8_tkey_id,void*data,uint8_tlen);#endif[单片机框架] [kv_sys] 实现一个简易KV键值系统[单片机框架] [kv_sys] 实现一个简易KV键值系统(升级版)

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号