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

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

[复制链接]
STMCU小助手 发布时间:2022-2-20 16:37
前言
3 P2 w% Q# i8 P5 Y很多 STM32 的 System Memory 中所带的 Bootloader 都支持 USB DFU 功能,可以使用 USB 更新代码,详见《AN2606:STM32™ 微控制器系统存储器自举模式》。; O2 d& R/ P) y; s) f5 I  d6 l
问题' U; W; y: t0 i9 m7 k" z3 [
某客户在其产品的设计中,需要使用了 STM32F072RBT6。客户在使用过程中,使用 System Memory 中的 USB DFU 功能对代码进行更新,并直接使用“Leave DFU mode”跑用户代码,进行功能观察。但是,发现 STM32F0 在点击“Leave DFU mode”按钮后,用户代码并没有正常工作,这是为什么呢?
- V: h8 }0 V7 p+ Q- R# W
3 r4 {$ o8 R3 z% G调研
9 i$ x4 D1 T- I1 u7 g1 n3 @1.了解问题8 w6 |& f6 U* L& i( y3 W$ x
客户使用了 STM32F072B-Discovery 板进行调试的。在使用\STM32F072BDiscovery_FW_V1.0.1\Projects\Peripheral_Examples\TIM_TimeBase例程进行测试,先使用 IAR 将此例程进行编译,生成TIM_TimeBase.hex 文件。使用 STM32 ST-Link Utility 软件进行烧写,烧写后可见 Discovery 板上 4 个 LED 灯在闪烁,确认3 q" C9 @" v) |; P' M* \1 U, H/ ]7 G$ t
例程及 hex 文件的正确性。/ |5 |" J, c- c; [
0 ?& I/ [1 C+ Z: |
使用 Dfu file manager 软件将 TIM_TimeBase.hex 文件生成 TIM_TimeBase.dfu 文件。将 STM32F072 的 Boot0 引脚拉高,对芯片进行复位,进入 System Memory 运行 Bootloader,使用 Mini USB 线连接 USB USER 口(CN2)和 PC,打开 DfuseDemo 软件,可发现 STM32F072 已经进入 DFU mode,如下:3 K* x) V8 D. a- E

5 f" {. _* _" P2 {9 D* }( A7 d 9KEC0YKV@(NB{(@N~XW[TBV.png 2 g3 y  p; L0 V+ U! G+ e3 c6 \

& ]: J4 v6 |( n点击右下的“Choose”按钮选择 TIM_TimeBase.dfu,勾选“Verify after download”后点击“Upgrade”进行代码烧写,烧写成功后,点击“Leave DFU mode”离开 DFU 模式,发现 Discovery 板确实没有任何变化,4 个 LED 灯并没有闪烁,也就是说,用户代码没有正常运行。
" ~: T% H( }/ e3 x# I但是客户使用 STM32F4-Discovery 板进行测试的时候,使用\STM32F4-Discovery_FW_V1.1.0\Project\Peripheral_Examples\TIM_TimeBase 例程,点击“Leave DFU mode”离开 DFU 模式后却可以正常运行程序。1 }% f8 N, t* F% H2 _9 ^, U* K
7 O. i. q7 \3 x9 x/ h
2.分析问题% I6 Q7 p: c' ?
先来看一下《AN3156:USB DFU protocol used in the STM32 bootloader》中对 Leave DFU mode 的描述:8 N& n2 b( I6 k

% ^" x0 k! c" r1 ^: X (S4K[UDWE[8O1WMU63ZA7TS.png ; o% E8 p' s" x7 w

  c+ [. l  r! u. c6 P, v& r此处描述了离开 DFU 模式时,STM32 所执行的一些操作:
; ^( j# H/ _$ L! |0 g* d1. 断开连接. z6 O7 }$ S# l  R' v9 k9 v
2. 初始化 Bootloader 所使用过的外设寄存器,并复位到默认值
/ X' O6 D$ M3 y5 d5 f& R% P2 k$ s3. 初始化用户应用程序主堆栈指针
% I, r% X( p8 ?$ \8 c4. 跳转到“地址+4”的位置,运行代码。
9 b( R, J9 v6 f) z5 B$ I" ?7 S: q. g! t   再看一下底下的注意:6 O' n9 j3 e! `4 |
+ p' A* d6 p7 l  C" v2 U, y. v
V2N69{H$PI4T`D1]I]{1]6S.png 8 f, s3 N& W* m; t

$ G4 W7 @; W2 i3 c6 B, [   此处说明,跳转到应用程序,只有在用户代码有正确的向量表设置,才能正常工作。
( t+ ?( [3 j1 d# D$ i4 M* g   现在开始检查\STM32F072B-Discovery_FW_V1.0.1\Projects\Peripheral_Examples\TIM_TimeBase 例程中,跟设置向量表相关的代码,并没有找到。也就是说这个例程,当使用 USB DFU 功能进行升级后,使用“Leave DFU mode”离开 DFU 模式进入用户代码,由于没有重新设置向量表,所以向量表还停留在 System Memory 中,开始运行程序没问题,但是一旦产生中断,需要进入向量表的时候,就会跳错向量表而出错。(注:如果使用的是 GPIO_IOToggle 则不会有问题,因为这个例程没有使用中断)" _! m! e, C6 |: Z5 J$ t
   再来看一下为什么 STM32F4-Discovery 板跑\STM32F4-Discovery_FW_V1.1.0\Project\Peripheral_Examples\TIM_TimeBase例程就没有问题呢?同样我们来搜索一下关于设置向量表的代码。在 system_stm32f4xx.c 的 SystemInit()函数中,可以找到以下代码:3 L, @5 l9 `3 J# q  ~
  1.    /* Configure the Vector Table location add offset address ------------------*/5 `0 R: d) o: c# u6 ?& z
  2.    #ifdef VECT_TAB_SRAM, `2 s# V/ I* m+ w* [0 R5 x
  3.    SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */8 R" ^7 A4 b# s: K3 A  h
  4.    #else3 N3 u; Y- h6 T' m; [; `. J8 ]" }- V
  5.    SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH) t* P. y1 J8 I5 W3 V$ o
  6.    */
    8 W7 `- q4 k% A* l: Z, {" m  b
  7.    #endif
复制代码
- M: l4 Z  a3 H' B( d5 S! C/ i
   此处为设置向量表,如果没有定义 VECT_TAB_SRAM,则设置向量表到 Main Flash Memory 中。这个函数在程序的最开始被运行,所以向量表得到了正确的设置,也就是为什么这个例程不会有问题的原因。
1 A5 V3 g& G4 O7 O" O; O
* }* A5 r+ }6 A% T5 [$ w7 Z8 a+ q% O2 y3 c2 f$ y
   3.问题解决" C6 E3 M  @- q5 c
   那既然搞清楚原因了,如何解决呢?4 _( A4 k' a' f) M9 \6 ^
   Cortex-M0 没有向量表重映射的功能,所以在 STM32F0 中,使用了 SYSCFG_CFGR1 寄存器中的 MEM_MODE 位来实现重映射,查看参考手册,可以找到:
+ T* S( c- U% C$ h- ^# d& \4 M
3 z' t1 Y7 `# P0 L SE}ZYH0R82X_W7[(%T3CQ~7.png 9 H6 V( i2 y" y9 W

& |1 L3 w5 D5 P" c) \0 i   从这可以看出,在 System Memory 运行 Bootloader 的时候,MEM_MODE 为 01,所以当离开 USB DFU 模式时,需要将这- F- `3 w5 ~+ s3 ?& o4 p
   个值修改为 x0。+ G- K: Z, N) U& s
   在 STM32F0 的标准外设库中,修改 MEM_MODE 对应的函数为 SYSCFG_MemoryRemapConfig(),其原型为:& p. P: {! f5 v/ V0 d3 y
  1.   /**, b$ R* e+ J; w3 i5 R( t' P3 D

  2. ! B2 l; @, E; A% F8 D$ q
  3. * @brief Configures the memory mapping at address 0x00000000.
    6 ]; t8 e# E: q+ H8 x
  4. * @param SYSCFG_MemoryRemap: selects the memory remapping.
    + F* T; |) Z+ L0 D9 x( v; e% h
  5. * This parameter can be one of the following values:' T  m7 I+ E8 M1 m" K' ~$ S
  6. * @arg SYSCFG_MemoryRemap_Flash: Main Flash memory mapped at 0x00000000: D- P; c6 ]! h0 n
  7. * @arg SYSCFG_MemoryRemap_SystemMemory: System Flash memory mapped at9 ~" {; M/ p4 @' O& c9 S) }. E2 a+ @1 z( t
  8.   0x00000000. }! X  V0 b2 o% T$ T. l9 C+ L
  9. * @arg SYSCFG_MemoryRemap_SRAM: Embedded SRAM mapped at 0x000000006 f, c, ~3 m6 ^% D0 N6 `
  10. * @retval None
    . c' n6 O( h" z6 B7 b2 {! g" |
  11.   */5 y2 ]1 c$ x2 j1 G$ B% f; W
  12.   void SYSCFG_MemoryRemapConfig(uint32_t SYSCFG_MemoryRemap)
    5 v. M6 y( Y* E' K
  13.   {2 R' W6 _! S1 a' \# W- ]0 ~: a
  14.   uint32_t tmpctrl = 0;
    2 l7 f9 B/ S0 W0 O# M
  15.   /* Check the parameter */
    ! k2 }/ q# ]" d. V' f
  16.   assert_param(IS_SYSCFG_MEMORY_REMAP(SYSCFG_MemoryRemap));
    ' G  R' v$ \* E
  17.   /* Get CFGR1 register value */! e$ R/ Z% Y' h% L: q4 R) d
  18.   tmpctrl = SYSCFG->CFGR1;
    3 o7 W6 q* D! _$ I2 n
  19.   /* Clear MEM_MODE bits */) p  _5 u4 w& Y; R
  20.   tmpctrl &= (uint32_t) (~SYSCFG_CFGR1_MEM_MODE);" C6 {9 ]2 ^. _7 @7 {
  21.   /* Set the new MEM_MODE bits value */
    $ p; a5 k9 L1 A& g
  22.   tmpctrl |= (uint32_t) SYSCFG_MemoryRemap;; q! @  M& r/ P8 E, O5 J0 R3 a5 h
  23.   /* Set CFGR1 register with the new memory remap configuration */
      Q/ e$ a7 t" M- R
  24.   SYSCFG->CFGR1 = tmpctrl;- H" ^5 T. k5 Z( S$ n0 m
  25.   }
复制代码
2 }# y. ^5 b# P, C: r2 y+ i
  在调用这个程序之前,还必须打开 SYSCFG 的外设时钟,所以我们需要在用户代码最开始的地方,也就是 SystemInit()中或者在 main()的最开始,加入以下代码:
9 y9 F% P. ^( g) W
  1.   /* Enable the SYSCFG peripheral clock*/
    # v* C( ~& h8 n; S
  2.   RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);7 d" A) W/ n- D, x" \. J& o
  3.   /* Remap SRAM at 0x00000000 */; y) x% n( F1 a$ ?
  4.   SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_Flash);
复制代码

8 [; J8 b5 J9 B  ?4 y2 Q  这样,就可以保证在用户代码中使用的是 Main Flash Memory 的向量表了。
6 `, p3 d' Y, W6 P! a- c  重新编译这个代码,并生成.dfu 文件,再使用 Dfuse Demo 进行烧写,点击“Leave DFU mode”离开 DFU 模式后即可发现4 个 LED 闪烁,代表代码正常运行了。1 `6 N0 Y1 n  n' B$ E, z( V
, J$ _3 H/ R: i9 V- \
  结论
( n4 h! q' f4 S* _$ v  由于在例程中并没有对向量表进行设置以确保用户代码的向量表为 Main Flash Memory 中,而用户在用户代码中也没有对其进行设置,导致在使用 System Memory 中使用 USB DFU 后离开 DFU 模式进入 Main Flash Memory 中运行用户代码,但向量表仍在 System Memory 中,造成中断产生时跳错向量表而造成出错。
9 z7 V. s  v7 H( Q% _
1 e3 h% F' _# o  `, C, x
: ]1 H2 |8 B/ }1 n  i6 a' B  处理
; E$ Q  N! r# \7 L7 X/ d  需要在用户代码中加入设置正确的向量表,以确保在运行用户代码时使用的是 Main Flash Memory 中的向量表。
& ^- ?, c1 P9 x( j, ?$ F* c0 }+ U0 Q9 }; R* R" z0 i
" X3 K( {, `5 [4 O, m& V) F
赞 收藏 评论0 发布时间:2022-2-20 16:37

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版