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

【经验分享】STM32F0x HAL库学习笔记(6)片内闪存(Flash)的读写操作

[复制链接]
STMCU小助手 发布时间:2021-11-24 14:59
本文开发环境:( N% \( M+ V0 z& D& x: U

3 C% u0 r6 U& e9 l2 r7 yMCU型号:STM32F051R8T6
' r( y' J, `0 h/ AIDE环境: MDK 5.254 h0 r, b4 {% S1 P. j
代码生成工具:STM32CubeMx 5.0.1, n! u7 [7 W7 C
HAL库版本:v1.9.0(STM32Cube MCU Package for STM32F0 Series)
# c3 ]+ e" g: J/ a$ `0 l/ R' Y& D' j. b- ^: q. P5 \
本文内容:
" u( O) W$ J( l! u: w! [6 N  P8 u4 V! b) h9 [& U  L
MCU片内Flash(闪存)的擦除与读写. P! M+ y9 m0 l1 o+ ~+ }
一个Flash读写例子9 Z, E* b2 X# G8 \, q

0 ?! }7 w. y7 }4 Z8 b; x$ ]Example8 V, y7 x# D" Q0 [+ Y
首先给出一个Flash程序一个完整的片段:/ x0 Y) F1 [+ p: w
  1. ... ...
    - C% Q1 u/ c& C4 \  Z7 E1 n9 T& I# v
  2. #include "stm32f0xx_hal.h"
    & D7 }/ ]% q" p( r
  3. ... ...2 ]4 ]4 H' c5 ]; G8 m
  4. static FLASH_EraseInitTypeDef EraseInitStruct = {
    % F( v( E! N9 L+ K9 d5 P+ ?
  5.         .TypeErase = FLASH_TYPEERASE_PAGES,       //页擦除
    ) N+ {/ B6 l* j0 v0 A4 n0 L
  6.         .PageAddress = 0x08008000,                //擦除地址
    1 D' Y( h. J# i$ P8 W" r
  7.         .NbPages = 1                              //擦除页数
    * ]" T7 M$ j/ h" n6 Y% T
  8. };# o9 r# H2 c1 Z1 F, m- T0 K
  9. ... ...
    4 \- w! ?3 [# ]
  10.   while (1)
    * b5 b% b  p' v4 k3 d
  11.   {6 i4 z$ |- T) u3 f% s% n0 K
  12.                 HAL_FLASH_Unlock();
    + l, z7 U' t) O4 k
  13.                 uint32_t PageError = 0;8 q" `6 L0 b  [3 I) D
  14.                 __disable_irq();                             //擦除前关闭中断
    ( B+ Y3 R0 e5 [" i# k5 T
  15.                 if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError) == HAL_OK)
    3 b! s$ a. R4 ?$ \& p% \
  16.                 {* O' C( I7 R2 O  S
  17.                         printf("擦除 成功\r\n");" ?( ^+ _! \5 a5 a! a* h
  18.                 }9 T1 M- b* m& @/ H& E. q8 q
  19.                 __enable_irq();                             //擦除后打开中断. Y" r4 J/ m: H, s. v( @
  20.                 uint32_t writeFlashData = 0x55555555;        //待写入的值
    # D, v* H* P: Z* P( Z! B  T: C
  21.                 uint32_t addr = 0x08008000;                  //写入的地址; i1 l8 _. H0 X( I: O5 |1 o8 C
  22.                 HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,addr, writeFlashData);. `2 X% t5 x* P  C& A2 V
  23.                 printf("at address:0x%x, read value:0x%x\r\n", addr, *(__IO uint32_t*)addr);
    + J8 V! M3 ]; Z2 c3 A6 @
  24.             HAL_FLASH_Lock();
    % y( O  V8 Z" z6 ?  l
  25.                 while(1);  R( f$ J# `$ l4 g: E( ?
  26.                
    7 T7 D$ a% D# |! W; `# T
  27.     /* USER CODE END WHILE */
    / r% z/ U8 i. B) e$ h
  28.     /* USER CODE BEGIN 3 */
    0 ?& G1 b" a/ O" ~' o, ?- X
  29.   }
复制代码
' L( X9 Y5 t% x/ p. ?7 y
本地代码段写在main.c中,STMCubeMx生成工程时已经在main.c文件中包含了各种头文件,所以这段代码可以直接使用的,如果我们想在其它文件中使用它,请记得包含HAL库头文件:
. ?+ N5 N' r5 r
  1. #include "stm32f0xx_hal.h"
