|
1、前言 想必做嵌入式產(chǎn)品開發(fā)都遇到過設(shè)備需要保存參數(shù),常用的方式就是按照結(jié)構(gòu)體的方式管理參數(shù),保存時將整個結(jié)構(gòu)體數(shù)據(jù)保存在 Flash 中,方便下次讀取。 1.1、目的本文時分析嵌入式/單片機中參數(shù)保存的幾種方式的優(yōu)點和缺點(僅針對單片機/嵌入式開發(fā)而言),同時針對以結(jié)構(gòu)體的方式解決一些弊端問題(重點在第 3 節(jié))。 2、參數(shù)保存格式2.1、結(jié)構(gòu)體格式該方式是嵌入式/單片機中開發(fā)最常用的,將所有的系統(tǒng)參數(shù)通過結(jié)構(gòu)體的方式定義,然后保存數(shù)據(jù),介紹一下該方式的優(yōu)缺點。 儲存方式:二進制 bin 文件格式 優(yōu)點: 管理簡單:無需額外的代碼直接就能很方便的管理參數(shù) 內(nèi)存最小:通過結(jié)構(gòu)體的形式保存在Flash中,占用內(nèi)存最小 缺點: 1.擴展性差: 從產(chǎn)品角度來說,產(chǎn)品需要升級,若是涉及增加參數(shù),則升級后參數(shù)通常無法校驗通過(通常包含長度校驗等),導(dǎo)致參數(shù)被恢復(fù)默認 若是每個模塊都存在自己的獨有結(jié)構(gòu)體參數(shù)定義,刪除/新增時勢必影響到其他的,導(dǎo)致設(shè)備升級后參數(shù)錯亂(結(jié)構(gòu)體中的變量地址在 bin 文件中是固定的) 2.閱讀性差:若參數(shù)需要導(dǎo)出,bin文件沒有可讀性 改進措施: 結(jié)構(gòu)體增加預(yù)留定義,若之后需要新增參數(shù),則在預(yù)留空間新增即可,能在一定程度上解決擴展性差的問題,即新增不影響原有的結(jié)構(gòu)體大小和其他成員變量的位置,刪除恢復(fù)成預(yù)留即可。 為啥說只能在一定程度上解決該問題,因為之后的升級某些模塊可能很長時間或者從不需要增加新的參數(shù),這種勢必就會造成內(nèi)存的無效占用,或者有些模塊頻繁增加參數(shù)導(dǎo)致預(yù)留大小不夠等問題,只能在前期設(shè)計時多加思考預(yù)留的分配情況(畢竟內(nèi)存只有那么大),改進如下: /*****************************2.2、JSON格式最近Json格式很是流行使用,特別是數(shù)據(jù)交換中用的很多,但是它也可以用來保存參數(shù)使用,JSON 的是 “{鍵:值}” 的方式。 儲存方式:字符串格式,即文本的形式 優(yōu)點: 擴展性好:由于Json的格式,找到對應(yīng)鍵值(一般都是該變量的標識),就能找到對應(yīng)的值 閱讀性好:有標識所以導(dǎo)出參數(shù)文件通過普通的文本文件打開都能看懂 缺點: 管理相對復(fù)雜:沒有結(jié)構(gòu)體那么簡單,不熟還得先學(xué)習(xí) JSON 的寫法 內(nèi)存占用較大:內(nèi)容不只有值,而且都按照字符串的形式保存的 使用相關(guān)困難:需要解析,C語言雖然有開源庫,但是由于語言性質(zhì)使用不方便,C++ 反而使用簡單 2.3、鍵值格式和上述的 JSON 格式很類似,都是鍵值對的格式,但是比JSON簡單 儲存方式:字符串格式,即文本的形式 優(yōu)點: 擴展性好:找到對應(yīng)鍵值(一般都是該變量的標識),就能找到對應(yīng)的值 閱讀性好:有標識所以導(dǎo)出參數(shù)文件通過普通的文本文件打開都能看懂 缺點: 內(nèi)存占用較大:內(nèi)容不只有值,而且都按照字符串的形式保存的 使用稍微困難:需要簡單解析處理 管理不變:不方便按照一定的規(guī)則管理各模塊的參數(shù) testParam=22.4 其他還有其他,如 xml (類似JSON)等,就不多介紹了 3、編譯器檢查結(jié)構(gòu)體的大小和成員變量的偏移在第 2 節(jié)中介紹了關(guān)于參數(shù)保存的三種方式,但是對于嵌入式單片機開發(fā)而言,F(xiàn)lash 大小不富裕,所以通常都是通過二進制的形式保存的,所以這節(jié)重點解決結(jié)構(gòu)體管理保存參數(shù)的擴展性問題。 先說一下痛點(雖然對擴展性問題做了改進措施,除了前面講到的問題,還有其他痛點,雖不算問題,但是一旦出現(xiàn)往往最要命) 在原來的預(yù)留空間中新增參數(shù),要確保新增后結(jié)構(gòu)體的大小不變,否則會導(dǎo)致后面的其他參數(shù)偏移,最后升級設(shè)備后參數(shù)出現(xiàn)異常(如果客戶升級那就是要命?。?確保第一點,就必須在每次新增參數(shù)都要計算檢查一下結(jié)構(gòu)體的大小有沒有發(fā)生變化,而且有沒有對結(jié)構(gòu)體中的其他成員也產(chǎn)生影響 每次新增參數(shù),手動計算和校驗 99% 可以檢查出來,但是人總有粗心的時候(加班多了,狀態(tài)不好…),且結(jié)構(gòu)體存在填充,一不留神就以為沒問題,提交代碼,出版本(測試不一定能發(fā)現(xiàn)),給客戶,升級后異常,客戶投訴、扣工資(難啊…) 遇到這種問題后:難道編譯器就不能在編譯的時候檢查這個大小或者結(jié)構(gòu)體成員的偏移嗎,每次手動計算校驗好麻煩啊,一不留神還容易算錯 # _ # 按照正常情況,編譯器可不知道你寫的結(jié)構(gòu)體大小和你想要的多大,所以檢查不出來(天啊,崩潰了0.0…) 別急,有另類的方式可以達到這種功能,在編譯時讓編譯器為你檢查,而且準確性 100%(當然,這個添加新參數(shù)時你還得簡單根據(jù)新增的參數(shù)大小減少預(yù)留的大小,這個是必須要的) 見代碼: 通過以上代碼,就能解決這個問題,這個寫法只占用文本大小,編譯后不占內(nèi)存?。。?/p> 用法: typedef struct假設(shè)新增了參數(shù),預(yù)留寫錯了,導(dǎo)致結(jié)構(gòu)體的大小不符合,則編譯時報錯,且提示內(nèi)容也能快速定位問題。 |
|
|
來自: myallmy > 《嵌入式軟件架構(gòu)》