1. 程式人生 > 實用技巧 >【STM32F407開發板使用者手冊】第29章 STM32F407的系統bootloader之USB DFU方式韌體升級

【STM32F407開發板使用者手冊】第29章 STM32F407的系統bootloader之USB DFU方式韌體升級

最新教程下載:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255

第29章 STM32F407的系統bootloader之USB DFU方式韌體升級

本章節為大家講解使用系統bootloader做程序升級的方法,即使不依賴外部boot引腳也可以方便升級。

DFU的全稱是Device Firmware Upgrade,即裝置韌體升級

29.1 初學者重要提示

29.2 跳轉到系統bootloader的程式設計

29.3 STM32CubeProg的安裝說明

29.3 STM32CubeProg的程式下載說明

29.4 USB DFU方式系統bootloader驅動移植和使用

29.6 實驗例程設計框架

29.7 實驗例程說明(MDK)

29.8 實驗例程說明(IAR)

29.9 總結

29.1 初學者重要提示

  1. 學習本章節前,務必優先學習第28章。
  2. 本章用到的相關軟體和文件下載:http://www.armbbs.cn/forum.php?mod=viewthread&tid=96573
  3. 軟體STM32CubeProg和DfuSe都支援USB DFU,但是兩個軟體不能都安裝使用,因為這兩個軟體的USB驅動不同,導致工作在系統bootloader模式的板子通過USB線接到電腦端時,只有一個軟體的驅動被識別。
  4. DfuSe是老版的USB DFU軟體,不推薦大家使用了。建議使用STM32CubeProg,此軟體實現了之前的DfuSe,STLINK小軟體和Flashloader三合一,並且支援外部EEPROM,NOR Flash,SPI Flash,NAND Flash等燒寫,也支援OTA程式設計。
  5. 本章節的USB DFU的下載軟體採用STM32CubeProg,如果想使用DfuSe的話,此貼有詳細說明:http://www.armbbs.cn/forum.php?mod=viewthread&tid=11185
  6. 當晶片工作在系統bootLoader的USB DFU模式,更新完畢程式後,不會自動退出USB DFU,需要重新復位晶片後才會退出。由於DFU模式會用到USB線,插拔USB線是難以避免的,所以是否支援自動退出,並不影響。

29.2 跳轉到系統bootLoader的程式設計

程式設計如下,基本是按照第28章3.2小節的方法進行設計

1.    /*
2.    ******************************************************************************************************
3.    *    函 數 名: JumpToBootloader
4.    *    功能說明: 跳轉到系統BootLoader
5.    *    形    參: 無
6.    *    返 回 值: 無
7.    ******************************************************************************************************
8.    
*/ 9. static void JumpToBootloader(void) 10. { 11. uint32_t i=0; 12. void (*SysMemBootJump)(void); /* 宣告一個函式指標 */ 13. __IO uint32_t BootAddr = 0x1FFF0000; /* STM32F4的系統BootLoader地址 */ 14. 15. /* 關閉全域性中斷 */ 16. DISABLE_INT(); 17. 18. /* 關閉滴答定時器,復位到預設值 */ 19. SysTick->CTRL = 0; 20. SysTick->LOAD = 0; 21. SysTick->VAL = 0; 22. 23. /* 設定所有時鐘到預設狀態,使用HSI時鐘 */ 24. HAL_RCC_DeInit(); 25. 26. /* 關閉所有中斷,清除所有中斷掛起標誌 */ 27. for (i = 0; i < 8; i++) 28. { 29. NVIC->ICER[i]=0xFFFFFFFF; 30. NVIC->ICPR[i]=0xFFFFFFFF; 31. } 32. 33. /* 使能全域性中斷 */ 34. ENABLE_INT(); 35. 36. /* 設定重對映到系統Flash */ 37. __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); 38. 39. /* 跳轉到系統BootLoader,首地址是MSP,地址+4是復位中斷服務程式地址 */ 40. SysMemBootJump = (void (*)(void)) (*((uint32_t *) (BootAddr + 4))); 41. 42. /* 設定朱堆疊指標 */ 43. __set_MSP(*(uint32_t *)BootAddr); 44. 45. /* 在RTOS工程,這條語句很重要,設定為特權級模式,使用MSP指標 */ 46. __set_CONTROL(0); 47. 48. /* 跳轉到系統BootLoader */ 49. SysMemBootJump(); 50. 51. /* 跳轉成功的話,不會執行到這裡,使用者可以在這裡新增程式碼 */ 52. while (1) 53. { 54. 55. } 56. }

