嵌入式设备参数存储技巧

李肖遥 2022-08-06 22:08
    关注、星标公众号,直达精彩内容

来源 | CSDN 路过的小熊

整理&排版 | 嵌入式应用研究院


1、前言

想必做嵌入式产品开发都遇到过设备需要保存参数,常用的方式就是按照结构体的方式管理参数,保存时将整个结构体数据保存在 Flash 中,方便下次读取。

1.1、目的

本文时分析嵌入式/单片机中参数保存的几种方式的优点和缺点(仅针对单片机/嵌入式开发而言),同时针对以结构体的方式解决一些弊端问题(重点在第 3 节)。

2、参数保存格式
2.1、结构体格式

该方式是嵌入式/单片机中开发最常用的,将所有的系统参数通过结构体的方式定义,然后保存数据,介绍一下该方式的优缺点。

储存方式:二进制 bin 文件格式

优点:

管理简单:无需额外的代码直接就能很方便的管理参数 内存最小:通过结构体的形式保存在Flash中,占用内存最小

缺点:

1.扩展性差:

从产品角度来说,产品需要升级,若是涉及增加参数,则升级后参数通常无法校验通过(通常包含长度校验等),导致参数被恢复默认

若是每个模块都存在自己的独有结构体参数定义,删除/新增时势必影响到其他的,导致设备升级后参数错乱(结构体中的变量地址在 bin 文件中是固定的)

2.阅读性差:若参数需要导出,bin文件没有可读性

改进措施:

结构体增加预留定义,若之后需要新增参数,则在预留空间新增即可,能在一定程度上解决扩展性差的问题,即新增不影响原有的结构体大小和其他成员变量的位置,删除恢复成预留即可。

为啥说只能在一定程度上解决该问题,因为之后的升级某些模块可能很长时间或者从不需要增加新的参数,这种势必就会造成内存的无效占用,或者有些模块频繁增加参数导致预留大小不够等问题,只能在前期设计时多加思考预留的分配情况(毕竟内存只有那么大),改进如下:

/*****************************
           改进之前
*****************************/
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
} TestParam_t;    /* 某模块参数 */
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    TestParam_t tTestParam;
} SystemParam_t; /* 系统参数 */
 
/*****************************
           改进之后
*****************************/
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    uint8_t reserve[6];    // 预留
} TestParam_t;    /* 某模块参数 */
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    TestParam_t tTestParam;
    uint8_t reserve[50];   // 预留
} SystemParam_t; /* 系统参数 */
2.2、JSON格式

最近Json格式很是流行使用,特别是数据交换中用的很多,但是它也可以用来保存参数使用,JSON 的是 “{键:值}” 的方式。

储存方式:字符串格式,即文本的形式

优点:

扩展性好:由于Json的格式,找到对应键值(一般都是该变量的标识),就能找到对应的值

阅读性好:有标识所以导出参数文件通过普通的文本文件打开都能看懂

缺点:

管理相对复杂:没有结构体那么简单,不熟还得先学习 JSON 的写法

内存占用较大:内容不只有值,而且都按照字符串的形式保存的

使用相关困难:需要解析,C语言虽然有开源库,但是由于语言性质使用不方便,C++ 反而使用简单

{
    "SYS":
    {
        "testParam" : 2,
        "testParam2" : 5,
        "tTestParam":
        {
            "testParam" : 2,
            "testParam2" : 5
        }
    }
}
 
//压缩字符串为:
{"SYS":{"testParam":2,"testParam2":5,"tTestParam":{"testParam":2,"testParam2":5}}}
2.3、键值格式

和上述的 JSON 格式很类似,都是键值对的格式,但是比JSON简单

储存方式:字符串格式,即文本的形式

优点:

扩展性好:找到对应键值(一般都是该变量的标识),就能找到对应的值

阅读性好:有标识所以导出参数文件通过普通的文本文件打开都能看懂

缺点:

内存占用较大:内容不只有值,而且都按照字符串的形式保存的

使用稍微困难:需要简单解析处理

管理不变:不方便按照一定的规则管理各模块的参数

testParam=2
testParam2=5
T_testParam=2
T_testParam2=5
2.4 其他

还有其他,如 xml (类似JSON)等,就不多介绍了

3、编译器检查结构体的大小和成员变量的偏移

