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

STM32心得:FLASH闪存编程原理与实验

[复制链接]
STMCU小助手 发布时间:2022-11-22 14:15
主要内容:
( A7 u% ]) ]) H" U* |1) STM32 Flash操作介绍;
8 R" z5 t* ]$ ?- g2) 寄存器和库函数介绍;2 @; a9 z1 d1 E) }4 S6 L" X$ ]  S
3) 相关实验代码解读。
5 |1 n' f" D( X. U3 }2 e8 i, S/ T2 J- Z2 f8 j
实验功能:利用 STM32 内部的 FLASH 来实现类似I2C通讯实验的效果(参考I2C通讯实验),不过这次是将数据直接存放在STM32内部,而不是存放在W25Q128。! P& p7 h! Z* e5 v
1. STM32 Flash操作介绍* o% R& w' `. f# v' |1 p# _
1.1 STM32编程方式* h. ?# l* w" M2 P) u5 D
1) 在线编程(ICP,In-Circuit Programming)1 W( f4 r2 D, b8 A5 v$ L/ w
通过JTAG/SWD协议(用JLINK或ST LINK下载程序)或者系统加载程序(Bootloader)下载用户应用程序(用串口下载)到微控制器中。$ M& c* _. {9 g
2) 在程序中编程(IAP,In Application Programming)' I2 h) L; m$ j( d" W, h
通过任何一种通信接口(如IO端口,USB,CAN,UART,I2C,SPI等)下载程序或者应用数据到存储器中。也就是说,STM32允许用户在应用程序中重新烧写闪存存储器中的内容。然而,IAP需要至少有一部分程序已经使用ICP方式烧到闪存存储器中(Bootloader)(与系统加载程序的Bootloaer不同)。+ I  `% f( F% ^$ I: p

, U( T8 ?* ?% ?$ F: }5 A7 G1.2 闪存模块存储器组织
" ~9 Y5 Z, q% o& S按照不同容量,存储器组织成32个1K字节/页(小容量),128个1K字节/页(中容量),256个2K字节/页(大容量)的主存储器和一些信息块等。战舰/精英板FLASH容量为512K,所以一共有256页(0-255)。
* \& ^  D0 x+ K
1 D9 f1 [1 T& }% A 20200915133520347.png
% H2 O" _: o$ G
0 d7 U( A9 k2 c. NSTM32的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等3部分组成。
$ O; Y$ m8 k' V: q( ]: n! w7 F1) 主存储器,该部分用来存放代码和数据常数(如const类型的数据)。对于大容量产品,其被划分为256页,每页2K字节。注意,小容量和中容量产品则每页只有1K字节。从上图可以看出主存储器的起始地址就是0X08000000, B0、B1都接GND的时候,就是从0X08000000开始运行代码的。0 y( c/ I2 q  H8 d% W% V( B) d+ g* x
2) 信息块,该部分分为2个小部分,其中启动程序代码(系统存储器),是用来存储ST自带的启动程序,用于串口下载代码,当B0接V3.3,B1接GND的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能。
! u8 p( U9 ?1 ^% }* f, q/ B3) 闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。' e; q/ E& |4 w
对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理。编程与擦除的高电压由内部产生。
, N9 }9 p- t. m$ h2 `% ^  d4 o# l/ N- Y
1.3 Flash闪存的读取4 `7 a- V3 I& m
内置闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相应的数据。例如,我们要从地址addr,读取一个半字(半字为16位,1个字为32位),可以通过如下的语句读取:
$ y3 M0 t2 F: x! f  o" {# [" C* N7 ~5 |/ {
  1. data=*(vu16*)addr;
复制代码
! ~& D" s6 j! Z2 t& g9 h* B
将addr强制转换为vu16指针,然后取该指针所指向的地址的值,即得到了addr地址的值。类似的,将上面的vu16改为vu8,即可读取指定地址的一个字节。: x, j# ^, K# ]; V# M; g! \+ J/ G
8 r& [' c  e' t6 G/ u8 e+ F
1.4 Flash闪存的编程(写)和擦除操作
. o# h9 k5 @3 R. Y8 V/ SSTM32的闪存编程是由FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含7个32位寄存器,他们分别是:
+ \$ Z! E% ^+ d6 x* N1) FPEC键寄存器 (FLASH_KEYR)% |/ K  |! p  b% ^
其中FPEC总共有3个键值:RDPRT键=0X000000A5、KEY1=0X45670123、KEY2=0XCDEF89AB。
3 B7 O( F' r2 f2) 选择字节键寄存器 (FLASH_OPTKEYR)9 X3 T4 F9 R0 q2 f6 k: k& N+ p
3) 闪存控制寄存器 (FLASH_CR)
- Z$ O+ Q- I6 |  |* S# W4) 闪存状态寄存器 (FLASH_SR)5 Z6 l$ ]7 \% b' j
5) 闪存地址寄存器 (FLASH_AR): k( ~4 r7 D3 u4 Q! u- {
6) 选择字节寄存器 (FLASH_OBR)
  q0 ?' D6 {/ U1 R, \" z3 d7) 写保护寄存器 (FLASH_WRPR)
8 f6 S! W: {9 ]9 d) {  |6 S4 Y4 [- o  ^& R
1.5 Flash编程注意事项

& e: W+ D" p& t6 X1.5.1 STM32复位后,FPEC模块是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列到FLASH_KEYR寄存器可以打开FPEC模块(即写入KEY1和KEY2),只有在写保护被解除后,我们才能操作相关寄存器。 叫Unlock。键值不正确会产生总线错误。, L& C. B" n  y- \
1.5.2 STM32闪存的编程每次必须写入16位(不能单纯的写入8位数据!),当FLASH_CR寄存器的PG位为‘1’时,在一个闪存地址写入一个半字将启动一次编程。写入任何非半字的数据,FPEC都会产生总线错误。
+ T2 N* i9 F6 N( Y! O" Z5 `1.5.3 在编程过程中(FALSH_SR的BSY位为‘1’),任何读写闪存的操作都会使CPU暂停,直到此次闪存编程结束。
; r( q/ j: t+ c) X2 @9 T1.5.4 STM32的FLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(也就是其值必须是0XFFFF),否则无法写入,在FLASH_SR寄存器的PGERR位将得到一个警告。# e) w3 }% G9 v) y2 l9 Z0 T

8 z6 {) f( z+ B) G% a) _) m1.6 STM32 Falsh编程过程3 y; a% {/ I" G+ i' U8 r  h
1.6.1 检查FLASH_CR的LOCK是否解锁,如果没有则先解锁;' K/ Q# |" v" m! C; [$ ?' }4 v/ }
1.6.2 检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的编程操作;
0 l& U$ M  {7 l% z( O1.6.3 设置FLASH_CR寄存器的PG位为‘1’在指定的地址写入要编程的半字;2 J- V( b7 K+ v' D- t1 ]7 S
1.6.4 等待BSY位变为‘0’;
# ]& m: m! W) f2 f) i1.6.5 读出写入的地址并验证数据。7 Q, v* P# L( x, h8 \

5 n. ~' D% i6 B2 w  N/ O3 b1.7 STM32 Flash擦除过程# c9 a2 m0 V$ v" [( t. f7 H
STM32 FLASH编程的时候,要先判断所写地址是否被擦除了。闪存擦除分为两种:页擦除和整片擦除。% m4 \/ H* B/ E3 s: M
1.7.1 Flash页擦除过程
/ @( b7 x* z. L$ u2 Y1) 检查FLASH_CR的LOCK是否解锁,如果没有则先解锁;, ~& c) Q" m- D* z2 `1 {
2) 检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的闪存操作;
/ n1 `! I# \$ t3) 设置FLASH_CR寄存器的PER位为‘1’;4 r, u- E) g  q- K4 O
4) 用FLASH_AR寄存器选择要擦除的页;; E# f& K5 C2 ~: [; o: [: C3 A3 j
5) 设置FLASH_CR寄存器的STRT位为‘1’;7 V4 C9 k7 l9 A+ e' H8 o; @  _
6) 等待BSY位变为‘0’;
7 M8 {7 M' u; P- T6 d7) 读出被擦除的页并做验证。& a& K) x  H2 Q+ m1 @
1.7.2 Flash整片擦除过程' k6 `  {. w  v) k4 K
1) 检查FLASH_SR寄存器的BSY位,以确定没有其他操作在进行;* h6 k1 N/ h9 [7 t6 q
2) 设置FLASH_CR寄存器的MER为1;- a6 d( C& i6 e) _6 U
3) 设置FLASH_CR寄存器的STRT为1;
9 f9 l: u7 y& G* o3 {  i4) 等待BSY为0;
, M- y& Q2 A+ R: U0 I' Q5) 读出所有页并验证。
  Y9 x$ q- ?+ G' W0 T
