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



  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是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/1024/1050/1160/1170
typedef struct
{
    void (*runBootloader)(void *arg);
    uint32_t version;
    const char *copyright;
    // 省略
} bootloader_api_entry_t;


// 適用i.MXRT1010/1060/1064
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/1024/1050/1060/1064
#define g_bootloaderTree (*(bootloader_api_entry_t **)0x0020001c)
// 適用i.MXRT1160/1170
#define g_bootloaderTree (*(bootloader_api_entry_t **)0x0021001c)

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

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

  • Note:實際上i.MXRT1015/1020/1050三款型號並不支持通過API進入ROM SDP模式,原因見附錄三
// 適用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;
    do
    {
        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();

    } while (0);
}

附3、i.MXRT1015/1020/1050 BootROM中run_bootloader()實現

  雖然我們在i.MXRT1015/1020/1050三款型號上讀API tree能夠發現runBootloader成員是有非0值的,但其具體函數如下,因此不支持重入ROM SDP模式,效果類似於軟復位。

void run_bootloader(void *arg)
{
    NVIC_SystemReset();
}

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

歡迎訂閱

文章會同時發布到我的 博客園主頁CSDN主頁知乎主頁微信公眾號 平台上。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM