日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

嵌入式設(shè)備參數(shù)存儲(chǔ)技巧

 myallmy 2022-06-22 發(fā)布于北京

1、前言

想必做嵌入式產(chǎn)品開發(fā)都遇到過設(shè)備需要保存參數(shù),常用的方式就是按照結(jié)構(gòu)體的方式管理參數(shù),保存時(shí)將整個(gè)結(jié)構(gòu)體數(shù)據(jù)保存在 Flash 中,方便下次讀取。

1.1、目的

本文時(shí)分析嵌入式/單片機(jī)中參數(shù)保存的幾種方式的優(yōu)點(diǎn)和缺點(diǎn)(僅針對(duì)單片機(jī)/嵌入式開發(fā)而言),同時(shí)針對(duì)以結(jié)構(gòu)體的方式解決一些弊端問題(重點(diǎn)在第 3 節(jié))。

2、參數(shù)保存格式
2.1、結(jié)構(gòu)體格式

該方式是嵌入式/單片機(jī)中開發(fā)最常用的,將所有的系統(tǒng)參數(shù)通過結(jié)構(gòu)體的方式定義,然后保存數(shù)據(jù),介紹一下該方式的優(yōu)缺點(diǎn)。

儲(chǔ)存方式:二進(jìn)制 bin 文件格式

優(yōu)點(diǎn):

管理簡單:無需額外的代碼直接就能很方便的管理參數(shù) 內(nèi)存最?。和ㄟ^結(jié)構(gòu)體的形式保存在Flash中,占用內(nèi)存最小

缺點(diǎn):

1.擴(kuò)展性差:

從產(chǎn)品角度來說,產(chǎn)品需要升級(jí),若是涉及增加參數(shù),則升級(jí)后參數(shù)通常無法校驗(yàn)通過(通常包含長度校驗(yàn)等),導(dǎo)致參數(shù)被恢復(fù)默認(rèn)

若是每個(gè)模塊都存在自己的獨(dú)有結(jié)構(gòu)體參數(shù)定義,刪除/新增時(shí)勢必影響到其他的,導(dǎo)致設(shè)備升級(jí)后參數(shù)錯(cuò)亂(結(jié)構(gòu)體中的變量地址在 bin 文件中是固定的)

2.閱讀性差:若參數(shù)需要導(dǎo)出,bin文件沒有可讀性

改進(jìn)措施:

結(jié)構(gòu)體增加預(yù)留定義,若之后需要新增參數(shù),則在預(yù)留空間新增即可,能在一定程度上解決擴(kuò)展性差的問題,即新增不影響原有的結(jié)構(gòu)體大小和其他成員變量的位置,刪除恢復(fù)成預(yù)留即可。

為啥說只能在一定程度上解決該問題,因?yàn)橹蟮纳?jí)某些模塊可能很長時(shí)間或者從不需要增加新的參數(shù),這種勢必就會(huì)造成內(nèi)存的無效占用,或者有些模塊頻繁增加參數(shù)導(dǎo)致預(yù)留大小不夠等問題,只能在前期設(shè)計(jì)時(shí)多加思考預(yù)留的分配情況(畢竟內(nèi)存只有那么大),改進(jìn)如下:

/*****************************
           改進(jìn)之前
*****************************/
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
} TestParam_t;    /* 某模塊參數(shù) */
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    TestParam_t tTestParam;
} SystemParam_t; /* 系統(tǒng)參數(shù) */
 
/*****************************
           改進(jìn)之后
*****************************/
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    uint8_t reserve[6];    // 預(yù)留
} TestParam_t;    /* 某模塊參數(shù) */
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    TestParam_t tTestParam;
    uint8_t reserve[50];   // 預(yù)留
} SystemParam_t; /* 系統(tǒng)參數(shù) */
2.2、JSON格式

最近Json格式很是流行使用,特別是數(shù)據(jù)交換中用的很多,但是它也可以用來保存參數(shù)使用,JSON 的是 “{鍵:值}” 的方式。

