你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們介紹的是i.MXRT1xxx裏SystemReset不復位的GPR寄存器的小妙用。微信
咱們知道稍大規模的項目代碼設計通常都是多人協做完成的,在項目開始階段的整體設計時,項目組長一般會將代碼按功能進行劃分,每一個功能塊代碼之間儘可能作到耦合度低、互不依賴、互不影響,這樣各功能能夠獨立進行單元測試,項目得以並行開發,後期經過事先定義好的接口/協議進行功能塊集成便可。架構
但上述方法在嵌入式軟件項目裏有時候會遇到功能塊集成後互相干擾的問題,由於嵌入式項目不少時候並非純軟件設計,也會跟片內外設資源打交道,而片內外設屬於硬件範疇,硬件模塊的工做是有先後狀態依賴的(這點在片內時鐘的配置上體現得尤爲明顯),出了問題傳統方法是具體分析具體解決,來一個就解決一個,但任何代碼的改動或者後期新特性的增長均可能會帶來新的潛在干擾問題。app
那麼對於上述困境,有沒有一個一勞永逸的解決方法?實際上是有的!那就是每一個功能塊在設計時都不要依賴芯片初始狀態,按照進入時先清理系統環境,而後作功能設計,退出時作一下系統恢復。這種方法雖然保險,可是會引入集成後項目總體運行低效的問題。今天痞子衡要在具體項目實戰中介紹一種利用i.MXRT芯片內System Reset後不復位的GPR寄存器來解決屬性上互斥的功能代碼塊集成互相干擾問題的方法。函數
恩智浦MCU SE團隊近期一直在加班加點趕一個大項目,這個項目是爲客戶產品OTA需求而生的。咱們知道在線升級是每一個智能產品都不可繞開的話題,恩智浦SE團隊爲了方便客戶在基於i.MXRT/LPC的產品上作在線升級,特別推出OTA參考設計,下面是功能架構簡圖:項目分爲SBL + SFW兩部分,SBL負責ISP本地更新(UART/USB)以及App切換管理;SFW是一個示例App,其除了客戶項目業務功能外,也集成了遠程更新功能(WiFi、以太、U盤、SD卡四種升級方式)。單元測試
在SBL代碼設計裏,主要有兩大子功能模塊:一個是ISP本地更新 (isp_boot_main),另外一個是App切換管理(sbl_boot_main),其中ISP本地更新屬於可選項,而App切換管理是必選。測試
SBL的主流程是上電啓動運行後,先執行ISP本地更新功能塊,在超時時間內,若是有收到來自Host的ISP命令,則進入ISP命令處理,此後除非執行ISP復位命令或者有板級復位,不然不會退出ISP;ui
若是在超時時間內沒有收到ISP命令,則轉到App切換管理功能塊。在驗證App時,若是發現Flash裏有合法App,則跳轉過去執行;若是沒有發現合法的App,會從新返回ISP本地更新(此時是無限超時)。大概主邏輯代碼以下:加密
#define COMPONENT_MCU_ISP #define ISP_TIMEOUT (5) // in seconds int main(void) { #if (defined(COMPONENT_MCU_ISP)) // 先嚐試isp本地更新(有5s超時) bool isInfiniteIsp = false; isp_boot_main(isInfiniteIsp); #endif // 無本地升級則進入app跳轉處理 sbl_boot_main(); } #if (defined(COMPONENT_MCU_ISP)) void isp_boot_main(bool isInfiniteIsp) { if (isInfiniteIsp || ISP_TIMEOUT) { // isp相關功能外設初始化 isp_boot_init(); // 在5s超時時間內/無限超時去等待isp命令 isp_boot_run(isInfiniteIsp); } } #endif void sbl_boot_main(void) { // 判斷Flash裏是否存在合法的app if (sbl_boot_go() != 0) { #if (defined(COMPONENT_MCU_ISP)) // 無合法app,重入isp本地更新(無限超時) bool isInfiniteIsp = true; isp_boot_main(isInfiniteIsp); #endif NVIC_SystemReset(); } else { // 有合法app,跳轉到app執行 sbl_do_boot(); } while (1); }
上述SBL設計裏,你會發現ISP本地更新和App切換管理兩個功能塊在執行上是互斥的,是Flash裏的App處理需求將它們聯繫在了一塊兒。從軟件集成角度來講,這兩個功能本不應互相影響,但實際上它們之間產生了互相影響,由於各自在設計時沒有遵循進入時清理系統、退出時恢復現場的準則,因此 isp_boot_main() 在超時結束後跳到 sbl_boot_main() 對其部分驗籤功能產生了影響,而 sbl_boot_main() 執行後沒找到合法App跳回 isp_boot_main() 時又出現ISP功能不正常的狀況,咱們須要解決這個問題。.net
第一小節裏描述的問題,能夠經過功能塊退出時恢復現場來解決,可是每一個模塊代碼量都比較大,使用代碼去逐一恢復現場不太容易。痞子衡想到的一個好辦法就是調用 NVIC_SystemReset() 函數來簡單粗暴地將芯片系統復位,咱們所須要作的就是尋找一個區域,可以臨時存放標誌位,而且這個區域內容不受系統軟復位的影響,芯片復位回來以後在SBL裏增長對標誌位的判斷處理,處理結束後再將標誌位清掉。設計
在尋找這個不受系統軟復位影響的區域前,咱們先對i.MXRT裏面的電源管理架構做個基本瞭解。下圖是i.MXRT1060的電源架構,除了USB和ADC模塊特殊供電需求外,芯片一共有四種電源輸入。在板級供電設計時,一般VDD_SNVS_IN須要單獨一路外部輸入(設計上應由電池供電),其餘三路電源VDD_HIGH_IN / DCDC_IN / VDD_SOC_IN可共用一路外部輸入(芯片POR_B引腳每每連在這個外部輸入控制上)。
VDD_HIGH_IN:給芯片內部LDO供電 DCDC_IN:給芯片內部DCDC模塊供電 VDD_SOC_IN:給芯片主系統(Core,SoC,Memory)供電 VDD_SNVS_IN:給芯片內部SNVS域相關模塊供電
從芯片系統復位等級上來分,一共有三類復位:第一類是藉助Cortex-M7內核SCB模塊的AIRCR寄存器中集成的SYSRESETREQ復位的支持、第二類是DCDC從新上電(POR_B復位)、第三類是總體從新上電。依據這三種不一樣程度的復位,痞子衡整理了i.MXRT上全部可存放標誌位的區域受不一樣復位類型影響狀況以下:
模塊 \ 復位類型 | NVIC_SystemReset() | POR_B和DCDC從新上電 | 總體從新上電 |
---|---|---|---|
TCM OCRAM IOMUXC_GPR SRC_GPR |
保持 | 復位 | 復位 |
IOMUXC_SNVS_GPR SNVS_LPGPR |
保持 | 保持 | 復位 |
Flash, eFuse | 保持 | 保持 | 保持 |
根據上表,咱們先排除掉NVM屬性的Flash和eFuse,它們不符合臨時存放、輕鬆讀寫的需求。TCM / OCRAM可用,但須要在SBL工程裏作特殊處理,分配一塊.noinit區,而且要肯定BootROM沒有使用這個區域,用起來仍是有點麻煩。IOMUXC_GPR / SRC_GPR用起來簡單,但它們已被SoC / BootROM佔用了,不能隨便使用,對芯片產生的影響未知。IOMUXC_SNVS_GPR / SNVS_LPGPR這兩個都不錯,但後者在使能加密時有時會被用來存放用戶密鑰。因此 IOMUXC_SNVS_GPR 寄存器纔是最佳選擇,這也是真正意義上開放給用戶自由使用的GPR寄存器。
如今咱們找到了理想的 IOMUXC_SNVS_GPR 寄存器,那麼可在SBL代碼中增長兩個函數 isp_cleanup_enter()、isp_cleanup_exit(),前者用於觸發軟復位前標記狀態,後者用於軟復位後讀取標記的狀態作相應處理。最終修改後的SBL主邏輯代碼以下:
#define CLEANUP_ISP_TO_SBL (0x5A) #define CLEANUP_SBL_TO_ISP (0xA5) bool isp_cleanup_exit(bool *isInfiniteIsp) { uint32_t flag = IOMUXC_SNVS_GPR->GPR0; switch (flag) { // SBL_TO_ISP軟復位狀況下,進入無限超時isp case CLEANUP_SBL_TO_ISP: *isInfiniteIsp = true; flag = 0x0; break; // 第一次上電或ISP_TO_SBL軟復位狀況下,直接退出isp case CLEANUP_ISP_TO_SBL: default: break; } IOMUXC_SNVS_GPR->GPR0 = 0x0; return flag; } void isp_cleanup_enter(uint32_t flag) { IOMUXC_SNVS_GPR->GPR0 = flag; NVIC_SystemReset(); } #if (defined(COMPONENT_MCU_ISP)) void isp_boot_main(bool isInfiniteIsp) { // 加一級對於Reset不復位flag的判斷處理 if (!isp_cleanup_exit(&isInfiniteIsp)) { if (isInfiniteIsp || ISP_TIMEOUT) { isp_boot_init(); isp_boot_run(isInfiniteIsp); // 標記flag爲ISP_TO_SBL,並觸發軟復位 isp_cleanup_enter(CLEANUP_ISP_TO_SBL); } } } #endif void sbl_boot_main(void) { if (sbl_boot_go() != 0) { #if (defined(COMPONENT_MCU_ISP)) // 標記flag爲SBL_TO_ISP,並觸發軟復位 isp_cleanup_enter(CLEANUP_SBL_TO_ISP); #endif NVIC_SystemReset(); } else { sbl_do_boot(); } while (1); }
至此,i.MXRT1xxx裏SystemReset不復位的GPR寄存器的小妙用痞子衡便介紹完畢了,掌聲在哪裏~~~
文章會同時發佈到個人 博客園主頁、CSDN主頁、知乎主頁、微信公衆號 平臺上。
微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就能夠在手機上第一時間看了哦。