! ~* W0 g+ Y, N( h  Y1.8 Flash操作相关寄存器. U6 _6 z# T" t( |) x, @/ W, X
1) FPEC键寄存器(FLASH_KEYR);
7 E6 q, [: E3 o  _- ]8 d( H8 E! Q2 b
20200915134120600.png & z# Q& k) \6 @1 v" y; U- w8 e
( c2 c) K8 |: Z8 C& s# W7 O! x
2) 闪存控制寄存器(FLASH_CR);; Z1 s- O. h0 {  C9 Q

3 v9 ^$ K6 S% e& R 20200915134217432.png
. d! ]+ d% A% I) V* @
! z; e8 o; U4 i2 Y' I& m$ S3) 闪存状态寄存器(FLASH_SR);# p# }+ g) p/ I6 K9 @( Y
5 v6 t* J. C5 b9 n' B: z8 b8 |# _" S5 o" @
20200915134306471.png
' w# K7 l% }9 i+ B# g! y% d3 c1 c" V6 A8 K' S
4) 闪存地址寄存器(FLASH_AR)。* Q9 E! w' u) J5 A
0 Q& l- l; r* ]& H
20200915134348987.png $ ]2 C, C3 u) |, d& v

; A! Q/ o+ ~6 Q8 y1.9 相关库函数
& ?, K9 x2 v) U) ~) S* X官方给出的库函数位于:stm32f10x_flash.c/stm32f10x_flash.h。9 G8 t( p% Q; A- g

  X" E3 f' H% j& J% O+ j* e2 I1.9.1 闪存操作常用库函数
* A4 d8 M: A( W- U
  1. void FLASH_Unlock(void);                                                                                      //检查 FLASH_CR寄存器的LOCK位//
    % t* v, r0 o3 g% ~3 F
  2. void FLASH_Lock(void);                                                                     //检查 FLASH_CR寄存器的LOCK位//
    4 x8 B) T9 _0 O
  3. FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);           //对主存储区编程//
    : `8 r# k0 v# R
  4. FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);       //对主存储区编程/// \4 E; n8 x* Q% ^' ]: z& r
  5. FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
    ! A' B1 j4 Q! Y4 Z$ j; s0 x0 F/ X
  6. FLASH_Status FLASH_ErasePage(uint32_t Page_Address);                       //对FLASH_AR寄存器进行操作//- d1 i0 N$ t1 u0 d5 L' W
  7. FLASH_Status FLASH_EraseAllPages(void);. @1 }+ r2 V& x1 `5 s8 G
  8. FLASH_Status FLASH_EraseOptionBytes(void);
    % @5 v: @: a5 J5 \, c2 t
  9. FLASH_Status FLASH_GetStatus(void);7 q5 B  o/ \" ~2 [6 G/ H' x
  10. FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);                          //等待FLASH_SR寄存器的BSY位变0//