在第 2 节中介绍了关于参数保存的三种方式,但是对于嵌入式单片机开发而言,Flash 大小不富裕,所以通常都是通过二进制的形式保存的,所以这节重点解决结构体管理保存参数的扩展性问题。

先说一下痛点(虽然对扩展性问题做了改进措施,除了前面讲到的问题,还有其他痛点,虽不算问题,但是一旦出现往往最要命)

在原来的预留空间中新增参数,要确保新增后结构体的大小不变,否则会导致后面的其他参数偏移,最后升级设备后参数出现异常(如果客户升级那就是要命啊) 确保第一点,就必须在每次新增参数都要计算检查一下结构体的大小有没有发生变化,而且有没有对结构体中的其他成员也产生影响

每次新增参数,手动计算和校验 99% 可以检查出来,但是人总有粗心的时候(加班多了,状态不好…),且结构体存在填充,一不留神就以为没问题,提交代码,出版本(测试不一定能发现),给客户,升级后异常,客户投诉、扣工资(难啊…) 遇到这种问题后:难道编译器就不能在编译的时候检查这个大小或者结构体成员的偏移吗,每次手动计算校验好麻烦啊,一不留神还容易算错 # _ #

按照正常情况,编译器可不知道你写的结构体大小和你想要的多大,所以检查不出来(天啊,崩溃了0.0…)

别急,有另类的方式可以达到这种功能,在编译时让编译器为你检查,而且准确性 100%(当然,这个添加新参数时你还得简单根据新增的参数大小减少预留的大小,这个是必须要的)

见代码:

/**
  * @brief 检查结构体大小是否符合
  *        在编译时会进行检查
  * @param type 结构体类型
  * @param size 结构体检查大小
  */
#define TYPE_CHECK_SIZE(type, size) extern int sizeof_##type##_is_error [!!(sizeof(type)==(size_t)(size)) - 1]
 
/**
  * @brief 结构体成员
  * @param type   结构体类型
  * @param member 成员变量
  */
#define TYPE_MEMBER(type, member) (((type *)0)->member)
 
 
/**
  * @brief 检查结构体成员大小是否符合
  *        在编译时会进行检查
  * @param type 结构体类型
  * @param member 结构体类型
  * @param size 结构体检查大小
  */
#define TYPE_MEMBER_CHECK_SIZE(type, member, size) extern int sizeof_##type##_##member##_is_error \
    [!!(sizeof(TYPE_MEMBER(type, member))==(size_t)(size)) - 1]
 
 
/**
  * @brief 检查结构体中结构体成员大小是否符合
  *        在编译时会进行检查
  * @param type 结构体类型
  * @param member 结构体类型
  * @param size 结构体检查大小
  */
#define TYPE_CHILDTYPE_MEMBER_CHECK_SIZE(type, childtype, member, size) extern int sizeof_##type##_##childtype##_##member##_is_error \
    [!!(sizeof(TYPE_MEMBER(type, childtype.member))==(size_t)(size)) - 1]
 
 
/**
  * @brief 检查结构体成员偏移位置是否符合
  *        在编译时会进行检查
  * @param type 结构体类型
  * @param member 结构体成员
  * @param value 成员偏移
  */
#define TYPE_MEMBER_CHECK_OFFSET(type, member, value) \
         extern int offset_of_##member##_in_##type##_is_error \
        [!!(__builtin_offsetof(type, member)==((size_t)(value))) - 1]
 
 
/**
  * @brief 检查结构体成员偏移位置是否符合
  *        在编译时会进行检查
  * @param type 结构体类型
  * @param member 结构体成员
  * @param value 成员偏移
  */
#define TYPE_CHILDTYPE_MEMBER_CHECK_OFFSET(type, childtype, member, value) \
         extern int offset_of_##member##_in_##type##_##childtype##_is_error \
        [!!(__builtin_offsetof(type, childtype.member)==((size_t)(value))) - 1]

通过以上代码,就能解决这个问题,这个写法只占用文本大小,编译后不占内存!!!

用法:

typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    uint8_t reserve[6];    // 预留
} TestParam_t;    /* 某模块参数 */
 
TYPE_CHECK_SIZE(TestParam_t, 8); // 检查结构体的大小是否符合预期
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    TestParam_t tTestParam;
    uint8_t reserve[54];   // 预留
} SystemParam_t; /* 系统参数 */
 
