本次实验的目的是使用stm32Cubeprogrammer与USB线烧写新的固件到stm32f407的flash里。
STM32的bootloader学习我是参考安富莱的stm32-v5教程与使用安富莱的stm32-v5开发板。ST为STM32芯片都准备了bootloader程序,目的是让用户使用UART、USB、CAN等方式方便地刷写FLASH的程序。stm32芯片进入bootloader程序有两种方式:
1、通过硬件的boot电路,接着按照对应的时序让mcu进入bootloader。
使用硬件的boot电路方法需要设计对应的硬件电路,而且还有对应的操作时序,相当麻烦。
2、使用本次从安富莱教程学习的方法:在应用程序里灵活地让mcu进入bootloader,进入bootloader后就可以使用stm32Cubeprogrammer方便地烧录新的代码到芯片内部的flash里。参考硬汉的牛叉实战教程:
实战技能分享,一劳永逸的解决BOOT跳转APP失败问题,含MDK AC5,AC6和IAR,同时制作了一个视频操作说
为了实验通过应用程序进入bootloader,然后通过usb,uart等外设升级mcu的升级,我准备了两份工程代码:
百度网盘地址:
链接: https://pan.baidu.com/s/1A3du82lGWUpcA8t0dUss8w?pwd=eamn 提取码: eamn
按照安富莱的教程使用USB线连接开发板的CN25接口(micro usb插座)。
这个micro usb插座实际连接到stm32f407IGT6的PA11与PA12引脚,如上图所示。
根据stm32f4的参考手册看到,bootloader支持很多外设,这一次学习的是DFU(PA11/12)。
根据安富莱的教程,如果使用AC6编译器的话,另外还需要在keil上设置一些配置。
; *************************************************************
; *** 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)
}
}
接着,重新编译整个工程!!一定要重新编译!!!
为什么这样去修改.sct文件?因为需要将一个全局变量(g_JumpInit)分配到RAM2内存里。当程序调用NVIC_SystemReset()去复位程序时,全局变量g_JumpInit不会被重置。只有开发板掉电之后,变量g_JumpInit才会被重置。
这段代码所放置的位置很有讲究,必须在其他外设还没有初始化之前就进入bootloader程序,以免干涉到bootloader程序的运行(干净的环境)。
它是硬汉哥这一次优化bootloader代码的关键点。
为了方便测试,使用按钮KEY1来触发。后续大家可以根据自己的情况来调用124行与125行的代码,让CPU复位后马上进入bootloader程序。
最后就是最重要的函数JumpToApp( )的实现了。
stm32f407为什么是0x1FFF0000,通过stm32f4xx的中文参考手册的第57页,可以看到系统存储器(bootloader)的地址范围是0x1FFF0000 - 0x1FFF77FF。
烧录代码成功后,LD1开始以100ms的间隔闪烁。
从代码可以看到,当按下K1按钮后,LD2将不再闪烁。因为程序跳转到bootloader程序那里去了。
打开软件之后,选择USB方式,再点击Connect。
连接成功后,在Target information能找到mcu的信息,还有左侧的Log栏目能看到Data read successfully!
接着,就是需要找到将要烧录的固件程序,它的格式是hex。
找到需要通过bootloader程序来更新程序的新固件,格式hex。
下载新的固件代码成功后,按下stm32-v5开发板的reset按钮,让mcu复位。如下图所示,LD1以更加高的频率进行闪烁了。通过USB的方式更新代码成功!!
通过修改.sct文件与按照以下的方式定义变量g_JumpInit后,它真的被存放在RAM2里边了吗?为此我通过.map文件来确认。
从.map文件看到,变量g_JumpInit被存放到内存0x2001c000里,而且大小是4个byte(字节),0x2001c000其实就是RAM2内存的起始地址。
通过debug模式,当按下按钮KEY1后,通过PC寄存器可以看到,程序运行在内存地址0x1FFF149F,这个内存地址就是在0x1FFF0000 ~ 0x1FFF77FF之间。
变量g_JumpInit并不一定要放在RAM2里面,在实际项目中,有的人会把它放在外部flash里,或者内部flash。目的是避免复位cpu时,变量g_JumpInit也跟着复位。