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

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

[复制链接]
STMCU小助手 发布时间:2021-11-24 14:59
本文开发环境:6 B7 Q" N9 R) t' z, i) r5 X+ _) Y

3 K( u9 y: G' G& S0 lMCU型号:STM32F051R8T61 A: B7 _3 t, B6 y3 \$ ]$ e6 L
IDE环境: MDK 5.25
" Y, \: |  ]- C. d7 @代码生成工具:STM32CubeMx 5.0.18 B* g0 M8 K1 b8 c. m$ k; h7 @$ {
HAL库版本:v1.9.0(STM32Cube MCU Package for STM32F0 Series)5 J$ g- Y7 \+ r3 f/ f
! N: Y. p* k; j" G
本文内容:3 L# l! q7 b" D' u# ?  f
: i" L; J/ a6 `7 a
MCU片内Flash(闪存)的擦除与读写
8 X4 I  C! }5 r8 E4 c3 y1 M# W- V一个Flash读写例子
  G; L5 ?+ `; o( A0 p* l- P% l1 p9 p6 L8 B$ O* G
Example" J/ \5 {( C1 l5 _* w" x
首先给出一个Flash程序一个完整的片段:
$ d- {" [8 U5 P& K% \7 m* E
  1. ... ...
    " [! S% r1 S7 Z) G6 {- N$ I! I
  2. #include "stm32f0xx_hal.h"
    4 m7 S% |% s5 X# J: b4 r
  3. ... ...) H6 F3 z- b7 g0 ^& g1 _
  4. static FLASH_EraseInitTypeDef EraseInitStruct = {" D6 t" I2 `  @3 r3 v. i
  5.         .TypeErase = FLASH_TYPEERASE_PAGES,       //页擦除
    3 l. s0 E0 @2 Y, [$ I0 t
  6.         .PageAddress = 0x08008000,                //擦除地址
    $ q9 e! E/ {4 L( A
  7.         .NbPages = 1                              //擦除页数5 j. ~% L' Q) d6 B5 x. w
  8. };$ f* X: X- f: Q. C8 ~9 K; |5 z
  9. ... ...( j+ f+ P1 r4 V- U
  10.   while (1)' i  R- Y4 U& n. C
  11.   {6 @' ]4 Z# ^* D# L
  12.                 HAL_FLASH_Unlock();! _# d% i1 X* J- C
  13.                 uint32_t PageError = 0;
      n, s# p* g% m) m' f' a
  14.                 __disable_irq();                             //擦除前关闭中断1 e5 \( s) ^/ K- C+ V& V
  15.                 if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError) == HAL_OK)
    ' r# b1 h% c( J6 \: ?
  16.                 {' E, p' z9 b- E- w& @! s
  17.                         printf("擦除 成功\r\n");2 H3 o2 r' E) K2 M" S( z- b) S4 m
  18.                 }% y4 J' Y" ~/ S& Q# t4 b' ?
  19.                 __enable_irq();                             //擦除后打开中断
    6 Q8 i( w( a9 M2 J5 q( ?
  20.                 uint32_t writeFlashData = 0x55555555;        //待写入的值) j7 d2 C7 P6 H% L: @8 }$ x! [
  21.                 uint32_t addr = 0x08008000;                  //写入的地址
    : S+ y- F6 n; O
  22.                 HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,addr, writeFlashData);
    0 Y( {# j6 M' i- b8 P
  23.                 printf("at address:0x%x, read value:0x%x\r\n", addr, *(__IO uint32_t*)addr);" U, A& }6 X, t
  24.             HAL_FLASH_Lock();7 ~6 y: S: f9 A6 ^# O) l6 b& z
  25.                 while(1);
    9 h' V# ~7 V( P/ v5 u5 ^
  26.                 6 d* l+ r3 z2 c2 i( Q- N2 h$ m
  27.     /* USER CODE END WHILE */- n! H2 j1 P2 |& N. B
  28.     /* USER CODE BEGIN 3 */. Z- Q: g! M% |7 e5 m5 V
  29.   }
复制代码
; @2 m9 l6 A/ Z
本地代码段写在main.c中,STMCubeMx生成工程时已经在main.c文件中包含了各种头文件,所以这段代码可以直接使用的,如果我们想在其它文件中使用它,请记得包含HAL库头文件:
( {, s) [" _9 a- I* U; L3 g
  1. #include "stm32f0xx_hal.h"
复制代码
& `/ K6 U2 @2 l- J
特别的,擦除flash的时候不能执行其他程序,所以擦除前要关闭中断,擦除后记得恢复中断状态。这段代码一旦运行,就会在特定地址写入特定的值,并可以通过串口助手查看数据:8 q4 D' T  I0 z3 ^
$ Q$ X- Z* ^' x% r( J9 a, D
20190420203712573.png

; u( Z7 v, [2 I3 G  h& m  z5 ]- I* B0 w8 U) g: p( ~
FLASH 简介
3 \; t& n0 q1 G+ R. Z; WReference manual (RM0091)手册给出了该系列的用户Flash的起始地址,Page,Sector的划分,这个是一个重要的参考信息,因为在Flash操作中,我们要写入数据需要先擦除数据,而擦除的最小单位是PAGE。单片机有几种启动模式,但大多数是从0x0800 0000这段代码开始运行的,我们的代码也是从这个地址开始烧录。这类似于我们计算机,常见开机就是从C盘(如果C是系统盘)启动,但是也可以设置从U盘启动。由于Flash的介绍,手册和网上已经有非常多的资料。9 @  N% @7 U! V" V+ V& L; s
. h: ]. z) a$ G- m  n8 ^
20190420144436525.png
6 V& Q6 n  g. K9 b2 G6 {

6 W1 K; |9 ^$ @! d; FFlash 的上锁与解锁
/ _' L3 y, J% Y5 n. I" x  e/ B在操作Flash之前,我们都需要对Flash进行解锁,对应的,操作完Flash之后,则需要对Flash进行上锁。这里的操作包括擦除,读和写等。HAL库提供了2个API,用户可以直接调用:1 A2 W4 T+ Y+ U) u

7 W) b, n$ P3 Z2 j
  1. HAL_StatusTypeDef HAL_FLASH_Unlock(void);9 `4 {9 D& U" B" ~1 F- F
  2. HAL_StatusTypeDef HAL_FLASH_Lock(void);
复制代码
; p$ l: @6 k% X1 |. z9 z/ R
Flash 的擦除3 \! u0 ?- B* }2 C# R' |! y
在擦除Flash之前,我们需要确定一些参数,擦除的地址,擦除的页数等,HAL库提供了一个与之相关的结构体:0 b' ?) d7 x* m# ?
, ^7 T1 l' C( V( z
  1. typedef struct) `6 k7 E# i( f5 G2 j, X$ J& W* g8 T
  2. {
    ) O: f, h% Y' ^8 M1 n) M$ |
  3.   uint32_t TypeErase;   /*!< TypeErase: Mass erase or page erase., ^, d  t# T! f! X. D. X
  4.                              This parameter can be a value of @ref FLASHEx_Type_Erase */
    % r! S. Y$ x9 X. Z4 m$ `" T: ?4 A

  5. 2 q  }9 B" ^4 i3 A
  6.   uint32_t PageAddress; /*!< PageAdress: Initial FLASH page address to erase when mass erase is disabled% i' ^' j; b; p" v; n% s5 A
  7.                              This parameter must be a number between Min_Data = FLASH_BASE and Max_Data = FLASH_BANK1_END */9 _5 T: Z6 Y& Q; J* i3 q" g( S% F

  8. ! m9 }. ?( S+ F/ U
  9.   uint32_t NbPages;     /*!< NbPages: Number of pagess to be erased.6 `1 g6 D9 a# C
  10.                              This parameter must be a value between Min_Data = 1 and Max_Data = (max number of pages - value of initial page)*/
    2 }) I/ B2 a9 }* y' S

  11. ) A" s2 K; w+ ?: R& ~5 B$ E
  12. } FLASH_EraseInitTypeDef;
    ( \. M" V. c% X/ c  `7 ]0 ~: z+ t
复制代码
9 N( ?7 P8 z" t& l# c; u
所以在使用擦除函数之前,我们先定义一个结构体,并初始化它:" @% [+ Z# C1 \! B# \' v
  1. static FLASH_EraseInitTypeDef EraseInitStruct = {' |, {' j" }' R& V$ {" e. N' z; j
  2.         .TypeErase = FLASH_TYPEERASE_PAGES,        //擦除类型:page擦除,即擦除整页。也可以选择擦除整片- n5 A; F; b# [+ x& J% l/ u
  3.         .PageAddress = 0x08008000,                 //擦除起始地址
    ' v% X8 o2 A/ ]5 R
  4.         .NbPages = 1                               //擦除页数
    , J+ u1 I; s" S' Q
  5. };
复制代码

1 s" h1 d7 D7 c% C9 B% x初始化好结构体之后,就可以使用HAL官方提供的API进行擦除:" [  R7 T0 \8 O6 @6 u0 V6 w' S

- O5 {( D0 M6 a' m" y7 @' d9 c) [
  1. HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
复制代码

$ @7 I2 b% J, k2 U其中第一个参数为指针,我们传入我们上文初始化好的结构体,第二个参数用来存放错误信息,我们新建一个值来存储,以下是示例代码:2 E" _! k5 s4 f1 V* r4 E
  1. ... ...
    5 U: \2 E9 i$ r  p' N; d8 r
  2. uint32_t PageError = 0;" v5 k8 Y1 C0 Y: Q" |
  3. HAL_FLASH_Unlock();
    9 ~* P8 e% v7 A' }7 v# p
  4. if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError) == HAL_OK)) ?, L" M, g0 n7 ~2 W' F
  5. {$ M1 v( r+ ?) [* h  W6 a8 _  |
  6.         printf("Erase Succeed\r\n");& ^3 n) P" N9 J3 A: ~
  7. }
    ' d" I0 j/ O! b1 e  r1 P
  8.         HAL_FLASH_Lock();
    ! x! y: S6 Z+ J6 r7 @
  9. ... ...
复制代码

( Z* l2 C& p! S  x如果你有ST-LINK或者J-LINK此类的调试器,可以使用硬件调试,然后查看0x08000000对应扇区的值,你会发现擦除后的数据全是0xFF。$ T; ~1 o% z4 I0 i
. ^2 B9 B. x; j1 V) _6 N/ y) @" L
Flash 的读写操作# @) [) p/ n0 e6 @8 v
Flash的写操作
- B2 r! }8 b) T$ I; t当我们擦除了一个扇区,该扇区就属于可以写状态,我们可以通过读Flash来确认我们写入的值,HAL库提供的写操作函数如下:
. g5 ^/ V7 e# n8 H  f% F3 J1 X. B5 M  u4 r
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
复制代码

