MCU(微控制器)在過去幾十年里在CPU性能、通信接口、模數(shù)和數(shù)模外設(shè)、內(nèi)存大小及讀寫次數(shù)等方面呈指數(shù)發(fā)展。我們專注于帶有非易失性嵌入式存儲(chǔ)器的MCU(我們?cè)赨SB閃存驅(qū)動(dòng)器、存儲(chǔ)器等內(nèi)擁有閃存),從首批帶有一次性編程(OTP)的器件到EPROM(電可編程只讀存儲(chǔ)器),再到EEPROM (在方程中增加了“可擦”一詞,能夠在不需要紫外線燈的情況下擦除它),到現(xiàn)在的嵌入式閃存(在某些情況下稱為Flash EEPROM),這是目前最常用的閃存。
EEPROM和Flash在概念上類似,兩者都是可電擦除和寫入的存儲(chǔ)器,但是它們之間也存在某些差異。最初Flash只能在大數(shù)據(jù)塊上寫入,但是現(xiàn)在兩者比較接近了,一個(gè)單字節(jié)、詞或雙詞都可以寫入,取決于架構(gòu)是支持8位、16位還是32位寫入操作,是否需要與偶數(shù)地址對(duì)齊,因此主要的區(qū)別是擦除過程。EEPROM的擦除大小很小(在大多數(shù)情況下,只能擦除一個(gè)單字節(jié)),而Flash需要在大扇區(qū)中(在某些情況下為數(shù)據(jù)塊或頁(yè)面)擦除,而且還取決于所使用的器件,扇區(qū)可以是幾個(gè)字節(jié)或是幾千個(gè)字節(jié)。
Flash受歡迎主要有兩個(gè)原因:一是隨著扇區(qū)的增加,其擦除流程與典型的EEPROM相比,速度更快。使用EEPROM工作時(shí),擦除過程很慢,通常一個(gè)字節(jié)以ms為單位。盡管Flash的擦除時(shí)間大致相同,但是它適用于擦除整個(gè)扇區(qū)。這樣,減少了對(duì)設(shè)備進(jìn)行編程的時(shí)間,因此也減少了制造流程所需的時(shí)間。另一個(gè)原因是Flash存儲(chǔ)器的造價(jià)低于EEPROM,因此構(gòu)建有許多Flash存儲(chǔ)器的MCU可實(shí)現(xiàn)更高的成本效益。
帶有嵌入式閃存的MCU支持系統(tǒng)內(nèi)編程。這意味著MCU可以在用于最終應(yīng)用的印刷電路板(PCB)上進(jìn)行編程。在某些情況下,需要增加一些額外電路,以進(jìn)入或退出編程模式,但是能夠在不移除設(shè)備的情況下進(jìn)行或先在套接字中進(jìn)行編程是值得的,這意味著即使軟件開發(fā)沒有完成,板卡也能夠完全填充。還可以在原始版本出來后升級(jí)軟件而不更改硬件,從而加快面市速度,因?yàn)榛贠TP或ROM的設(shè)備需要完整的軟件版本才能夠推出。
演進(jìn)的下一步是向MCU的Flash存儲(chǔ)器添加自編程功能,允許MCU在運(yùn)行時(shí)執(zhí)行寫入/擦除操作。這樣會(huì)帶來兩大好處:一是代碼本身可以包含再次對(duì)整個(gè)應(yīng)用進(jìn)行編程的例程,允許通過外設(shè)進(jìn)行遠(yuǎn)程更新(這些通常稱為引導(dǎo)程序,所使用的外設(shè)通常是串行接口),因此具有很高的靈活性,能夠在安裝后更新設(shè)計(jì);二是能夠在運(yùn)行時(shí)存儲(chǔ)非易失性數(shù)據(jù),如空調(diào)的溫度或電視上編程后的頻道。
Flash自編程
下面闡述了在沒有雙閃存陣列的情況下如何在系統(tǒng)中和運(yùn)行時(shí)寫入閃存。
要在運(yùn)行時(shí)進(jìn)行寫入和擦除流程,則需要對(duì)任何一側(cè)施加較高電壓或清除各個(gè)位。當(dāng)對(duì)Flash庫(kù)施加高電壓時(shí),無法讀取整個(gè)存儲(chǔ)器,因此有兩種典型的替代方案讓系統(tǒng)保持工作:一種是其它存儲(chǔ)器運(yùn)行寫入/擦除(通常是RAM)Flash的代碼,第二種方法是在執(zhí)行Flash操作時(shí),為CPU提供一種拖延代碼執(zhí)行的方法。
這兩種方法都另有一個(gè)限制:由于中斷矢量通常位于Flash存儲(chǔ)器,因此在執(zhí)行Flash命令時(shí)需要禁用中斷功能,因?yàn)镃PU在那段時(shí)間無法讀取Flash,而且在需要時(shí),也不能獲取中斷矢量。在使用I2C、UART或USB等串行外設(shè)運(yùn)行的系統(tǒng)中,這些外設(shè)可以每秒數(shù)kB(如UART或I2C總線)或每秒數(shù)MB(如USB)的速率交流信息;禁用中斷功能幾毫秒便可能導(dǎo)致丟失大量信息。因此,系統(tǒng)需要設(shè)計(jì)成在修改Flash時(shí)允許停止這些串行通信,然后當(dāng)中斷功能再次啟用時(shí),恢復(fù)所有信息。
雙組Flash實(shí)施方案
雙組Flash意味著同一器件中有兩個(gè)不同的Flash塊。本文從這里開始以飛思卡爾MC9S08MM128 MCU為例進(jìn)行闡述。該器件擁有128kB的Flash存儲(chǔ)器,分成兩個(gè)64kB的陣列。上一章解釋過在寫入或擦除Flash的這段時(shí)間,整個(gè)Flash塊都不能讀取。提到有兩種替代方法來執(zhí)行Flash操作:CPU拖延或從RAM運(yùn)行。同一邏輯適用于一個(gè)雙組Flash,但是由于現(xiàn)在有兩個(gè)不同的組,因此代碼可以在Flash A中運(yùn)行以寫入或擦除Flash B,反之亦然。
當(dāng)使用非易失性存儲(chǔ)器來存儲(chǔ)變量時(shí),雙組Flash可以設(shè)計(jì)為將所有非易失性變量都存儲(chǔ)在一個(gè)Flash組中,即:一個(gè)塊用作偽EEPROM,代碼在另一個(gè)組中。例如,所有數(shù)據(jù)都將存儲(chǔ)在Flash B中,寫入和擦除存儲(chǔ)器的代碼將在Flash A中,以便更加高效地使用RAM和堆棧。在雙組Flash MCU中也不需要CPU 拖延。系統(tǒng)可以保持運(yùn)行,因?yàn)橹挥幸话氲拇鎯?chǔ)器需要高電壓,另一半可以繼續(xù)正常的代碼執(zhí)行。當(dāng)構(gòu)建應(yīng)用來避免阻塞代碼時(shí),這尤為重要(代碼的各部分,要么停止CPU,或在環(huán)路等待事件發(fā)生以繼續(xù)代碼執(zhí)行,在這種情況下等待Flash命令完成)。
將數(shù)據(jù)保存在Flash B中的另一個(gè)好處是不需要禁用中斷功能,因?yàn)橹袛嗍噶勘硎荈lash A的一部分。這意味著所有串行通信、模數(shù)轉(zhuǎn)換、定時(shí)器等都可以保持運(yùn)行,啟用中斷功能,代碼可以在命令執(zhí)行的過程中進(jìn)行跳轉(zhuǎn),提取中斷矢量,執(zhí)行中斷服務(wù)例程,并返回,以驗(yàn)證Flash操作是否完成,以及是否需要啟動(dòng)新操作。
另外一個(gè)特點(diǎn)是向執(zhí)行整個(gè)設(shè)計(jì)遠(yuǎn)程升級(jí)的應(yīng)用添加故障容忍功能。可以將新項(xiàng)目版本保存在一個(gè)Flash組中,在另一個(gè)組中作為備份進(jìn)行保存。一旦上傳了新版本并通過了驗(yàn)證,那么以前的版本便可以擦除。在系統(tǒng)設(shè)計(jì)級(jí),可以總是使用Flash A在Flash B中寫入新版本,反之亦然。這樣,即使在更新過程中發(fā)生了故障,也不會(huì)丟失工作版本。
EEPROM仿真
使用Flash存儲(chǔ)非易失性信息的一個(gè)限制是字節(jié)必須處于已擦除狀態(tài)(所有位都設(shè)為邏輯“1”)才能夠?qū)懭?。這意味著擦除操作將所有位都從扇區(qū)轉(zhuǎn)換為“1”,而寫入操作將某些或全部位都改為“0”。這樣產(chǎn)生的問題是,如果一個(gè)變量發(fā)生了改變,需要進(jìn)行非易失性備份,那么首先需要擦除字節(jié),但是由于Flash不能逐個(gè)字節(jié)擦除,因此需要擦除整個(gè)扇區(qū)。
執(zhí)行EEPROM仿真的例程旨在使用Flash而不是單字節(jié)寫入和擦除功能來提供EEPROM功能。一般做法是使用需要存儲(chǔ)在Flash中的所有變量創(chuàng)建一個(gè)結(jié)構(gòu);該結(jié)構(gòu)添加一個(gè)字段,指示該扇區(qū)是否活動(dòng)(這應(yīng)該是寫入的最后一個(gè)字節(jié),以驗(yàn)證所有數(shù)據(jù)是否已經(jīng)正確寫入)。當(dāng)需要在Flash中更新某些信息時(shí),復(fù)制整個(gè)結(jié)構(gòu)。每當(dāng)字節(jié)改變時(shí)都進(jìn)行非易失性更新,或根據(jù)定時(shí)器持續(xù)進(jìn)行備份作為應(yīng)用執(zhí)行的一部分。
根據(jù)應(yīng)用類型,可能進(jìn)行某些改變,以減少執(zhí)行EEPROM仿真或增加系統(tǒng)強(qiáng)勁性所需的Flash容量。例如,如果使用一個(gè)Flash扇區(qū),非易失性結(jié)構(gòu)將寫入同一扇區(qū),只要適合扇區(qū)大小,能寫入多少次就寫多少次(因此,建議結(jié)構(gòu)大小適合扇區(qū)內(nèi)的準(zhǔn)確次數(shù),通常是兩種大小的功率)。在Flash扇區(qū)填滿后,代碼需要擦除扇區(qū)并重新開始。這種方法的好處是只使用一個(gè)Flash扇區(qū),限制是如果在扇區(qū)擦除步驟發(fā)生斷電,那么所有信息都會(huì)丟失。另外,F(xiàn)lash耐用性也將加倍。
另一種方法是使用兩個(gè)扇區(qū)進(jìn)行EEPROM仿真。只有在把信息寫入新扇區(qū)后才擦除一個(gè)扇區(qū),因此在Flash中總是有信息的有效副本,從而更加強(qiáng)韌,能夠確保即使在擦除或?qū)懭脒^程中發(fā)生掉電,信息也不會(huì)丟失,還增加了存儲(chǔ)非易失性信息所需的Flash容量。根據(jù)應(yīng)用要求來確定應(yīng)該使用哪種方法。
案例研究: 如何在飛思卡爾S08系列中寫入/擦除Flash
在S08系列中執(zhí)行寫入或擦除操作的步驟與此類似。如果要獨(dú)立進(jìn)行寫入、突發(fā)寫入、擦除或整體擦除,第一步是用一些數(shù)據(jù)寫入Flash位置(區(qū)別在于如果命令是擦除或整體擦除,那么所寫入的數(shù)據(jù)是沒有影響的)。之后,寄存器FCMD(Flash命令)需要寫入要執(zhí)行的操作,然后在Flash狀態(tài)寄存器中寫入一個(gè)位來下發(fā)命令,代碼需要檢查下發(fā)的Flash命令是否會(huì)產(chǎn)生錯(cuò)誤。在單組Flash部署中,代碼需要等待設(shè)置Flash命令完成標(biāo)志,以便它可以返回正常的代碼執(zhí)行,對(duì)于雙組Flash,在檢查了下發(fā)Flash命令沒有導(dǎo)致錯(cuò)誤產(chǎn)生后將立即返回執(zhí)行其它代碼部分。建議在下發(fā)新命令前,代碼總是檢查以前的命令是否已經(jīng)完成,以避免潛在的問題。
下面的文本框是關(guān)于如何為MCU部署Flash命令的代碼示例。
#define Flash_Busy() FSTAT_FCCF
#define EraseSectorFlashB(Addr) FlashB_Command(Addr, 0xff, FLASH_ERASE_CMD)
#define WriteByteFlashB(Addr, Data) FlashB_Command(Addr, Data, FLASH_PROGRAM_CMD)
void main(void)
{
unsigned char FlashErasedAddress = 0x4000;
unsigned char FlashWrittenAddress = 0x4000;
unsigned char FlashWrittenData = 'A';
if (!Flash_Busy())
{
EraseSectorFlashB(FlashErasedAddress);
}
if (!Flash_Busy())
{
WriteByteFlashB(FlashWrittenAddress, FlashWrittenData);
}
對(duì)于雙組Flash:
本節(jié)顯示了Flash B部分主要文件調(diào)用擦除和單字節(jié)寫入例程的典型實(shí)施方案。宏定義允許為兩種目的使用相同的例程,因?yàn)檫@兩種操作非常相似。下面是一種推薦的寫入/擦除Flash例程的部署方法。
#pragma CODE_SEG FLASH_A
unsigned char FlashB_Command(unsigned int FlashAddress, unsigned char FlashData, unsigned char Command)
{
/* Write Data into Flash*/
(*(volatile unsigned char *)(FlashAddress)) = FlashData;
/* Write Command */
FCMD = Command;
/* Launch command by setting FSTAT.FCBEF to 1 */
FSTAT = 0x80;
/* Wait at least 4 cycles to read the Error Flags */
_asm NOP;
_asm NOP;
_asm NOP;
_asm NOP;
/* Check if Flash Access Error or Protection Violation Error are Set */
if (FSTAT & (FSTAT_FACCERR_MASK|FSTAT_FPVIOL_MASK))
{
/* If so, finish the function returning FLASH_ERROR to indicate error */
FlashClearErrorFlags();
return (FLASH_ERROR);
}
/* Return FLASH_OK to indicate that the function executed Ok */
return (FLASH_OK);
}
#pragma CODE_SEG DEFAULT
所有寄存器和位名稱對(duì)應(yīng)于飛思卡爾S08系列MCU中現(xiàn)有的名稱。
結(jié)論
飛思卡爾雙組Flash是一個(gè)簡(jiǎn)單的想法,通過增強(qiáng)性能、避免CPU拖延情況、在代碼執(zhí)行過程中保持中斷服務(wù)例程、不需要把例程復(fù)制到RAM,簡(jiǎn)化了應(yīng)用設(shè)計(jì)。有了這些功能,可以更容易地設(shè)計(jì)和部署在代碼執(zhí)行過程中需要寫入或擦除Flash存儲(chǔ)器的最終應(yīng)用。
引導(dǎo)程序或EEPROM仿真等應(yīng)用通過考慮正確的存儲(chǔ)器分配并消除一些限制(如在Flash例程執(zhí)行過程中停止通信外設(shè)),利用該功能,從而提高效率。