复制代码
; E! ^9 ~% }, O3 t
1.10 Flash操作总结
4 J+ ?, ~% E% I4 I' o' n1.10.1 锁定解锁函数
. U$ {1 Y" A) j' ], h: t- X. m, G在对FLASH进行写操作前必须先解锁,解锁操作也就是必须在FLASH_KEYR寄存器写入特定的序列(KEY1和KEY2),固件库函数实现:
8 x# c! a5 P3 g4 h" B1 E" `, O5 ^/ {) G9 W( }
  1. void FLASH_Unlock(void);
复制代码
) `- c6 m7 C( r
同理,在对FLASH写操作完成之后,我们要锁定FLASH:$ @3 h7 s* \/ F$ Q: i& D
" x( R) I0 E3 J) l- \/ Z8 @
  1. void FLASH_Lock(void);
复制代码
; g9 O& A' p- T- i* ]( i
1.10.2 写操作函数
$ F/ z8 \) {/ B* h( G' ^- g$ q固件库提供了三个FLASH写函数:# b. U1 u- ~8 ^. A$ R1 `
* L5 M- M+ m. X- G6 f
  1. FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
    - J7 \$ R4 X$ X% W6 {0 a% r4 h/ m/ {: O2 f. _
  2. FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);* D: J; [; G6 x4 W3 i! c! ~
  3. FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
复制代码
/ K" [5 I2 A$ r! i  B
顾名思义:FLASH_ProgramWord为 32位字写入函数,其他分别为16位半字写入和用户选择字节写入函数。这里需要说明,32位字节写入实际上是写入的两次16位数据,写完第一次后地址+2,这与我们前面讲解的STM32闪存的编程每次必须写入16位并不矛盾。写入8位实际也是占用的两个地址了,跟写入16位基本上没啥区别。+ p; t4 q+ D% [  R  U5 Q
7 N  u# {; \" T0 o- E
1.10.3 擦除函数
9 ~# _3 g! p( G2 k7 j/ [/ ?固件库提供三个FLASH擦除函数:1 X+ V1 y9 `8 D) |

$ T5 B' t6 z) m- ~3 l# R+ ~
  1. FLASH_Status FLASH_ErasePage(uint32_t Page_Address);6 S( y( m$ g2 U$ w& Y4 O7 P
  2. FLASH_Status FLASH_EraseAllPages(void);
    ; {5 U/ B" ~" f0 y% |2 {
  3. FLASH_Status FLASH_EraseOptionBytes(void);
复制代码
% y! ~* N) O" v  f" {7 |
顾名思义:第一个函数是页擦除函数,根据页地址擦除特定的页数据,第二个函数是擦除所有的页数据,第三个函数是擦除用户选择字节数据。这三个函数使用非常简单。
5 Y1 t: k$ S3 n7 }
9 ^9 x- F  r& `* k& `6 u1.10.4 获取状态函数+ C; c3 j8 I6 P8 `& J* V+ ?
主要是用的函数是:
, |; G  `' v% y. S# N
( ?' f( H1 R  q
  1. FLASH_Status FLASH_GetStatus(void);
复制代码

7 ^' m( |9 T. ~返回值是通过枚举类型定义的:) i; L/ w, @* `  D1 i4 T

! m: S% I* [% e+ S/ \6 K
  1. typedef enum
    ' z( B' |. E+ T& l, M9 B
  2. {
    2 }5 c; [4 Y. B$ L
  3. FLASH_BUSY = 1,    //忙
    / H; d, t+ U3 `! ~9 s2 l
  4. FLASH_ERROR_PG,    //编程错误$ h6 I: Z; y+ r  C0 W
  5. FLASH_ERROR_WRP,   //写保护错误
    : c6 w- [/ L( q6 T8 X+ }* C9 Q
  6. FLASH_COMPLETE,    //操作完成1 m3 C' I) M& k! }
  7. FLASH_TIMEOUT      //操作超时
    / c3 @! {/ I3 K7 t% ]. c
  8. }FLASH_Status;
复制代码
4 |" N& @2 [: _& P
1.10.5 等待操作完成函数( N+ p# T  w# q7 j# N0 K0 B# t
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。使用的函数是:! a' J, E: s( c1 Y

. |  v6 D/ H7 |+ V
  1. FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
复制代码
- D. H! Q1 u8 D/ K) q$ F
入口参数为等待时间,返回值是FLASH的状态,这个很容易理解,这个函数本身我们在固件库中使用得不多,但是在固件库函数体中间可以多次看到。
5 _- t0 t& D  Y4 v( K% d6 m
7 o1 f" x4 g% X/ {1 ~0 J1.10.6 读FLASH特定地址数据函数$ w$ O* X: z+ B
有写就必定有读,而读取FLASH指定地址的半字的函数固件库并没有给出来,这里我们自己写的一个函数:
4 @  {) V0 r. W! b9 T- f
$ e& L# T. K4 I
  1. u16 STMFLASH_ReadHalfWord(u32 faddr)# S1 e! R" j1 x4 t) Z! D9 R
  2. {
    3 R# v0 M. ?" k9 z- l8 E1 r; I# c
  3. return *(vu16*)faddr;
    : Y# k! A) f! h
  4. }
复制代码

( D9 ?# y0 F; u# c2. 相关实验代码解读/ a# b2 {) m( T1 F, _
2.1 stmflash.h头文件代码解读
/ N  @7 w$ c$ K! h: ?& [

4 A) k+ }1 I5 }* Q
  1. /**
    4 i' W! a/ {3 b) p/ e0 r2 C6 x
  2. ********************************  STM32F10x  *********************************
    % \) v  |6 ]" \  i+ T: o3 o
  3. * @文件名称: STMFLASH.h
    + y  A5 p$ z6 {: s6 _
  4. * @作者名称: -----( |$ r) C- E4 S7 }. h; J
  5. * @库版本号: V3.5.0
    & U$ N0 H# p) A4 U3 R7 S5 [: r
  6. * @工程版本: V1.0.0! Y9 [1 l- ^! d9 m7 D& @
  7. * @开发日期: 2020年9月15日2 B( e/ D3 g1 Z$ F" K3 d
  8. * @摘要简述: STMFLASH头文件: x( L( i* V* c3 h
  9. ******************************************************************************/
    6 l* P- u; u5 b- C( F- {& D# M
  10. /*----------------------------------------------------------------------------
    8 I# c  ~5 f& i2 U, b7 l
  11. * @更新日志:
    0 Y* i( L; w  _! F
  12. * @无
      X" L8 w# X9 Z4 e
  13. * ---------------------------------------------------------------------------*/
    0 g# @& I3 h% G! ^- s( k& F
  14. #ifndef __STMFLASH_H__
    " N3 t6 F( g, ~" z4 M0 h" {& x
  15. #define __STMFLASH_H__# c) ^' N4 z# N$ T! Q
  16. /* 包含的头文件 ---------------------------------------------------------------*/- P( {/ [; _, H& ^
  17. #include "stm32f10x.h"! B) D' r$ e' s9 \9 j7 A
  18. /* 常量 ----------------------------------------------------------------------*/
    # N- H6 K# u0 {  |# i" I) t5 }
  19. #define STM32_FLASH_SIZE 512                    /* STM32F103ZET6的FLASH是512K */% S+ b; o/ f4 n+ x0 [5 i
  20. #define STM32_FLASH_WREN 1                 /* 使能FLASH写入(0,不是能;1,使能) */
    * a# D- T( I2 G$ z* W$ L( b6 K
  21. #define STM32_FLASH_BASE 0x08000000                  /* STM32 FLASH的起始地址 */
    + y/ z! L$ }0 }9 U  q/ v6 w. U+ j
  22. /* 函数申明 ------------------------------------------------------------------*/! [$ V2 F* e/ U; e' _) {
  23. u16  STMFLASH_ReadHalfWord(u32 faddr);                                 /* 读出半字 */  " C; p+ k' M" K+ Q+ Q
  24. u32  STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);                       /* 指定地址开始读取指定长度数据 */
    + U6 k1 f6 h8 `4 I" U
  25. void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len);     /* 指定地址开始写入指定长度的数据 */
    - t) \# E& p* `( @. T
  26. void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);        /* 从指定地址开始写入指定长度的数据 *// I# Q& F/ Z# a  i- g5 D& `
  27. void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);           /* 从指定地址开始读出指定长度的数据 */   7 N0 @% |  r6 W1 u! Z
  28. #endif /* __STMFLASH_H */, _. L1 d! W( D% Q( b3 ]' D
  29. /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/
复制代码
& @6 ^, J* }) @( w
2.2 stmflash.c文件代码解读% L& b7 S5 z! O5 \8 O

$ g: ~2 h, r( l& J! P
  1. /**2 ], C, L! e! N$ q6 L
  2. ********************************  STM32F10x  ********************************** i$ h; |! Y8 \& |9 r% s
  3. * @文件名称: stmflash.c) U3 Q5 A9 h3 I
  4. * @作者名称: -----) C% A4 e5 i8 W3 x9 ~1 q
  5. * @库版本号: V3.5.07 b! d9 `( u% E& U+ J7 u# |- B
  6. * @工程版本: V1.0.0
    4 Y/ j3 s0 D+ N; J3 Y  T
  7. * @开发日期: 2020年9月15日
    $ L/ |$ B  R3 C
  8. * @摘要简述: stmflash源文件8 b8 N# w! \. ~3 M  A( q
  9. ******************************************************************************/
    6 M/ N- A5 ~5 r# ]2 a2 v/ U! j
  10. /*-----------------------------------------------------------------------------# L. c  N# x* n# X  i7 r
  11. * @更新日志:& t+ Y9 r, s; W9 r1 n9 M0 X+ n1 Y
  12. * @无0 o1 Q; D7 m( x9 d& f) a
  13. * ---------------------------------------------------------------------------*/
    $ n' Y& A% p+ A5 U
  14. /* 包含的头文件 ---------------------------------------------------------------*/
    5 e4 `$ f. S9 m2 S0 H
  15. #include "stmflash.h"
    $ X% n& y; G2 @5 D
  16. #include "delay.h"
    6 w7 M1 F  t3 J; Z; [$ ]+ m
  17. #include "usart.h"
    4 c* K# h5 w; j
  18. /************************************************
    5 k% @) f9 Y+ N; M  b1 p9 w
  19. 函数名称:u16 STMFLASH_ReadHalfWord(u32 faddr)
    : s8 f: U. U: C3 i2 \3 [+ R
  20. 函数功能:读取指定地址的半字(16位数据)
    ! e+ k5 ~6 ~/ y$ w' o4 ?" g
  21. 入口参数:u32 faddr:读地址(此地址必须为2的倍数!!)6 l. \0 y2 C# V. t7 [5 B
  22. 返回参数:对应数据
    . q2 \0 Q' @* [& x+ R
  23. 开发作者:-----) O- e4 X5 K+ Z1 }# Q/ G9 \. }9 F
  24. *************************************************/8 a; f9 _% _  k" g
  25. u16 STMFLASH_ReadHalfWord(u32 faddr)" v8 B! l+ i1 f, q
  26. {/ l3 V5 w* o7 B
  27. return *(vu16*)faddr;
    + u: A! I6 |0 F3 r6 n
  28. }
    & S8 z) a0 e$ D& R: J
  29. #if STM32_FLASH_WREN    /* 如何使能写入,默认使能 */   # V% k3 W( w4 J2 r/ Z! o
  30. /***************************************************************************0 ?% R/ p+ X1 U. x) q! n
  31. 函数名称:STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)  4 H# p2 X% a' P$ p3 X
  32. 函数功能:不检查的写入
      k' Q( ]: o! \
  33. 入口参数:WriteAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数   t( ], l% A( J- z: E9 A
  34. 返回参数:无  m( ]0 i" l# T' L4 [
  35. 开发作者:-----
    + v# f/ w$ ^7 P/ H8 m6 X
  36. ****************************************************************************/
    & k& \. D: J" i  Y, U( ]4 R
  37. void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
    " h( a( x' r7 g+ h
  38. {        
    4 C( T; e7 n  Z; M0 ]1 K
  39. u16 i;+ z7 m7 A/ l1 D# e* p9 N/ W6 D  O: f
  40. for(i=0;i<NumToWrite;i++)* v2 J/ f- s% ~
  41. {4 F% f- n( }% s
  42.   FLASH_ProgramHalfWord(WriteAddr,pBuffer<i>);4 z& a- Z( P; I. j- }# x  `
  43.   WriteAddr+=2;                                  /* 地址增加2 */
    - @! u4 G: t1 o/ }
  44. }  
    7 i$ d! {  b5 U( E
  45. } . J& s3 Z" ?1 [# k8 u+ X* f
  46. /***************************************************************************: q/ V1 A! v# a# I6 ]/ q2 U
  47. 函数名称:STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)  
    ; B! z& R, e6 q
  48. 函数功能:从指定地址开始写入指定长度的数据- v/ Y0 s! O+ V: T1 s
  49. 入口参数:WriteAddr:起始地址(此地址必须为2的倍数!!);, C0 b% c" k! W, @3 D5 P
  50.          pBuffer:数据指针;
    . e% [- `( [) U: ]& I
  51.          NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
      M2 q' A# Z# z6 ]$ j, a
  52. 返回参数:无4 u' e$ p% D/ J: ?8 Q- `* X
  53. 开发作者:-----
    6 G8 x7 D9 _4 ?
  54. ****************************************************************************/. k; ?, }( J6 R; p0 V
  55. #if STM32_FLASH_SIZE<256
    & t9 ~/ e4 a: n/ ?: u
  56. #define STM_SECTOR_SIZE 1024                          /* 字节 */
    # K2 Y3 |' Q( Y/ E3 j  O6 ~6 y
  57. #else
    + G) M% A0 d: r& B" ?4 O! p  c2 R
  58. #define STM_SECTOR_SIZE 2048/ \3 g) S( [! k# U# y
  59. #endif   
    ' e6 O- r" h$ _% j
  60. u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];      /* 最多是2K字节 */6 z8 y4 X  h, b; ~
  61. void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) 4 X6 m2 E" t. e2 H$ j" d" T5 S# }
  62. {3 Z2 p* x5 {- f5 z2 g8 v
  63. u32 secpos;    /* 扇区地址 */
    5 B# V# A  u; U6 L
  64. u16 secoff;    /* 扇区内偏移地址(16位字计算) */
    - W/ ?& C9 ~) }' r. a; G) G& f7 d2 j
  65. u16 secremain; /* 扇区内剩余地址(16位字计算) */    2 U: B6 N: |, V- M$ P9 G$ r$ t
  66. u16 i;   
    * A, i8 |+ H9 s1 U+ K
  67. u32 offaddr;   /* 去掉0X08000000后的地址 */( ^( B6 F. {/ t+ g# l
  68. if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;  /* 非法地址 */, b2 K1 B" R' F* t2 g9 p: Y$ w8 w
  69. FLASH_Unlock();                                               /* 解锁 */
    , `/ q# r' e- y& k1 C2 t
  70. offaddr=WriteAddr-STM32_FLASH_BASE;                     /* 实际偏移地址 */
    ' W/ {  j, W5 J8 u, e9 G, @
  71. secpos=offaddr/STM_SECTOR_SIZE;                        /* 扇区地址  0~127 for STM32F103RBT6 */
    1 e/ ]2 C: O9 |! D
  72. secoff=(offaddr%STM_SECTOR_SIZE)/2;                        /* 在扇区内的偏移(2个字节为基本单位.) */
    ! r0 Y  }! A/ }7 e
  73. secremain=STM_SECTOR_SIZE/2-secoff;                        /* 扇区剩余空间大小 */   / c4 [4 B+ V8 X; B7 v
  74. if(NumToWrite<=secremain)secremain=NumToWrite;          /* 不大于该扇区范围 */8 k% N% A5 Q* z0 j; K, W' y) h
  75. while(1) 1 B; ]3 f- `  G; I
  76. {
    : \# T4 \5 m3 l0 f5 b
  77.   STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);  /* 读出整个扇区的内容 */
    ! @6 l' P* L' y2 J# C% s: P
  78.   for(i=0;i<secremain;i++)                                                                /* 校验数据 */
    $ N8 X7 j8 u; U: c
  79.   {
    4 m3 e, b& [9 u2 Y& O. S3 {
  80.    if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;                                                                                   /* 需要擦除 */     - I; o$ h5 I1 E2 P: z/ c
  81.   }
    1 T, F5 J- t5 Z. d+ y5 }- B
  82.   if(i<secremain)                                                                         /* 需要擦除 */) r* L: R6 a4 Q9 o3 F
  83.   {- f+ A# L! D1 u" F' Z8 W! h+ w
  84.    FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);                                         /* 擦除这个扇区 */
    8 U( Y# l; s  X  S& U
  85.    for(i=0;i<secremain;i++)                                                                                                      /* 复制 */
    8 v9 X5 P& f# ?
  86.    {
    % t( J* v1 Q5 t
  87.     STMFLASH_BUF[i+secoff]=pBuffer<i>;   
    * M) ^" p6 r/ d; k- e9 E
  88.    }
    5 h' U1 {4 M1 @9 f
  89.    STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);  /* 写入整个扇区 */  $ _- i3 D3 O& t) ^
  90.   }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);                   /* 写已经擦除了的,直接写入扇区剩余区间 */     ! n# Q( K$ W& X# Y2 j5 m9 v9 x
  91.   if(NumToWrite==secremain)break;                                                                           /* 写入结束了 */
    6 f2 _* k7 r/ A8 ]5 ^
  92.   else                                                                                                                         /* 写入未结束 */* e  [. V2 g( `/ @
  93.   {
    + U7 Z  S4 p! s6 B
  94.    secpos++;                                                                                             /* 扇区地址增1 */) Y4 ]( i+ f  F0 Q6 K5 Y4 U
  95.    secoff=0;                                                                                                             /* 偏移位置为0 */   
    . c" b( s9 P5 z! z3 @/ K: \' h
  96.    pBuffer+=secremain;                                                        /* 指针偏移 */7 f0 g) m% a- t
  97.    WriteAddr+=secremain;                                                      /* 写地址偏移 */    ( Q- p3 Y. W$ V1 B; T; g
  98.    NumToWrite-=secremain;                                                                                         /* 字节(16位)数递减 *// M9 G2 ]. ^1 W# C1 u5 I8 C" v2 p
  99.    if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;             /* 下一个扇区还是写不完 */
    3 O# ]# O9 _# l' `8 V
  100.    else secremain=NumToWrite;                                                                                  /* 下一个扇区可以写完了 */$ b5 E( k3 T+ i1 H
  101.   }  
    6 U  _2 ^* P3 v
  102. };
    + y6 F  M% T; M+ f5 u6 R+ L" R' d
  103. FLASH_Lock();                                                                                                             /* 上锁 */
    2 z+ A9 W* G+ R, X" w1 p/ [3 W( X3 ^
  104. }
    ; o/ ~; n3 p5 s7 e; |
  105. #endif
    5 e1 t; q& }! j* G& c
  106. /******************************************************************************; l4 Q2 C* h/ r5 ]3 ?) \% w5 G
  107. 函数名称:STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   
    4 S( B  p5 t" E" t: {3 P5 A. c
  108. 函数功能:从指定地址开始读出指定长度的数据
    5 B  p. I" U1 {9 g
  109. 入口参数:ReadAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数/ S# N% G. _3 s+ c, p2 {6 c7 v: [
  110. 返回参数:无
    ( h' v( }$ p% F8 i
  111. 开发作者:-----3 k' w  N8 K; _1 {7 B$ E
  112. *******************************************************************************/
    * W2 z+ R1 o2 O$ M4 b3 q
  113. void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   
    & q7 O; L. m5 }- _
  114. {
      _) }9 K: e3 @+ z; l
  115. u16 i;
    8 Q1 y8 E' U3 M* s+ A
  116. for(i=0;i<NumToRead;i++)
    $ M" o1 K+ |& v" Z
  117. {; K  {/ @0 v6 V! A# ~) H6 X8 ^
  118.   pBuffer<i>=STMFLASH_ReadHalfWord(ReadAddr);         /* 读取2个字节 */4 H' J0 t  O  P; e) \3 K
  119.   ReadAddr+=2;                                                                /* 偏移2个字节 */4 a. v- z" l: [: U: Q% Q7 M4 k
  120. }
    4 l0 _, L9 k1 w) S8 D
  121. }4 c' r5 e: W* q) z
  122. /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/% p, T# l. A# D
  123. </i></i></i>
