天天看點

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

痞子衡最近在參與一個基于 i.MXRT1170 的項目,項目有個需求,需要在 Flash 裡實時儲存一些關鍵資料(初步設 512 bytes),掉電能恢複。這些資料在通路方式上要友好,最好是很簡單的 API 接口,上層無需操心關鍵這些資料在 Flash 裡是如何存儲以及具體存儲在什麼位置,隻需在意關鍵資料儲存和讀取的操作即可(就像在 RAM 裡動态存取那樣)。

大家好,我是痞子衡,是正經搞技術的痞子。今天給大家帶來的是痞子衡的個人小項目 - kFlashFile。

根據上述需求,痞子衡做了一個參考設計,命名為 kFlashFile,目前是 v1.0 版本。痞子衡寫了比較詳細的設計文檔,特地分享給大家,如果大家有更好的建議和想法,歡迎在文章下面留言。

項目位址:https://github.com/JayHeng/kFlashFile

kFlashFile 是一個基于 NOR Flash 的輕量級檔案資料存儲方案,用于需要斷電資料儲存的項目。

kFlashFile 主要為 i.MXRT 系列設計,但其分層架構設計使其也可輕松移植到其他 MCU 平台。

kFlashFile 從設計上分為三層:

最底層是Driver層:即Low-level驅動,這層是MCU相關的,對于i.MXRT來說,就是FlexSPI子產品的驅動。 中間是Adapter層:主要用于适配底層Driver,不同MCU其Driver接口函數可能不同,是以會在這一層做到接口統一。 最頂層是API層:純軟體邏輯設計來實作檔案資料存儲,提供了四個非常簡易的API。
痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

kFlashFile 是一個檔案資料存儲的設計,file_read()、file_save()是兩個必備的 API,此外也提供業界通用 API 接口file_init()、file_deinit()。

kflash_file_init(): 用于初次配置設定Flash空間來存儲檔案資料,并且指定檔案長度。如果目前指定的Flash空間裡存在有效檔案資料,那麼繼續複用。 kflash_file_read(): 用于擷取目前有效存儲的檔案資料,檔案資料可以部分讀取。 kflash_file_save(): 用于實時寫入最新的檔案資料,檔案資料可以部分更新。 kflash_file_deinit(): 用于清除目前配置設定的Flash空間裡的檔案資料,以便下次重新配置設定。

kFlashFile 将配置設定的 Flash 空間分成兩個部分,前面是檔案資料區(Data Sectors),後面是檔案頭區(Header Sectors)。

檔案資料區:從區内起始位址開始按序存放一份份檔案資料,隻要檔案資料出現無法覆寫的更新(即 Flash 無法改寫的特性),便會在下一個新位址重新存儲。如果資料區滿了,便擦除區内起始位址處的曆史檔案資料,繼續循環存儲。

檔案頭區:區内 Sector 起始位址放一個 Magic 值(4位元組),用于辨別檔案頭。然後開始按序記錄一份份檔案資料在檔案資料區裡的位置資訊(預設用 2byte 去記錄一份檔案資料的位置)。如果目前 Header Sector 存儲滿了,便換到下一個 Header Sector 繼續記錄。

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

kFlashFile 設計上使用 kflash_file_t 型作為 API 主參數,這個參數原型定義如下:

managedStart: 表示檔案存儲區映射首位址,即 kflash_file_init() 調用時的 memStart 值加上 Flash 在記憶體裡映射首位址,managedStart 需要以 Flash Sector 大小對齊。 managedSize: 表示檔案存儲區總大小,即 kflash_file_init() 調用時的 memSize 值,需要是 Flash Sector 大小的整數倍。 activedStart: 表示目前有效檔案資料存儲的映射首位址,需要以 Flash Page 大小對齊。 activedSize: 表示目前有效檔案資料長度,需要是 Flash Page 大小的整數倍。 recordedIdx: 表示目前有效檔案頭所在的 Header Sector 索引。 recordedPos: 表示 Header Sector 中用于存儲目前有效檔案資料位置資訊的區域偏移。 buffer[]: 目前有效的檔案資料暫存區。

在 i.MXRT 系列上,kFlashFile 的 Driver 層即 FlexSPI NOR 驅動,這個驅動既可以采用 MCU SDK 版本,也可以采用 BootROM 版本。

此處推薦 BootROM 版本的 FlexSPI NOR 驅動,因為這個驅動曆經多個 MCU ROM 的洗禮,已經相當成熟穩定。這裡簡單講下其中 Flash 操作的函數:

flexspi_nor_flash_erase(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, uint32_t length):這個函數實作Flash擦除,雖然形參裡是任意設定的start, address,但實際擦除還是以Sector對齊的,函數内部會對start和address做自動對齊。 flexspi_nor_flash_page_program(uint32_t instance, flexspi_nor_config_t *config, uint32_t dstAddr, const uint32_t *src):這個函數實作Flash程式設計,一次固定寫一整個Page大小的資料,即使dstAddr不是以Page對齊,實際寫入的Page資料也不會跨實體Page(會自動跳回同一個實體Page首位址,這是Flash自身特性)。

