你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F0 使用 DFU 升级后 Leave DFUMode 不能运行用户代码

[复制链接]
STMCU小助手 发布时间:2022-2-20 16:37
前言
" N2 s; ~$ q- Q6 z+ W$ h很多 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”按钮后,用户代码并没有正常工作,这是为什么呢?
2 d0 s; Z+ o& L/ c% ]
$ P1 y# E( N( o2 L3 x调研9 v1 ]( S# b$ s
1.了解问题
, ^5 b$ l& o9 f0 P" M1 @客户使用了 STM32F072B-Discovery 板进行调试的。在使用\STM32F072BDiscovery_FW_V1.0.1\Projects\Peripheral_Examples\TIM_TimeBase例程进行测试,先使用 IAR 将此例程进行编译,生成TIM_TimeBase.hex 文件。使用 STM32 ST-Link Utility 软件进行烧写,烧写后可见 Discovery 板上 4 个 LED 灯在闪烁,确认
! n  O$ s: c1 o5 U: G( x例程及 hex 文件的正确性。' [% f. S9 D- u, N, U6 h

! N+ F8 x5 \% T# _( ]9 d# p) \使用 Dfu file manager 软件将 TIM_TimeBase.hex 文件生成 TIM_TimeBase.dfu 文件。将 STM32F072 的 Boot0 引脚拉高,对芯片进行复位,进入 System Memory 运行 Bootloader,使用 Mini USB 线连接 USB USER 口(CN2)和 PC,打开 DfuseDemo 软件,可发现 STM32F072 已经进入 DFU mode,如下:
, N3 ]( o0 @( [0 l; l# u" P% M- Q( b& u* S% h  K( u
9KEC0YKV@(NB{(@N~XW[TBV.png 2 a. C& j* A, z" Q
; x  A& n. @+ K# _% ~
点击右下的“Choose”按钮选择 TIM_TimeBase.dfu,勾选“Verify after download”后点击“Upgrade”进行代码烧写,烧写成功后,点击“Leave DFU mode”离开 DFU 模式,发现 Discovery 板确实没有任何变化,4 个 LED 灯并没有闪烁,也就是说,用户代码没有正常运行。
( D, z! w' v- C9 r# I但是客户使用 STM32F4-Discovery 板进行测试的时候,使用\STM32F4-Discovery_FW_V1.1.0\Project\Peripheral_Examples\TIM_TimeBase 例程,点击“Leave DFU mode”离开 DFU 模式后却可以正常运行程序。5 H* \5 t  P+ Z; e

$ n! h% D& L& J: u/ L9 p$ z; i6 B2.分析问题
2 d* S- s! g- k' s先来看一下《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
(S4K[UDWE[8O1WMU63ZA7TS.png
" d: g2 d4 ?/ J* H: c( C: o1 Y) U, Z
此处描述了离开 DFU 模式时,STM32 所执行的一些操作:
0 N) A: ]) u3 I( i" w1. 断开连接
. ?4 C1 W" T! s1 |% v' L' W2. 初始化 Bootloader 所使用过的外设寄存器,并复位到默认值1 `& k. Y, [) [% S4 M1 ^2 _5 L
3. 初始化用户应用程序主堆栈指针
. q1 i" L3 H# z- n, v4. 跳转到“地址+4”的位置,运行代码。* T) J- n( M* K* J& T" }: I
   再看一下底下的注意:
% R, B' h2 A% V2 F( n% M8 J( I& V. C6 g2 ~3 \( O9 ^
V2N69{H$PI4T`D1]I]{1]6S.png " _6 d8 X! L# C' U% u/ o

+ `9 C) U+ S6 }   此处说明,跳转到应用程序,只有在用户代码有正确的向量表设置,才能正常工作。
+ s; |$ i3 R: f1 @$ b   现在开始检查\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
  1.    /* Configure the Vector Table location add offset address ------------------*/9 p$ B! a) ?. S
  2.    #ifdef VECT_TAB_SRAM9 f+ b' D3 l* D# ]
  3.    SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
    , w8 U+ k# f. @- W8 E0 e* F: p
  4.    #else
    : f) [0 L) D$ D
  5.    SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH
    & x/ l* a9 z; g" ?5 q# K
  6.    */
    # \; v3 y2 a. F; r7 `' r% [) ~
  7.    #endif
复制代码

; g' E$ p9 c! ~7 h   此处为设置向量表,如果没有定义 VECT_TAB_SRAM,则设置向量表到 Main Flash Memory 中。这个函数在程序的最开始被运行,所以向量表得到了正确的设置,也就是为什么这个例程不会有问题的原因。
. b9 x" |" G2 i
6 o+ v$ }, Y' q  [% r
. E% f8 z1 }. E( o* `   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
SE}ZYH0R82X_W7[(%T3CQ~7.png " J/ E) Q1 B$ p6 |6 `
/ {: Z/ H8 M% j% ?
   从这可以看出,在 System Memory 运行 Bootloader 的时候,MEM_MODE 为 01,所以当离开 USB DFU 模式时,需要将这
7 {1 N5 D5 O' @1 d* C- x   个值修改为 x0。) A7 Q. J- F6 n' x' Q) ^
   在 STM32F0 的标准外设库中,修改 MEM_MODE 对应的函数为 SYSCFG_MemoryRemapConfig(),其原型为:
; t) s" ]! k$ [9 c  p- d
  1.   /**; R* w& M5 V- i
  2. 1 J/ U; ~8 c0 ]  `4 t$ s( _' A
  3. * @brief Configures the memory mapping at address 0x00000000.
    9 y$ h0 I! `, e' g# {. l, b& J
  4. * @param SYSCFG_MemoryRemap: selects the memory remapping.
    ( m+ K) u9 k, Q, |
  5. * This parameter can be one of the following values:, U! w% g& d( I: y" N
  6. * @arg SYSCFG_MemoryRemap_Flash: Main Flash memory mapped at 0x00000000+ V& v1 Z5 G. B: I6 _; `% j% X
  7. * @arg SYSCFG_MemoryRemap_SystemMemory: System Flash memory mapped at0 L0 D6 ^* Y" A) h/ u% M/ k
  8.   0x00000000  i8 F/ z, b% n
  9. * @arg SYSCFG_MemoryRemap_SRAM: Embedded SRAM mapped at 0x00000000
    ' m& ]+ v! V( \7 P
  10. * @retval None
    ( S; f4 w9 s' X# j( n( _- J" M6 P
  11.   */$ Y& K0 w% N8 S& H/ S7 d9 z; _! S& {
  12.   void SYSCFG_MemoryRemapConfig(uint32_t SYSCFG_MemoryRemap)+ M' I  o6 e$ t6 {
  13.   {6 q  b% S# f# r1 Z8 y
  14.   uint32_t tmpctrl = 0;
    ( f- K! Y! }: V7 @/ d. m
  15.   /* Check the parameter */
    ' a0 W% ~  `$ I! `+ G) v
  16.   assert_param(IS_SYSCFG_MEMORY_REMAP(SYSCFG_MemoryRemap));
    * T9 N4 a2 q" R. ?- k/ q+ |4 _
  17.   /* Get CFGR1 register value */4 g3 A- x" k' f3 o% @
  18.   tmpctrl = SYSCFG->CFGR1;
    * \  ^# K; H- ~
  19.   /* Clear MEM_MODE bits */
    ( {. S$ T% x$ e6 j/ N+ B/ `
  20.   tmpctrl &= (uint32_t) (~SYSCFG_CFGR1_MEM_MODE);9 v1 p6 W6 L1 X
  21.   /* Set the new MEM_MODE bits value */
    7 w, |" B+ K8 o# P
  22.   tmpctrl |= (uint32_t) SYSCFG_MemoryRemap;
    $ K/ p2 b6 v  J4 S8 S
  23.   /* Set CFGR1 register with the new memory remap configuration */
    5 i5 @( z/ W( F' M5 P1 s+ k: c  _1 K
  24.   SYSCFG->CFGR1 = tmpctrl;: r: p" H! s& u& }8 x0 S7 ^9 Z( n2 ]
  25.   }
复制代码
: a( w8 g: D4 a  T1 M
  在调用这个程序之前,还必须打开 SYSCFG 的外设时钟,所以我们需要在用户代码最开始的地方,也就是 SystemInit()中或者在 main()的最开始,加入以下代码:
# H8 m& Q9 o2 Q7 p, }" l+ R& ]9 \
  1.   /* Enable the SYSCFG peripheral clock*/
    & C# k' j' m" Q1 D8 `- ~. S
  2.   RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    8 W8 `. A9 J4 Y' ~  s" \
  3.   /* Remap SRAM at 0x00000000 */- C$ C$ _2 t( L3 M
  4.   SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_Flash);
复制代码

, ^5 _  M, m  X( Y7 k  这样,就可以保证在用户代码中使用的是 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

5 ]  A( y) D. }  t  结论$ N! @! l  y3 i" L% R$ A
  由于在例程中并没有对向量表进行设置以确保用户代码的向量表为 Main Flash Memory 中,而用户在用户代码中也没有对其进行设置,导致在使用 System Memory 中使用 USB DFU 后离开 DFU 模式进入 Main Flash Memory 中运行用户代码,但向量表仍在 System Memory 中,造成中断产生时跳错向量表而造成出错。
5 R9 ~" s4 `* X; D* a: ~0 ~& q& Z, B, e% o  `) J. t

  p# G. m! n3 \  处理& d/ S+ J8 q/ v, W/ f; h
  需要在用户代码中加入设置正确的向量表,以确保在运行用户代码时使用的是 Main Flash Memory 中的向量表。! s/ i% A  Q8 M9 i2 q' o

: f+ M) e6 B$ b. y' H; ^# w5 a
- P$ q1 i9 f' f' `+ q: _
收藏 评论0 发布时间:2022-2-20 16:37

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版