复制代码

: k8 N* q1 [* t4 m' E! d7 K! p8 N特别的,擦除flash的时候不能执行其他程序,所以擦除前要关闭中断,擦除后记得恢复中断状态。这段代码一旦运行,就会在特定地址写入特定的值,并可以通过串口助手查看数据:
4 d% K% O8 ?7 D7 K
- |; f! Q, `! S/ K  W
20190420203712573.png

. e+ |4 N( Z# l/ O+ f8 X
0 C0 U" s; u# i+ ?" T# O# M4 rFLASH 简介. o* Q* _, u) Y. B, v% ?* J
Reference manual (RM0091)手册给出了该系列的用户Flash的起始地址,Page,Sector的划分,这个是一个重要的参考信息,因为在Flash操作中,我们要写入数据需要先擦除数据,而擦除的最小单位是PAGE。单片机有几种启动模式,但大多数是从0x0800 0000这段代码开始运行的,我们的代码也是从这个地址开始烧录。这类似于我们计算机,常见开机就是从C盘(如果C是系统盘)启动,但是也可以设置从U盘启动。由于Flash的介绍,手册和网上已经有非常多的资料。+ w! s3 Y8 E' n- k1 l5 ]( T6 p6 Z
3 K! ~% k2 p0 U( E1 p3 D+ W. i7 [
20190420144436525.png
0 B$ T" o( m+ W, P6 @

# r, q' V( A! U8 A3 L, x# sFlash 的上锁与解锁
+ p- o1 u1 \7 B! i4 w2 A# r8 j在操作Flash之前,我们都需要对Flash进行解锁,对应的,操作完Flash之后,则需要对Flash进行上锁。这里的操作包括擦除,读和写等。HAL库提供了2个API,用户可以直接调用:! K# j9 K  d' u# S% u

: E; h( Y/ i* I% J& `
  1. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
    $ t+ r3 ~% ~+ o
  2. HAL_StatusTypeDef HAL_FLASH_Lock(void);
复制代码
0 H; x$ P# U- y  e2 I: C
Flash 的擦除5 ?" f7 U" y' a1 j5 E% D5 Q) _  C
在擦除Flash之前,我们需要确定一些参数,擦除的地址,擦除的页数等,HAL库提供了一个与之相关的结构体:- \: O& k+ C8 Z
0 [* W4 ]7 o7 @" d: M& ?' ^/ r
  1. typedef struct7 r/ Y4 F2 ^1 C  J5 }2 V/ s
  2. {/ k1 Z0 z9 T/ X3 T) D5 {
  3.   uint32_t TypeErase;   /*!< TypeErase: Mass erase or page erase.
    ( d( \. T2 K& X& k3 L& ~
  4.                              This parameter can be a value of @ref FLASHEx_Type_Erase */3 B! w! M: j' v. H7 e

  5. ! l4 a! N' z+ E% t3 _2 R: R
  6.   uint32_t PageAddress; /*!< PageAdress: Initial FLASH page address to erase when mass erase is disabled. a5 i, g4 [' O3 V# t( k; k8 y2 ?
  7.                              This parameter must be a number between Min_Data = FLASH_BASE and Max_Data = FLASH_BANK1_END */8 K& U  D8 A# ~- C1 }$ d* N; f

  8. 4 Z+ h+ L. O: z9 }+ w% t
  9.   uint32_t NbPages;     /*!< NbPages: Number of pagess to be erased.
    5 @' @9 s+ T5 R' R& B6 u
  10.                              This parameter must be a value between Min_Data = 1 and Max_Data = (max number of pages - value of initial page)*/3 U. W! i0 y9 Z' C1 B4 s* }

  11. & w3 E8 U1 \! m: n. N
  12. } FLASH_EraseInitTypeDef;
    & X) L' w3 T5 F4 Y" d/ v0 J2 L
复制代码