這裡把程式設計中的幾個關鍵地方做個說明:

  • 第12行,宣告一個函式指標。
  • 第13行,這個要特別注意,F4的系統bootloader地址。
  • 第19到21行,設定滴答定時器到復位值。
  • 第24行,此函式比較省事,可以方便的設定F4所有時鐘到復位值,內部時鐘使用HSI。
  • 第27到31行,清除所有中斷掛起標誌並關閉中斷,這裡是直接通過一個for迴圈設定了NVIC所有配置位,共8組。

  • 第37行,將系統bootloader的地址對映到0x0000 0000。這點非常重要,根本原因是F4的系統bootloader要從0x00000000地址讀取中斷向量。
  • 第40行,將系統bootLoader的中斷復位服務程式的入口地址賦給第12行宣告的函式,使用者執行這個函式時,就會直接跳轉過去。
  • 第43行,設定主堆疊指標位置,即系統bootloader的首地址儲存的就是棧地址。
  • 第46行,這個設定在RTOS應用程式中比較重要,因為基於Cortex-M核心的RTOS任務堆疊基本都是使用執行緒堆疊指標PSP。但系統bootLoader使用的是主堆疊指標MSP,所以務必要設定下,同時讓M核心工作於特權級。此暫存器的作用如下:

  • 第49行,跳轉到系統bootLoader。

29.3 STM32CubeProg的安裝說明

STM32CubeProg的安裝比較簡單如果大家的電腦中缺少JAVA環境,會提示安裝,按照提示操作即可。

這裡特別注意USB DFU驅動的安裝,如果大家的電腦上安裝了DfuSe軟體,那邊板子工作在系統bootLoader模式時,電腦端的裝置管理器識別出來的標識是這樣的:

如果用STM32CubeProg的話,務必要將此驅動刪掉,滑鼠右擊此標識,選擇解除安裝,彈出如下對話方塊:

解除安裝完畢後,重啟電腦,然後執行STM32CubeProg安裝目錄裡面的STM32Bootloader.bat即可,最後插上裝置就可以正常識別了。識別後的標識:

29.4 STM32CubeProg的程式下載說明

這裡把兩種下載方式都做個說明,一種是設定外部boot引腳進行下載,另一種是設定程式跳轉到系統bootloader進行下載。

29.4.1 設定boot引腳跳轉到系統bootLoader

  • 第1步:此介面插上USB線:

  • 第2步:板子上電前按住右下角的BOOT引腳。

  • 第3步:板子上電3秒左右,鬆手。

在電腦端裝置管理器就可以看到已經識別出來:

29.4.2 應用程式跳轉到系統bootloader

應用程式跳轉到系統bootLoader比較方便,無需使用者操作外接的boot引腳了,只需呼叫本章第2小節的程式就可以跳轉。本章配套的例子是使用者按下按鍵K1後執行跳轉程式,大家可以根據需要實現各種觸發跳轉的方式。跳轉成功後,在電腦端裝置管理器裡面也會看到bootloader標識:

29.4.3 STM32CubeProg下載程式設定

識別成功後就可以下載程式了。

第1步,選擇USB方式,點選Connect按鈕。

識別成功後的效果如下:

這裡要特別注意一點,如果使用者沒有關閉這個軟體,多次插拔USB線時,記得點選這裡的重新整理按鈕,因為有時候這個軟體不會自動顯示出來,點選重新整理按鈕才行。

第2步,新增要下載的hex檔案,勾選需要設定的選項,點選啟動程式設計。

  • Start address選項不填的話,預設會下載到內部Flash的首地址,保險起見,大家也可以填上首地址0x0800 0000,或者其它要下載的地址。
  • Run after programming選項勾選或者不勾選均可,因為測試發現STM32CubeProg不支援USB DFU程式設計後執行。這樣特別說一點,如果勾上此選項後,下載完畢程式後,會自動斷開連線,並彈出一些列視窗,最終彈出下面這個視窗:

彈出這個視窗並不是表示下載失敗了,而是下載完成後退出了系統bootloader。

第3步,完成下載後的效果如下:

下載完成後板子重新上電就可以看到程式已經成功下載了。

29.5 USB DFU方式系統Bootloader驅動移植和使用

系統bootloader的移植比較簡單,僅需新增本章第2小節的程式到自己工程裡面即可。裡面有個開關中斷API,是在bsp.h檔案裡面定義的:

/* 開關全域性中斷的巨集 */
#define ENABLE_INT()    __set_PRIMASK(0)    /* 使能全域性中斷 */
#define DISABLE_INT()    __set_PRIMASK(1)    /* 禁止全域性斷 */

29.6 實驗例程設計框架

通過程式設計框架,讓大家先對配套例程有一個全面的認識,然後再理解細節,本次實驗例程的設計框架如下:

第1階段,上電啟動階段:

  • 這部分在第14章進行了詳細說明。

第2階段,進入main函式:

  • 第1部分,硬體初始化,主要是HAL庫,系統時鐘,滴答定時器和LED。
  • 第2部分,應用程式設計部分,K1按鍵按下後跳轉到系統bootloader。。

29.7 實驗例程說明(MDK)

配套例子:

V5-009_基於系統bootloader的USB介面方式IAP升級(USB DFU)

實驗目的:

  1. 學習基於系統bootloader的USB介面方式IAP升級。

實驗內容:

  1. STM32的系統儲存區自帶BootLoader,可以方便的實現串列埠,I2C,CAN,SPI,USB等介面方式的程序升級。
  2. 如果使用系統BootLoader支援的介面升級方式,基本就不需要使用者自己做BootLoader了。
  3. 除了通過boot引腳控制啟動地址,也可以直接從應用程式裡面跳轉到系統儲存區。