TYPE_CHECK_SIZE(SystemParam_t, 64); // 检查结构体的大小是否符合预期
TYPE_MEMBER_CHECK_OFFSET(SystemParam_t, tTestParam, 2); // 检查结构体成员tTestParam偏移是否符合预期

假设新增了参数,预留写错了,导致结构体的大小不符合,则编译时报错,且提示内容也能快速定位问题。

版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

关注我的微信公众号,回复“加群”按规则加入技术交流群。


点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

李肖遥 公众号“技术让梦想更伟大”,作者:李肖遥,专注嵌入式,只推荐适合你的博文,干货,技术心得,与君共勉。
评论 (0)
  • UNISOC Miracle Gaming奇迹手游引擎亮点:• 高帧稳帧:支持《王者荣耀》等主流手游90帧高画质模式,连续丢帧率最高降低85%;• 丝滑操控:游戏冷启动速度提升50%,《和平精英》开镜开枪操作延迟降低80%;• 极速网络:专属游戏网络引擎,使《王者荣耀》平均延迟降低80%;• 智感语音:与腾讯GVoice联合,弱网环境仍能保持清晰通话;• 超高画质:游戏画质增强、超级HDR画质、游戏超分技术,优化游戏视效。全球手游市场规模日益壮大,游戏玩家对极致体验的追求愈发苛刻。紫光展锐全新U
    紫光展锐 2025-05-07 17:07 89浏览
  • 随着智能驾驶时代到来,汽车正转变为移动计算平台。车载AI技术对存储器提出新挑战:既要高性能,又需低功耗和车规级可靠性。贞光科技代理的紫光国芯车规级LPDDR4存储器,以其卓越性能成为国产芯片产业链中的关键一环,为智能汽车提供坚实的"记忆力"支持。作为官方授权代理商,贞光科技通过专业技术团队和完善供应链,让这款国产存储器更好地服务国内汽车厂商。本文将探讨车载AI算力需求现状及贞光科技如何通过紫光国芯LPDDR4产品满足市场需求。 车载AI算力需求激增的背景与挑战智能驾驶推动算力需求爆发式
    贞光科技 2025-05-07 16:54 79浏览
  • 多功能电锅长什么样子,主视图如下图所示。侧视图如下图所示。型号JZ-18A,额定功率600W,额定电压220V,产自潮州市潮安区彩塘镇精致电子配件厂,铭牌如下图所示。有两颗螺丝固定底盖,找到合适的工具,拆开底盖如下图所示。可见和大部分市场的加热锅一样的工作原理,手绘原理图,根据原理图进一步理解和分析。F1为保险,250V/10A,185℃,CPGXLD 250V10A TF185℃ RY 是一款温度保险丝,额定电压是250V,额定电流是10A,动作温度是185℃。CPGXLD是温度保险丝电器元件
    liweicheng 2025-05-05 18:36 211浏览
  • 某国产固态电解的2次和3次谐波失真相当好,值得一试。(仅供参考)现在国产固态电解的性能跟上来了,值得一试。当然不是随便搞低端的那种。电容器对音质的影响_电子基础-面包板社区  https://mbb.eet-china.com/forum/topic/150182_1_1.html (右键复制链接打开)电容器对音质的影响相当大。电容器在音频系统中的角色不可忽视,它们能够调整系统增益、提供合适的偏置、抑制电源噪声并隔离直流成分。然而,在便携式设备中,由于空间、成本的限
    bruce小肥羊 2025-05-04 18:14 161浏览
  • 二位半 5线数码管的驱动方法这个2位半的7段数码管只用5个管脚驱动。如果用常规的7段+共阳/阴则需要用10个管脚。如果把每个段看成独立的灯。5个管脚来点亮,任选其中一个作为COM端时,另外4条线可以单独各控制一个灯。所以实际上最多能驱动5*4 = 20个段。但是这里会有一个小问题。如果想点亮B1,可以让第3条线(P3)置高,P4 置低,其它阳极连P3的灯对应阴极P2 P1都应置高,此时会发现C1也会点亮。实际操作时,可以把COM端线P3设置为PP输出,其它线为OD输出。就可以单独控制了。实际的驱
    southcreek 2025-05-07 15:06 104浏览
  • 你是不是也有在公共场合被偷看手机或笔电的经验呢?科技时代下,不少现代人的各式机密数据都在手机、平板或是笔电等可携式的3C产品上处理,若是经常性地需要在公共场合使用,不管是工作上的机密文件,或是重要的个人信息等,民众都有防窃防盗意识,为了避免他人窥探内容,都会选择使用「防窥保护贴片」,以防止数据外泄。现今市面上「防窥保护贴」、「防窥片」、「屏幕防窥膜」等产品就是这种目的下产物 (以下简称防窥片)!防窥片功能与常见问题解析首先,防窥片最主要的功能就是用来防止他人窥视屏幕上的隐私信息,它是利用百叶窗的
    百佳泰测试实验室 2025-04-30 13:28 622浏览
  • 想不到短短几年时间,华为就从“技术封锁”的持久战中突围,成功将“被卡脖子”困境扭转为科技主权的主动争夺战。众所周知,前几年技术霸权国家突然对华为发难,导致芯片供应链被强行掐断,海外市场阵地接连失守,恶意舆论如汹涌潮水,让其瞬间陷入了前所未有的困境。而最近财报显示,华为已经渡过危险期,甚至开始反击。2024年财报数据显示,华为实现全球销售收入8621亿元人民币,净利润626亿元人民币;经营活动现金流为884.17亿元,同比增长26.7%。对比来看,2024年营收同比增长22.42%,2023年为7
    用户1742991715177 2025-05-02 18:40 195浏览
  • ‌一、高斯计的正确选择‌1、‌明确测量需求‌‌磁场类型‌:区分直流或交流磁场,选择对应仪器(如交流高斯计需支持交变磁场测量)。‌量程范围‌:根据被测磁场强度选择覆盖范围,例如地球磁场(0.3–0.5 G)或工业磁体(数百至数千高斯)。‌精度与分辨率‌:高精度场景(如科研)需选择误差低于1%的仪器,分辨率需匹配微小磁场变化检测需求。2、‌仪器类型选择‌‌手持式‌:便携性强,适合现场快速检测;‌台式‌:精度更高,适用于实验室或工业环境。‌探头类型‌:‌横向/轴向探头‌:根据磁场方向选择,轴向探头适合
    锦正茂科技 2025-05-06 11:36 319浏览
  • 这款无线入耳式蓝牙耳机是长这个样子的,如下图。侧面特写,如下图。充电接口来个特写,用的是卡座卡在PCB板子上的,上下夹紧PCB的正负极,如下图。撬开耳机喇叭盖子,如下图。精致的喇叭(HY),如下图。喇叭是由电学产生声学的,具体结构如下图。电池包(AFS 451012  21 12),用黄色耐高温胶带进行包裹(安规需求),加强隔离绝缘的,如下图。451012是电池包的型号,聚合物锂电池+3.7V 35mAh,详细如下图。电路板是怎么拿出来的呢,剪断喇叭和电池包的连接线,底部抽出PCB板子
    liweicheng 2025-05-06 22:58 225浏览
  • 浪潮之上:智能时代的觉醒    近日参加了一场课题的答辩,这是医疗人工智能揭榜挂帅的国家项目的地区考场,参与者众多,围绕着医疗健康的主题,八仙过海各显神通,百花齐放。   中国大地正在发生着激动人心的场景:深圳前海深港人工智能算力中心高速运转的液冷服务器,武汉马路上自动驾驶出租车穿行的智慧道路,机器人参与北京的马拉松竞赛。从中央到地方,人工智能相关政策和消息如雨后春笋般不断出台,数字中国的建设图景正在智能浪潮中徐徐展开,战略布局如同围棋
    广州铁金刚 2025-04-30 15:24 346浏览
  • 5小时自学修好BIOS卡住问题  更换硬盘故障现象:f2、f12均失效,只有ESC和开关机键可用。错误页面:经过AI的故障截图询问,确定是机体内灰尘太多,和硬盘损坏造成,开机卡在BIOS。经过亲手拆螺丝和壳体、排线,跟换了新的2.5寸硬盘,故障排除。理论依据:以下是针对“5小时自学修好BIOS卡住问题+更换硬盘”的综合性解决方案,结合硬件操作和BIOS设置调整,分步骤说明:一、判断BIOS卡住的原因1. 初步排查     拔掉多余硬件:断开所有外接设备(如
    丙丁先生 2025-05-04 09:14 93浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