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)如下: /***************************** 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++ 反而使用簡單
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 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è)是必須要的) 見代碼:
通過以上代碼,就能解決這個(gè)問題,這個(gè)寫法只占用文本大小,編譯后不占內(nèi)存?。?! 用法: typedef struct 假設(shè)新增了參數(shù),預(yù)留寫錯(cuò)了,導(dǎo)致結(jié)構(gòu)體的大小不符合,則編譯時(shí)報(bào)錯(cuò),且提示內(nèi)容也能快速定位問題。 |
|