痞子衡嵌入式:一種i.MXRT下從App中進入ROM序列下載模式的方法

痞子衡發表於2020-08-10

  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是i.MXRT下在App中利用ROM API進ISP/SDP模式的方法

  我們知道i.MXRT系列分為兩大陣營:CM33核心的i.MXRTxxx系列和CM7核心的i.MXRT1xxx系列,但是這兩個陣營都有一個共性,那就是都沒有內部非易失性儲存器(NVM)並且BootROM裡都整合了序列下載功能。

  序列下載功能即BootROM中實現了通過序列介面(UART/USB...)與上位機通訊,將客戶App資料燒錄進外部啟動裝置中,這個功能主要用於量產,但在實際應用過程中,通過首推的啟動引腳(ISP[2:0] / BT_MODE[1:0])配置進入序列下載模式的方式有時候不方便,因為引腳狀態不方便切換,本文痞子衡將給大家介紹一種不需要切換啟動引腳進入序列下載模式的方法。

一、配置啟動引腳進序列下載模式

  痞子衡很早前寫過兩篇文章,詳細介紹了 《i.MXRTxxx的序列下載模式(ISP)》《i.MXRT1xxx的序列下載模式(SDP)》,此處再簡單回顧一下。

1.1 i.MXRTxxx之ISP

  i.MXRTxxx系列用於配置進入序列下載模式的啟動引腳有3個,即ISP[2:0],這三個引腳在系統軟復位後由BootROM直接進行軟取樣,BootROM根據ISP[2:0]值判斷是否進入序列下載模式,序列介面支援UART/SPI/USB-HID三種,燒錄演算法是直接整合在BootROM中的,可以直接燒錄App。

1.2 i.MXRT1xxx之SDP

  i.MXRT1xxx系列用於配置進入序列下載模式的啟動引腳有2個,即BT_MODE[2:0],這兩個引腳僅在系統POR復位時由系統硬取樣儲存到非易失性暫存器SRC->SBMR2中,BootROM從SRC->SBMR2暫存器中獲取BT_MODE[2:0]值判斷是否進入序列下載模式,序列介面支援UART/USB-HID兩種,因為BootROM中沒有整合燒錄演算法,所以需要載入一個專用的Flashloader來燒錄App。

二、切換啟動引腳帶來的不便

  在恩智浦官方i.MXRT開發板設計上,外部啟動引腳是連線的撥碼開關,因此我們可以通過切換撥碼開關並復位的方式來進入ROM序列下載模式,但實際應用場景下,客戶板卡並不會留有撥碼開關,更多的是用上下拉電阻的方式確定啟動模式,而且預設設定的啟動模式是從Flash啟動。

  當客戶板卡首次上電,且連線的啟動Flash是空白時,即使啟動模式設定的是從Flash啟動,但由於Flash裡並沒有App,因此BootROM在啟動App失敗後還是會自動進入序列下載模式,這意味著至少可以進一次序列下載模式。當成功使用序列下載模式將App燒錄進啟動Flash之後,再次上電,此時板卡便會從ROM跳轉到App執行,這種情況下,除非改變啟動引腳輸入狀態,不然永遠不會再次進入序列下載模式。而在客戶板卡上改變啟動引腳狀態便意味著要重新焊接板子,改變啟動引腳的上下拉電阻,這當然很不方便。

三、藉助ROM API進入序列下載模式

  那麼有沒有不改變啟動引腳狀態就進入ROM序列下載模式的方法呢?答案當然是有。痞子衡之前寫過一篇文章 《瞭解i.MXRTxxx系列ROM API及其與i.MXRT1xxx系列的差異》,把i.MXRT全系列ROM API都捋了一遍。如果你足夠細心會發現它們都有一個共同的API,名字叫runBootloader:

// 適用i.MXRT500/600/1015/1020/1050
typedef struct
{
    void (*runBootloader)(void *arg);
    uint32_t version;
    const char *copyright;
    // 省略
} bootloader_api_entry_t;


// 適用i.MXRT1010/1060/1064/1170
typedef struct
{
    const uint32_t version;
    const char *copyright;
    void (*runBootloader)(void *arg);
    // 省略
} bootloader_api_entry_t;

  關於這個runBootloader API函式可在參考手冊中找到相關解釋,從文件中來看,這個函式的作用主要有兩個:一、IAP後直接去啟動新更新的App;二、重新進ROM序列下載模式去更新App。這第二個功能不正是我們要的效果嗎,讓我們試一試。

  根據前面介紹的ROM API知識,讓我們在App中把runBootloader函式重定義一下,runBootloader函式原型與API中原型保持一致,其函式實現就直接呼叫API:

