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

STM32F412擦除内部FLASH时间过长

[复制链接]
aimejia 发布时间:2018-5-22 14:36
本帖最后由 aimejia 于 2018-5-23 11:31 编辑
3 h/ b6 T$ D2 I$ b/ S  l  L7 f6 P! z% }3 D" h
1 前言     
( G# o9 V4 B% m# h& [  k7 U0 C  客户反馈在使用STM32F412的时候,擦除sector 8~11发现时间过长,从而导致意外触发IWDG复位。6 u1 `! h# h- w
5 a' \+ g4 C6 F/ |% R
2 问题分析
4 B& V& i" k* m6 M1 g( v8 S# P, R* F) `2.1 问题详情
6 X' v* S+ a) ^1 Z- _/ S     通过与客户邮件和电话沟通,了解到客户主要是想使用内部FLASH暂时保存IAP升级时的程序数据,在IAP升级的过程中,需要首先擦除内部FLASH中一块足够大的空间,然后再写入升级数据。客户的工程中有使用到IWDG,喂狗间隔大约1.5S,客户的通过SysTick的方式计算出擦除Sector8大约需要2ms,因此认为若一次擦除sector8~11大约需要8ms,于是在代码中一次性擦除sector8~11后最后再来喂狗,但是,这样会触发IWDG复位,这个与预期不一致,固此产生疑问。
/ O2 c- h# T1 X( Q1 t2 X0 a: V0 t7 [) t1 U, j! l% [
2.2 问题重现
% }* t6 D2 G% ]. l      使用NUCLEO-F412ZG板尝试重现客户问题,主要代码如下:* ~2 h/ |9 E' h
  1. int main(void)
    7 f) |. V$ j3 K1 H
  2. {0 T( I+ O' \+ h4 N9 T  w  K, \' N

  3. - j0 m! f6 K$ }7 T8 O5 l4 W8 v; ?
  4.   /* USER CODE BEGIN 1 */5 r+ W. K# Y. X
  5. uint32_t beginTick =0,endTick =0;
    6 s5 E& H* m% }* W1 F; `/ v
  6. uint32_t curSysTick=0,endSysTick =0;
    , ], b* a, a# m3 g4 u/ Q: n
  7.   /* USER CODE END 1 */2 \7 o; J& E2 J

  8. / ]( [0 n8 p8 R& X" ]) K
  9.   /* MCU Configuration----------------------------------------------------------*/7 j/ ^' n2 `+ R2 ~+ q, O
  10. ! a$ D5 O$ F1 x% i1 \
  11.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */# P% L) Z- O" [- P8 n/ w% D' T6 o
  12.   HAL_Init();
    4 i( H2 t, Q2 V+ E% w

  13. / V. {0 k  l4 K6 [" u- l
  14.   /* Configure the system clock */
    0 ]% c+ j; s- y. n* w
  15.   SystemClock_Config();
    ' r  N& c6 e! w1 Z: _; f/ F& v2 M9 [
  16. ; D" |( ~2 f1 _2 Z* T
  17.   /* Initialize all configured peripherals */
    ( k: l- `9 V' [1 ~2 B6 o
  18.   MX_GPIO_Init();
    3 [3 ]0 q8 Q. K  n  {
  19.   MX_IWDG_Init();$ k& u) M$ G! ?& i& N
  20. 1 O* Q, Y+ ~2 z8 t: i' b0 C  l7 _
  21.   /* USER CODE BEGIN 2 */
    6 V- i5 ?6 ^2 {7 z2 C* M- V5 ^
  22. if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET) //如果是看门狗复位9 _1 G6 {( b% v" p( `# d4 w
  23.   {5 r8 Z$ g! J" ~4 `5 W
  24.     /* Clear reset flags */9 s. {0 `, ~, L/ B1 e
  25. HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);  Z; [0 a5 F! y
  26.     __HAL_RCC_CLEAR_RESET_FLAGS();% \  \' d; _5 g- e' o
  27. Error_Handler();" L2 J4 V8 s" |" U
  28.   }
    9 A* s6 b; \" a/ H. ?7 G
  29. 2 ^1 t+ ]$ R+ j  g
  30.     HAL_FLASH_Unlock();
    6 d* R7 U+ q- {' A' u" {6 _5 D2 M
  31.   /* Fill EraseInit structure*/& v7 H. M* N/ L3 R  a( ^' E+ J  U7 I
  32.   EraseInitStruct.TypeErase     = FLASH_TYPEERASE_SECTORS;! ?) V' G3 A! c- r, Y# a6 e
  33.   EraseInitStruct.VoltageRange  = FLASH_VOLTAGE_RANGE_3;) t' m* n* X$ h) {5 l" [4 [
  34.   EraseInitStruct.Sector        = FLASH_SECTOR_8;
    , W) q5 b. W" x$ o2 h/ f
  35.   EraseInitStruct.NbSectors     = 1;
    2 k9 T' ?; I" D: W0 d$ W6 @- z

  36. $ S( o. G/ ^5 H$ V$ q% V7 ?
  37. //  if(HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError) != HAL_OK)
    5 ?& w' N8 j' e% G  i' ?; d# m
  38. //  {2 L0 S: e  P% ]6 R- U
  39. //      Error_Handler();
    0 C$ F3 @# S( y
  40. //  }
    / v2 j0 C- t' ]# m- N3 H- x0 m$ k

  41. 6 \& q: j" g1 Z2 ~4 P+ ~# C
  42. beginTick =HAL_GetTick();8 R" P# z! }2 |5 ~; j
  43. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);  J  q% U2 D* t3 E6 Y
  44. curSysTick =SysTick->VAL;; p# \  s% F) g( c" j. q/ N: M
  45. if(HAL_FLASHEx_Erase_IT(&EraseInitStruct)!= HAL_OK) //擦除sector80 d( V4 _% E% K. C
  46. {6 r8 g  K1 f- G1 G- E
  47.     Error_Handler();  ]2 l3 H% e  w; c: O9 C
  48. }- g+ y( }4 G' }, v, N
  49. endSysTick =SysTick->VAL; // curSysTick, endSysTick保存着SysTick寄存器的值
    * s8 _* D, P% ?4 K
  50. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET); //PC8波形表示擦除FLASH的时间间隔
    9 }. f7 q( G8 U& ?8 R; B
  51. endTick =HAL_GetTick();     // beginTick, endTick保存着全局变量Tick的值# |! a4 {: y0 A7 [4 g
  52. g_TickCount =endTick -beginTick; //变量Tick的时间差+ i8 ]. E. M& M6 X( }& N
  53. HAL_IWDG_Refresh(&hiwdg);$ q% u) k8 [3 x% i
  54.   /* USER CODE END 2 */- J0 _8 e# L1 T5 y+ V
  55. 2 l- b9 y7 o. {
  56.   /* Infinite loop */
    # x. F. e! G9 a9 u7 [9 {: M, D. w
  57.   /* USER CODE BEGIN WHILE */
    * o& D! o4 V' z4 J9 k6 p
  58.   while (1)
    8 t6 p' F+ n( r0 s+ `. Q, ~( k
  59.   {3 a0 g' f; X6 d. \
  60.   /* USER CODE END WHILE */
    0 Q4 U9 [& ]9 K2 M( y8 n9 u
  61. - u; b1 h1 L$ V# N7 J
  62.   /* USER CODE BEGIN 3 */" Q3 D% M# m" D% K- e! V% p
  63. if (HAL_IWDG_Refresh(&hiwdg) != HAL_OK)
      q6 _2 Y, G$ E
  64.     {
    * A2 |1 l# F$ ~  `
  65.       /* Refresh Error */
    ; A8 N" U# O" F! Z% o  S. f
  66.       Error_Handler();
    8 Q- e; A  v+ f3 A, r. j. y4 V; L
  67.     }
    ! E/ q" A7 v; u4 c
  68. HAL_Delay(10);
    + N0 O- E+ \' I3 L. q9 p
  69.   }2 f1 V; k) `& F  C3 r1 E
  70.   /* USER CODE END 3 */
    & t9 f: g' n0 \0 Z) Y% ^
  71. }
复制代码
   此外,同时在每个SysTick中断输出一个波形,用来检测SysTick是否正常:
  1. void HAL_SYSTICK_Callback(void)4 b% X3 Q$ Q! H" }3 |3 E
  2. {
    8 F/ V0 @& Q! Y5 x9 T
  3.     HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_11);//用PA11来检测SysTick波形
    $ W' d, f1 x& @: X  ~& }, [8 {
  4. }
复制代码
   
9 j! \7 c/ ], f0 _2 M) R7 G: P   最终得出如下波形9 t+ W: S# a/ z8 M% r
1.jpg
图1 擦除FLASH时间与SysTick输出波形

' A: ]# \) d' t6 u$ p" ?        如上图,黄色为PC8脚波形,表示擦除FLASH的时间,下面蓝色为PA11管脚波形,表示SysTick波形。
; L, S  F* c5 U   从上图可以看出擦除sector8所需要的时间是800ms,这个与客户认为的2ms是不一致的。查看STM32F412的数据手册,在第6.3.12节中可以看到如下信息:
6 z0 _7 |8 ?# N& g
/ `+ J9 |  c9 M) p3 X4 w
2.png
图2 擦除FLASH扇区所需要的时间

% o! g( ]+ D& Q8 f: r        如上图,在PSIZE=32时,擦除一个128K的扇区需要大概1S(典型值)的时间,而我们从图1中实际测出的为800ms,这个基本相差不大,单与客户认为的2ms相去甚远,基本上我们认为这里的800ms是正确的结果,但是这个又是什么原因导致客户通过SysTick测出的值是错误的呢?
% C/ E2 \$ L+ p4 E6 ?/ C0 x) F9 x( o; O2 K% b1 _
   实际上,从图1我们也可以看出,在擦除FLASH的期间,SysTick是没有波形的(见图1下面蓝色波形),同时在参考手册3.5节中有如下信息:
+ Z% z- r' {2 N/ ~( m9 U" E

7 p9 z- M) F  p! `  n  N  n
3.png
图3 参考手册中关于擦除扇区的描述

7 J, _+ q3 `! I( |0 i' W      这句话的意思是说,在擦除FLASH的期间,若尝试读取FLASH,则会被暂停,实际这个”读取”是指取指,我们都知道,程序的执行首先得通过从FLASH中通过I-BUS取出指令后才可以执行。这里SysTick之所以会被暂停掉,就是因为在擦除FLASH期间,为了执行SysTick中断例程,内核会尝试从FLASH取指,从而导致被暂停掉,进而全局变量uwTick的值没有机会增加。下图是调试界面: 7 J) L9 ]7 s& G) g1 Z
6 G" L  o" L3 a3 U# O# b2 k
4.png
图4 调试
" c' H: |1 {2 {2 |/ f9 b
       如上图,在执行擦除扇区后,SysTick的全局变量uwTick就增加了1,但SysTick在内核中的寄存器还是有变化的。这个与我们的预想一致。
: l' t4 o% J+ c3 @' g7 k0 C8 i
' I6 i) t  n& V1 I, d   最后客户通过每擦除一个扇区喂一次狗的方式解决了问题,而在此期间不能依靠SysTick的值来计算时间。% D1 ?  A6 S/ w) l) N$ o0 A
) r6 T/ R9 X8 B
3 结论9 q$ [! U/ _2 C4 y9 j6 O* \/ P
       在擦除FLASH期间,取指操作会被暂停掉,且SysTick所对应的全局变量uwTick值是不会增加的。另外,通过函数HAL_FLASHEx_Erase_IT()来执行擦除FLASH和通过函数HAL_FLASHEx_Erase()所花费时间没有差别,只不过前者在擦除完成后会产生一个中断,而后者没有。可以通过外设RTC来计算擦除FLASH的时间,从而绕开限制。# h  S0 I' }4 U9 m* K, f$ E' \
' f# u. V; h6 l6 S

' L. U: _  p( X8 D% @. R. O
% T/ v3 G' W/ H) d! h) c转载自flydream0

" i& L( s( G* }0 B) e+ k7 e8 O/ R1 s6 H- ~( ]9 D) A
7 O& D1 |" Q: b+ P6 ^& b% _, h

/ Y1 |# n+ R$ [& C; m- o) u2 I/ x, `0 [+ G

/ g0 ^: q7 z% @( j6 a: q3 ], |' D% F4 R1 S- J
" k5 E* `6 o3 N( w; c% t
+ w0 M5 h" f; S; H/ J; D" f

1 m! \2 N  r. a  v9 p+ c! Q5 F6 `2 e  V' k2 C
1 A. n# W% v! }* B' b* {# n  o1 r

$ q* Z# S. v) b% c, A+ X" h
& j) E8 J& }! |& M+ @
赞 1 收藏 1 评论8 发布时间:2018-5-22 14:36

举报

8个回答
Inc_brza 回答时间:2018-5-22 15:32:21
我觉得有必要修改一下排版
aimejia 回答时间:2018-5-22 16:16:34
Inc_brza 发表于 2018-5-22 15:32
- W; _" A- H+ g+ F) q我觉得有必要修改一下排版

1 C" L1 A/ g  A! ^, Q/ U好的,已经进行一下调整了。
队长shiwo 回答时间:2018-5-22 17:50:37
这附的代码看得好慌,看不下去
kylongmu 回答时间:2018-5-22 23:02:32
2ms,这是超新手吧?开发用keil下一次的时间没感觉吗?
aimejia 回答时间:2018-5-23 13:02:40
队长shiwo 发表于 2018-5-22 17:50
! W) Y: S* ?9 M0 d0 @这附的代码看得好慌,看不下去
+ k: o7 D, J4 W; E
已经调整了一下哦
kkbralack 回答时间:2019-11-22 17:06:31
之前也遇到同样的问题,SysTick感觉不是1ms变加,文章受益啦,谢谢。
kkbralack 回答时间:2019-11-22 17:07:23
队长shiwo 发表于 2018-5-22 17:50
; k$ s( i" T0 e3 w5 ^. f) ]这附的代码看得好慌,看不下去

9 d6 E: S- D; K4 ^这个应该就是官网提供的
liganchina 回答时间:2020-7-17 10:54:39
mark一下

所属标签

相似技术帖

官网相关资源

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