
前言 很多 STM32 的 System Memory 中所带的 Bootloader 都支持 USB DFU 功能,可以使用 USB 更新代码,详见《AN2606:STM32™ 微控制器系统存储器自举模式》。; ~; G( r4 u' B# s# U; h 问题& u0 T/ k- X; H- n 某客户在其产品的设计中,需要使用了 STM32F072RBT6。客户在使用过程中,使用 System Memory 中的 USB DFU 功能对代码进行更新,并直接使用“Leave DFU mode”跑用户代码,进行功能观察。但是,发现 STM32F0 在点击“Leave DFU mode”按钮后,用户代码并没有正常工作,这是为什么呢? 调研9 v1 ]( S# b$ s 1.了解问题 客户使用了 STM32F072B-Discovery 板进行调试的。在使用\STM32F072BDiscovery_FW_V1.0.1\Projects\Peripheral_Examples\TIM_TimeBase例程进行测试,先使用 IAR 将此例程进行编译,生成TIM_TimeBase.hex 文件。使用 STM32 ST-Link Utility 软件进行烧写,烧写后可见 Discovery 板上 4 个 LED 灯在闪烁,确认 例程及 hex 文件的正确性。' [% f. S9 D- u, N, U6 h 使用 Dfu file manager 软件将 TIM_TimeBase.hex 文件生成 TIM_TimeBase.dfu 文件。将 STM32F072 的 Boot0 引脚拉高,对芯片进行复位,进入 System Memory 运行 Bootloader,使用 Mini USB 线连接 USB USER 口(CN2)和 PC,打开 DfuseDemo 软件,可发现 STM32F072 已经进入 DFU mode,如下: % M- Q( b& u* S% h K( u ![]() ; x A& n. @+ K# _% ~ 点击右下的“Choose”按钮选择 TIM_TimeBase.dfu,勾选“Verify after download”后点击“Upgrade”进行代码烧写,烧写成功后,点击“Leave DFU mode”离开 DFU 模式,发现 Discovery 板确实没有任何变化,4 个 LED 灯并没有闪烁,也就是说,用户代码没有正常运行。 但是客户使用 STM32F4-Discovery 板进行测试的时候,使用\STM32F4-Discovery_FW_V1.1.0\Project\Peripheral_Examples\TIM_TimeBase 例程,点击“Leave DFU mode”离开 DFU 模式后却可以正常运行程序。5 H* \5 t P+ Z; e 2.分析问题 先来看一下《AN3156:USB DFU protocol used in the STM32 bootloader》中对 Leave DFU mode 的描述:# I. j6 q c9 I5 X% N# k1 j ) f4 J9 [0 x$ R ![]() * H: c( C: o1 Y) U, Z 此处描述了离开 DFU 模式时,STM32 所执行的一些操作: 1. 断开连接 2. 初始化 Bootloader 所使用过的外设寄存器,并复位到默认值1 `& k. Y, [) [% S4 M1 ^2 _5 L 3. 初始化用户应用程序主堆栈指针 4. 跳转到“地址+4”的位置,运行代码。* T) J- n( M* K* J& T" }: I 再看一下底下的注意: % M8 J( I& V. C6 g2 ~3 \( O9 ^ ![]() 此处说明,跳转到应用程序,只有在用户代码有正确的向量表设置,才能正常工作。 现在开始检查\STM32F072B-Discovery_FW_V1.0.1\Projects\Peripheral_Examples\TIM_TimeBase 例程中,跟设置向量表相关的代码,并没有找到。也就是说这个例程,当使用 USB DFU 功能进行升级后,使用“Leave DFU mode”离开 DFU 模式进入用户代码,由于没有重新设置向量表,所以向量表还停留在 System Memory 中,开始运行程序没问题,但是一旦产生中断,需要进入向量表的时候,就会跳错向量表而出错。(注:如果使用的是 GPIO_IOToggle 则不会有问题,因为这个例程没有使用中断), V8 w& I" ?) y6 y: f 再来看一下为什么 STM32F4-Discovery 板跑\STM32F4-Discovery_FW_V1.1.0\Project\Peripheral_Examples\TIM_TimeBase例程就没有问题呢?同样我们来搜索一下关于设置向量表的代码。在 system_stm32f4xx.c 的 SystemInit()函数中,可以找到以下代码:8 v4 R; n& Z/ e4 M4 @: p! ^# J
此处为设置向量表,如果没有定义 VECT_TAB_SRAM,则设置向量表到 Main Flash Memory 中。这个函数在程序的最开始被运行,所以向量表得到了正确的设置,也就是为什么这个例程不会有问题的原因。 3.问题解决3 O5 `9 e- |5 C9 O' C0 d) X3 m. H 那既然搞清楚原因了,如何解决呢?* x# m7 d9 A5 X' T Cortex-M0 没有向量表重映射的功能,所以在 STM32F0 中,使用了 SYSCFG_CFGR1 寄存器中的 MEM_MODE 位来实现重映射,查看参考手册,可以找到:0 J0 R& y+ }. T* g* F( i / q$ M- |& ^, n, e9 R ![]() / {: Z/ H8 M% j% ? 从这可以看出,在 System Memory 运行 Bootloader 的时候,MEM_MODE 为 01,所以当离开 USB DFU 模式时,需要将这 个值修改为 x0。) A7 Q. J- F6 n' x' Q) ^ 在 STM32F0 的标准外设库中,修改 MEM_MODE 对应的函数为 SYSCFG_MemoryRemapConfig(),其原型为:
在调用这个程序之前,还必须打开 SYSCFG 的外设时钟,所以我们需要在用户代码最开始的地方,也就是 SystemInit()中或者在 main()的最开始,加入以下代码:
这样,就可以保证在用户代码中使用的是 Main Flash Memory 的向量表了。1 I, |3 i$ d# f1 t9 L9 X 重新编译这个代码,并生成.dfu 文件,再使用 Dfuse Demo 进行烧写,点击“Leave DFU mode”离开 DFU 模式后即可发现4 个 LED 闪烁,代表代码正常运行了。8 E0 X& Z0 u0 c; {4 _' z& r 结论$ N! @! l y3 i" L% R$ A 由于在例程中并没有对向量表进行设置以确保用户代码的向量表为 Main Flash Memory 中,而用户在用户代码中也没有对其进行设置,导致在使用 System Memory 中使用 USB DFU 后离开 DFU 模式进入 Main Flash Memory 中运行用户代码,但向量表仍在 System Memory 中,造成中断产生时跳错向量表而造成出错。 & q& Z, B, e% o `) J. t 处理& d/ S+ J8 q/ v, W/ f; h 需要在用户代码中加入设置正确的向量表,以确保在运行用户代码时使用的是 Main Flash Memory 中的向量表。! s/ i% A Q8 M9 i2 q' o |