// 適用i.MXRT500
#define g_bootloaderTree ((bootloader_api_entry_t *)0x0302f000)
// 適用i.MXRT600
#define g_bootloaderTree ((bootloader_api_entry_t *)0x0303f000)
// 適用i.MXRT1010/1015/1020/1050/1060/1064/1170
#define g_bootloaderTree (*(bootloader_api_entry_t **)0x0020001c)

void runBootloader(void* arg)
{
    g_bootloaderTree->runBootloader(arg);
}

  App中有了runBootloader函式,下一步就是傳參呼叫。先說呼叫,其實這裡就相當於切換啟動引腳操作了,因為我們不想切換啟動引腳,所以我們需要在App中插入一段runBootloader函式呼叫程式碼,並且需要為它設計一個專用的觸發方式(比如可以是某個引腳中斷,也可以是串列埠收到某個命令等等,這裡客戶自由發揮)。解決了呼叫問題,下一步就是傳什麼引數,參考手冊裡有詳細的引數各bit定義,下面是進入USB下載模式的示例程式碼:

// 適用i.MXRTxxx
uint32_t arg = 0xeb130000;
// 適用i.MXRT1xxx
uint32_t arg = 0xeb100000;

// 進入ROM USB下載模式
runBootloader(&arg);

  下圖是i.MXRT500中arg位定義,進入USB下載模式引數值應是0xeb130000:

  下圖是i.MXRT1060中arg位定義,進入USB下載模式引數值應是0xeb100000或0xeb110000:

  runBootloader(&arg)函式執行完之後,此時在USB OTG1口上插上USB線應該可以看到電腦裝置上重新列舉了HID裝置,然後就可以使用配套上位機工具(比如MCUBootUtility)進行App更新下載了。

四、附錄

  附錄收錄了i.MXRT兩大陣營代表型號的ROM API中runBootloader具體實現,其中i.MXRTxxx系列對應例項是bootloader_user_entry()函式,i.MXRT1xxx系列對應例項是run_bootloader()函式,這兩個函式的核心思想都是在晶片某個非易失性(軟復位不置位)的暫存器中將使用者傳入的引數值儲存下來,然後呼叫NVIC軟復位函式重新進入BootROM,由BootROM來處理使用者傳入的引數:

附1、i.MXRT500 BootROM中bootloader_user_entry()實現

#define SET_USER_APP_BOOT_OPTIONS(val) ((*(volatile uint32_t *)(SYSCTL0_BASE + 0x384)) = val)

void bootloader_user_entry(void *arg)
{
    SET_USER_APP_BOOT_OPTIONS(*(uint32_t *)arg);

    NVIC_SystemReset();
}

附2、i.MXRT1060 BootROM中run_bootloader()實現

enum
{
    kEnterBootloader_Tag = 0xEB,
    kEnterBootloader_Mode_Default = 0,
    kEnterBootloader_Mode_SerialDownloader = 1,

    kEnterBootloader_SerialInterface_Auto = 0,
    kEnterBootloader_SerialInterface_USB = 1,
    kEnterBootloader_SerialInterface_UART = 2,

    kEnterBootloader_ImageIndex_Max = 3,
};

typedef union
{
    struct
    {
        uint32_t imageIndex:4;
        uint32_t reserved:12;
        uint32_t serialBootInterface:4;
        uint32_t bootMode:4;
        uint32_t tag:8;
    }B;
    uint32_t U;
}run_bootloader_ctx_t;

void run_bootloader(void *arg)
{
    const run_bootloader_ctx_t *ctx = (const run_bootloader_ctx_t*)arg;
	if (ctx->B.tag != kEnterBootloader_Tag)
	{
		break;
	}
	if (ctx->B.bootMode > kEnterBootloader_Mode_SerialDownloader)
	{
		break;
	}
	if (ctx->B.imageIndex > kEnterBootloader_ImageIndex_Max)
	{
		break;
	}

	SRC->GPR[3] = ctx->U;

	__DSB();
	__ISB();

	NVIC_SystemReset();
}

  至此,i.MXRT下在App中利用ROM API進ISP/SDP模式的方法痞子衡便介紹完畢了,掌聲在哪裡~~~

歡迎訂閱

文章會同時釋出到我的 部落格園主頁CSDN主頁知乎主頁微信公眾號 平臺上。

微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。

相關文章