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

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

[复制链接]
STMCU小助手 发布时间:2021-11-24 14:59
本文开发环境:5 J) @+ Z7 [' U  @

. ~  ^6 w  i! O: L8 v: @$ o9 uMCU型号:STM32F051R8T6
4 L! K4 V( b3 M! e* e$ Y, E9 r2 v4 d' UIDE环境: MDK 5.25
0 b3 U/ B5 o9 U: d4 C代码生成工具:STM32CubeMx 5.0.1; {$ J- h( d# X2 p
HAL库版本:v1.9.0(STM32Cube MCU Package for STM32F0 Series)% b6 |% u3 X4 C  j2 v

# \) V1 m8 p3 o( q本文内容:: V7 s3 l& M1 h/ V

9 H- k. H: @5 c* H0 t/ \MCU片内Flash(闪存)的擦除与读写, j. k6 J9 b/ L& o0 _& K/ |
一个Flash读写例子- F' @4 {. I  s' ]3 t6 D' ?

+ d' l* d, u' j( y" e. _$ [2 t/ Y- |/ ^Example
& v0 v$ G5 J" f1 |- d首先给出一个Flash程序一个完整的片段:
9 |5 c8 Y; _2 k
  1. ... .... A* j1 a' L) ]% V  e. o& k
  2. #include "stm32f0xx_hal.h"
    , X1 T+ I+ K" I4 o$ H7 G
  3. ... ...' D' ^! v7 k0 G
  4. static FLASH_EraseInitTypeDef EraseInitStruct = {9 ~6 _2 }7 a( T+ `% y9 n7 a
  5.         .TypeErase = FLASH_TYPEERASE_PAGES,       //页擦除4 n* f$ O6 I& U
  6.         .PageAddress = 0x08008000,                //擦除地址
    , `( `7 ~6 I: x% r
  7.         .NbPages = 1                              //擦除页数
    % i5 F' B( Z7 G4 j, T2 r
  8. };
    , o" [8 p* f! g9 k- O/ k6 J
  9. ... ...
    . f( B+ U0 H! Z9 n( p4 E
  10.   while (1)
    1 ]. z0 a3 Z* c) h3 q! e, W
  11.   {" T) x  ^# a) n0 A) h- z
  12.                 HAL_FLASH_Unlock();
    8 }+ n" e9 f/ v' i
  13.                 uint32_t PageError = 0;+ H8 l9 b/ X2 }' T
  14.                 __disable_irq();                             //擦除前关闭中断
    ! T% E/ `5 Y; `9 H
  15.                 if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError) == HAL_OK)7 B3 E% u. {9 n$ _. d3 j
  16.                 {
    + b) z' z; H9 {* w' u# `
  17.                         printf("擦除 成功\r\n");" i9 h( x6 G! w, n3 M
  18.                 }
    9 u6 V- T- ?1 H" O3 R
  19.                 __enable_irq();                             //擦除后打开中断
    & D; [2 s9 c( k* |4 N+ \$ P
  20.                 uint32_t writeFlashData = 0x55555555;        //待写入的值: I+ M) i. _  F9 U3 o$ Q' b% R
  21.                 uint32_t addr = 0x08008000;                  //写入的地址/ s; ~* ^# x& P! F* s
  22.                 HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,addr, writeFlashData);5 Z4 Y2 Z# z5 K+ t, K" |3 j/ A7 F
  23.                 printf("at address:0x%x, read value:0x%x\r\n", addr, *(__IO uint32_t*)addr);8 ~! X( O( `' d9 C0 k0 N* f) d. f
  24.             HAL_FLASH_Lock();
    : b- D! W9 U/ {* q, F! ]: p
  25.                 while(1);. G# }$ T, g1 ~2 q, E0 z5 l- z
  26.                
    ! o, G5 X9 R5 I
  27.     /* USER CODE END WHILE */! f8 K: _2 K( o  l/ [, c& _8 F
  28.     /* USER CODE BEGIN 3 */. u- O/ v1 K% T- t5 f7 X3 p+ l
  29.   }
复制代码

- T1 I. m0 \7 w4 i本地代码段写在main.c中,STMCubeMx生成工程时已经在main.c文件中包含了各种头文件,所以这段代码可以直接使用的,如果我们想在其它文件中使用它,请记得包含HAL库头文件:
9 n: k8 }( o- X" V$ X9 Q
  1. #include "stm32f0xx_hal.h"
复制代码
1 @  q; O$ q) D6 `( G; Z' l
特别的,擦除flash的时候不能执行其他程序,所以擦除前要关闭中断,擦除后记得恢复中断状态。这段代码一旦运行,就会在特定地址写入特定的值,并可以通过串口助手查看数据:- e2 E% M  h8 n+ F9 R

* s. v" \( @4 R) M# x" s- C+ O; u
20190420203712573.png

; g" s- K6 X, X& A* F/ k9 p9 w
/ i: l6 a( S" ^7 B( kFLASH 简介
2 C: F: l6 Q- h* bReference manual (RM0091)手册给出了该系列的用户Flash的起始地址,Page,Sector的划分,这个是一个重要的参考信息,因为在Flash操作中,我们要写入数据需要先擦除数据,而擦除的最小单位是PAGE。单片机有几种启动模式,但大多数是从0x0800 0000这段代码开始运行的,我们的代码也是从这个地址开始烧录。这类似于我们计算机,常见开机就是从C盘(如果C是系统盘)启动,但是也可以设置从U盘启动。由于Flash的介绍,手册和网上已经有非常多的资料。4 e$ J2 S- \. \, f

8 x. \1 z9 A* U) p5 Q
20190420144436525.png

) t: _0 C( @7 @2 X: z& C; U( ^3 K4 ]8 B- x
Flash 的上锁与解锁
/ ]6 i" _1 o, ^  k在操作Flash之前,我们都需要对Flash进行解锁,对应的,操作完Flash之后,则需要对Flash进行上锁。这里的操作包括擦除,读和写等。HAL库提供了2个API,用户可以直接调用:& P$ Y/ d5 [: D7 t: j
) N% I4 O$ F% T  M
  1. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
    ) g3 {+ g+ P7 e( H4 Z
  2. HAL_StatusTypeDef HAL_FLASH_Lock(void);
复制代码

9 \& ]5 k5 t! E1 ]Flash 的擦除
( G0 x( `  T# a在擦除Flash之前,我们需要确定一些参数,擦除的地址,擦除的页数等,HAL库提供了一个与之相关的结构体:$ Z0 e8 ]! z! F6 v

; Q" l0 M3 ]: T+ I4 O
  1. typedef struct: n: M& e7 Z" ^2 p
  2. {$ c) I$ _1 j3 N1 M; b. Q5 H$ g
  3.   uint32_t TypeErase;   /*!< TypeErase: Mass erase or page erase.
    " K1 l" r2 v; Y+ `+ b+ |3 E7 t9 \
  4.                              This parameter can be a value of @ref FLASHEx_Type_Erase */
    ; y/ B$ @0 s- y2 O& Y

  5. ' ]" Q; K  p+ f; ?7 ~) D
  6.   uint32_t PageAddress; /*!< PageAdress: Initial FLASH page address to erase when mass erase is disabled
    2 k- q- Y0 l6 z3 h
  7.                              This parameter must be a number between Min_Data = FLASH_BASE and Max_Data = FLASH_BANK1_END */* Z0 ~$ I% m' S+ v' ?3 K& ^+ m

  8. 3 n' p& O, q' P% t' v/ D
  9.   uint32_t NbPages;     /*!< NbPages: Number of pagess to be erased.
    9 D- @. z1 W; h) c- p8 u  R* i' V. b6 }
  10.                              This parameter must be a value between Min_Data = 1 and Max_Data = (max number of pages - value of initial page)*/
    " E5 _. J& c( O8 n

  11. ! {5 v% H3 Z( V, a
  12. } FLASH_EraseInitTypeDef;
    % y& B! L, O. o! X6 L( I) K# c& t
复制代码
1 k1 s! B. W; y2 O1 t
所以在使用擦除函数之前,我们先定义一个结构体,并初始化它:
/ R% u0 s; M6 \& ^
  1. static FLASH_EraseInitTypeDef EraseInitStruct = {$ i) B/ R5 R1 p+ @( _/ J" w
  2.         .TypeErase = FLASH_TYPEERASE_PAGES,        //擦除类型:page擦除,即擦除整页。也可以选择擦除整片( f0 ~3 y  ^& ]7 [: L
  3.         .PageAddress = 0x08008000,                 //擦除起始地址1 ?- z& a+ r' e
  4.         .NbPages = 1                               //擦除页数. Z( [4 j# r6 a2 _" X: A% m
  5. };
复制代码

  y0 z% a  j/ q# c/ o. a, ?% B初始化好结构体之后,就可以使用HAL官方提供的API进行擦除:* Y" r: v3 C3 K* \( `& @9 |
: m' h3 x' l8 f! ~/ f( k/ v
  1. HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
复制代码

& X9 t2 L  `) `7 B6 n7 @其中第一个参数为指针,我们传入我们上文初始化好的结构体,第二个参数用来存放错误信息,我们新建一个值来存储,以下是示例代码:
9 K7 r! @, `: y. i5 E5 H2 l
  1. ... ...
    0 P2 V( e0 Y% N! H2 h
  2. uint32_t PageError = 0;
    ; F  B# P$ E$ T/ k' O4 }& Q$ l
  3. HAL_FLASH_Unlock();' n) k7 D+ w( ?/ T
  4. if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError) == HAL_OK)+ f8 F/ f" n' L: ]
  5. {
    : e# i/ y9 G& o2 @* P
  6.         printf("Erase Succeed\r\n");( e: E0 }1 T" u* L: _0 Q# n" P
  7. }
    , I, L7 h( C& h0 ]
  8.         HAL_FLASH_Lock();$ H7 T8 p8 C1 a" ?' n' z
  9. ... ...
复制代码

. V3 D, G2 \( M5 X2 p) J' U8 P& O如果你有ST-LINK或者J-LINK此类的调试器,可以使用硬件调试,然后查看0x08000000对应扇区的值,你会发现擦除后的数据全是0xFF。* _8 u8 L: Y% y
$ D$ a+ h+ S! i1 C6 z2 h1 v
Flash 的读写操作" ^5 l: F! x# @5 k
Flash的写操作4 e9 d: G- a& {
当我们擦除了一个扇区,该扇区就属于可以写状态,我们可以通过读Flash来确认我们写入的值,HAL库提供的写操作函数如下:8 I6 @. K3 T9 P2 N9 r: `* m( K
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
复制代码
; t& D& F2 M! N8 y7 ?# y
其中:
+ N5 m( d; q, Z' ]
  1. - TypeProgram :写类型,只可以是半字(2个字节)或字(4个字节)
    ; J3 F3 q6 Y2 @- D; w
  2. - Address     :地址
    % u8 t$ f. f. a: }& `9 }
  3. - Data        :写入的数据的值
复制代码

. S* {! {( s- b& Q! M& z7 N" NFlash的读操作
+ Y; Q6 C( ?# n! k2 o* `Flash 只需要所在地址,就可以读取对应的值:5 k6 h3 a5 d& V9 a/ _
  1. uint32_t pValue= *(__IO uint32_t*)(addr);
复制代码

- t3 n/ e% [: U$ K# u9 |* C以下程序写入一个值,并将整个值读取出来:0 ]% J3 _# z' ?' r
  1. ... ...
    4 J0 a. Q& W7 B! Q) d5 P
  2. uint32_t writeFlashData = 0x55555555;* L/ f! U2 a6 I4 m; a0 J2 k
  3. uint32_t addr = 0x08008000;
    9 ~: k+ l2 b- I/ X1 h" j7 H" U( _9 P
  4. HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,addr, writeFlashData);9 ?# P: l2 j; {5 y
  5. printf("at address:0x%x, read value:0x%x\r\n", addr, *(__IO uint32_t*)addr);! u, ]- B/ x1 m+ G
  6. HAL_FLASH_Lock();4 ~6 D) P7 z- c3 K& w
  7. ... ...
复制代码
( u( A4 T1 e8 N" C% [5 ~8 R0 C3 K
注意9 B, L) S; C9 p! X* T$ f
由于我们经常使用Flash来存放用户程序,所以要特别注意我们读写的时候不破坏到原程序,本文因为程序很小,不可能写到0x08008000,所以直接用这个地址作为例子,在实际运行中,我们需要设计要那些区域是可以用来读写的,避免出现程序错误。
9 ^1 [& g  p1 b! X& z
# W' g# d! k% l% v2 G. {; b* [
) `' p: s, H- }% Q( u
1 u7 _9 T# [2 Z5 g/ f! Q
收藏 评论0 发布时间:2021-11-24 14:59

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版