复制代码
( L) G6 X' v* f6 v
2.3 main.c文件代码解读* c) Y* R% L9 ]- z7 x# C9 L
) v" t9 Q' [; F* \
  1. /**& G5 W  l3 F7 ~( T' w
  2. ********************************  STM32F10x  *********************************, C6 J8 H: i3 D
  3. * @文件名称: main.c: x: f! E! l7 U2 ?* ~
  4. * @作者名称: -----; A+ A# @$ ^% i$ T
  5. * @库版本号: V3.5.0/ X, I- q+ b. Q& F4 ]
  6. * @工程版本: V1.0.0
    & P, A( u) A4 M& _8 E8 ~6 D( p
  7. * @开发日期: 2020年9月15日5 [4 h8 \. R7 I. w6 O1 n9 y7 \3 T
  8. * @摘要简述: main源文件
    ; H( a) q4 K/ H% F6 T2 r; U
  9. ******************************************************************************/, b8 _; K1 s/ ^; E7 ~/ a
  10. /*-----------------------------------------------------------------------------
    6 {. x/ F/ @1 ?; r3 n1 {9 A" d
  11. * @更新日志:6 i3 g4 h% P; P( i: u5 q
  12. * @无
    . `/ N4 ?" i. t; M7 ]9 o
  13. * ---------------------------------------------------------------------------*/6 n$ C/ `1 D% r& j; ?
  14. /* 包含的头文件 ---------------------------------------------------------------*/$ d: t6 u: `6 h4 h
  15. #include "led.h"% Q' ^- K* w( m4 J
  16. #include "key.h"0 g" n  ]% G& f3 |2 s$ o
  17. #include "delay.h"
    0 V2 d& X  B' A$ E
  18. #include "usart.h"  1 R) J+ l7 X2 Y1 l9 f7 d" r
  19. #include "stmflash.h"
    ; e5 E; l2 Z% N. l/ I
  20. /* 包含的头文件 ---------------------------------------------------------------*/
    6 d5 o; m* t$ A* n/ Y; m
  21. const u8 TEXT_Buffer[]={"STM32F103 FLASH TEST"};   /* 要写入到STM32 FLASH的字符串数组 */+ s% R" g8 P5 [4 `& o/ X
  22. #define SIZE sizeof(TEXT_Buffer)                             /* 数组长度 */' P% q" L( Q7 L9 N/ L: h$ T
  23. #define FLASH_SAVE_ADDR  0X08070000                    /* 设置FLASH保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000) */" ^3 k3 y1 ?+ X: Y# U9 t2 Q0 l
  24. /****************************************************************
    % a0 T4 ^4 T, S  O+ ]8 V9 X
  25. 函数名称:int main()$ U1 o$ \2 z0 O  E" ~2 Q4 z, x6 ?
  26. 函数功能:主函数0 w2 A( w( r+ o. ^. A1 ~& {+ k
  27. 入口参数:无
    ( h, z9 s2 [1 b" M. ?! Y) ^' ?
  28. 返回参数:int# b7 u! X8 v2 Y: \: L7 n5 {0 n
  29. 开发作者:-----
    % k6 C$ T) a1 ]) N
  30. *****************************************************************/
      r; P, }. z+ o# P
  31. int main(void)
    9 i. n# K2 d& x  F# u
  32. {  
    2 @6 m# ^7 U- q, G. M
  33. u8 key;
    8 z: S- q# J3 S6 |: o
  34. u16 i=0;
    5 g* G6 O5 u& h* v4 Y
  35. u8 datatemp[SIZE];7 z7 K6 z' m  A3 {0 g4 T' E
  36. delay_init();                                                               /* 延时函数初始化 */   " N2 n8 I* j4 ]  H2 C8 c+ R
  37. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);   /* 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级 */6 Q0 U$ J4 z" k6 Q* F3 ~
  38. uart_init(115200);                                                  /* 串口初始化为115200 */
    8 w: J1 e/ c, h5 H: M* g
  39. LED_Init();                                                           /* 初始化LED管脚接口 */
    4 N0 C* \5 S  }3 q1 E6 H: o& ~
  40. KEY_Init();                                                                  /* 初始化KEY管脚接口 */
    & l6 v* Z$ K4 O5 T
  41. printf("KEY1:Write  KEY0:Read");  
    & g6 S4 P; y. Q9 F4 e. u, J- D
  42. while(1)
    6 \5 B% J8 n0 r- a  [( r
  43. {  D7 m' K8 D8 U2 @
  44.   key=KEY_Scan(0);                                 /* 0表示不支持连按 */2 p, G8 h2 v2 b
  45.   if(key==KEY1_PRES)                               /* KEY1按下,写入STM32 FLASH */! v$ Y1 A0 g, H! {
  46.   {/ g1 |5 s9 G% ~# U" H# `4 ~; w
  47.       printf("Start Write FLASH....\r\n");  R# W- M8 c, O& o: d2 H' I
  48.    STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);- z1 Q. u4 `8 f1 i; a  J8 N
  49.    printf("FLASH Write Finished!\r\n");            /* 提示传送完成 */* a: H* p8 s9 [8 f$ a9 o+ ]
  50.   }
    + ]& B& C8 c' l) R7 y
  51.   if(key==KEY0_PRES)                                               /* KEY0按下,读取字符串并显示 */
      C8 \3 ?; H. J, {( M
  52.   {
    . t& S) r% y+ z% }- G+ [, S
  53.    printf("Start Read FLASH....\r\n");
    7 a$ r% i- S6 Q
  54.    STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);' M" o' C6 b& C  [* x
  55.    printf("The Data Readed Is:%s\r\n",datatemp);  /* 提示传送完成 */
    5 t2 ?0 h7 e5 i% |0 I
  56.   }
    - G5 \! ~8 f7 z' Q+ u" E/ x/ Q3 K
  57.   i++;
    ) E$ p9 j  W5 E
  58.   delay_ms(10);  , r- d/ `, @5 P5 t- `0 N6 B6 ]8 N
  59.   if(i==20)
    % ^" m; {3 Z( O4 ]: ]
  60.   {4 n8 J2 |4 P5 H" f: g2 `
  61.    LED0=!LED0;                                                            /* 提示系统正在运行 */
    ( Q. `8 i! q8 g- @* U
  62.    i=0;
    3 ?! m6 s0 a9 Q/ J: d+ N
  63.   }     
    9 a; G: a4 `4 ^/ P7 Z
  64. } ; v, t) ~+ U5 `# V7 c( g
  65. }
    5 L8 I% ?! O$ J! A  q
  66. /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/
    , O8 Q$ i4 u7 l0 `# I2 {
复制代码
8 ^" |1 J" K# U
3. 实验结果5 v. O: }7 K& i

/ O1 z' F; G% `! B 20200915140953835.jpg ; X; _5 R3 K% {
2 j& I, p6 O+ m4 ^7 y1 u8 x
————————————————
% g. e8 |# I# `5 b+ w版权声明:天亮继续睡0 O! R1 p1 T  p4 ]* s

0 W; y9 v; B6 Q+ P1 C1 J
  Z+ R* @+ c+ k) ]
赞 收藏 评论0 发布时间:2022-11-22 14:15

举报

0个回答

所属标签

相似技术帖

官网相关资源

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