STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写


文章目录

  • 一、前言
  • 二、硬件部分
    • 2.1、stm32-v5开发板
    • 2.2、stm32-v5原理图
  • 三、KEIL
    • 3.1、Device Target
    • 3.2、Linker
  • 四、代码
    • 4.1、main.c
  • 五、实验开始
    • 5.1、烧录boot文件夹的工程代码
    • 5.2、stm32CubeProgrammer
  • 六、细节补充
    • 6.1、变量g_JumpInit究竟被存放在哪里?
    • 6.2、进入bootloader程序后,MCU真的运行在内存0x1FFF0000~0x1FFF77FF吗?
    • 6.3、变量g_JumpInit一定要存放在RAM2里面吗?

一、前言 本次实验的目的是使用stm32Cubeprogrammer与USB线烧写新的固件到stm32f407的flash里。
STM32的bootloader学习我是参考安富莱的stm32-v5教程与使用安富莱的stm32-v5开发板。ST为STM32芯片都准备了bootloader程序,目的是让用户使用UART、USB、CAN等方式方便地刷写FLASH的程序。stm32芯片进入bootloader程序有两种方式:
1、通过硬件的boot电路,接着按照对应的时序让mcu进入bootloader。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

使用硬件的boot电路方法需要设计对应的硬件电路,而且还有对应的操作时序,相当麻烦。
2、使用本次从安富莱教程学习的方法:在应用程序里灵活地让mcu进入bootloader,进入bootloader后就可以使用stm32Cubeprogrammer方便地烧录新的代码到芯片内部的flash里。参考硬汉的牛叉实战教程:
实战技能分享,一劳永逸的解决BOOT跳转APP失败问题,含MDK AC5,AC6和IAR,同时制作了一个视频操作说
为了实验通过应用程序进入bootloader,然后通过usb,uart等外设升级mcu的升级,我准备了两份工程代码:
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

百度网盘地址:
链接: https://pan.baidu.com/s/1A3du82lGWUpcA8t0dUss8w?pwd=eamn 提取码: eamn
二、硬件部分 2.1、stm32-v5开发板
按照安富莱的教程使用USB线连接开发板的CN25接口(micro usb插座)。
2.2、stm32-v5原理图 这个micro usb插座实际连接到stm32f407IGT6的PA11与PA12引脚,如上图所示。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

根据stm32f4的参考手册看到,bootloader支持很多外设,这一次学习的是DFU(PA11/12)。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

三、KEIL 3.1、Device Target 根据安富莱的教程,如果使用AC6编译器的话,另外还需要在keil上设置一些配置。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

3.2、Linker AC6编译器的话,需要修改.sct文件。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; *************************************************************LR_IROM1 0x08000000 0x00100000{; load region size_region ER_IROM1 0x08000000 0x00100000{; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } RW_IRAM1 0x20000000 0x0001C000{; RW data .ANY (+RW +ZI) } RW_IRAM2 0x2001C000 UNINIT 0x00000004{ *(.bss.NoInit) } }

接着,重新编译整个工程!!一定要重新编译!!!
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

为什么这样去修改.sct文件?因为需要将一个全局变量(g_JumpInit)分配到RAM2内存里。当程序调用NVIC_SystemReset()去复位程序时,全局变量g_JumpInit不会被重置。只有开发板掉电之后,变量g_JumpInit才会被重置。
四、代码 4.1、main.c STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

这段代码所放置的位置很有讲究,必须在其他外设还没有初始化之前就进入bootloader程序,以免干涉到bootloader程序的运行(干净的环境)。
它是硬汉哥这一次优化bootloader代码的关键点。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

为了方便测试,使用按钮KEY1来触发。后续大家可以根据自己的情况来调用124行与125行的代码,让CPU复位后马上进入bootloader程序。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

最后就是最重要的函数JumpToApp( )的实现了。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

stm32f407为什么是0x1FFF0000,通过stm32f4xx的中文参考手册的第57页,可以看到系统存储器(bootloader)的地址范围是0x1FFF0000 - 0x1FFF77FF。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

五、实验开始 5.1、烧录boot文件夹的工程代码 烧录代码成功后,LD1开始以100ms的间隔闪烁。
从代码可以看到,当按下K1按钮后,LD2将不再闪烁。因为程序跳转到bootloader程序那里去了。

5.2、stm32CubeProgrammer 打开软件之后,选择USB方式,再点击Connect。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

连接成功后,在Target information能找到mcu的信息,还有左侧的Log栏目能看到Data read successfully!
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

接着,就是需要找到将要烧录的固件程序,它的格式是hex。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

找到需要通过bootloader程序来更新程序的新固件,格式hex。

下载新的固件代码成功后,按下stm32-v5开发板的reset按钮,让mcu复位。如下图所示,LD1以更加高的频率进行闪烁了。通过USB的方式更新代码成功!!

六、细节补充 6.1、变量g_JumpInit究竟被存放在哪里? 通过修改.sct文件与按照以下的方式定义变量g_JumpInit后,它真的被存放在RAM2里边了吗?为此我通过.map文件来确认。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

从.map文件看到,变量g_JumpInit被存放到内存0x2001c000里,而且大小是4个byte(字节),0x2001c000其实就是RAM2内存的起始地址。

6.2、进入bootloader程序后,MCU真的运行在内存0x1FFF0000~0x1FFF77FF吗? 通过debug模式,当按下按钮KEY1后,通过PC寄存器可以看到,程序运行在内存地址0x1FFF149F,这个内存地址就是在0x1FFF0000 ~ 0x1FFF77FF之间。
STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写
文章图片

6.3、变量g_JumpInit一定要存放在RAM2里面吗? 【STM32|bootloader | 基于STM32F407 - 使用STM32Cubeprogrammer的USB DFU进行固件烧写】变量g_JumpInit并不一定要放在RAM2里面,在实际项目中,有的人会把它放在外部flash里,或者内部flash。目的是避免复位cpu时,变量g_JumpInit也跟着复位。

    推荐阅读