因為 flexspi_nor_flash_page_program() 每次都要固定程式設計整個 Page 資料,不夠靈活,是以我新寫了一個 flexspi_nor_flash_program() 函數,這個函數支援程式設計使用者自定義長度的資料,并且支援跨實體 Page 去寫:

flexspi_nor_flash_program(uint32_t instance, flexspi_nor_config_t *config, uint32_t dstAddr, const uint32_t *src, uint32_t length):

需要特别注意,對于 SDR 模式的 Flash,最小程式設計長度可以是 1Byte;而 DDR 模式的 Flash,最小程式設計長度應是 2Bytes(如果這 2Bytes 位址上有一個 Byte 内容是 0xFF,該 Byte 依舊可以被再次程式設計)。

此外 flexspi_nor_flash_program() 函數有一個限制,即傳入的 src 源資料首位址必須 4 位元組對齊,哪怕你隻想寫入 2 個位元組,這是 FlexSPI 子產品底層對驅動的要求。

kFlashFile 的 Adapter 層是對 Driver 層做了一層封裝,用于屏蔽硬體相關特性。該層與 MCU 以及闆載 Flash 型号息息相關。下面的宏定義适用 i.MXRT1170 晶片以及連接配接在 FlexSPI1 上的 Octal Flash(MX25UM51345):

kFlashFile 的 Adapter 層接口函數如下,參數是硬體無關的,是以上層可以輕松基于這些接口函數做純軟體邏輯設計。

kFlashFile 的 API 功能設計思路前面介紹過了,這裡介紹具體代碼實作,先來看幾個關鍵的宏定義:

kflash_file_init() 函數處理流程如下:

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

如果是首次指定 Flash 空間,那麼直接将全部空間擦除幹淨,并在第一個 Header Sector 中寫入初始檔案頭(Magic + 檔案資料位置值 0),即最新有效檔案資料在 Flash 空間檔案資料區的首位址。

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

這裡有一個特殊的設計,檔案資料區其實并不是直接存儲使用者寫入的檔案資料,而是将使用者檔案資料全部按位取反之後再存儲進 Flash。這裡假定使用者資料初始應該是全 0,然後更改主要是将 0 值改為其他值,取反之後,正好對應 Flash 裡的 bit1 程式設計為 bit0(Flash 擦除後是全 0xFF),這樣可以充分利用 Flash 覆寫操作以減少擦除次數。

函數中比較關鍵的步驟是找尋目前 Flash 空間中是否存在有效檔案資料,方法是周遊 Header Sector,發現存在 Magic 便繼續尋找最新檔案資料位置資訊存放的區域(預設 2 位元組),按照前面的設計,隻需要按序讀取區域内容,直到遇到 0xFFFF 為止。

kflash_file_read() 函數最簡單了,直接從緩存區 buffer 裡擷取資料即可,因為每次更新檔案資料操作完成之後都會将最新檔案資料放在 buffer 裡。

kflash_file_save() 函數是最核心的函數了,這裡邏輯比較複雜,涉及檔案資料區全部滿了之後的動作,以及檔案頭區某個 Sector 滿了的動作。其處理流程如下:

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

當有一個新檔案資料要求儲存時,首先會判斷這個檔案能不能在 Flash 中直接覆寫存儲,如果能,那就直接覆寫存儲,檔案頭完全不需要更新,這種情況比較簡單。

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

如果新檔案資料無法直接覆寫存儲,那麼首先判斷檔案資料區是否滿了,如果上一個檔案資料已經存在了檔案資料區的最後位置,此時需要擦除資料區第一個 Sector 從頭開始存儲。如果沒有到最後位置,那就按序往下存儲。

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

新檔案資料已經儲存到資料區之後,此時需要處理檔案頭,記錄這個新檔案資料的位置。如果檔案頭區已經記錄到目前 Sector 的最後位置,需要切換到下一個 Sector 開始存儲,切換存儲完新位置後,将之前 Sector 擦除。如果沒有,那就按序在目前 Sector 繼續記錄。

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

kflash_file_deinit() 函數也比較簡單,就是将檔案頭區域 Header Sectors 全部擦除即可,檔案資料區内容可以不用管,下次重新配置設定 Flash 時會做擦除。

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

文章會同時釋出到我的 部落格園首頁、CSDN首頁、微信公衆号 平台上。

微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

  最後歡迎關注痞子衡個人微信公衆号【痞子衡嵌入式】,一個專注嵌入式技術的公衆号,跟着痞子衡一起玩轉嵌入式。

痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案
痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案
痞子衡嵌入式:kFlashFile v1.0 - 一個基于Flash的掉電資料存取方案

  衡傑(痞子衡),目前就職于恩智浦MCU系統部門,擔任嵌入式系統應用工程師。

  專欄内所有文章的轉載請注明出處:http://www.cnblogs.com/henjay724/

  與痞子衡進一步交流或咨詢業務合作請發郵件至 [email protected]

  可以關注痞子衡的Github首頁 https://github.com/JayHeng,有很多好玩的嵌入式項目。

  關于專欄文章有任何疑問請直接在部落格下面留言,痞子衡會及時回複免費(劃重點)答疑。

  痞子衡郵箱已被私信擠爆,技術問題不推薦私信,堅持私信請先掃碼付款(5元起步)再發。