儲(chǔ)存方式:字符串格式,即文本的形式

優(yōu)點(diǎn):

擴(kuò)展性好:由于Json的格式,找到對(duì)應(yīng)鍵值(一般都是該變量的標(biāo)識(shí)),就能找到對(duì)應(yīng)的值

閱讀性好:有標(biāo)識(shí)所以導(dǎo)出參數(shù)文件通過普通的文本文件打開都能看懂

缺點(diǎn):

管理相對(duì)復(fù)雜:沒有結(jié)構(gòu)體那么簡單,不熟還得先學(xué)習(xí) JSON 的寫法

內(nèi)存占用較大:內(nèi)容不只有值,而且都按照字符串的形式保存的

使用相關(guān)困難:需要解析,C語言雖然有開源庫,但是由于語言性質(zhì)使用不方便,C++ 反而使用簡單

{
    'SYS':
    {
        'testParam' : 2,
        'testParam2' : 5,
        'tTestParam':
        {
            'testParam' : 2,
            'testParam2' : 5
        }
    }
}
 
//壓縮字符串為:
{'SYS':{'testParam':2,'testParam2':5,'tTestParam':{'testParam':2,'testParam2':5}}}
2.3、鍵值格式

和上述的 JSON 格式很類似,都是鍵值對(duì)的格式,但是比JSON簡單

儲(chǔ)存方式:字符串格式,即文本的形式

優(yōu)點(diǎn):

擴(kuò)展性好:找到對(duì)應(yīng)鍵值(一般都是該變量的標(biāo)識(shí)),就能找到對(duì)應(yīng)的值

閱讀性好:有標(biāo)識(shí)所以導(dǎo)出參數(shù)文件通過普通的文本文件打開都能看懂

缺點(diǎn):

內(nèi)存占用較大:內(nèi)容不只有值,而且都按照字符串的形式保存的

使用稍微困難:需要簡單解析處理

管理不變:不方便按照一定的規(guī)則管理各模塊的參數(shù)

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

還有其他,如 xml (類似JSON)等,就不多介紹了

3、編譯器檢查結(jié)構(gòu)體的大小和成員變量的偏移

在第 2 節(jié)中介紹了關(guān)于參數(shù)保存的三種方式,但是對(duì)于嵌入式單片機(jī)開發(fā)而言,F(xiàn)lash 大小不富裕,所以通常都是通過二進(jìn)制的形式保存的,所以這節(jié)重點(diǎn)解決結(jié)構(gòu)體管理保存參數(shù)的擴(kuò)展性問題。

先說一下痛點(diǎn)(雖然對(duì)擴(kuò)展性問題做了改進(jìn)措施,除了前面講到的問題,還有其他痛點(diǎn),雖不算問題,但是一旦出現(xiàn)往往最要命)

