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

STM32操作访问flash,包括写入数据到flash和从flash读取数据

[复制链接]
aimejia 发布时间:2018-6-1 13:17
本帖最后由 aimejia 于 2018-6-1 13:19 编辑 ' S# I$ }5 J( k7 J
% v% L" g3 T" t& o0 [8 Q3 c2 a
STM32中存储区分为:随机存取存储器RAM和只读存储器ROM。
7 Z  ]# d3 @; z& J; H" R
# X8 N2 Z. p/ [. |% X/ `其中:
( Z7 T3 X. v2 J/ ]& f) W
2 B; n; v3 B0 ]4 oRAM为常说的内存,比如手机的2G内存4G内存等,就是程序跑起来的时候所占用的存储空间,特点是掉电数据丢失。/ D0 E, C0 V+ ~

/ _$ u* b4 M( Z. v; s3 p1 y5 @ROM为常说的硬盘,比如手机的64G和128G等,可以简单的理解为硬盘的存储空间,特点是掉电数据不丢失,所以又叫“非易失性存储器件”。 $ L/ y5 [( [6 n9 d, c

# P/ }. @6 `/ MROM又包含:EEPROM和flash。
: x  Y8 D2 f& u5 Z4 h) O/ Z
; ?8 u, a* R) J- K; F: w7 f画个嵌入式产品存储器件的思维导图如下(如有什么地方不对,恳请大神们进行指正):
+ F+ ?" G4 E/ J; l( ~3 k% W1 a) N/ x5 [( n, r
1.png

# M, ~: c. j$ H
嵌入式设备存储器件思维导图

" f$ W' p, {& h+ c& ]( u作为ROM的一份子,flash的特点自然是掉电数据不丢失。但是,flash在STM32中比较重要,程序也是保存在这个地方,所以轻易不让用户进行随意的读写,以避免不必要的问题。' K6 o0 C6 Z! |+ e! ^/ J1 q
. ]7 |( b& d; e0 n3 h
而这篇博客就先简单记录一下flash的访问流程和方法(读和写),具体原理以后理解深刻了再做补充。
! X; d6 u  {9 {) b6 H" F% R6 X' M4 j0 v4 y
1、STM32 FLASH操作流程. {; t7 {6 h3 ~1 U8 M' Q

' H8 S4 Z- P, r9 X  g/ m3 C, |Flash操作已经属于嵌入式设备中很底层的操作了,直接对地址进行存取,简单描述,Flash操作大致需要以下流程:; a5 g3 y8 J* |. |  s5 X
# C3 u* T9 i) a0 D4 n2 ]2 A+ e1 G
1、确定要写入Flash的首地址(稍后介绍确定地址的方法)' {, F& z7 R: A& B( h
2、解锁Flash9 Z# x: }9 \" f' r) ~
3、对Flash进行操作(写入数据)( A/ D3 V% c0 K9 J% ]4 w
4、对Flash重新上锁4 V7 }8 q/ f6 m% y0 _

  M2 T* `* a% m! J2 D$ G1.1 如何查找并选定要写入Flash十六进制地址值的方法
* a4 J8 P. {* u4 c  a+ `
5 I+ t- {" G$ x& h; G3 I2 X要想选定安全的Flash地址进行读写,可以根据自己的STM32 MCU型号,查找数据手册,确定FLASH的地址区段,因为起始段会存储代码,所以一定要避开起始段,以避免数据错误。(我一般是根据Flash大小计算Flash的最末尾地址,往前推一段地址空间,在这里一般不会对代码中的数据产生覆盖等影响)
  I8 K( W6 O8 V+ O( }! N1 x
  R4 f# |. u* @& ]5 h/ |/ K) a! Y我此次操作Flash使用的MCU是STM32103C8T6,所以以该型号MCU为例进行描述:' D/ o5 ?9 F5 ^6 r3 m7 V- K5 P6 w

, }2 X. X. H; s0 Y在数据手册中,可以看到STM32103C8T6的flash起始地址是0x0800 0000(如下图所示),而STM32103C8T6的Flash大小为64K,可以计算出STM32103C8T6的Flash地址范围是:0x0800 0000——0x0800 FFFF(计算方法参考另一篇博客:STM32内存大小与地址的对应关系以及计算方法)。这里选取0x0800 F000作为读写操作的起始地址,对于C8T6这款MCU,操作这个起始地址应该算是很安全的范围了。
" \! ?( f1 ], [  T. ]" [! V! X+ |. L# c* G: t9 d+ G3 \
2.png
' y! q! J7 }' x, H0 G" \# L
- g3 d- `9 ^8 k8 l
2、Flash基本知识点
! a3 Q9 x2 n* G% {3 v' r, x& \! v$ p4 H. M; R# |
2.1 Flash容量
1 s1 E5 j4 a6 ^. x% \6 i
4 A; Z( R- Y1 z8 `Flash根据容量大小可以分为以下三种:8 o( g5 J" g% A) T3 W
/ A+ D' L# _' h6 |' Y
1、小容量产品:Flash大小为1-32KB(STM32F10X_LD)/ ^: J; X, F8 M4 n+ n/ i9 y* U6 t
2、中容量产品:Flash大小为64-128KB(STM32F10X_MD)
4 N9 q# C, ]( ?1 x, c' \3、大容量产品:Flash大小为256KB以上(STM32F10X_HD)
3 A% L$ v) v1 h
' u& r0 g2 _4 k/ J: G8 N: K2.2 ST库对Flash操作的支持
; {" K, |; U: r
+ r$ {5 e/ [; |+ l- v0 M1 y; {ST库中对Flash操作主要提供了以下几类操作API函数:
  U- u& H$ R. N3 }5 J4 d
: h+ D5 }- ]. X; O1、Flash解锁、锁定函数
7 k# V) N& T+ r) C, ~* v, {2 @. Q! W$ z3 h! W% }6 d4 J
void FLASH_Unlock(void);//解锁函数:在对Flash操作之前必须解锁
4 Z" q' ~* D0 |4 I$ W
; m4 Z0 }1 S9 V9 l& Rvoid FLASH_Lock(void);//锁定函数:同理,操作完Flash之后必须重新上锁
# Y# y( p) h! B* x' p5 D) d' E& [7 A1 B+ n5 T; }0 l2 ]/ }4 w
2、Flash写操作函数 & J3 T, ]3 k# J2 V5 v: X: c

( l% h- b- G& L( t1 K6 U/ \FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);//32位字写入函数
9 O6 s+ Z4 F+ q, E- ?8 j' J; n
3 t2 K/ T0 T3 C; QFLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);//16位半字写入函数
# U- a$ H) X. A+ _  ~- \  [6 ~
  g% R# E  A- J; p* XFLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);//用户选择字节写入函数
+ v- v( g1 x5 T% D3 B% z
9 s& r& W7 |2 ?# o0 y! S注:这里需要说明,32 位字节写入实际上是写入的两次 16 位数据,写完第一次后地址+2,这与我们前面讲解的 STM32 闪存的编程每次必须写入 16 位并不矛盾。写入 8位实际也是占用的两个地址了,跟写入 16 位基本上没啥区别。- k8 ~/ S& l. d3 `' m6 i3 t

0 ]+ v7 a. L2 ]% u9 n3、Flash擦除函数 # b7 C. l3 [# z  ^. |6 ]
( o* e9 C  |# M. r. i2 w# g
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);  z: h' ?/ ~8 w6 ^( Q  T, M# k
+ |7 [: n  A9 G
FLASH_Status FLASH_EraseAllPages(void);5 u) R. d) O5 l. Z; ^0 r- k

7 |& W( O  u0 ~FLASH_Status FLASH_EraseOptionBytes(void);
4 O5 y5 h* p7 Q
1 t8 M0 F8 U* S7 L. R, Q4、获取Flash状态 / C; g2 E& a0 q2 E
- C+ t+ j% U- w. H: m) g, N
FLASH_Status FLASH_GetStatus(void);
- k' |4 q+ V5 _8 @0 {$ s7 h1 F
8 R0 B( o3 u1 _9 D获取Flash状态函数,主要是为了获取Flash的状态,以便于根据状态对Flash进行操作。该函数返回值是通过枚举类型定义的,在代码中可以看到FLASH_Status类型定义如下(具体含义看注释即可):
' m. L1 t9 @+ |0 k  j! S                                                 typedef enum
8 r$ @0 V$ P$ ?2 {                                                 {
3 o( Q* J! }( Q& W                                                    FLASH_BUSY = 1,        //忙
6 V# Y2 S& ?8 w: m3 R+ c                                                    FLASH_ERROR_PG,      //编程错误 3 s) K$ i5 W5 P; p
                                                    FLASH_ERROR_WRP,   //写保护错误 7 N4 U7 v3 }" [* H
                                                    FLASH_COMPLETE,      //操作完成 " b: p$ ]( ~( e/ r0 O2 X" J
                                                    FLASH_TIMEOUT         //操作超时 1 O7 c4 H9 s. C& N: g
                                                  }FLASH_Status;
1 M: Y. E8 A6 Z& m; R' r
% L- O* X9 m% j# \% h5、等待操作完成函数
+ S3 ]+ p1 ?. [" C1 Q+ w" m4 }$ Z3 J: ~1 }
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout); 0 {8 D5 n; D5 d( C
9 h) b7 l1 J$ s- H' K- \: P5 o
注:在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。
) O" Z) m" |% ~: A! a* g2 a- h7 y. r2 B2 y3 R8 _+ \* v) o4 q
3、OK,上干货,上代码
  ~3 P% t& T- Q: N2 o; _  i& i* Y, b+ F
根据ST库提供的上述函数,我们可以自己编写Flash的读写操作代码如下:# w8 j" I0 f, y8 {
' r# t7 h$ G# S
3.1 先定义一个Flash操作的起始地址宏定义和Flash状态指示标志位
. W, [8 ^" f" N( E* ~8 [
  1. #define STARTADDR 0x0800F000 //STM32F103C8T6适用& X: G* g# B( N( v. j

  2. ! Z9 Z4 A7 P4 P
  3. volatile FLASH_Status FLASHStatus = FLASH_BUSY; //Flash操作状态变量
复制代码
3.2 编写各个读写函数9 K, G+ w; W$ B' u
  1. //////////////////////////////////////////////////////////////////////////////////4 |& O2 v( f7 G4 J; Z. v6 T
  2. // Name:        WriteFlashOneWord
    & T6 P% p2 c' e' V% V1 u  K
  3. //
    1 m' h! e( P$ u9 K! ~9 b1 q
  4. // Function:    向内部Flash写入32位数据
    ) p" `$ U: Q" E+ ~9 t
  5. //9 B$ y% |' L5 X& T$ [/ \7 n
  6. // Input:       WriteAddress:数据要写入的目标地址(偏移地址)$ O5 Q6 Z( N7 L5 ]9 R' K4 ?  k
  7. //              WriteData:   写入的数据
    : _: H/ K' ^$ C7 @- u
  8. /////////////////////////////////////////////////////////////////////////////////// p* S0 o- b: }
  9. void WriteFlashOneWord(uint32_t WriteAddress, uint32_t WriteData)! ]; ?! G4 }0 ]$ W9 e4 H9 B
  10. {     e: _0 Y8 h8 s2 O9 L' S
  11.     FLASH_UnlockBank1();9 x4 B# @4 |5 w- i; N
  12.     FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
    1 E3 E; e. D2 r. |. c* F

  13. 3 H5 ~1 u! E% J, J
  14.     FLASHStatus = 1;    //清空状态指示标志位
    1 z  X) y4 u1 O3 k  d
  15.     FLASHStatus = FLASH_ErasePage(STARTADDR);  
    ! g' D! h3 ?* V- ?$ ]
  16.     if(FLASHStatus == FLASH_COMPLETE)   
    7 ?! Q' _0 v; s7 A  a
  17.     {  9 {: S- R; y1 X4 `- o
  18.         FLASHStatus = 1;    //清空状态指示标志位7 x% k. _8 d' `4 ~7 i
  19.         FLASHStatus = FLASH_ProgramWord(STARTADDR+WriteAddress, WriteData); //flash.c 中API函数
    : d- u0 L! E* o( V: q" N! f
  20.     }3 y7 P/ S) L& M% H" k4 Y$ ?

  21. 6 i: Z* j" T# X! `  _4 X  T6 b
  22.     FLASHStatus = 1;    //清空状态指示标志位% d5 r4 A' T7 A& ], b* t  s$ Q
  23.     FLASH_LockBank1();    ! j, s% U8 m$ a) O) Y9 w# k
  24. }
    6 m+ |* r+ _  K9 v2 p1 h2 e( [
  25. 9 s- o: h3 I6 I; G+ A' P
  26. //////////////////////////////////////////////////////////////////////////////////- K+ u' g8 v! @- |: z
  27. // Name:        WriteFlashData8 a, Q# Q9 @& g7 V) c
  28. //
    1 `# z1 D) [. i; L
  29. // Function:    向内部Flash写入数据4 Q6 D3 @- ?: }
  30. //
    - V/ k5 T' K3 o! }6 C, n$ Z5 b
  31. // Input:       WriteAddress:数据要写入的目标地址(偏移地址)
    & j7 F6 D) D% z. n
  32. //              data[]:      写入的数据首地址# L( ?. C! }" t) y
  33. //              num:         写入数据的个数
    * M9 C, L6 X" }+ u$ H
  34. //////////////////////////////////////////////////////////////////////////////////9 T9 E+ R7 N* j1 U0 @+ c- m
  35. void WriteFlashData(uint32_t WriteAddress, uint8_t data[], uint32_t num)4 f* u7 ]1 S+ P$ h9 \  A
  36. {
    $ b  K2 H4 O' V7 @; Y" ^7 I8 E/ v+ N
  37.     uint32_t i = 0;8 g: i( T* V( l% z& y
  38.     uint16_t temp = 0;4 W+ D8 |! J8 H; n
  39. 0 y9 H7 L, I2 R9 H. F
  40.     FLASH_UnlockBank1();    //解锁flash' e' O# {* [# a1 ?) I
  41.     FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
    6 k9 X  T/ o- h, p4 Y( G

  42. 0 U# y3 S8 H$ @8 ~
  43.     FLASHStatus = 1;        //清空状态指示标志位
    ) f/ Y6 K4 d6 `" S4 i
  44.     FLASHStatus = FLASH_ErasePage(STARTADDR);//擦除整页$ `9 ]4 x8 r8 s! a% s
  45.     if(FLASHStatus == FLASH_COMPLETE)//flash操作完成. M- B& m  F; M) a3 I9 H
  46.     {' v1 K5 s& J& U$ j- t1 X6 U+ D
  47.         FLASHStatus = 1;    //清空状态指示标志位
    ( B8 m" v/ A) r' F
  48.         for(i=0; i<num; i++), e+ V* G& d, x; `+ M
  49.         {
    1 ]: b: i" C0 h" I
  50.             temp = (uint16_t)data[i];
    ; K" {" l# h! B% w! ~' {
  51.             FLASHStatus = FLASH_ProgramHalfWord(STARTADDR+WriteAddress+i*2, temp);//写入数据
    - j* W2 p' z; q$ {. F% R
  52.         }' c' R9 d* r0 W8 |. w* s
  53.     }
    8 X- S# y# c+ V( o6 C! g& U

  54. & U, |: ?* X% N3 l6 i
  55.     FLASHStatus = 1;    //清空状态指示标志位# k/ M& r5 Q; l7 g4 a2 N, k, s
  56. - Y/ \  u# b0 N1 G* k. r
  57.     FLASH_LockBank1();  //锁定flash( {' T, R" m# E, V# L
  58. }
复制代码
  1. //////////////////////////////////////////////////////////////////////////////////0 R, x6 B( h! P% W& E0 K9 M
  2. // Name:        ReadFlashNBtye/ @" m2 B7 F5 o+ M
  3. //# ]6 h( r5 A+ _4 q! O+ j
  4. // Function:    从内部Flash读取N字节数据
    ; ]8 y4 [9 Q: R& y9 W" d- F
  5. //
    : b. o& h( v1 D; m1 m, p# |4 d
  6. // Input:       ReadAddress:数据地址(偏移地址)* j# w! W9 ~' @
  7. //              ReadBuf:读取到的数据存放位置指针
    5 a+ l6 C* m* c1 o, B' M; E. y
  8. //              ReadNum:读取字节个数, o4 H6 E8 k- u# I& K3 u
  9. //
    4 {! Y6 \1 B" E+ k
  10. // Output:      读取的字节数# H4 K& K/ }4 B$ f2 s
  11. //////////////////////////////////////////////////////////////////////////////////
    ' w! p% y# ~- ^( _: y- G
  12. int ReadFlashNBtye(uint32_t ReadAddress, uint8_t *ReadBuf, int32_t ReadNum)( y) M1 o( q( a- P, V; V+ o& t  c3 E
  13. {   & O4 a6 F( z* r1 o1 q. |6 s
  14.     int DataNum = 0;
    9 [& u" V. p. I6 K5 _$ v; s

  15. 7 s8 ]2 z; Y5 y' s
  16.     ReadAddress = (uint32_t)STARTADDR + ReadAddress;  1 `5 z- V1 C/ o5 E' e
  17.     while(DataNum < ReadNum)   - W1 }( K# o; L# r/ |, U
  18.     {        
    $ `" F4 P6 y9 s# F* a
  19.         *(ReadBuf + DataNum) = *(__IO uint8_t*) ReadAddress++;  
    3 h2 _2 I8 N2 c5 @0 F4 J
  20.         DataNum++;     2 E# i; _$ f1 J* d
  21.     }
    ( n8 b5 @* `$ O9 N

  22. : q0 q- x: H7 |; k
  23.     return DataNum;   
    - k4 H+ H* X  j6 g! l
  24. }
    8 N5 K/ ~7 I9 g7 m

  25. * T$ O' f* `+ \6 ~) k8 h
  26. //////////////////////////////////////////////////////////////////////////////////3 j7 F9 R2 k. B
  27. // Name:        ReadFlashData
    3 h5 J% }; q: |
  28. //5 h- x" _0 C" [. t3 h3 c" @
  29. // Function:    从内部Flash读取num字节数据4 P6 o( e9 \  u. X: J
  30. //
    6 m6 }. s  p* n, m2 D
  31. // Input:       ReadAddress:数据地址(偏移地址)
    ! Y  a0 O0 V+ C* a. T& D' W( c2 T
  32. //              dest_Data:  读取到的数据存放位置指针3 T5 S8 Y" M, Y! b
  33. //              num:        读取字节个数0 d2 ^2 F2 d  g* V! `8 R) J) `, h5 u
  34. //////////////////////////////////////////////////////////////////////////////////% e! Q/ n; G9 E, Z1 _
  35. void ReadFlashData(uint32_t ReadAddress, uint8_t *dest_Data, uint32_t num)
    0 C- m9 m1 E6 |6 P; D1 `, L
  36. {( y% {) U% ]: }6 Y6 u0 d! d
  37.     int i = 0;" P: c: y9 A9 A* r0 B
  38.     ReadAddress = (uint32_t)STARTADDR + ReadAddress;
    ( {: D4 X# t% P3 z
  39.     while(i < num) : N, i/ m4 A- K$ s* e3 P
  40.     {
    # D! _4 U! Z1 a* ~+ [; Z
  41.         *(dest_Data+i) = *(__IO uint16_t*) ReadAddress;1 d, q. [+ L5 {2 }. Q. }, i
  42.         ReadAddress += 2;7 J( Q7 e4 @7 A8 c& C' [

  43. 7 g2 s* ?' p# g* C% Y0 ^6 t3 c2 b
  44.         i++;6 E' T, _5 T# w: n6 c( W1 @
  45.     }' N+ r+ \, @! `" l/ O2 ~. _
  46. }
复制代码

. Y# [9 x0 A% V: }' R' N; \
: z0 Y+ a  b/ @  C- t
/ n( j' k( `" {" N  U# |
; V) }3 G$ B% E3 p. K% K+ p9 }转载自执念3 K6 {" ]6 n* d. W
0 i) f$ H$ g( L  B

评分

参与人数 1 活跃值 +9 收起 理由
STMCU + 9

查看全部评分

赞 收藏 评论3 发布时间:2018-6-1 13:17

举报

3个回答
钟工 回答时间:2018-6-4 11:11:09
我这几天一直在调试,为了想省个EEPORM芯片,但就是没调通。执行FLASHStatus = FLASH_ErasePage(STARTADDR);//擦除整页这句3 w% R4 |7 s7 d/ n7 X% M# T5 S
和FLASHStatus = FLASH_ProgramWord(STARTADDR+WriteAddress, WriteData); //flash.c 中API函数9 v7 v8 ?! ^( Z' i7 `% s
这句时就会死机,一直找不到哪里的原因。求助!
钟工 回答时间:2018-6-4 11:12:23
我现在把你的代码搬进去了,也是不行啊,一样会死机,不知道哪里的原因了,快折腾疯了
Beyond_007 回答时间:2019-6-4 10:47:08
对于写入或擦擦数据出现死机的情况,原因可能是操作地址上有程序代码。一般要先初始化一页或几页数据(写入、擦擦操作地址)用于防止程序代码占用该预留空间。注意防止编译器将该段数据初始化代码优化掉。

所属标签

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