一、存儲芯片W25Q系列
w25q 系列生產的加工的商家很多,但是裏面的分佈和命名規則都是一樣的。比如華邦的w25q64,spi通訊接口,64就是指 64Mbit 也就是 8M 的容量。而我們平時的8266-12f的 32Mbit 就是 4M 容量。
以 w25q32 爲例,裏面的存儲分佈。w25q32把4M容量分爲了 64 塊,每一塊又分爲 16 個扇區,而每個扇區佔 4K 大小。由此可計算到,w25q32有 32Mbit / 8 * 1024 / 16 / 4 = 64 塊 ,有 64 * 16 = 1024 個扇區。
注:1B=8 Bit ,1KB=1024B ,1MB=1024KB
二、ESP8266內存分佈
- 程序區:代碼編譯生成的 bin 文件,燒錄到 Flash 佔用的區域,請勿改寫;
- 系統參數區: esp_iot_sdk 中底層用於存放系統參數的區域,請勿改寫;
- 用戶參數區:上層應用程序存儲用戶參數的區域。開發者請根據實際使用的 Flash size 設置,可 以參考文檔“2A-ESP8266__IOT_SDK_User_Manual” 中的 “Flash Map” 一章。
2.1 Non-FOTA
2.2 FOTA
更多圖查看:
鏈接:https://blog.csdn.net/k7arm/article/details/51812021
三、Flash讀寫接口
SPI Flash 接口位於 /ESP8266_NONOS_SDK/include/spi_flash.h。
system_param_xxx 接口位於 /ESP8266_NONOS_SDK/include/user_interface.h。
3.1 spi_flash_erase_sector
3.2 spi_flash_write
注意:
- Flash 請先擦再寫。
- Flash 讀寫必須 4 字節對齊。
示例代碼:
#define N 0x7C
uint32 data[M];
spi_flash_erase_sector (N);
spi_flash_write (N*4*1024, data, M*4);
3.3 spi_flash_read
3.4 system_param_save_with_protect
3.5 system_param_load
示例代碼:
/***************Flash 讀寫結構體聲明***************/
struct esp_platform_saved_param {
// 服務器參數
uint8 devkey[40];
uint8 token[40];
uint8 activeflag;
char server_domain[64];
ip_addr_t server_ip;
int server_port;
// AP參數
uint8 ssid[32];
uint8 password[64];
int authmode;
uint8 ssid_hidden;
// 填充
uint8 pad[2];
};
/***************Flash 讀寫結構體定義***************/
struct esp_platform_saved_param esp_param;
/***************Flash 寫入數據***************/
system_param_save_with_protect(0x7D, &esp_param, sizeof(esp_param));
/***************Flash 讀出數據***************/
system_param_load(0x7D, 0, &esp_param, sizeof(esp_param));
四、Flash讀寫保護
4.1 Espressif Flash讀寫保護示例
4.1.1 實現原理
Espressif Flash 讀寫保護示例,使用 三個 sector(扇區)實現(每 sector 4KB),提供 4KB 的可靠存儲空間。 將 sector 1 和 sector 2 作爲數據 sector,輪流讀寫,始終分別存放“本次”數據和“前一次”數據, 確保了至少有一份數據存儲安全;sector 3 作爲 flag sector,標誌最新的數據存儲 sector。
保護機制如下:
- 初始上電時,數據存儲在 sector 2 中,從 sector 2 中將數據讀到 RAM。
- 第一次寫數據時,將數據寫入 sector 1。此時若突然掉電,sector 1寫入失敗,sector 2 & 3數據未改變;重新上電時,仍是從 sector 2 中 讀取數據,不影響使用。
- 改寫 sector 3,將標誌置爲 0,表示數據存於 sector 1。此時若突然掉電,sector 3 寫入失敗,sector 1 & 2 均存有一份完整數據;重新上電時,因 sector 3 無有效 flag,默認從 sector 2 中讀取數據,則仍能正常使用,只是未能包含掉電前對 sector 1 寫入的數據。
- 再一次寫數據時,先從 sector 3 讀取 flag,若 flag 爲0,則上次數據存於 sector 1,此次應將數據寫入 sector 2;若 flag 爲非 0,則認爲上次數據存於 sector 2,此次應將數據寫入 sector 1。此時若寫數據出錯,請參考步驟 2、 3的說明,同理。
- 寫入 sector 1(或 sector 2)完成後,纔會寫 sector 3,重置 flag。注意:只有數據扇區(sector 1或 sector 2)寫完之後,纔會寫 flag sector(sector 3),這樣即使 flag sector 寫入出錯,兩個數據扇區都已存有完整數據內容,目前默認會讀取 sector 2。
4.1.2 軟件示例
在 IOT_Demo 中,使用 0x3C000 開始的 4 個 sector(每 sector 4KB),作爲用戶參數存儲區。 其中 0x3D000、 0x3E000、 0x3F000 這 3 個 sector 實現了讀寫保護的功能,並存儲了應用級參數 esp_platform_saved_param。
圖中“有讀寫保護的存儲區”, IOT_Demo 中建議調用 system_param_load 和 system_param_save_with_protect 進行讀寫。
system_param_load - 讀 Flash 用戶參數區數據
system_param_save_with_protect - 寫 Flash 用戶參數區數據
參數 struct esp_platform_saved_param 定義了目前樂鑫存儲於 Flash 的用戶應用級數據,用戶只需將自己要存儲的數據添加到結構體 struct esp_platform_saved_param 後面,調用上述兩個函數進行 Flash 讀寫即可。
4.2 Flash讀寫保護參考一
方法: “輪流寫入”+“首部記數”+“尾部校驗”
佔用空間: 2 個 sector,共計 8KB;提供 4KB 的帶數據保護存儲空間。
原理:
仍然 採用兩個數據 sector 輪流寫入來做備份數據保護,只是不再專門設立 flag sector。 記一個 counter,寫入數據 sector 的首部,每次寫入時計數加一,用記數比較來判別下一次應寫入哪個 sector;在數據尾部加入校驗碼(CRC、checksum 等任一種校驗方式),用以驗證數據的完整性。
(1) 假設初次上電,數據存儲在 sector A, sector A 的記數爲初始值 0xFF,從 sector A 將數據讀入 RAM。
(2) 第一次數據寫入 sector B,則在 sector B 首部信息中記錄 counter 爲 1,尾部加入校驗碼。
(3) 再次寫入數據時,先分別讀取 sector A/B 的 counter 值進行比較,此次應當將數據寫入 sector A, sector A 首部記錄 counter 爲 2,尾部加入校驗碼。
(4) 若發生突然掉電,當前正在寫入的 sector 數據丟失,重新上電時,先比較 sector A/B 的 counter 值,讀取 counter 值較大的完整 sector,根據 sector 尾部的校驗碼進行校驗,當前 sector 數據是否可靠,若校驗通過,則繼續執行;若校驗失敗,則讀取另一個 sector 的數據,校驗,並執行。
4.3 Flash讀寫保護參考二
方法: “備份扇區”+“尾部校驗”
佔用空間: 2 個 sector,共計 8KB;提供 4KB 的帶數據保護存儲空間。
原理:
始終往 sector A 讀寫數據,每次寫入時,同樣寫一遍 sector B 作爲 sector A 的備份扇區,每個 sector 尾部均加入校驗碼(CRC、checksum等任一種校驗方式)。
(1) 從 sector A 讀取數據,並進行校驗。
(2) 數據寫入 sector A,尾部爲校驗碼。
(3) sector A 寫入完成後,同樣的數據也寫入 sector B 進行備份。
(4) 若發生突然掉電,當前正在寫入的 sector 數據丟失,重新上電時,先從 sector A 讀取數據,根據尾部的校驗碼進行校驗,sector A 數據是否可靠,若校驗通過,則繼續執行;若校驗失敗,則讀取 sector B 的數據,校驗,並執行。
• 由 Leung 寫於 2018 年 9 月 14 日
• 參考:《ESP8266 Flash 讀寫說明》[25l1]
《ESP8266 Non-OS SDK IoT_Demo 指南》[q878]
《ESP8266 Non-OS SDK API 參考》[7qq6]