引 言
文件在現代電子設備中非常重要。處理器通過操作系統對不同類型的文件進行訪問,但是在嵌入式領域和低功耗定制 Linux解決方案中,相關的方案比較少見。很多嵌入式開發者習慣把數據放在代碼中,導致每次改動都需要升級固件代碼。JesFS嵌入式文件系統就是要解決這類問題——專門為小型低功耗設備設計。 電影《終結者2-審判日》中,機器人T 800(施瓦辛格)說過“我有詳細的文件”,以此來表明他是一個強大的戰斗機器人。這里說到了關鍵:文件無疑是高科技產品的關鍵元素! 這一點,同樣適用于所有的嵌入式系統。這篇文章將為您介紹一個合適的嵌入式文件系統。
文件與代碼
文件本質是一個基本的容器,用來組織和傳輸各種類型的信息。大到服務器,小到單片機,都可以理解這些信息,操作系統針對不同的文件提供了接口。 在嵌入式系統領域和低功耗定制 Linux解決方案中,相關方案很少。在很多重要項目中,代碼和數據沒有分離,常常導致項目失敗。舉例來說,系統參數放在代碼里很容易,但是如果以后想修改,每次都必須升級固件。然而,現在通常的做法是在線更新。 筆者從業于高海拔地區的科學測量和相關儀器領域,幾個月前有一次相關的經歷:在海拔3000米的山上,為一個老舊設備更改配置。經過精疲力盡的挖掘后,筆者認為通過互聯網更新,比在-20 ℃的雪地中挖洞更新要容易得多。最終,筆者決定使用文件系統。 雖然有很多嵌入式文件系統,但這些文件系統大多只適配它們所在的領域。所以筆者決定自己開發一個。最終,JesFS(Jo’sembeddedFileSystem 的縮寫)誕生了,面向微小系統和極低功耗的應用。
JesFS
設計JesFS最重要的標準就是可靠性和小封裝,同樣重要的還有數據完整性和遠程固件升級。配合JesFS,還需要開發JesFS的bootloader,幫助處理器讀取 AES加密固件。bootloader 和JesFS完全獨立,這里不做討論。Jurgen Wickenhauser 目前全部精力都在 JesFS 上。JesFS 已經是一個重要不可或缺的工具。JesFS在GPL v3[1]協議下開源。
參考實例
JesFS使用標準C編譯器編譯。接下來將會在TI的CC1310 開發板上展示一些功能。CC1310 基于32位ARM Cortex - M3架構,具有868/915 MHz無線傳輸功能。
CC13xx/26xx系列處理器的Launchpad開發平臺物美價廉,配備TI的CCSTUDIO[3]IDE,可以進行源代碼調試。CC13xx/26xx開發平臺配備了一片1MB的串行Flash存儲器,大小只有2mmx3mm! 免費的TI-RTOS可以提供一個工業級的實時操作系統以及TI大量的技術支持。JesFS 的源代碼既可以在Launchpad開發平臺上編譯,也可以使用 Windows或Linux平臺的標準C編譯器進行編譯,但是需要具有模擬Flash存儲器驅動的支持。
小型 Flash存儲芯片
嵌入式系統通常不需要太多的存儲空間,1~16 MB 就足夠了。Flash存儲芯片現在可以制造得非常小:1 MB Flash可以小到1.24 mmx1.29 mm(見圖2)。
JesFS文件系統本身占用的空間非常小,僅僅占用RAM的200字節就可以運行得很好。還有一方面是經濟原因:許多大型文件系統使用RAM作為緩存,不僅很復雜,占用很多的存儲空間,而且切換操作模式(比如休眠或關機)會很慢。Jes-FS設計之初就考慮到低功耗的問題,可以在幾毫秒內實現休眠和喚醒。而且,串行存儲器在睡眠模式下消耗電流小于1μA,非常適合電池供電設備。
針對 loT 的獨有功能
JesFS的一個基本特性是:可以把嵌入式系統的文件通過互聯網自動地鏡像存儲到服務器,服務器因此具有嵌入式文件系統的一個實時更新的副本。這個特性也可以用在分鐘級獲取數據的場景,但是為了降低功耗,一般不會頻繁通過互聯網對比更新服務器副本(通常幾小時一次)。 即使傳輸了新文件、遠程升級了固件,只要有需要,JesFS就可以通過特殊標志位(時間戳和CRC32 哈希校驗)很容易地從服務器上找回文件。如果展開這些特殊的用法將有無數種可能的應用場景。所有的特性和腳本,作者都經過仔細測試。關于FesFS文件系統功能的詳細描述,可以參考相關鏈接[1]。
解決目標:未關閉的文件
對大多數文件系統來說,如果沒有打開文件的寫入權限,就只能讀文件。那么問題來了:一個異常可能導致未關閉文件,甚至最壞的情況下導致數據丟失。而JesFS不會出現這種情況,文件在任何時候都可以打開讀寫,而且不會導致數據丟失。這個特性非常適合日志文件,例如,嵌入式系統存儲新數據,同時服務器更新到最新數據。
JesFS 的限制
JesFS甩掉沉重的負擔后,訪問次數還沒有調整到最優。未來還將對未關閉文件做一些小的調整和限制。因為是針對資源有限的小型嵌入式系統設計的簡單文件系統,所以對文件名長度進行了限制,最多21個字符。為了減輕CPU負擔,也沒有實現索引數據。總體來說,就是一個扁平化的文件系統。針對小型系統的實際應用,這些限制不應引起任何相關的限制。JesFS文件系統的簡潔和易用,可以大大提高效率。作者本人就是最嚴格的測試者。
串行 Flash存儲器
設計軟件細節之前,先介紹幾個關于Flash存儲器的基本知識。Flash存儲器有許多種類型,在嵌入式系統領域主要會用到串行NOR Flash芯片。為了能讓JesFS更好地適配硬件,有些需要詳細說明,同時也涉及到一些底層驅動的開發。 總體來說,幾乎所有相關的Flash存儲器都有類似的結構,只是在容量、靜態電流、最大時鐘速率、數據位數方面有所不同。所有串行 NOR Flash芯片都有6個共同特性: ①空存儲器,默認存儲位為‘1’; ②寫入數據時,根據需要把存儲位‘1’設置成‘0’; ③相反,擦除操作把數據位從‘0’恢復成‘1’; ④每一個寫操作可以寫入1~256字節數據,大概需要1 ms; ⑤數據以塊或扇區或全部擦除,扇區通常4KB,擦除一個扇區需要20~200 ms(扇區擦除通常很少用到); ⑥擦除操作是有次數限制的,通常不超過100 000次。 訪問串行 Flash 芯片很容易,只需要進行4步操作(見圖): ①SELECT引腳電平從1到0后的幾納秒內,芯片激活; ②通過CLK引腳(時鐘)和DIN引腳(數據)寫入數據和命令; ③通過CLK(時鐘)引腳和DOUT(數據/狀態)引腳讀取數據和狀態; ④SELECT 引腳電平從0到1,結束操作。
圖? 8腳串行 Flash 引腳分配
串行 Flash 芯片最大時鐘速率通常很高(>50MHz),多數微處理器的硬件驅動不能充分利用。這里參考設計平臺使用的CC1310處理器的12 MHz時鐘速率,可以達到很不錯的1MB/s的數據吞吐量。 下面推薦適用于小型系統的通用存儲芯片,雖然有些有兼容的替代芯片,但替代芯片功耗可能會比較高: ◆MX25R8035F (1 MB) to MX25R1635F (16 MB) ◆W25Q80DL (1 MB) to W25Q128JV (16 MB) ◆GD25Q16 (2 MB)?
黑盒Demo:家用飛行記錄儀
許多嵌入式設備在幾年的生命周期內都可以無錯誤運行,但偶爾也會產生錯誤。產生錯誤時,我們會問:“發生了什么?”要回答這個問題,我們需要有足夠的歷史日志數據分析和診斷。舉例來說,內部參數(如溫度、電壓、濕度)是否發生了改變?是否有人進行了調整?之前是否有過類似的問題?這些問題與飛機上的黑盒子相似。 然而,嵌入式系統通常沒有足夠的空間做這些。不過,JesFS文件系統的未關閉文件可以穩定、高效地記錄診斷數據。JesFS可以避免產生僵尸數據,即使是在復位或斷電的極端情況下,也可以寫入數據。 依靠兩個文件來實現這一目的: ◆ 主文件Data. pri 可以寫入最大數據長度為HIS-TORY定義的長度; ◆ 改名為輔助名Data. sec; ◆ 新創建的輔助文件替換原有的輔助文件。 通過定義HISTORY,可以得到特定長度的歷史數據,從而確保歷史數據的歷史記錄數量是一個常量,且不會超過2倍的歷史記錄。通過檢索數據來獲得正確的順序,首先是Data. sec,然后是 Data. pri。 可以在相關鏈接[1]中找到針對各種編譯器(PC和嵌入式)的BlackBox演示的完整的文檔化源代碼,包括Jes-FS的源代碼文件。這里打印的清單顯示了代碼的摘錄:
***************************************
*log_blackbox(char * logtext,?uint16_t len)
*Thisfunktion logs one line to the the history
*****************************************
int16_tlog_blackbox(char* logtext,?uint16_tlen){
FS_DESCfs_desc,fs_desc_sec; //2 JesFs file descriptors int16_t res;
res=fs_start(FS_START_RESTART);
//(fast) WAKE JesFs (might be sleeping)if(res) return res;
//Flags (seedocu) : CREATE File if not exists and open in
//RAW mode,(RAW needed because in RAW -Mode file is not truncated if existing)
res=fs_open( &fs_desc,"Data,?pri",SF_OPEN_CREATE SF_OPEN_RAW);
if(res)return res;
//Place (internal) file pointer to the end of the file to allow write fs_read(&fs_desc,NULL,0xFFFFFFFF);
//(dummy) read as much as possible
//write the new data (ASCII,?from function rguments) to
//the file
res=fs_write(&fs_desc,logtext,tlen);
if(res) return res;
//Show what was written
uart_printf("Pos:%u Log: %s",fs_desc. file_len,?"logtext);
//Now make a file shift if more data than defined in HISTORY if(fs_desc. file_len>= HISTORY){
uart_printf("Shift 'Data. pri' ->'Data. sec' ");
//Optionally delete and (butCREATE in any case) backup file
res= fs_open (&fs_desc_sec,"Data. sec",?SF_OPEN if(res)return res;CREATE);
if(res) return res;
//rename (full) data file to secondary file
res=fs_rename( &fs_desc,?&fs_desc_sec);
if(res) return res;
}
fs_deepsleep(); ?//Set Filesystem to UltraLowPowerMode
return0; ?//OK
驅動程序基礎
JesFS使用上層和底層驅動程序。底層驅動程序針對串行Flash,再次被劃分為通用命令 JesFS_ml.c和串行SPI的硬件控制部分(這里是jesfs_tirto. c)。上層驅動程序在相關鏈接[1]中有詳細描述。 閃存的處理非常簡單(大多數閃存芯片使用的特殊命令在這里不使用),但是很少有命令是相關的,幸運的是,對于幾乎所有可用的串行 Flash 芯片都是相同的。對于更大的容量(≥32MB),也只是把地址從3字節擴展到了4字節。 源代碼文件更精確地記錄了命令。以下是十六進制記數法的命令概述。 9Fh:讀取一個3字節的ID(十六進制),其中對制造商、類型和容量(十六進制2的乘方)進行了編碼。對于在CC1310中的MX25R8035芯片,這個ID是“C22814”,其中C2h=Macronix,28h=Type, 14h=20=220=1 MB。 B9h:讓閃存芯片進入深度睡眠。 ABh:在5~50 μs內喚醒閃存芯片。 03h:讀操作。首先,準備讀取3字節地址(對32 MB 的容量來說是4字節),讀取完數據后,cs#變高。 06h:必須在每次寫/刪除操作之前發送。 02h:寫操作。首先發送3或4個地址字節,然后是1~256字節數據,不超過256字節。 20h:刪除扇區。首先發送3或4個地址字節,然后刪除相關的4 KB扇區。 05h:測試之前的寫/刪除命令是否已經完成。
Flash中的組織結構
實現的細節和圖形可以在JesFS[1]的文檔和源代碼中找到,這里只畫了結構。JesFS將閃存分為3種不同的扇區類型。每個邏輯扇區與閃存芯片中的一個物理4KB扇區對齊。因此,一個容量為1 MB的閃存芯片最多可以存儲255個文件。 扇區0具有特殊的意義,因為它用作文件索引。每個索引條目占用4個字節。因此,可以將索引視為一個包含1024個"無符號長整數"的數組。對于0扇區,必須遵守以下規定: ◆ 索引扇區0僅在格式化期間刪除; ◆ 前3個條目包含格式化信息 Magic value、Flash ID (參見命令9Fh)和格式化日期(從 1970.1.1開始的Unix秒數),FFFF FFFFh值被計算為無效/未格式化; ◆ 其余的1021項是文件heads的起始地址,所有這些項必須能被212=4096(扇區大小)整除(JesFS 會檢查)。理論上,最多可以有1021個文件。 對于1至n扇區有5種可能: ◆ 扇區為空(包含FFh值的完整字節); ◆ 扇區是活躍文件的頭; ◆ 扇區是指向已刪除文件的頭; ◆ 扇區是活躍文件的一部分; ◆ 扇區是活躍文件中已刪除的部分。 head是索引指向的扇區。因此,對heads進行特殊處理是很重要的,因為只有在格式化時才會刪除索引中的值。每個扇區總是以3個(或12個 heads)"unsigned long"類型的值開始: ◆ 使用magic value(頭,文件或空); ◆ 此區塊的所有者(引用頭部,如果該塊本身是頭部,則引用FFFF FFFFh值); ◆指向該文件的下一個區塊的指針,或者FFFF FFFFh(如果這是鏈的最后一個塊); 然后要么是文件數據,要么是一個head: ◆文件長度,以字節或FFFF FFFFh表示; ◆ 可選的CRC32 哈希校驗(如果在CRC32 模式下寫入并關閉的); ◆ 文件的創建日期(自1970.1.1起,以Unix秒為單位); ◆ 文件名稱(最多21個字符,不包括最終字符0h); ◆文件的打開標志(1字節); ◆ 一個被保留的空字節。 一個 head總是占用48個字節,一個普通的數據扇區占用12個字節,起始部分是管理信息。其結構如圖所示。
圖 索引總是指向文件頭的扇區0?
刪除扇區僅僅做個標記。所使用的Magic values是這樣選擇的:通過使用“0”進行額外的覆蓋(發生得非常快)來主動刪除狀態。只有當一個扇區確實需要,并且沒有空閑扇區的時候,才能通過命令20h刪除,這需要更多的時間。
磨損平衡和未關閉文件
使用Flash時,所有扇區的使用頻率都應該相等,這一點非常重要,因為通常只能擦寫約10萬次。所以你可以在很短的時間內在一個循環中“殺死”一個扇區。這種避免策略稱為“磨損平衡”。對于 JesFS,這意味著一個文件(由于索引中的固定條目)最多可以被刪除100 000次。乍一看,擦寫次數似乎很多,但對于嵌入式系統來說,這個限制在某些應用程序中肯定是有問題的。另一方面,應該清楚,即使每小時每個扇區擦寫一次,也會經過11年。對大多數項目來說,還是很充裕的。 為了盡可能少地刪除扇區,讓活躍或使用過的文件保持打開也是一個辦法;被刪除的字節的值總是FFh。如果有問題的文件從未使用這個值(例如,只保存 ASCII數據或用轉義序列替換FFh),也可以通過簡單讀取(最多一個FFh字符)來確定文件的結尾。JesFS有一個非常快的子程序,可以在幾毫秒內找到一個16 MB文件的結尾。
CRC-32哈希和數據完整性
只有很少的文件經常更改。因此,JesFS在寫(和讀)一個文件時可以加CRC-32校驗,與“文件關閉”中頭部的長度一起記錄下來。通過這種方式,可以在任何時候通過簡單地讀取文件來檢查文件的完整性。所使用的CRC-32算法是行業標準(ISO 3309等),甚至得到了PHP(一種常見的服務器腳本語言)的支持。 在JesFS開始時(或者在系統每次喚醒之后)執行一個全面的測試來檢查JesFS的基本完整性。JesFS非常注意 bug。演示的源代碼中還包含一項功能,可以檢查完整的JesFS“心臟和腎臟”(即所有扇區和文件)。
引導程序和固件升級
由于JesFS的結構非常簡單,即使是一個簡單的引導程序也可以輕松地讀取文件并測試完整性。但是,引導程序通常只綁定到特定的CPU架構或控制器類型。
測試 JesFS
測試JesFS的最簡單方法是使用CC13xx/26xx系列的launchpad開發平臺,這些板卡大約30歐元。在Github 上的JesFS文檔中,還可以找到一個簡單的控制臺應用程序的手冊。 除此之外,還可以在運行 Windows或Linux的PC上編譯JesFS,在RAM中模擬Flash芯片。生成的image文件完全兼容Flash芯片。如果微控制器的數據格式是lit-tle-endian的,可以很容易地通過編程器進行傳輸或讀取,大多數 ARM SoC(如 MSP430、MSP432等)都支持。
下一步做什么?
根據讀者的反饋,這個報告僅僅是一個開始。文件系統不是一件簡單的事情,但是如果JesFS能夠成為一種通用的工具,作者將會非常高興,并深信它具有驚人的潛力。 當然,JesFS不僅限于CC13xx/26XX系列的控制器,而且在原則上幾乎適用于 16位以上的所有處理器。Jurgen Wickenhauser 目前正在為MSP430系列移植Jes-FS,還有許多可以想到的與JesFS結合的項目,特別是無處不在的物聯網提供了無限的可能性。如果你真的感興趣,可以通過作者的電子郵件(joembedded@gmail. com)聯系。
相關鏈接
[1] JesFS V1.1 auf Github: https://github. com/joem-bedded/JesFS
[2] TI-Launchpad: www,ti,com/tool/LAUNCHXL-CC1310
[3] CCSTUDIO:www.ti. com/tool/CCSTUDIO
[4] Macronix Flash: www. macronix. com/en - us/prod-ucts/NOR - Flash/Serial-NOR - Flash
Von Wickenhauser:從小就對電子產品充滿熱情。在學習了電氣工程之后,于 1992 年把愛好變成了職業,在德國南部從事科學環境測量儀器方面的開發工作。
作者:Von Wickenh?user
譯者:透鏡
審核編輯:黃飛
?
評論
查看更多