實驗操作:

  1. K1鍵按下,跳轉到系統BootLoader。

上電後串列埠列印的資訊:

波特率 115200,資料位 8,奇偶校驗位無,停止位 1。

程式設計:

系統棧大小分配:

硬體外設初始化

硬體外設的初始化是在 bsp.c 檔案實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬體裝置。該函式配置CPU暫存器和外設的暫存器並初始化一些全域性變數。只需要呼叫一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 
       STM32F407 HAL 庫初始化,此時系統用的還是F407自帶的16MHz,HSI時鐘:
       - 呼叫函式HAL_InitTick,初始化滴答時鐘中斷1ms。
       - 設定NVIV優先順序分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鐘到168MHz
       - 切換使用HSE。
       - 此函式會更新全域性變數SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於程式碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。
       - 預設不開啟,如果要使能此選項,務必看V5開發板使用者手冊第8章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();        /* 初始化串列埠 */
    bsp_InitExtIO();    /* 初始化擴充套件IO */
    bsp_InitLed();        /* 初始化LED */    
    BEEP_InitHard();    /* 初始化蜂鳴器 */
}

主功能:

主程式實現如下操作:

  • 啟動一個自動重灌軟體定時器,每100ms翻轉一次LED2。
  • K1鍵按下,跳轉到系統BootLoader。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程式入口
*    形    參: 無
*    返 回 值: 錯誤程式碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;    /* 按鍵程式碼 */

    
    bsp_Init();        /* 硬體初始化 */
    PrintfLogo();    /* 列印例程名稱和版本等資訊 */
    PrintfHelp();    /* 列印操作提示 */
    
    bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重灌的定時器 */
    
    while (1)
    {
        bsp_Idle();        /* 這個函式在bsp.c檔案。使用者可以修改這個函式實現CPU休眠和喂狗 */

        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        /* 按鍵濾波和檢測由後臺systick中斷服務程式實現,我們只需要呼叫bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下,K1鍵按下,跳轉到系統BootLoader */
                    JumpToBootloader();
                    break;
                    
                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        }
    }
}

29.8 實驗例程說明(IAR)

配套例子:

V5-009_基於系統bootloader的USB介面方式IAP升級(USB DFU)

實驗目的:

  1. 學習基於系統bootloader的USB介面方式IAP升級。

實驗內容:

  1. STM32的系統儲存區自帶bootLoader,可以方便的實現串列埠,I2C,CAN,SPI,USB等介面方式的程序升級。
  2. 如果使用系統bootLoader支援的介面升級方式,基本就不需要使用者自己做bootLoader了。
  3. 除了通過boot引腳控制啟動地址,也可以直接從應用程式裡面跳轉到系統儲存區。

實驗操作:

  1. K1鍵按下,跳轉到系統bootLoader。

上電後串列埠列印的資訊:

波特率 115200,資料位 8,奇偶校驗位無,停止位 1。

程式設計:

系統棧大小分配:

硬體外設初始化

硬體外設的初始化是在 bsp.c 檔案實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬體裝置。該函式配置CPU暫存器和外設的暫存器並初始化一些全域性變數。只需要呼叫一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 
       STM32F407 HAL 庫初始化,此時系統用的還是F407自帶的16MHz,HSI時鐘:
       - 呼叫函式HAL_InitTick,初始化滴答時鐘中斷1ms。
       - 設定NVIV優先順序分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鐘到168MHz
       - 切換使用HSE。
       - 此函式會更新全域性變數SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於程式碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。
       - 預設不開啟,如果要使能此選項,務必看V5開發板使用者手冊第8章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();        /* 初始化串列埠 */
    bsp_InitExtIO();    /* 初始化擴充套件IO */
    bsp_InitLed();        /* 初始化LED */    
    BEEP_InitHard();    /* 初始化蜂鳴器 */
}

主功能:

主程式實現如下操作:

  • 啟動一個自動重灌軟體定時器,每100ms翻轉一次LED2。
  • K1鍵按下,跳轉到系統BootLoader。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程式入口
*    形    參: 無
*    返 回 值: 錯誤程式碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;    /* 按鍵程式碼 */

    
    bsp_Init();        /* 硬體初始化 */
    PrintfLogo();    /* 列印例程名稱和版本等資訊 */
    PrintfHelp();    /* 列印操作提示 */
    
    bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重灌的定時器 */
    
    while (1)
    {
        bsp_Idle();        /* 這個函式在bsp.c檔案。使用者可以修改這個函式實現CPU休眠和喂狗 */

        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        /* 按鍵濾波和檢測由後臺systick中斷服務程式實現,我們只需要呼叫bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下,K1鍵按下,跳轉到系統BootLoader */
                    JumpToBootloader();
                    break;
                    
                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        }
    }
}

29.9 總結

本章節為大家介紹的USB DFU方式還是非常實用的,特別是產品硬體不帶boot引腳時。