在應(yīng)用編程(IAP)應(yīng)該是MCU使用過程中常見的一種功能,MCU廠商為了加速用戶的產(chǎn)品開發(fā),都會在其SDK中添加相關(guān)示例,而且也有不少用戶只需要簡單的集成,就完成了該功能并實現(xiàn)產(chǎn)品的量產(chǎn)。但你是否真的會在Flash上存數(shù)據(jù)?
首先我們需要從片內(nèi)和片外存儲談起,MCU內(nèi)部的非易失性存儲器絕大多數(shù)都是基于FLASH的(也有少部分是EEPROM的,比如NXP LPC80x系列),MCU外部的非易失性存儲器可以有FLASH,EEPROM,其中EEPROM比較常見的是I2C接口,F(xiàn)LASH有NAND, NOR, SPI NOR等等。
實際應(yīng)用過程中,我們到底應(yīng)該怎么選擇呢?需要存儲的數(shù)據(jù)量大小是首要問題,我們可以根據(jù)它來大致設(shè)定規(guī)則
![14a07d16-33c2-11ed-ba43-dac502259ad0.png](https://file1.elecfans.com//web2/M00/96/63/wKgZomTnHySAazwyAAQ3mDRnfH8996.png)
-
當(dāng)數(shù)據(jù)量大于128MB時,NAND FLASH或者SD(TF)卡是常見的選擇,但考慮到NAND FLASH會有壞塊的問題,編寫存儲軟件時需要考慮磨損均衡已經(jīng)壞塊管理的問題,想要做的非??煽坎⒉蝗菀?。
-
當(dāng)數(shù)據(jù)量小于128MB,大于2MB時,可以選擇NOR FLASH或EEPROM,EEPROM技術(shù)上的優(yōu)勢是耐久性好,可以反復(fù)多次編程,最高可達(dá)100W次的擦寫循環(huán),NOR FLASH相比EEPROM容量會更大,平均成本更好,但是壽命會少個0,最大只能到10W次的擦寫循環(huán)。
EEPROM(AT24C02)
FLASH(IS25WP032)
-
當(dāng)數(shù)據(jù)量小于2MB時,就可以考慮將其放入MCU內(nèi)部的FLASH中,一旦決定把數(shù)據(jù)放MCU內(nèi)部FLASH時,就面臨兩個問題:
(1)MCU片內(nèi)FLASH承諾的壽命更短,一般可能是1~5W次,好像也有幾百次的,鑒于大佬已經(jīng)停線,這里就不點名了。關(guān)于這個參數(shù)切記要參考手冊,不同廠家的參數(shù)不一樣,同一廠家的不同系列也可能有差異
NXP Kinetis系列MCU
STM32 F103xx
(2) 擦寫過程可能需要關(guān)閉中斷,雖然這個不是必須的,但除非MCU的Datasheet明確有寫支持,絕大多數(shù)MCU都是有這方面的要求,并且IAP編程的函數(shù)需要運行于RAM或ROM中,因為擦寫過程中,如果MCU沒有特殊的設(shè)計(比如下圖NXP Kinetis K64系列帶有雙Block flash是可以支持WWR),該過程是無法進(jìn)行取指的,關(guān)中斷的目的是防止外部中斷觸發(fā)運行于FLASH上的ISR,如果用戶不希望關(guān)中斷,則必須要將可能發(fā)生中斷的函數(shù)Relocate到RAM中
//RAM中執(zhí)行的編程函數(shù) static status_t flash_command_sequence(flash_config_t *config) { uint8_t registerValue; __set_PRIMASK(1); #if FLASH_DRIVER_IS_FLASH_RESIDENT /* clear RDCOLERR & ACCERR & FPVIOL flag in flash status register */ FTFx->FSTAT = FTFx_FSTAT_RDCOLERR_MASK | FTFx_FSTAT_ACCERR_MASK | FTFx_FSTAT_FPVIOL_MASK; status_t returnCode = flash_check_execute_in_ram_function_info(config); if (kStatus_FLASH_Success != returnCode) { __set_PRIMASK(0); return returnCode; } /* We pass the ftfx_fstat address as a parameter to flash_run_comamnd() instead of using * pre-processed MICRO sentences or operating global variable in flash_run_comamnd() * to make sure that flash_run_command() will be compiled into position-independent code (PIC). */ callFlashRunCommand((FTFx_REG8_ACCESS_TYPE)(&FTFx->FSTAT)); __set_PRIMASK(0); #else /* clear RDCOLERR & ACCERR & FPVIOL flag in flash status register */ FTFx->FSTAT = FTFx_FSTAT_RDCOLERR_MASK | FTFx_FSTAT_ACCERR_MASK | FTFx_FSTAT_FPVIOL_MASK; /* clear CCIF bit */ FTFx->FSTAT = FTFx_FSTAT_CCIF_MASK; /* Check CCIF bit of the flash status register, wait till it is set. * IP team indicates that this loop will always complete. */ while (!(FTFx->FSTAT & FTFx_FSTAT_CCIF_MASK)) { } #endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */ /* Check error bits */ /* Get flash status register value */ registerValue = FTFx->FSTAT; /* checking access error */ if (registerValue & FTFx_FSTAT_ACCERR_MASK) { __set_PRIMASK(0); return kStatus_FLASH_AccessError; } /* checking protection error */ else if (registerValue & FTFx_FSTAT_FPVIOL_MASK) { __set_PRIMASK(0); return kStatus_FLASH_ProtectionViolation; } /* checking MGSTAT0 non-correctable error */ else if (registerValue & FTFx_FSTAT_MGSTAT0_MASK) { __set_PRIMASK(0); return kStatus_FLASH_CommandFailure; } else { __set_PRIMASK(0); return kStatus_FLASH_Success; } }
(3) 擦寫時間對系統(tǒng)的影響,之前講過擦寫過程是不能執(zhí)行FLASH code的,所以勢必會讓系統(tǒng)pending住,而這個pending時間可以在Datasheet中尋找答案,一般擦除是按照sector/block大小來的,編程時按照word或者page大小來的,擦除時間一般更長,對系統(tǒng)影響更大。用戶需要考慮的是系統(tǒng)是否可以接受每個控制周期,等待1個擦除最小單位的編程時間,這是由于有可能需要編程的數(shù)據(jù)比較大,如果同一時間更新勢必會影響到控制周期,但如果把擦寫任務(wù)分配到每個控制周期,就可以將影響降低到最小,以NXP?。耍椋睿澹簦椋螅脸粋€Sector典型值是14ms,每次寫入Longword(8字節(jié))所需要的時間是65us,當(dāng)需要升級的時候,在接收到升級命令的第一幀處理時,增加sector刪除命令,本次運算周期會增加14ms的pending時間,然后每周期編程8字節(jié)數(shù)據(jù)。如果系統(tǒng)需要將數(shù)據(jù)存在內(nèi)部flash上,就必須接受某一個周期增加14ms的pending,隨著flash寫入次數(shù)的增多,14ms還會增大到114ms
在確定了上述問題之后就可以完成存儲設(shè)備的選型,但軟件處理還需要注意以下幾個要點,這些問題MCU廠家一般是不會提供解決方案的,需要用戶根據(jù)自己的需求來完成:
-
冗余備份:有些時候用戶需要更新正在使用的參數(shù)/固件,考慮到更新失敗的情況下,需要支持參數(shù)/固件版本回退,所以需要兩片備份區(qū)進(jìn)行冗余備份。
-
Magic number or 校驗碼:Magic number一般就是在FLASH編程結(jié)束時候,寫一個位數(shù)較高的(64bit)值到固定地址,使用加載數(shù)據(jù)時,先判斷Magic number是否正確,以此判斷編程過程是否完整,切記Magic number要在編程時先擦后寫(第一個擦,最后一個寫),否則無法起到驗證效果。這種驗證方式比較初級,對于可靠性要求更高的場合,一般會使用校驗碼的方式(CRC或者M(jìn)D5),特別是參數(shù)文件通過通信的方式傳輸?shù)組CU中,雖然通信過程可能也有校驗(比如Modbus RTU的CRC),但是無法保證數(shù)據(jù)在RAM緩沖區(qū)不發(fā)生位翻轉(zhuǎn)的情況,大多數(shù)MCU都不支持ECC功能,在這種情況下是無法糾錯的,如果對端設(shè)備會將文件的校驗碼發(fā)過來進(jìn)行校驗,就可以避免該問題的發(fā)生,雖然RAM出現(xiàn)位翻轉(zhuǎn)的概率非常低,但是如果發(fā)生位翻轉(zhuǎn)的參數(shù)是設(shè)備的保護(hù)點或者PWM死區(qū)值,一旦出現(xiàn)錯誤,就可能會發(fā)生設(shè)備故障或損毀,甚至出現(xiàn)安全事故。
-
編程過程掉電或復(fù)位:在編程的過程中出現(xiàn)掉電或者復(fù)位是經(jīng)常出現(xiàn)的一種異常情況。比如震動導(dǎo)致電源接觸不良,或者系統(tǒng)異常導(dǎo)致看門狗無法喂狗都有可能出現(xiàn),還有使用POE電源對端設(shè)備直接斷電都有可能造成這種異常。所以一定要考慮在這種情況下的異常處理,比如上電加載數(shù)據(jù)的時候,如果校驗失敗,可以使用一組安全數(shù)據(jù)或者更新前的數(shù)據(jù)。對于帶操作系統(tǒng)的應(yīng)用,建議硬件添加系統(tǒng)掉電檢測電路,在檢測到掉電事件(如24V掉電)發(fā)生時,如果當(dāng)前沒有寫入Flash,則lock住寫入進(jìn)程,如果當(dāng)前正在寫入Flash,則立即調(diào)用sync函數(shù),盡快將數(shù)據(jù)從緩存寫入Flash(硬件上MCU VDD加個稍微大點的電容,多扛一段時間),不要等待系統(tǒng)后臺寫入。
-
二次編程:有些MCU內(nèi)部的Flash是帶有校驗機(jī)制的,對于同一地址在擦除完成后,只能進(jìn)行一次編程,如果進(jìn)行二次編程寫入不同數(shù)值進(jìn)去,可能會除非校驗失敗,導(dǎo)致該地址無法正常讀寫。
總結(jié),這里列了一個表格,當(dāng)需要IAP的時候,可以根據(jù)所選芯片的參數(shù)判斷是否滿足自身系統(tǒng)的需求(下表已經(jīng)填入示例數(shù)據(jù)):
NVM | 擦除編程最小單元 | 擦除編程所用時間 | 擦除編程壽命 | 編程過程是否需要關(guān)中斷 | 編程數(shù)據(jù)的校驗 | 編程過程中掉電/復(fù)位 |
---|---|---|---|---|---|---|
EEPROM | 不需要擦,可以按字節(jié)或者Page寫入 | 寫周期5ms | 1000000 | 不需要 | 軟件添加 | 軟件添加 |
NOR FLASH | Sector = 4K, 最小寫入Page = 256B | Sector擦70ms,Page寫0.2ms | 100000 | 不需要 | 軟件添加 | 軟件添加 |
MCU FLASH | Sector = 2KB, 最小寫入8Byte | Sector擦14ms,寫65us | 50000 | 需要 | 軟件添加 | 軟件添加 |
審核編輯:湯梓紅
評論