. h" c4 k4 [/ Z8 U5 P所以在使用擦除函数之前,我们先定义一个结构体,并初始化它:
7 I  ^! Q9 y5 d6 H% A8 L* O9 J
  1. static FLASH_EraseInitTypeDef EraseInitStruct = {) t$ ?3 S5 C- U+ p! g0 M. y* y
  2.         .TypeErase = FLASH_TYPEERASE_PAGES,        //擦除类型:page擦除,即擦除整页。也可以选择擦除整片
    " j) G! V4 ?* ^4 h5 ]/ d5 T
  3.         .PageAddress = 0x08008000,                 //擦除起始地址
    ( N, V4 N* `( z7 Z3 T8 m+ {6 j, E
  4.         .NbPages = 1                               //擦除页数1 a" ?/ o% Z1 f. h3 R' }
  5. };
复制代码
. ?) E5 [0 t5 Q- s4 F+ x6 o
初始化好结构体之后,就可以使用HAL官方提供的API进行擦除:
. H; B/ R: w' G* X7 V& y* F6 A, Y- F4 c. M- t
  1. HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
复制代码
: Q* \( @; l! o8 ]( t8 @# R% |) p
其中第一个参数为指针,我们传入我们上文初始化好的结构体,第二个参数用来存放错误信息,我们新建一个值来存储,以下是示例代码:9 D2 y6 I& K6 ]8 J' n! q
  1. ... ...# G1 T' u& o: T, f4 U$ U, Z
  2. uint32_t PageError = 0;
    $ h# U  `- |/ ]6 P. O
  3. HAL_FLASH_Unlock();
    , Y9 k9 O- G; R' `$ Q* k
  4. if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError) == HAL_OK)
    ' b# C4 I1 K+ k: a+ T& e7 I
  5. {' X! P' v! p2 O
  6.         printf("Erase Succeed\r\n");: K# d2 K  E! d8 S
  7. }3 S. n/ E( O0 i* S- `# \6 S1 |
  8.         HAL_FLASH_Lock();4 [( w  {6 I/ F
  9. ... ...
复制代码
7 f* F( k0 S$ D! T' u& F" h
如果你有ST-LINK或者J-LINK此类的调试器,可以使用硬件调试,然后查看0x08000000对应扇区的值,你会发现擦除后的数据全是0xFF。2 s2 [- B) v4 K) p, J  Q

* S/ _0 \, l5 Z% N# B  H+ S$ F) aFlash 的读写操作- m" o& ]* ?: w7 o% w
Flash的写操作
' V, e6 C0 h+ L" X* Z+ q当我们擦除了一个扇区,该扇区就属于可以写状态,我们可以通过读Flash来确认我们写入的值,HAL库提供的写操作函数如下:" ]3 v& i4 _1 p& j, x
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
复制代码
( ]; t# p8 G: Y5 ~- m7 L3 v
其中:) A' G5 H7 s4 d: r% z& W! C. z
  1. - TypeProgram :写类型,只可以是半字(2个字节)或字(4个字节)
    4 \1 y* {/ l2 @: F  e! l* h% A4 f0 ~
  2. - Address     :地址7 R5 [( ?  e* ^2 b
  3. - Data        :写入的数据的值
复制代码
3 h5 H6 K7 C% M. v4 O9 U) m2 Y/ P
Flash的读操作. }! F& v( ?% `
Flash 只需要所在地址,就可以读取对应的值:; f+ F, X3 `" @: H$ c1 g- b0 o+ \& }
  1. uint32_t pValue= *(__IO uint32_t*)(addr);
复制代码

- z. S; K5 p5 d5 J4 m以下程序写入一个值,并将整个值读取出来:/ `$ _: Q6 Z: a5 P; V5 @
  1. ... ...0 [" M5 {$ N9 b& B
  2. uint32_t writeFlashData = 0x55555555;  V6 I! \) y% _) _2 C" m# q$ K
  3. uint32_t addr = 0x08008000;, g: C6 Z5 E% h& a
  4. HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,addr, writeFlashData);6 R" f' V. V" s3 G4 p" r
  5. printf("at address:0x%x, read value:0x%x\r\n", addr, *(__IO uint32_t*)addr);& m# C1 I# a8 p: J* z) y& l( h
  6. HAL_FLASH_Lock();  s: q' q3 ?% }# w, A0 F$ c
  7. ... ...
复制代码
8 k+ h# u* w7 \- f
注意
8 L7 O9 T8 s8 j' K( d由于我们经常使用Flash来存放用户程序,所以要特别注意我们读写的时候不破坏到原程序,本文因为程序很小,不可能写到0x08008000,所以直接用这个地址作为例子,在实际运行中,我们需要设计要那些区域是可以用来读写的,避免出现程序错误。( {# X) s  w5 Z7 i: y
  i; Y- ?. e5 C0 Y' \
5 ~, G$ W" o9 T4 ~% q- Z& ^

3 d, R5 @3 m" l  J" W1 e8 Y3 N; G
赞 收藏 评论0 发布时间:2021-11-24 14:59

举报

0个回答

所属标签

相似技术帖

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