在原來的預(yù)留空間中新增參數(shù),要確保新增后結(jié)構(gòu)體的大小不變,否則會(huì)導(dǎo)致后面的其他參數(shù)偏移,最后升級(jí)設(shè)備后參數(shù)出現(xiàn)異常(如果客戶升級(jí)那就是要命?。?確保第一點(diǎn),就必須在每次新增參數(shù)都要計(jì)算檢查一下結(jié)構(gòu)體的大小有沒有發(fā)生變化,而且有沒有對(duì)結(jié)構(gòu)體中的其他成員也產(chǎn)生影響

每次新增參數(shù),手動(dòng)計(jì)算和校驗(yàn) 99% 可以檢查出來,但是人總有粗心的時(shí)候(加班多了,狀態(tài)不好…),且結(jié)構(gòu)體存在填充,一不留神就以為沒問題,提交代碼,出版本(測試不一定能發(fā)現(xiàn)),給客戶,升級(jí)后異常,客戶投訴、扣工資(難啊…) 遇到這種問題后:難道編譯器就不能在編譯的時(shí)候檢查這個(gè)大小或者結(jié)構(gòu)體成員的偏移嗎,每次手動(dòng)計(jì)算校驗(yàn)好麻煩啊,一不留神還容易算錯(cuò) # _ #

按照正常情況,編譯器可不知道你寫的結(jié)構(gòu)體大小和你想要的多大,所以檢查不出來(天啊,崩潰了0.0…)

別急,有另類的方式可以達(dá)到這種功能,在編譯時(shí)讓編譯器為你檢查,而且準(zhǔn)確性 100%(當(dāng)然,這個(gè)添加新參數(shù)時(shí)你還得簡單根據(jù)新增的參數(shù)大小減少預(yù)留的大小,這個(gè)是必須要的)

見代碼:

/**
  * @brief 檢查結(jié)構(gòu)體大小是否符合
  *        在編譯時(shí)會(huì)進(jìn)行檢查
  * @param type 結(jié)構(gòu)體類型
  * @param size 結(jié)構(gòu)體檢查大小
  */
#define TYPE_CHECK_SIZE(type, size) extern int sizeof_##type##_is_error [!!(sizeof(type)==(size_t)(size)) - 1]
 
/**
  * @brief 結(jié)構(gòu)體成員
  * @param type   結(jié)構(gòu)體類型
  * @param member 成員變量
  */
#define TYPE_MEMBER(type, member) (((type *)0)->member)
 
 
/**
  * @brief 檢查結(jié)構(gòu)體成員大小是否符合
  *        在編譯時(shí)會(huì)進(jìn)行檢查
  * @param type 結(jié)構(gòu)體類型
  * @param member 結(jié)構(gòu)體類型
  * @param size 結(jié)構(gòu)體檢查大小
  */
#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 檢查結(jié)構(gòu)體中結(jié)構(gòu)體成員大小是否符合
  *        在編譯時(shí)會(huì)進(jìn)行檢查
  * @param type 結(jié)構(gòu)體類型
  * @param member 結(jié)構(gòu)體類型
  * @param size 結(jié)構(gòu)體檢查大小
  */
#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 檢查結(jié)構(gòu)體成員偏移位置是否符合
  *        在編譯時(shí)會(huì)進(jìn)行檢查
  * @param type 結(jié)構(gòu)體類型
  * @param member 結(jié)構(gòu)體成員
  * @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 檢查結(jié)構(gòu)體成員偏移位置是否符合
  *        在編譯時(shí)會(huì)進(jìn)行檢查
  * @param type 結(jié)構(gòu)體類型
  * @param member 結(jié)構(gòu)體成員
  * @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]

通過以上代碼,就能解決這個(gè)問題,這個(gè)寫法只占用文本大小,編譯后不占內(nèi)存?。?!

用法:

typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    uint8_t reserve[6];    // 預(yù)留
} TestParam_t;    /* 某模塊參數(shù) */
 
TYPE_CHECK_SIZE(TestParam_t, 8); // 檢查結(jié)構(gòu)體的大小是否符合預(yù)期
 
typedef struct
{
    uint8_t testParam;
    uint8_t testParam2;
    TestParam_t tTestParam;
    uint8_t reserve[54];   // 預(yù)留
} SystemParam_t; /* 系統(tǒng)參數(shù) */
 
TYPE_CHECK_SIZE(SystemParam_t, 64); // 檢查結(jié)構(gòu)體的大小是否符合預(yù)期
TYPE_MEMBER_CHECK_OFFSET(SystemParam_t, tTestParam, 2); // 檢查結(jié)構(gòu)體成員tTestParam偏移是否符合預(yù)期

假設(shè)新增了參數(shù),預(yù)留寫錯(cuò)了,導(dǎo)致結(jié)構(gòu)體的大小不符合,則編譯時(shí)報(bào)錯(cuò),且提示內(nèi)容也能快速定位問題。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多