ESP8266學(xué)習(xí)筆記(2)——內(nèi)存分布及Flash讀寫接口

一、存儲芯片W25Q系列

esp8266-12f

w25q 系列生產(chǎn)的加工的商家很多,但是里面的分布和命名規(guī)則都是一樣的。比如華邦的w25q64,spi通訊接口,64就是指 64Mbit 也就是 8M 的容量。而我們平時的8266-12f的 32Mbit 就是 4M 容量。

w25q32 為例,里面的存儲分布。w25q32把4M容量分為了 64 塊,每一塊又分為 16 個扇區(qū),而每個扇區(qū)占 4K 大小。由此可計算到,w25q32有 32Mbit / 8 * 1024 / 16 / 4 =?64 塊 ,有 64 * 16 =?1024 個扇區(qū)。

注:1B=8 Bit ,1KB=1024B ,1MB=1024KB


二、ESP8266內(nèi)存分布

? 程序區(qū):代碼編譯生成的 bin 文件,燒錄到 Flash 占用的區(qū)域,請勿改寫;

? 系統(tǒng)參數(shù)區(qū): esp_iot_sdk 中底層用于存放系統(tǒng)參數(shù)的區(qū)域,請勿改寫;

? 用戶參數(shù)區(qū):上層應(yīng)用程序存儲用戶參數(shù)的區(qū)域。開發(fā)者請根據(jù)實際使用的 Flash size 設(shè)置,可 以參考文檔“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 字節(jié)對齊。

示例代碼:

??? #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 讀寫結(jié)構(gòu)體聲明***************/

struct esp_platform_saved_param {

// 服務(wù)器參數(shù)

uint8 devkey[40];

uint8 token[40];

uint8 activeflag;

char server_domain[64];

ip_addr_t server_ip;

int server_port;

?// AP參數(shù)

uint8 ssid[32];

uint8 password[64];

int authmode;

uint8 ssid_hidden;

// 填充

uint8 pad[2];

};

/***************Flash 讀寫結(jié)構(gòu)體定義***************/

struct esp_platform_saved_param esp_param;

/***************Flash 寫入數(shù)據(jù)***************/

system_param_save_with_protect(0x7D, &esp_param, sizeof(esp_param));

/***************Flash 讀出數(shù)據(jù)***************/

system_param_load(0x7D, 0, &esp_param, sizeof(esp_param));?


四、Flash讀寫保護(hù)

4.1 Espressif Flash讀寫保護(hù)示例

4.1.1 實現(xiàn)原理

Espressif Flash 讀寫保護(hù)示例,使用?三個 sector(扇區(qū))實現(xiàn)(每 sector 4KB),提供 4KB 的可靠存儲空間。 將 sector 1 和 sector 2 作為數(shù)據(jù) sector,輪流讀寫,始終分別存放“本次”數(shù)據(jù)和“前一次”數(shù)據(jù), 確保了至少有一份數(shù)據(jù)存儲安全;sector 3 作為 flag sector,標(biāo)志最新的數(shù)據(jù)存儲 sector。

保護(hù)機(jī)制如下:

1. 初始上電時,數(shù)據(jù)存儲在 sector 2 中,從 sector 2 中將數(shù)據(jù)讀到 RAM。

2. 第一次寫數(shù)據(jù)時,將數(shù)據(jù)寫入 sector 1。此時若突然掉電,sector 1寫入失敗,sector 2 & 3數(shù)據(jù)未改變;重新上電時,仍是從 sector 2 中 讀取數(shù)據(jù),不影響使用。

3. 改寫 sector 3,將標(biāo)志置為 0,表示數(shù)據(jù)存于 sector 1。此時若突然掉電,sector 3 寫入失敗,sector 1 & 2 均存有一份完整數(shù)據(jù);重新上電時,因 sector 3 無有效 flag,默認(rèn)從 sector 2 中讀取數(shù)據(jù),則仍能正常使用,只是未能包含掉電前對 sector 1 寫入的數(shù)據(jù)。

4. 再一次寫數(shù)據(jù)時,先從 sector 3 讀取 flag,若 flag 為0,則上次數(shù)據(jù)存于 sector 1,此次應(yīng)將數(shù)據(jù)寫入 sector 2;若 flag 為非 0,則認(rèn)為上次數(shù)據(jù)存于 sector 2,此次應(yīng)將數(shù)據(jù)寫入 sector 1。此時若寫數(shù)據(jù)出錯,請參考步驟 2、 3的說明,同理。

5. 寫入 sector 1(或 sector 2)完成后,才會寫 sector 3,重置 flag。注意:只有數(shù)據(jù)扇區(qū)(sector 1或 sector 2)寫完之后,才會寫 flag sector(sector 3),這樣即使 flag sector 寫入出錯,兩個數(shù)據(jù)扇區(qū)都已存有完整數(shù)據(jù)內(nèi)容,目前默認(rèn)會讀取 sector 2。