2 G+ g5 D, O  H0 }( `$ i其中:
/ ?  g" V9 q. D& w, u. \1 ?) X# z* i
  1. - TypeProgram :写类型,只可以是半字(2个字节)或字(4个字节)2 o$ j7 M2 J. Y9 O5 o4 e( R1 Z" h
  2. - Address     :地址1 Q& `1 x. ~$ L
  3. - Data        :写入的数据的值
复制代码
3 _/ w: v  ]; G% Y
Flash的读操作: Q3 B  l& j5 b8 o
Flash 只需要所在地址,就可以读取对应的值:3 q. y$ Q( a! h$ @. [5 z
  1. uint32_t pValue= *(__IO uint32_t*)(addr);
复制代码
6 [9 w" z# O1 G$ Z, h9 r5 m
以下程序写入一个值,并将整个值读取出来:7 \/ U6 ]/ y  p5 h
  1. ... ...
    . c7 e0 @) n$ S/ e" B* X
  2. uint32_t writeFlashData = 0x55555555;
    1 B3 X/ t" Y' a: A
  3. uint32_t addr = 0x08008000;% }  m1 z- W& V: U, L4 n
  4. HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,addr, writeFlashData);
    8 c0 l+ S. \6 R0 M; K
  5. printf("at address:0x%x, read value:0x%x\r\n", addr, *(__IO uint32_t*)addr);$ k5 e3 h  R. l
  6. HAL_FLASH_Lock();& j* e8 X# U/ j3 k/ z
  7. ... ...
复制代码
6 W7 h* k7 A9 ]8 K. H( ]
注意
' V; p6 c6 {$ g3 P3 b2 p由于我们经常使用Flash来存放用户程序,所以要特别注意我们读写的时候不破坏到原程序,本文因为程序很小,不可能写到0x08008000,所以直接用这个地址作为例子,在实际运行中,我们需要设计要那些区域是可以用来读写的,避免出现程序错误。; z. v+ H: y+ \! t: D
, V" _4 w7 b0 Y6 \  G# s

! s/ h6 _$ \$ P# `3 t- c+ q6 O; r) Z( p$ q4 V( n9 D3 X) y
收藏 评论0 发布时间:2021-11-24 14:59

举报

0个回答

所属标签

相似技术帖

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