問題:
該問題由客戶提出,發(fā)生在STM32F103VDT6器件上。據(jù)客戶工程師講述,在其產(chǎn)品設計中使用了STM32片上Flash模擬了EEPROM的功能,用于存貯數(shù)據(jù)。在軟件調試時,發(fā)現(xiàn)開啟此功能,會影響到USART通信,導致偶爾發(fā)生個別數(shù)據(jù)接收不到的現(xiàn)象。 調研: 檢查其軟件代碼,發(fā)現(xiàn)其中對Flash上數(shù)據(jù)的更新操作分為如下幾個步驟: 1. 保存Flash頁上的數(shù)到RAM中; 2. 擦除Flash頁; 3. 修改RAM中的數(shù)據(jù); 4. 將RAM中的數(shù)據(jù)寫回Flash頁上; 對照STM32的數(shù)據(jù)手冊,查找到相關的數(shù)據(jù): 1. 字寫入時間 40uS ~ 70uS; 2. 頁擦除時間40mS; 檢查軟件代碼,找到對USART的設置: 1. 波特率115200BPS; 2. 幀格式為1 個起始位,8個數(shù)據(jù)位,2個停止位; 檢查軟件代碼,發(fā)現(xiàn)其對USART的接收數(shù)據(jù)采用中斷的方式進行讀取。 結論: 通過計算,USART的每個幀的傳輸時間為: 該時間大于Flash的字寫入時間,小于Flash的頁擦除時間。所以,在Flash頁擦除期間有可能發(fā)生多次字節(jié)幀的傳輸。而在此其間,由于Flash接口不可用,CPU不能取指令,導致中斷得不到及時響應,從而發(fā)生接收到的數(shù)據(jù)未及時讀走而被覆蓋的現(xiàn)象。 處理: 在內存中建立循環(huán)緩沖區(qū),開啟DMA 通道。一旦USART 有數(shù)據(jù)收到,DMA 負責將其傳輸至循環(huán)緩沖區(qū)中。軟件定期檢測循環(huán)緩沖區(qū)中是否有接收到的數(shù)據(jù),如果有則加以處理。 建議: 在只有一個Flash 模塊的STM32 中,CPU對 Flash 接口的使用具有獨占性。該接口不能同時進行多個操作,比如,在寫操作的同時進行讀取操作,或在擦除操作的同時進行讀取操作,即便讀、寫操作的地址不同,或者所讀數(shù)據(jù)不在被擦除的頁上也不行。因此,當程序運行在Flash 上情況下,在對Flash 進行寫入、擦除操作時,往往會因為CPU 取不到指令而造成程序執(zhí)行的停頓。這一現(xiàn)象會引發(fā)系統(tǒng)對外部事件響應上的不及時,必須采取相應的措施加以避免。
通常,對于通信接收數(shù)據(jù)這類事件,可以使用DMA 進行輔助,避免數(shù)據(jù)被覆蓋。對于外部中斷、定時事件等必須要軟件及時響應的事件來說,可以將中斷向量表轉移到 RAM 中,同時將中斷服務程存放在 RAM 中執(zhí)行。CPU 的VTOR 寄存器用來存放中斷向量表的偏移地址,修改該寄存器的值,可以改變中斷向量表在內存空間中存放的地址,詳見Cortex-M3 的技術參考手冊。
在IAR 開發(fā)工具下,定義一個在RAM 中執(zhí)行的函數(shù),可以使用其擴展關鍵字__ramfunc,舉例如下: __ramfunc void EXTI9_5_IRQHandler(void) { If (EXTI_GetITStatus(EXTI_Line9)!=RESET) { /*clearthe EXTI line9 pending bit*/ EXTI_ClearITPendingBit(EXTI_Line9); …… } } 在Keil MDK 中,可以將相關的中斷服務程序單獨放在一個“.c”文件中,然后通過修改scatter 文件來實現(xiàn)在RAM中執(zhí)行,例如中斷服務程序放在“exti9.c”中: LD_ROM 0x8000000 0x10000 { EX_ROM 0x08000000 0x10000 { Startup.o(RESET,+FIRST) ANY(+RO) } EX_RAM 0x20000000 { exti9.o(+RO) ;將中斷服務函數(shù)放在RAM中 } RW_RAM +O { startup.o(STACK,+ZI) .ANY(+RW,+ZI). } }
話題延伸: 我們再從另一角度來思考和探討該問題,拋磚引玉吧。 顯然,導致程序發(fā)生執(zhí)行停頓的一個關鍵原因是對Flash 進行了寫或者擦除操作。如果再排除這一原因,也就不會發(fā)生程序執(zhí)行停頓了。所以,要解決這一問題,可以將數(shù)據(jù)暫存在RAM 當中,而不是每次修改后都立刻更新Flash。可以考慮利用STM32 的PVD 功能監(jiān)控電源電壓,發(fā)現(xiàn)有掉電傾向立刻啟動寫Flash 操作,將RAM中暫存的數(shù)據(jù)寫到Flash 中。這樣,在整個STM32 運行期間,只有到了最后時刻才對Flash進行了寫操作,而這時也不需要再響應什么事件了(也沒時間響應了)。這是一個不錯的思路,當然,很多細節(jié)需要仔細斟酌。
首先,為節(jié)約這最后的每一微秒時間,F(xiàn)lash頁面的擦除要提前完成。若將這一操作放在STM32 開始監(jiān)控各種事情以后自然不妥,因為同樣會造成事件響應不及時。看來,最佳時間段只有系統(tǒng)初始化階段了。
其次,在擦除Flash 期間發(fā)生掉電要如何處置?這時,備份在RAM 中的數(shù)據(jù)會來不及寫回Flash 而丟失。為了避免這一情況的發(fā)生,可以使用雙頁交替存貯的方式(傳說中的乒乓操作)保存數(shù)據(jù)。兩個Flash 頁上分別存有兩個版本的數(shù)據(jù),一新一舊,并在最后附有版本號及校驗和。系統(tǒng)初始化時,選擇的舊版本頁面或無效頁面進行擦除。這樣,既便此時發(fā)生掉電,也無須向Flash 寫回備份數(shù)據(jù),因為最新數(shù)據(jù)仍在Flash中。而每次向Flash 更新數(shù)據(jù)時,都以更新的版本號進行標注,原來的新本數(shù)據(jù)自然的變成了舊版本。 ------ 微信公眾號 融創(chuàng)芯城(一站式電子元器件、PCB/PCBA購買,項目眾包,方案共享平臺) 再次,為了盡量延長最后的可用于寫Flash 的時間,盡量降低系統(tǒng)功耗是非常有必要的。關掉PLL,直接使用OSC 送來的時鐘作為系統(tǒng)時鐘,可以明顯的降低功耗。關掉所有外設的時鐘可以進一步降低系統(tǒng)的功耗。根據(jù)片外電路的特性,調整I/O 的輸出狀還能進一步降低系統(tǒng)功耗。
|