最新教程下載: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 初學者重要提示
- 學習本章節前,務必優先學習第28章。
- 本章用到的相關軟件和文檔下載:http://www.armbbs.cn/forum.php?mod=viewthread&tid=96573 。
- 軟件STM32CubeProg和DfuSe都支持USB DFU,但是兩個軟件不能都安裝使用,因為這兩個軟件的USB驅動不同,導致工作在系統bootloader模式的板子通過USB線接到電腦端時,只有一個軟件的驅動被識別。
- DfuSe是老版的USB DFU軟件,不推薦大家使用了。建議使用STM32CubeProg,此軟件實現了之前的DfuSe,STLINK小軟件和Flashloader三合一,並且支持外部EEPROM,NOR Flash,SPI Flash,NAND Flash等燒寫,也支持OTA編程。
- 本章節的USB DFU的下載軟件采用STM32CubeProg,如果想使用DfuSe的話,此貼有詳細說明:http://www.armbbs.cn/forum.php?mod=viewthread&tid=11185 。
- 當芯片工作在系統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)
實驗目的:
- 學習基於系統bootloader的USB接口方式IAP升級。
實驗內容:
- STM32的系統存儲區自帶BootLoader,可以方便的實現串口,I2C,CAN,SPI,USB等接口方式的程序升級。
- 如果使用系統BootLoader支持的接口升級方式,基本就不需要用戶自己做BootLoader了。
- 除了通過boot引腳控制啟動地址,也可以直接從應用程序里面跳轉到系統存儲區。
實驗操作:
- 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)
實驗目的:
- 學習基於系統bootloader的USB接口方式IAP升級。
實驗內容:
- STM32的系統存儲區自帶bootLoader,可以方便的實現串口,I2C,CAN,SPI,USB等接口方式的程序升級。
- 如果使用系統bootLoader支持的接口升級方式,基本就不需要用戶自己做bootLoader了。
- 除了通過boot引腳控制啟動地址,也可以直接從應用程序里面跳轉到系統存儲區。
實驗操作:
- 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引腳時。