4.1.2 軟件示例

IOT_Demo 中,使用 0x3C000 開始的 4 個 sector(每 sector 4KB),作為用戶參數(shù)存儲區(qū)。 其中 0x3D000、 0x3E000、 0x3F000 這 3 個 sector 實現(xiàn)了讀寫保護(hù)的功能,并存儲了應(yīng)用級參數(shù) esp_platform_saved_param。

圖中“有讀寫保護(hù)的存儲區(qū)”, IOT_Demo 中建議調(diào)用?system_param_load system_param_save_with_protect 進(jìn)行讀寫。

system_param_load - 讀 Flash 用戶參數(shù)區(qū)數(shù)據(jù)

system_param_save_with_protect - 寫 Flash 用戶參數(shù)區(qū)數(shù)據(jù)

參數(shù) struct esp_platform_saved_param 定義了目前樂鑫存儲于 Flash 的用戶應(yīng)用級數(shù)據(jù),用戶只需將自己要存儲的數(shù)據(jù)添加到結(jié)構(gòu)體 struct esp_platform_saved_param 后面,調(diào)用上述兩個函數(shù)進(jìn)行 Flash 讀寫即可。

4.2 Flash讀寫保護(hù)參考一

方法: “輪流寫入”+“首部記數(shù)”+“尾部校驗”

占用空間: 2 個 sector,共計 8KB;提供 4KB 的帶數(shù)據(jù)保護(hù)存儲空間。

原理:

仍然?采用兩個數(shù)據(jù) sector 輪流寫入來做備份數(shù)據(jù)保護(hù),只是不再專門設(shè)立 flag sector。 記一個 counter,寫入數(shù)據(jù) sector 的首部,每次寫入時計數(shù)加一,用記數(shù)比較來判別下一次應(yīng)寫入哪個 sector;在數(shù)據(jù)尾部加入校驗碼(CRC、checksum 等任一種校驗方式),用以驗證數(shù)據(jù)的完整性。

(1) 假設(shè)初次上電,數(shù)據(jù)存儲在 sector A, sector A 的記數(shù)為初始值 0xFF,從 sector A 將數(shù)據(jù)讀入 RAM。

(2) 第一次數(shù)據(jù)寫入 sector B,則在 sector B 首部信息中記錄 counter 為 1,尾部加入校驗碼。

(3) 再次寫入數(shù)據(jù)時,先分別讀取 sector A/B 的 counter 值進(jìn)行比較,此次應(yīng)當(dāng)將數(shù)據(jù)寫入 sector A, sector A 首部記錄 counter 為 2,尾部加入校驗碼。

(4) 若發(fā)生突然掉電,當(dāng)前正在寫入的 sector 數(shù)據(jù)丟失,重新上電時,先比較 sector A/B 的 counter 值,讀取 counter 值較大的完整 sector,根據(jù) sector 尾部的校驗碼進(jìn)行校驗,當(dāng)前 sector 數(shù)據(jù)是否可靠,若校驗通過,則繼續(xù)執(zhí)行;若校驗失敗,則讀取另一個 sector 的數(shù)據(jù),校驗,并執(zhí)行。

4.3 Flash讀寫保護(hù)參考二

方法: “備份扇區(qū)”+“尾部校驗”

占用空間: 2 個 sector,共計 8KB;提供 4KB 的帶數(shù)據(jù)保護(hù)存儲空間。

原理:

始終往 sector A 讀寫數(shù)據(jù),每次寫入時,同樣寫一遍 sector B 作為 sector A 的備份扇區(qū),每個 sector 尾部均加入校驗碼(CRC、checksum等任一種校驗方式)。

(1) 從 sector A 讀取數(shù)據(jù),并進(jìn)行校驗。

(2) 數(shù)據(jù)寫入 sector A,尾部為校驗碼。

(3) sector A 寫入完成后,同樣的數(shù)據(jù)也寫入 sector B 進(jìn)行備份。

(4) 若發(fā)生突然掉電,當(dāng)前正在寫入的 sector 數(shù)據(jù)丟失,重新上電時,先從 sector A 讀取數(shù)據(jù),根據(jù)尾部的校驗碼進(jìn)行校驗,sector A 數(shù)據(jù)是否可靠,若校驗通過,則繼續(xù)執(zhí)行;若校驗失敗,則讀取 sector B 的數(shù)據(jù),校驗,并執(zhí)行。



? 由 Leung 寫于 2018 年 9 月 14 日

? 參考:《ESP8266 Flash 讀寫說明》[25l1]

???????????? 《ESP8266 Non-OS SDK IoT_Demo 指南》[q878]

? ? ? ? ? ? ?《ESP8266 Non-OS SDK API 參考》[7qq6]

???????????? 《Esp8266 進(jìn)階之路24》

???????????? 《ESP8266 Flash》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容