物聯網下的RTOS開發(四)——數據存儲

數據存儲

在window或者linux下開發程序,程序員往往不會關心文件存儲的介質。因爲操作系統已經爲我們封裝好了操作硬件(磁盤)的可能會用的絕大部分接口。只要我們調用一下相關IO接口就能完成對磁盤的操作。而目前IOT設備資源很限,用於存儲的可以是ram或者flash,ram的容量往往是幾K字節到幾百K字節,flash往往小於8M字節。這給我們帶來沒有很好地文件系統支持。磁盤的擦寫均勻管理,掉電數據保存等問題,這些都會增加開發的複雜性,帶來數據保存穩定性以及代碼可移植性的問題。
接下去我會根據工作開發中遇到的經驗提出幾種現有的方案,以及推薦的做法。

一、存儲介質

IOT系統下的存儲介質可以簡單分爲ram和flash。我們知道ram掉電就會丟失,但是注意是掉電而不是重啓。這樣我們就有機會將重啓原因,或者一些必須重啓才能完成工作而又要保留flag的一些參數存儲在這裏。
Flash分爲norflash 和 nandflash,IOT的設備這兩種都會遇到,如果能上nandflash的系統主芯片能夠提供的資源也會相對豐富,存儲的接口一般會提供,這裏我們主要討論norflash。flash的特點是,只能將bit爲1的地方寫成0,這樣寫之前就要擦除操作。讀取flash的速度是很快的,但是寫很慢,擦除更慢。這裏我用上海復旦微FM25Q16A型號爲例,以下圖片摘自其datasheet,可以看出擦除一個sector(4K)正常需要35ms的時間。對於MCU來說這個時間已經有點久了。如果程序設計不當,會影響其他任務的執行,甚至可能導致看門狗喂狗不及時而導致系統重啓的情況。

在這裏插入圖片描述在這裏插入圖片描述

另外還有一種因爲故事機和智能音箱而再次興起的容量相對比較大的spi ram,一般在幾M字節。其本質還是ram,這種存儲方式在樂鑫推出的ESP32的模組方案應用中做的比較好,樂鑫官方支持了這種應用。
從長遠性考慮,我們還需要關注一個參數就是flash的擦寫次數,一般爲10W次的數量級,需要考慮壽命。這個在本系列的產品設計相關的章節也會有具體說明。

二、物聯網場景

數據存儲的場景無非是存儲臨時變量參數,系統配置,業務數據。

  • 臨時變量

臨時變量一般是存在ram中,寫程序的編譯器已經幫我們完成了。
這裏特別說一下noinit聲明的變量。對於需要重啓保留上一次臨時變量參數用於本次操作的可以使用這種技巧。要實現這個功能需要了解嵌入式系統的初始化啓動過程以及編譯鏈接一些知識,把相關的變量指定爲noinit關鍵字就可以。主流的編譯工具IAR和MDK都支持noinit的配置。
比如WiFi的正常模式和sniffer模式切換都往往需要重啓,這時可以將上一次工作的結果放在ram中而不必每次都寫flash來完成。

  • 系統配置

系統配置,存在於掉電不會丟失的flash中。系統上電初始化的時候都被讀取,對於這些參數有限是關鍵配置,有些丟失可以重新獲取的。比如串口波特率,在固定場景下通訊就是關心信息,不可丟失。而服務器下發的配置,或者祕鑰這些,丟了可以問服務器再要的,這些允許丟失。但是從工程師角度而言我們希望肯定是做到穩定不會丟失,接口調用也簡單,移植性強。下文模型設計中重點介紹。

  • 業務數據

在物聯網的應用場景中,iot設備大多用於傳感接入點。也就是用於數據採集,如溫度,電流電壓,位置信息等上報。這些數據大多隻是存在ram中,然後通過網絡發送到服務器做統一存儲展示分析。對於上報週期不頻繁的數據,比如一個小時一次,一天一次的數據,我們需要考量這個數據的可靠性到達的設計。這種數據的來源可能是在每個小時凍結,每天零點凍結的。凍結是指需要做存儲,在接下去的某個時刻需要將此數據上報的,實時性要求不高,但是數據可靠性要求很高,這樣我們必須設計存儲----上傳----應答了。

三、模型設計

存儲的應用場景不同,採用的方方法也不盡相同。這裏我給出針對flash三類常用的方法,並列舉各自的優缺點。

  • 直接存儲

直接操作Flash芯片的存儲地址,flash驅動提供常規支持以下幾個函數
flash_read()
flash_write()
flash_erase()
這個模式的有點就是簡單,缺點是操作過程中掉電會引起數據丟失。可以應用於現場配置參數的設定,人員在現場。以後運行過程中只讀不寫的場景。
此方法的很通用,設計上無需太多考慮,場景的可靠性不高的情況下,可以使用。

  • 可靠性直接存儲

爲了解決運行過程中數據讀寫,不丟失的核心思想是備份,寫之前先備份。
提供一種乒乓球類似參考。

struct savePara {
    int _magic;        // 用於校驗的數據合法性
    int cnt;            // 存儲的計數
    
    userStruct para;  // 用戶存儲數據結構體
};

開闢兩個獨立空間(sector爲單位),magic用於記錄數據的合法性,magic可以隨意的一個固定值,只要讀出來時候對應就可以。cnt用於記錄數據的新舊,cnt小的表示數據比較舊。於是有以下讀寫操作。
數據讀取的步驟:

  1. 讀取兩個區塊的cnt,如果magic均合法,cnt大的是最新數據
  2. 如果magic均不合法,則無數據
  3. 如果magic一個合法,則合法爲新數據

數據寫入步驟:

  1. 讀取兩個區域的結構體,記錄cnt的值,並比較出該數據塊的新舊;
  2. 擦除cnt小的一塊,以新數據塊的cnt++後,將用戶數據寫入
  • 文件系統應用

其實用戶更多關注的業務,存儲更多應該交給系統來完成,文件系統就提供了這個功能。他有統一的接口來實現數據的存儲和讀寫,弱化硬件的相關性。另外,上面提到flash的擦寫次數是有限的。如果每次存儲都對同一個位置進行擦除,那麼壽命就是10W次。如果每次寫入flash數據的位置不在同一個地方,這樣就能增加存儲的次數。而這裏的難點就是如何實現這個算法。這個算法也叫磨損均衡(Wear-Leveling)。一些文件系統有這個功能,有些則沒有。這裏推薦LittleFS,最要針對小系統設計,且功能齊全。源代碼託管在GitHub上。https://github.com/ARMmbed/littlefs
但是不管怎麼說,文件系統仍然帶來了ROM和RAM的額外開銷,這也是他的弊端。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章