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


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

  咱們知道i.MXRT系列分爲兩大陣營:CM33內核的i.MXRTxxx系列和CM7內核的i.MXRT1xxx系列,可是這兩個陣營都有一個共性,那就是都沒有內部非易失性存儲器(NVM)而且BootROM裏都集成了串行下載功能。算法

  串行下載功能即BootROM中實現了經過串行接口(UART/USB...)與上位機通信,將客戶App數據燒錄進外部啓動設備中,這個功能主要用於量產,但在實際應用過程當中,經過首推的啓動引腳(ISP[2:0] / BT_MODE[1:0])配置進入串行下載模式的方式有時候不方便,由於引腳狀態不方便切換,本文痞子衡將給你們介紹一種不須要切換啓動引腳進入串行下載模式的方法。api

1、配置啓動引腳進串行下載模式

  痞子衡很早前寫過兩篇文章,詳細介紹了 《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。工具

2、切換啓動引腳帶來的不便

  在恩智浦官方i.MXRT開發板設計上,外部啓動引腳是鏈接的撥碼開關,所以咱們能夠經過切換撥碼開關並復位的方式來進入ROM串行下載模式,但實際應用場景下,客戶板卡並不會留有撥碼開關,更多的是用上下拉電阻的方式肯定啓動模式,並且默認設置的啓動模式是從Flash啓動。ui

  當客戶板卡首次上電,且鏈接的啓動Flash是空白時,即便啓動模式設置的是從Flash啓動,但因爲Flash裏並無App,所以BootROM在啓動App失敗後仍是會自動進入串行下載模式,這意味着至少能夠進一次串行下載模式。當成功使用串行下載模式將App燒錄進啓動Flash以後,再次上電,此時板卡便會從ROM跳轉到App執行,這種狀況下,除非改變啓動引腳輸入狀態,否則永遠不會再次進入串行下載模式。而在客戶板卡上改變啓動引腳狀態便意味着要從新焊接板子,改變啓動引腳的上下拉電阻,這固然很不方便。.net

3、藉助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函數可在參考手冊中找到相關解釋,從文檔中來看,這個函數的做用主要有兩個:1、IAP後直接去啓動新更新的App;2、從新進ROM串行下載模式去更新App。這第二個功能不正是咱們要的效果嗎,讓咱們試一試。code

  根據前面介紹的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更新下載了。

4、附錄

  附錄收錄了i.MXRT兩大陣營表明型號的ROM API中runBootloader具體實現,其中i.MXRTxxx系列對應實例是bootloader_user_entry()函數,i.MXRT1xxx系列對應實例是run_bootloader()函數,這兩個函數的核心思想都是在芯片某個非易失性(軟復位不置位)的寄存器中將用戶傳入的參數值保存下來,而後調用NVIC軟復位函數從新進入BootROM,由BootROM來處理用戶傳入的參數:

附一、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();
}

附二、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主頁知乎主頁微信公衆號 平臺上。

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

相關文章
相關標籤/搜索