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

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

[复制链接]
STMCU小助手 发布时间:2021-11-24 14:59
本文开发环境:0 V3 i5 z% K5 ~+ d
. B: {* ~) f  X& c+ t6 e- B! R) ~
MCU型号:STM32F051R8T6$ v/ R; E7 J& g$ B( o9 D: y
IDE环境: MDK 5.25
1 `; X! a( B; J, d, c" H代码生成工具:STM32CubeMx 5.0.1
* M+ }+ x5 t$ d7 XHAL库版本:v1.9.0(STM32Cube MCU Package for STM32F0 Series)/ {* x/ z0 G1 r3 u  X

$ t& t6 a5 r- p" a* l* g本文内容:; `* E' w6 P/ T8 I8 [. ^
8 c  x2 G* }( p8 J. e# y; d4 G
MCU片内Flash(闪存)的擦除与读写5 W! c$ s4 O, V
一个Flash读写例子
6 {! @  x, j: |
! x) @( j' t3 @2 SExample
3 T0 k! \3 N# @- g* W9 o首先给出一个Flash程序一个完整的片段:: M3 S; \! T3 ]  E3 K' \3 \
  1. ... ...0 T; t# }( H% G8 f6 q$ q3 i
  2. #include "stm32f0xx_hal.h"
    8 Q0 ]; ]6 D5 f; V2 C
  3. ... ...
    7 @$ k* V3 V% T, d$ E
  4. static FLASH_EraseInitTypeDef EraseInitStruct = {
    9 f! |/ D( U2 v( s* S8 u( t4 P
  5.         .TypeErase = FLASH_TYPEERASE_PAGES,       //页擦除; p" S1 v/ W8 z0 U& j. I5 w9 A$ m& E
  6.         .PageAddress = 0x08008000,                //擦除地址
    $ E* P4 Q# p; y9 C
  7.         .NbPages = 1                              //擦除页数: c, v3 Q& Y- Z7 _  S1 }- y
  8. };) x5 d0 r( K1 u& v
  9. ... ...
    $ v9 e! G( y3 N$ |4 K5 N
  10.   while (1)
    # j' j: t+ T8 o, \0 U
  11.   {
    ; y0 y2 `% q1 P3 ^
  12.                 HAL_FLASH_Unlock();' |* N& l( m% D7 C+ L7 g
  13.                 uint32_t PageError = 0;
    4 O/ U/ C  p8 G4 O3 Z9 c% T$ W
  14.                 __disable_irq();                             //擦除前关闭中断' T/ E" w4 Y; v/ }
  15.                 if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError) == HAL_OK)4 Y  I# k! B/ U; {% n
  16.                 {+ |3 q5 O4 @2 W: K" x9 g! Z
  17.                         printf("擦除 成功\r\n");7 e9 X) Q3 o- D* A7 t! V! B' Q
  18.                 }0 O( B3 q& ^9 Z. {0 Z
  19.                 __enable_irq();                             //擦除后打开中断
    & C1 S* W8 M, m
  20.                 uint32_t writeFlashData = 0x55555555;        //待写入的值
    . f: z! h- {" k5 i/ Q* @
  21.                 uint32_t addr = 0x08008000;                  //写入的地址
    ' J6 e2 Y; @: }
  22.                 HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,addr, writeFlashData);
    & w' A' ]5 x/ \  i( K, V3 x/ A
  23.                 printf("at address:0x%x, read value:0x%x\r\n", addr, *(__IO uint32_t*)addr);& J# a% Y2 \0 e1 g
  24.             HAL_FLASH_Lock();
    ( Q6 O1 N  x, n6 l$ X% X/ _# Z4 i
  25.                 while(1);
    9 d, I9 H! t4 j
  26.                 # U# S) N1 u+ [0 x2 c
  27.     /* USER CODE END WHILE */
    8 ^5 g7 j# ~3 q! \5 @$ _% x
  28.     /* USER CODE BEGIN 3 */
    8 {( c" A! x: N# y
  29.   }
复制代码
; L+ p# B  q" \( g  c
本地代码段写在main.c中,STMCubeMx生成工程时已经在main.c文件中包含了各种头文件,所以这段代码可以直接使用的,如果我们想在其它文件中使用它,请记得包含HAL库头文件:, }0 H4 G7 E0 Z8 u; B" _4 u# t
  1. #include "stm32f0xx_hal.h"
复制代码
1 h  d- s; s) j( z
特别的,擦除flash的时候不能执行其他程序,所以擦除前要关闭中断,擦除后记得恢复中断状态。这段代码一旦运行,就会在特定地址写入特定的值,并可以通过串口助手查看数据:
8 E/ B# B; L' w" n
8 Q4 O# D/ {% B9 s( ^- W! {  ]8 r
20190420203712573.png
: ~+ z; E& l* H4 O( z
! X- D# _( ~& |1 W! Y
FLASH 简介' y. l# m6 j+ Y  d# E8 {- ^
Reference manual (RM0091)手册给出了该系列的用户Flash的起始地址,Page,Sector的划分,这个是一个重要的参考信息,因为在Flash操作中,我们要写入数据需要先擦除数据,而擦除的最小单位是PAGE。单片机有几种启动模式,但大多数是从0x0800 0000这段代码开始运行的,我们的代码也是从这个地址开始烧录。这类似于我们计算机,常见开机就是从C盘(如果C是系统盘)启动,但是也可以设置从U盘启动。由于Flash的介绍,手册和网上已经有非常多的资料。
5 N. k$ @% k: s+ C, c1 R
9 O# U+ `6 m0 i" R) T% ^% i$ E
20190420144436525.png

( k  s0 x8 r! W
$ G# A& ~; v6 r- e/ B* Q' dFlash 的上锁与解锁7 j1 s5 [5 {$ R8 t/ \+ a9 r6 F* p
在操作Flash之前,我们都需要对Flash进行解锁,对应的,操作完Flash之后,则需要对Flash进行上锁。这里的操作包括擦除,读和写等。HAL库提供了2个API,用户可以直接调用:
" [9 e4 g  G9 T: \' R
% B; z* u) W# l& k  x6 H. B
  1. HAL_StatusTypeDef HAL_FLASH_Unlock(void);  w- G/ T- U  i
  2. HAL_StatusTypeDef HAL_FLASH_Lock(void);
复制代码

  L, [1 [- ^  ^8 ^4 H' ?Flash 的擦除! `& f0 G( w# C* B4 ]/ {" E; m* ?
在擦除Flash之前,我们需要确定一些参数,擦除的地址,擦除的页数等,HAL库提供了一个与之相关的结构体:* n$ s5 ^; y) ~% X. S3 h* ?
* y; J# `/ r- j1 @% p8 X. N
  1. typedef struct
    & a" l" K9 B6 w9 z
  2. {
    3 y, \8 ]$ x7 Z
  3.   uint32_t TypeErase;   /*!< TypeErase: Mass erase or page erase.
    8 `) f) `# p" ?! p) ]/ J
  4.                              This parameter can be a value of @ref FLASHEx_Type_Erase */# s+ Z6 I1 l* b# _' k# n* _* R2 ?
  5. 4 T* Z5 W( j0 C) {8 e0 C0 V
  6.   uint32_t PageAddress; /*!< PageAdress: Initial FLASH page address to erase when mass erase is disabled
    ! h  @$ N2 x; N0 v% z8 v
  7.                              This parameter must be a number between Min_Data = FLASH_BASE and Max_Data = FLASH_BANK1_END */, h9 Z/ m8 Z' Q( ]' ?: [

  8. ' I5 I: x2 y1 L8 r1 s7 m4 a
  9.   uint32_t NbPages;     /*!< NbPages: Number of pagess to be erased.
    9 l. ?5 ^- p: w2 i; @- r' v& u
  10.                              This parameter must be a value between Min_Data = 1 and Max_Data = (max number of pages - value of initial page)*/- c9 ]9 w1 ^- C+ P4 W  e
  11. 0 x( \; N; ?' ^5 d# Q
  12. } FLASH_EraseInitTypeDef;, {2 w7 m9 F4 S( u
复制代码

1 _6 O+ H1 S8 R4 x' [所以在使用擦除函数之前,我们先定义一个结构体,并初始化它:' u0 l. Y1 K( \' o0 e  l
  1. static FLASH_EraseInitTypeDef EraseInitStruct = {
    $ @8 O/ [$ U5 V
  2.         .TypeErase = FLASH_TYPEERASE_PAGES,        //擦除类型:page擦除,即擦除整页。也可以选择擦除整片
    + R" l" x- d1 ?) E
  3.         .PageAddress = 0x08008000,                 //擦除起始地址4 p% _' i  w2 x3 q5 Z: ]6 o* l
  4.         .NbPages = 1                               //擦除页数5 w' ?1 l  u. c' s; M; a3 q
  5. };
复制代码

% v. Q3 D( f8 [; R- e& l. \9 d1 G初始化好结构体之后,就可以使用HAL官方提供的API进行擦除:
) b' N) \0 R$ P5 I% U
$ x4 B& o7 a0 b4 l
  1. HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
复制代码
( P' L/ ]8 B8 T( `8 Z3 e& u5 p. E+ z
其中第一个参数为指针,我们传入我们上文初始化好的结构体,第二个参数用来存放错误信息,我们新建一个值来存储,以下是示例代码:
# ?* O3 A" P) v
  1. ... ...( r2 H8 z- a7 J) i: @& x: P' ^! d' h
  2. uint32_t PageError = 0;6 b! a! l3 @4 V( Q& o
  3. HAL_FLASH_Unlock();
    0 m0 ?, r0 j# n
  4. if (HAL_FLASHEx_Erase(&EraseInitStruct,&PageError) == HAL_OK)% h! Q2 L; y. ?. }+ N! W
  5. {/ g' |8 ?9 V" s; S
  6.         printf("Erase Succeed\r\n");$ J* S1 y' m+ q4 S$ J; d( [% G
  7. }& j8 C9 v, ]+ ?  g# m& g
  8.         HAL_FLASH_Lock();
    : |8 K* M* U% {" o
  9. ... ...
复制代码

: O. }3 K. @+ F如果你有ST-LINK或者J-LINK此类的调试器,可以使用硬件调试,然后查看0x08000000对应扇区的值,你会发现擦除后的数据全是0xFF。
! c+ Y* h9 K* _$ [
$ g1 [5 `: R3 g3 A% W. BFlash 的读写操作
4 V8 V) f. y) }7 ]/ sFlash的写操作- z' M8 r/ s2 M6 d8 Q& A4 P
当我们擦除了一个扇区,该扇区就属于可以写状态,我们可以通过读Flash来确认我们写入的值,HAL库提供的写操作函数如下:
+ [# E' w; V7 m! B( r
  1. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
复制代码

0 N' F$ [/ F. Z$ n9 R其中:
& Q$ z. F' g/ J  k8 c& s: }
  1. - TypeProgram :写类型,只可以是半字(2个字节)或字(4个字节)6 g+ P# ^) m+ v% D" U
  2. - Address     :地址
    ; |/ \5 L  p2 R% R5 d
  3. - Data        :写入的数据的值
复制代码
' K5 ]2 o" D3 {) {# q' R
Flash的读操作+ p# W. P% B8 t& s$ n; r9 D1 _
Flash 只需要所在地址,就可以读取对应的值:
, ?2 N/ [. Y4 ^) Z0 |: l5 I! P7 e
  1. uint32_t pValue= *(__IO uint32_t*)(addr);
复制代码
* T; {1 @( Y" h! h$ ^: J
以下程序写入一个值,并将整个值读取出来:
& Y+ G- j9 N" w( g
  1. ... ..., z* N; o' A9 Z. p) {( f; \1 {. T
  2. uint32_t writeFlashData = 0x55555555;+ ?# R6 ^! H4 l0 L! }7 V4 L8 d
  3. uint32_t addr = 0x08008000;: K; P3 S9 E8 O; m
  4. HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,addr, writeFlashData);
    : f( K" p' ]7 ~0 r( e
  5. printf("at address:0x%x, read value:0x%x\r\n", addr, *(__IO uint32_t*)addr);/ N& f1 }% F) y0 B# D
  6. HAL_FLASH_Lock();
    + P! W8 b. [+ T; y' c
  7. ... ...
复制代码
! \& v, y% |1 g6 \" g; W9 }# }
注意
0 f5 v; t7 f* q由于我们经常使用Flash来存放用户程序,所以要特别注意我们读写的时候不破坏到原程序,本文因为程序很小,不可能写到0x08008000,所以直接用这个地址作为例子,在实际运行中,我们需要设计要那些区域是可以用来读写的,避免出现程序错误。
$ o7 Q. H/ B* U: W, o, V0 V- {2 `
; e/ j6 t! G1 v0 u% ?# n

' ?1 _- J1 d% h
收藏 评论0 发布时间:2021-11-24 14:59

举报

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