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

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

[复制链接]
STMCU小助手 发布时间:2022-11-22 14:15
主要内容:
; N% w2 A! z# a0 q% A1) STM32 Flash操作介绍;0 h. x7 ~0 h; z7 g+ S
2) 寄存器和库函数介绍;
; W- S: u! W2 L- A. W3) 相关实验代码解读。
" M9 q0 p& {7 @* ^  ^5 `  c  I2 C/ [4 |3 m, L7 X
实验功能:利用 STM32 内部的 FLASH 来实现类似I2C通讯实验的效果(参考I2C通讯实验),不过这次是将数据直接存放在STM32内部,而不是存放在W25Q128。+ r( M+ ~' p8 i+ N
1. STM32 Flash操作介绍* J. g( I1 U; D. H
1.1 STM32编程方式
3 A- u6 N8 i) n5 n' `/ G) D1) 在线编程(ICP,In-Circuit Programming)9 C$ T% g0 ?" b, Q, s8 B5 l/ h# b
通过JTAG/SWD协议(用JLINK或ST LINK下载程序)或者系统加载程序(Bootloader)下载用户应用程序(用串口下载)到微控制器中。- R4 S! A( `' B( U4 b$ l" ?$ Y. g, O
2) 在程序中编程(IAP,In Application Programming)
3 v$ ]' P" [& k2 x通过任何一种通信接口(如IO端口,USB,CAN,UART,I2C,SPI等)下载程序或者应用数据到存储器中。也就是说,STM32允许用户在应用程序中重新烧写闪存存储器中的内容。然而,IAP需要至少有一部分程序已经使用ICP方式烧到闪存存储器中(Bootloader)(与系统加载程序的Bootloaer不同)。1 J" D5 w- v. o$ ?" B; h
) i' T) g6 R* q; |5 f
1.2 闪存模块存储器组织
6 r, _1 N; s6 Q4 C* X按照不同容量,存储器组织成32个1K字节/页(小容量),128个1K字节/页(中容量),256个2K字节/页(大容量)的主存储器和一些信息块等。战舰/精英板FLASH容量为512K,所以一共有256页(0-255)。/ b( X2 @; c6 r6 t
1 P, ~  j+ O1 B: [' @4 M$ V* u
20200915133520347.png , Y2 U- n5 {( j3 Y5 B
5 W% [6 s: z4 X1 X
STM32的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等3部分组成。
+ f+ G7 Q4 J2 ~8 ?1 p2 X* k9 i1) 主存储器,该部分用来存放代码和数据常数(如const类型的数据)。对于大容量产品,其被划分为256页,每页2K字节。注意,小容量和中容量产品则每页只有1K字节。从上图可以看出主存储器的起始地址就是0X08000000, B0、B1都接GND的时候,就是从0X08000000开始运行代码的。
& M1 |  D" b4 J& W2) 信息块,该部分分为2个小部分,其中启动程序代码(系统存储器),是用来存储ST自带的启动程序,用于串口下载代码,当B0接V3.3,B1接GND的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能。
1 @7 f9 G' p: ?5 Z3) 闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。
0 g1 X' v: D4 ?对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理。编程与擦除的高电压由内部产生。
( B# C/ I& G) t& x( f" t; D6 M: `( k, w9 Y9 G" K* i
1.3 Flash闪存的读取
9 x; t9 e; \" R+ I2 s9 b内置闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相应的数据。例如,我们要从地址addr,读取一个半字(半字为16位,1个字为32位),可以通过如下的语句读取:
! ]5 i* z5 ?: t4 k4 @- }7 ?  h3 J4 P4 R' p  b
  1. data=*(vu16*)addr;
复制代码
+ p8 Z7 d4 m0 s" B  R* Y! h6 `# Q2 F" T
将addr强制转换为vu16指针,然后取该指针所指向的地址的值,即得到了addr地址的值。类似的,将上面的vu16改为vu8,即可读取指定地址的一个字节。
9 D/ J( r! Q4 Y$ G7 g, L1 v: y0 N4 T
  B% v3 z9 h2 j! `8 f6 N1.4 Flash闪存的编程(写)和擦除操作& ^6 S. i# N4 q. U( p$ T
STM32的闪存编程是由FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含7个32位寄存器,他们分别是:
( L# E6 ]; I- B( n" ^$ X1) FPEC键寄存器 (FLASH_KEYR)
/ [8 l$ [  @5 c7 w* M7 G# l* G其中FPEC总共有3个键值:RDPRT键=0X000000A5、KEY1=0X45670123、KEY2=0XCDEF89AB。
! ?" v# o; a6 z8 ^9 H8 c2) 选择字节键寄存器 (FLASH_OPTKEYR)9 q8 c# x; ], l1 p
3) 闪存控制寄存器 (FLASH_CR)
& Q) k1 i, p# s) i* J, A% c* x4) 闪存状态寄存器 (FLASH_SR)4 L: [! @0 N& u5 a
5) 闪存地址寄存器 (FLASH_AR)
: c$ }6 q; X$ M  x1 y; ?$ d; a6) 选择字节寄存器 (FLASH_OBR)3 {9 Z/ N( ?' t/ _* k2 b4 g& Q/ p
7) 写保护寄存器 (FLASH_WRPR)* T0 @6 }6 Y4 G$ O2 e/ ^
" q& z* ?4 t( m4 r& Y
1.5 Flash编程注意事项
4 V$ u* C0 h: r$ H" p
1.5.1 STM32复位后,FPEC模块是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列到FLASH_KEYR寄存器可以打开FPEC模块(即写入KEY1和KEY2),只有在写保护被解除后,我们才能操作相关寄存器。 叫Unlock。键值不正确会产生总线错误。+ H0 ?" h7 V* v! H  N
1.5.2 STM32闪存的编程每次必须写入16位(不能单纯的写入8位数据!),当FLASH_CR寄存器的PG位为‘1’时,在一个闪存地址写入一个半字将启动一次编程。写入任何非半字的数据,FPEC都会产生总线错误。
( [( Y6 E) [2 I  w1 Y1.5.3 在编程过程中(FALSH_SR的BSY位为‘1’),任何读写闪存的操作都会使CPU暂停,直到此次闪存编程结束。
# T) u7 g. B$ r( q; \1.5.4 STM32的FLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(也就是其值必须是0XFFFF),否则无法写入,在FLASH_SR寄存器的PGERR位将得到一个警告。
6 t0 Q2 Q% p! g3 F; X) x- Q# Y0 R" u, g+ R; D6 O3 s+ |
1.6 STM32 Falsh编程过程
" _! r9 Z+ F3 H8 e1.6.1 检查FLASH_CR的LOCK是否解锁,如果没有则先解锁;
: |  `, R- ]6 ^* c& S1.6.2 检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的编程操作;
4 q& ]( d# S$ b" ^( B2 Z) N- `1.6.3 设置FLASH_CR寄存器的PG位为‘1’在指定的地址写入要编程的半字;
( D* x+ N% E& |" n5 Z. Z& Y1.6.4 等待BSY位变为‘0’;6 O+ s" s  ?/ S0 S
1.6.5 读出写入的地址并验证数据。
( V4 {, D; R# n2 I
9 g" n( F& B2 l1 w% }1.7 STM32 Flash擦除过程
( k2 v8 B$ D. S& x& V7 a& u3 r; |STM32 FLASH编程的时候,要先判断所写地址是否被擦除了。闪存擦除分为两种:页擦除和整片擦除。. J7 G( v8 @# K: J. E
1.7.1 Flash页擦除过程8 L: `* H9 m0 I) r  J+ z0 E5 h
1) 检查FLASH_CR的LOCK是否解锁,如果没有则先解锁;
5 a$ i& {; F  q- d$ g5 k2) 检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的闪存操作;
0 V# Y+ J! ^$ u3) 设置FLASH_CR寄存器的PER位为‘1’;4 S/ p: N" p% X1 Z2 o1 Y
4) 用FLASH_AR寄存器选择要擦除的页;
3 }8 H$ f  l# Y# [# `% T. D5) 设置FLASH_CR寄存器的STRT位为‘1’;
3 F2 T% g( d$ n3 I# D- [6) 等待BSY位变为‘0’;
% O+ x5 y" t/ h# }7) 读出被擦除的页并做验证。
* O# @. k3 `# w) {( r" J1.7.2 Flash整片擦除过程0 u! R4 v7 n. k9 n0 H: S
1) 检查FLASH_SR寄存器的BSY位,以确定没有其他操作在进行;$ X3 x' Y: D2 n1 K  X, r: ]
2) 设置FLASH_CR寄存器的MER为1;
; r' R6 H3 V" c$ M0 Z4 |% h3) 设置FLASH_CR寄存器的STRT为1;
& Y, e" j- b( t$ S# b4) 等待BSY为0;( Z9 O, C& Q; o7 a' k6 |  J
5) 读出所有页并验证。' }4 y: {1 F; B) D# s9 J3 d2 b" s

; t& q( Q0 j: N& G; M1.8 Flash操作相关寄存器
. N# C+ f4 _$ _/ z% S2 n1) FPEC键寄存器(FLASH_KEYR);
& _! u# }, ~/ `/ u: }, z2 l' [% j' }9 H* k% v  ^( [
20200915134120600.png ' x  ^" W' L* ^8 V, X( O
/ c; _9 O1 r3 }( f) ^; A$ v
2) 闪存控制寄存器(FLASH_CR);- a# @6 C7 K3 O$ i& @
7 j& z5 S+ ]3 I! s" a
20200915134217432.png : ?$ h  y1 m) V; B( Y/ ?" Q2 G  k6 T
# f2 P2 L  {. B- c, P: d
3) 闪存状态寄存器(FLASH_SR);
$ V" N( u/ H) |# S+ _7 V- [. _) j2 o$ v, X2 N- q7 q+ _; Q5 J3 R
20200915134306471.png
; U1 Q, s2 f! ^, i% i7 h! Y  u: N: c, M' i0 ?: L5 d
4) 闪存地址寄存器(FLASH_AR)。
$ w. U" y- ^* g+ v7 e2 E$ [9 c
7 ]( Q% y. J3 L* T9 _* O: p 20200915134348987.png
$ p6 ~) H: M& i+ k5 M3 T& V5 z3 T9 x) y6 I$ R
1.9 相关库函数, J: A9 d8 b* o$ z4 p  P/ D
官方给出的库函数位于:stm32f10x_flash.c/stm32f10x_flash.h。
4 F! u" P1 Y% D6 U$ w" {0 m& V1 T4 X6 B# Z/ J
1.9.1 闪存操作常用库函数
* |5 F+ a* Z  F1 R4 u) D
  1. void FLASH_Unlock(void);                                                                                      //检查 FLASH_CR寄存器的LOCK位//9 X4 }8 f; H( A7 B/ K1 v3 p) X
  2. void FLASH_Lock(void);                                                                     //检查 FLASH_CR寄存器的LOCK位//* \' S$ J  e6 c! Y0 e
  3. FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);           //对主存储区编程//
    7 P7 I0 C' H! D9 ]: D
  4. FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);       //对主存储区编程//% a; V, ~: j" x
  5. FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);2 T% M! s: A" ^, [) \3 h- j
  6. FLASH_Status FLASH_ErasePage(uint32_t Page_Address);                       //对FLASH_AR寄存器进行操作//
    7 C4 w  w3 J! R8 G# |6 }, E- J/ Y4 B
  7. FLASH_Status FLASH_EraseAllPages(void);# e% [$ T( f  e% m9 I, s
  8. FLASH_Status FLASH_EraseOptionBytes(void);
    ) K; s: w4 m5 V) `9 R0 Y3 ]
  9. FLASH_Status FLASH_GetStatus(void);. q2 U5 x0 y+ ?: ]
  10. FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);                          //等待FLASH_SR寄存器的BSY位变0//
复制代码
! \1 a# U7 W4 Z8 D4 a) f0 i0 H
1.10 Flash操作总结& n% h3 {3 H( Y" U6 [% K+ d
1.10.1 锁定解锁函数
. F  P  y/ N1 h  @) N% |& }2 |在对FLASH进行写操作前必须先解锁,解锁操作也就是必须在FLASH_KEYR寄存器写入特定的序列(KEY1和KEY2),固件库函数实现:9 E9 C8 Y% F+ `3 Z

6 z+ l. q* r% G! Z. L; ~3 X) l
  1. void FLASH_Unlock(void);
复制代码

' f( ^1 r" i8 R9 ?0 B同理,在对FLASH写操作完成之后,我们要锁定FLASH:  U/ ~- [' u) U) t

+ |4 v2 L6 l# Q3 T
  1. void FLASH_Lock(void);
复制代码

& Z" o& R, C3 o6 {( ?. c8 l1.10.2 写操作函数
5 |5 M& S+ n3 @7 U3 P+ H" T& W固件库提供了三个FLASH写函数:7 E* w! C& r9 ]

7 I& m: }, h" F2 h5 e. i
  1. FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);/ `  R3 y  I* c# _. W+ Q1 z2 l& X
  2. FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
    : e7 A- B& \) A- s, F
  3. FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
复制代码

" n; m( O& @9 {& V: Q0 ~顾名思义:FLASH_ProgramWord为 32位字写入函数,其他分别为16位半字写入和用户选择字节写入函数。这里需要说明,32位字节写入实际上是写入的两次16位数据,写完第一次后地址+2,这与我们前面讲解的STM32闪存的编程每次必须写入16位并不矛盾。写入8位实际也是占用的两个地址了,跟写入16位基本上没啥区别。: \; v, Z6 B7 u' v
# m/ Y& Y" X4 d7 J0 L* G- b
1.10.3 擦除函数
. ?5 S9 _( F( i5 q固件库提供三个FLASH擦除函数:
$ d- s: I# \; v# Y9 i, y" j0 ]0 a0 k* Q
  1. FLASH_Status FLASH_ErasePage(uint32_t Page_Address);' E, P4 J9 K6 z2 C$ }  K* e$ [
  2. FLASH_Status FLASH_EraseAllPages(void);
    - z. Q# P/ c4 q! _- ^, C
  3. FLASH_Status FLASH_EraseOptionBytes(void);
复制代码

  N" O% X% U# W: h8 X顾名思义:第一个函数是页擦除函数,根据页地址擦除特定的页数据,第二个函数是擦除所有的页数据,第三个函数是擦除用户选择字节数据。这三个函数使用非常简单。
8 z5 K1 Q: J* Z1 V6 W3 q. h
. A/ q4 O4 {6 y) N5 G! y1.10.4 获取状态函数# O' Q7 u! B& ~! }6 E6 D) u
主要是用的函数是:. Y3 F$ z  f6 k0 [2 r# e! e

9 m' J# p- r7 J2 b" L; T) d
  1. FLASH_Status FLASH_GetStatus(void);
复制代码

4 u5 t' l/ h- n) Z返回值是通过枚举类型定义的:2 h; d3 M+ ^- h5 k; u% l5 {

# O1 k- g2 Z5 x6 L- U
  1. typedef enum
    3 q9 l& J$ L* \* n) P5 J- Q
  2. { ) x  |; H& Z* B. I. T1 s' z
  3. FLASH_BUSY = 1,    //忙
    7 _+ ]: u" n7 B$ Z
  4. FLASH_ERROR_PG,    //编程错误* R: j, W( Z7 m& P4 C  e) E
  5. FLASH_ERROR_WRP,   //写保护错误( q# Q0 O$ r& ^( j
  6. FLASH_COMPLETE,    //操作完成7 A! G# h( d* V. I9 `
  7. FLASH_TIMEOUT      //操作超时9 t% O# A+ J0 ?' Q
  8. }FLASH_Status;
复制代码

; z- C( d$ J2 T9 V# a. [; X5 N1.10.5 等待操作完成函数5 T7 s: s- f* \! `, B! V
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。使用的函数是:
; p. L6 \( }, \2 g; }* \, ^  g
$ C4 W' A& |; c' B
  1. FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
复制代码

! E4 J/ \% A# u" X" ^入口参数为等待时间,返回值是FLASH的状态,这个很容易理解,这个函数本身我们在固件库中使用得不多,但是在固件库函数体中间可以多次看到。' U/ k( ?+ Y  a& ?) k
, ?- n; s0 c' R- l9 c% i
1.10.6 读FLASH特定地址数据函数( a! k" Q0 u, ^& t
有写就必定有读,而读取FLASH指定地址的半字的函数固件库并没有给出来,这里我们自己写的一个函数:! M3 u. l- H( t$ c8 A% f
+ U. q9 B9 D' v; N
  1. u16 STMFLASH_ReadHalfWord(u32 faddr)! y5 M9 l) b. @0 U4 U" {9 m; x$ V
  2. {1 ]  T, p4 w' y
  3. return *(vu16*)faddr; ) @1 {+ U( j  y$ n4 \* Z) D8 z1 r
  4. }
复制代码

9 t8 P) c* g* v1 v0 v! g( i2. 相关实验代码解读) ]2 G. m9 r3 L! V0 n
2.1 stmflash.h头文件代码解读

2 w' Q! U7 }! C& k! {3 v9 j! G- s$ c
  1. /**
    , h6 S* s! L- l! [) h" N+ W
  2. ********************************  STM32F10x  *********************************
    5 v2 t5 b. N2 ]) B) R( k0 t. U
  3. * @文件名称: STMFLASH.h
    ) y1 p( N' @1 q9 z6 ]
  4. * @作者名称: -----2 }; |6 y$ R. Q+ G0 u. b
  5. * @库版本号: V3.5.0
    8 l3 E& a* l6 K% o& M1 z
  6. * @工程版本: V1.0.0
    " \2 n% f, x$ e" L8 N
  7. * @开发日期: 2020年9月15日
    : P% w9 D& e/ X0 w. n) q2 O, K
  8. * @摘要简述: STMFLASH头文件# J+ t4 P5 W: ^5 d
  9. ******************************************************************************/
    ) H; z! J+ Z7 V$ m) j/ s- J
  10. /*----------------------------------------------------------------------------0 p' l& p9 \2 N# D9 _% c
  11. * @更新日志:2 ~$ P( Z3 N( `, L' i8 b: z! c
  12. * @无
    0 i  w5 r; N1 v' i) t- E/ F
  13. * ---------------------------------------------------------------------------*/
    ( v2 t# V+ Y. L. w3 M4 H' P
  14. #ifndef __STMFLASH_H__  l, I5 c4 G2 l
  15. #define __STMFLASH_H__
    1 {8 _* y* Q8 D& {+ @' b3 @  J
  16. /* 包含的头文件 ---------------------------------------------------------------*/! v% u7 i2 ^2 w. h0 \
  17. #include "stm32f10x.h"/ }9 N0 G  \: q9 \) k
  18. /* 常量 ----------------------------------------------------------------------*/  p* |: I! O: z2 L/ M# t# Z% e9 b) X
  19. #define STM32_FLASH_SIZE 512                    /* STM32F103ZET6的FLASH是512K */
    % F1 [6 V/ U) P8 M- |9 \
  20. #define STM32_FLASH_WREN 1                 /* 使能FLASH写入(0,不是能;1,使能) */: F& z1 L! r! n) u1 P/ t
  21. #define STM32_FLASH_BASE 0x08000000                  /* STM32 FLASH的起始地址 */$ ^& a8 k+ D, m7 C- v
  22. /* 函数申明 ------------------------------------------------------------------*/9 R' l5 y; }2 f$ _
  23. u16  STMFLASH_ReadHalfWord(u32 faddr);                                 /* 读出半字 */  
    8 f+ Q& \& J) T: p1 ^/ u; C) w
  24. u32  STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);                       /* 指定地址开始读取指定长度数据 */
    4 Q' B/ R$ ?) H3 X, @  C8 V$ d
  25. void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len);     /* 指定地址开始写入指定长度的数据 */0 k, ]$ z8 t* f$ |6 V9 U/ X
  26. void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);        /* 从指定地址开始写入指定长度的数据 */
    9 n% h- M3 y& c8 y
  27. void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);           /* 从指定地址开始读出指定长度的数据 */   
    $ |2 M1 l. H$ t' C4 f
  28. #endif /* __STMFLASH_H */2 K5 d. o6 C3 [! v1 R$ d4 w! \9 W
  29. /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/
复制代码

. A+ h" Z2 \, g+ N; r+ m& N2.2 stmflash.c文件代码解读
0 L' u$ ]+ y: L9 H3 ~6 a4 H
; X/ _* U- R9 o
  1. /*** v+ n3 d7 _1 p% t8 r6 Y
  2. ********************************  STM32F10x  *********************************. v  X7 ]5 h% n" `, v: {/ }
  3. * @文件名称: stmflash.c
    ( _/ x1 F4 F7 z5 ~, ]
  4. * @作者名称: -----
    7 k8 F+ t/ k$ D( d6 _' @$ X
  5. * @库版本号: V3.5.0% F4 G3 a- Z( i5 u1 w* w
  6. * @工程版本: V1.0.0
    ! E4 q6 g( ]2 Q, t( q1 B, Y  D. q1 H
  7. * @开发日期: 2020年9月15日
    - \. R! V& _" W' x- \7 s
  8. * @摘要简述: stmflash源文件6 D. t: @* t, S7 ~& M, \$ n
  9. ******************************************************************************/8 y; i# Z6 K' Y' e8 K" e1 Z' T
  10. /*-----------------------------------------------------------------------------
    , U: p; b/ [, A& y  b
  11. * @更新日志:
    5 o2 }1 K. b/ }' ?# p4 O0 F
  12. * @无
    0 e7 [+ o+ C4 `1 \/ c1 A7 o
  13. * ---------------------------------------------------------------------------*/4 S- [) E+ l; f% H
  14. /* 包含的头文件 ---------------------------------------------------------------*/
    * O6 N6 k3 y/ p5 V5 G
  15. #include "stmflash.h". r+ T8 V3 g2 ~2 O7 b: D% e
  16. #include "delay.h"  E% I: v* Z. I' c5 ?: C. w9 c! \
  17. #include "usart.h"
    $ ^7 u0 M7 s, G3 O
  18. /************************************************
    / ^9 k2 o& ^' X3 s% Z9 B, ?
  19. 函数名称:u16 STMFLASH_ReadHalfWord(u32 faddr)0 b% p# T5 E- }) ]4 o, s
  20. 函数功能:读取指定地址的半字(16位数据)
    2 s! Q, e3 `% A8 W
  21. 入口参数:u32 faddr:读地址(此地址必须为2的倍数!!)
    . W) w& v7 s+ w" O% M2 t/ d3 w, `
  22. 返回参数:对应数据; Q3 g. C* B- ]: i5 T
  23. 开发作者:-----
    3 W7 a6 L" N; T' q* T* _: a
  24. *************************************************/& _7 g0 q1 z" P; b- b
  25. u16 STMFLASH_ReadHalfWord(u32 faddr)
    * j+ Z5 b: X# A  F" x
  26. {5 I" O7 w( e1 I
  27. return *(vu16*)faddr; 6 ?) }  H; @' L  k
  28. }
    9 ~2 ]1 c. P- F/ c3 V
  29. #if STM32_FLASH_WREN    /* 如何使能写入,默认使能 */   
    + ?7 \# o1 q/ Z2 ?" ^# V
  30. /***************************************************************************
    # E7 o; f& H, o7 w, n6 H! A5 I6 `7 P6 {
  31. 函数名称:STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)  
    ( T6 N  V5 W& N% V1 a$ e4 S6 b
  32. 函数功能:不检查的写入6 H: ~& X& u6 z( Z5 ~
  33. 入口参数:WriteAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数 1 B5 w4 G8 P1 d. F  h! i
  34. 返回参数:无4 `6 Q: t" c6 O! t9 _; r$ p1 @
  35. 开发作者:-----6 d) p9 u( g8 U4 U! `! S* m' V) d
  36. ****************************************************************************/$ q& ^/ |9 s3 s! d: f+ E8 ]) S
  37. void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   9 |5 n9 `9 [7 W$ Q+ U& R
  38. {        , C2 I0 i" [8 v' ?% u' G
  39. u16 i;/ Q! X. I" ~0 i/ x+ X7 {# }
  40. for(i=0;i<NumToWrite;i++), q- q: b1 O' [. X- z1 q9 g
  41. {7 c4 a, j# f; j+ x; u4 R
  42.   FLASH_ProgramHalfWord(WriteAddr,pBuffer<i>);
    $ m- C+ D" L5 n1 X9 Z& W; |
  43.   WriteAddr+=2;                                  /* 地址增加2 */7 k4 z' l( @! j
  44. }  
    , k! l" Z# j" U5 _
  45. } 0 A8 w4 q4 ?/ G4 E8 T
  46. /***************************************************************************
    . k& e3 ?: G( J# q5 C, a4 C9 F
  47. 函数名称:STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)  " c" ]4 F' V$ w. O' W
  48. 函数功能:从指定地址开始写入指定长度的数据
    0 X0 m' u: L9 b1 G7 \( c1 F* k
  49. 入口参数:WriteAddr:起始地址(此地址必须为2的倍数!!);# `- x  ?' q' A+ `5 V+ W
  50.          pBuffer:数据指针;0 W/ f0 m- F/ z# K% ^2 |
  51.          NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)* q; S% ]" h  ]: V, _
  52. 返回参数:无
    , y2 |+ Z5 N9 I4 B
  53. 开发作者:-----
    7 ~2 O2 u  n! L" {7 }6 j2 o
  54. ****************************************************************************/5 d5 M: V9 ~+ J. D* G2 t; o
  55. #if STM32_FLASH_SIZE<256) C. B7 L2 ]) a/ D; c+ }
  56. #define STM_SECTOR_SIZE 1024                          /* 字节 */% j- |7 G$ Q5 _$ o# R/ z1 A
  57. #else $ g% r' t: Z) b  S
  58. #define STM_SECTOR_SIZE 2048' y4 t- R: g+ D
  59. #endif   
    9 b1 A* K: e# R8 z( l4 p$ s* d
  60. u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];      /* 最多是2K字节 */
    " {1 ]1 n; ~# ~5 C2 V. V
  61. void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
    1 s1 f8 r( v, V: r3 R# T) y
  62. {
    * _) [% X: m% b4 v: G' k
  63. u32 secpos;    /* 扇区地址 */
    * h9 K! e# y4 B2 N- L
  64. u16 secoff;    /* 扇区内偏移地址(16位字计算) */
    9 I' _; k3 g' d! R1 F
  65. u16 secremain; /* 扇区内剩余地址(16位字计算) */   
    2 S* z( X1 C7 D; N  H! [5 g
  66. u16 i;   
    " P/ x6 e2 \& f& Z) Q6 Y6 N/ m/ M
  67. u32 offaddr;   /* 去掉0X08000000后的地址 */
    4 f' A. p; I" J. a! v+ S0 M+ i
  68. if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;  /* 非法地址 */
    8 I* K2 w) @/ }, U
  69. FLASH_Unlock();                                               /* 解锁 */, _) {% d& M6 l. g# u0 W+ M
  70. offaddr=WriteAddr-STM32_FLASH_BASE;                     /* 实际偏移地址 */
    / A* C; S0 }) O. ]# D) a
  71. secpos=offaddr/STM_SECTOR_SIZE;                        /* 扇区地址  0~127 for STM32F103RBT6 */
    % s4 I- x9 h) z) a
  72. secoff=(offaddr%STM_SECTOR_SIZE)/2;                        /* 在扇区内的偏移(2个字节为基本单位.) */. K* n) G' j. p" Y5 u& C$ x, o
  73. secremain=STM_SECTOR_SIZE/2-secoff;                        /* 扇区剩余空间大小 */   - m3 J6 c- ]- o# r4 Z. h
  74. if(NumToWrite<=secremain)secremain=NumToWrite;          /* 不大于该扇区范围 */, O& f# m7 A! P' S# Q0 o5 }
  75. while(1)
    ! q2 f6 \+ S. V# F8 n
  76. { / Y( v* E# y/ Y2 `6 `
  77.   STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);  /* 读出整个扇区的内容 */
    6 B' n* I3 G8 Y
  78.   for(i=0;i<secremain;i++)                                                                /* 校验数据 */: F' v- ~6 H0 d" k0 [! f
  79.   {3 g3 v; C4 w* e) v9 I6 [1 n* F% P
  80.    if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;                                                                                   /* 需要擦除 */     , ~8 C' g6 L+ A
  81.   }
    ! f5 _- T7 l/ c4 Z. n# ]% w5 p$ c7 S3 y8 j
  82.   if(i<secremain)                                                                         /* 需要擦除 */
    5 T# D' Q5 O; i( `
  83.   {' H6 j0 J. Y1 B! `$ f% u
  84.    FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);                                         /* 擦除这个扇区 */  w6 P$ ^4 y& |) ~
  85.    for(i=0;i<secremain;i++)                                                                                                      /* 复制 */$ K4 e9 w# i6 U! n
  86.    {
      q$ V6 r0 r6 X1 r% d1 P
  87.     STMFLASH_BUF[i+secoff]=pBuffer<i>;   * `9 A7 l3 r' ~. D; e5 i
  88.    }6 Z$ p- q0 b& U' o- D
  89.    STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);  /* 写入整个扇区 */  
    4 k; T  I( d8 T8 U7 ]$ O- |3 G5 U' I
  90.   }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);                   /* 写已经擦除了的,直接写入扇区剩余区间 */     
    0 A% G; Z% {, V9 Z
  91.   if(NumToWrite==secremain)break;                                                                           /* 写入结束了 */
    # d) i7 V1 F  e
  92.   else                                                                                                                         /* 写入未结束 */0 r2 K$ `' _( N7 f7 |! c
  93.   {
    4 r( }  O% v/ |, ]3 n
  94.    secpos++;                                                                                             /* 扇区地址增1 */
      P3 Y/ ^" R# s$ [0 ~2 c# E: ]$ c
  95.    secoff=0;                                                                                                             /* 偏移位置为0 */   6 L# b/ I6 Q* i; y& q! c
  96.    pBuffer+=secremain;                                                        /* 指针偏移 */
      Q7 }7 [, \, o, e+ f! z% i* @# Q
  97.    WriteAddr+=secremain;                                                      /* 写地址偏移 */   
    0 H: ^7 p4 `- P* i8 g0 g* `5 ]/ }. K! F
  98.    NumToWrite-=secremain;                                                                                         /* 字节(16位)数递减 */
    2 e4 X- d! V0 w7 F
  99.    if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;             /* 下一个扇区还是写不完 */7 Q; c* @- ]$ e6 y
  100.    else secremain=NumToWrite;                                                                                  /* 下一个扇区可以写完了 */. a! X+ h: R# `0 W: F# u4 k7 g
  101.   }  ) Q0 b+ X. T. w  J
  102. };
    7 y& i' m5 G/ O4 K
  103. FLASH_Lock();                                                                                                             /* 上锁 */
    # I" L- |1 q& l- I+ S9 _3 b- R
  104. }0 a. I6 P) S9 N! U! r
  105. #endif
    + k9 V0 n5 I, ?5 B+ t! M% @5 [
  106. /******************************************************************************
    , {3 A! O6 O+ g
  107. 函数名称:STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   - M& D3 `1 I& j( _8 [6 X) }
  108. 函数功能:从指定地址开始读出指定长度的数据
    3 X0 g9 m2 R' ?
  109. 入口参数:ReadAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数6 K4 }8 w, n% ]/ H+ H* I
  110. 返回参数:无" g+ c6 Y# }% u4 F$ H: l2 ^
  111. 开发作者:-----
    4 U, e0 D- ]" ?; P
  112. *******************************************************************************/- b6 S3 M( C$ {/ M
  113. void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   
    ( X% K! X9 u1 m" d/ ^8 U8 `9 S0 N
  114. {
    ! ]6 ^8 ?4 X+ ]
  115. u16 i;
    2 c- }8 S, {: m2 _9 f% Q
  116. for(i=0;i<NumToRead;i++)
    * V) R* n; P1 Z. C
  117. {
    & D$ W. ]! h9 S& a6 ^
  118.   pBuffer<i>=STMFLASH_ReadHalfWord(ReadAddr);         /* 读取2个字节 */6 t) N; @8 Q4 W! @2 [* W
  119.   ReadAddr+=2;                                                                /* 偏移2个字节 */& E; Q, u' u9 N3 A1 s% I1 f& }
  120. }1 X; m' b! h1 q. ^$ O5 q) E# p! U
  121. }
    1 i/ R5 ]& a) |9 ~" D- x, f
  122. /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/
    8 G# ^! P4 A2 f* ~+ ^: N: M- k4 b) O
  123. </i></i></i>
复制代码
; n/ ^+ p; Q" ~0 i
2.3 main.c文件代码解读
! V; M* S+ K% v$ o; S) p2 R" b; `5 c: \/ r
  1. /**
    # t3 @3 q9 ^$ K! o, n
  2. ********************************  STM32F10x  *********************************$ Z3 F; o" s. G8 {; [, C# W
  3. * @文件名称: main.c
    & V0 |. e) ]7 q
  4. * @作者名称: -----( x- _9 g* f( a0 M
  5. * @库版本号: V3.5.0
    4 f8 `6 d" \; I: O( P' R6 q0 [7 w
  6. * @工程版本: V1.0.0
    3 d/ ?8 {1 G& o5 L& s
  7. * @开发日期: 2020年9月15日
    , P1 ~- K9 T* b3 K. D, c
  8. * @摘要简述: main源文件5 V, J  c/ d9 F5 ^8 Q
  9. ******************************************************************************/
    * L, P2 u1 b. u
  10. /*-----------------------------------------------------------------------------! ]. G  P5 d# i0 y
  11. * @更新日志:
    , a# W3 }3 b1 F3 H( z1 g4 P
  12. * @无5 Y# W( W0 w8 t0 R' `6 F
  13. * ---------------------------------------------------------------------------*/: ?$ \1 e! p: M; p
  14. /* 包含的头文件 ---------------------------------------------------------------*/, H4 f: D. m# X4 K1 G9 n5 H
  15. #include "led.h"4 F; T" s8 g/ b, c
  16. #include "key.h"
    * E9 c$ r& }2 z, w7 _/ f0 w
  17. #include "delay.h"9 a2 m( V+ O% h1 a
  18. #include "usart.h"  
    , d7 {$ C/ ?+ E3 k& ~
  19. #include "stmflash.h"
    " a$ T2 z5 X5 z. |3 N( l$ k
  20. /* 包含的头文件 ---------------------------------------------------------------*/- u: I0 \7 m. b) i3 w# Q
  21. const u8 TEXT_Buffer[]={"STM32F103 FLASH TEST"};   /* 要写入到STM32 FLASH的字符串数组 */
    ) [5 _& E$ H& _4 `
  22. #define SIZE sizeof(TEXT_Buffer)                             /* 数组长度 */
    4 `( s( F/ W7 z0 f$ ?! F5 Y3 M
  23. #define FLASH_SAVE_ADDR  0X08070000                    /* 设置FLASH保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000) */
    . ^/ D# i/ E# V0 K
  24. /****************************************************************
    " O/ L" V! X& V1 q
  25. 函数名称:int main()5 q/ Y, e' j* ^
  26. 函数功能:主函数
    ; G  s& |! o) {$ q) x7 R
  27. 入口参数:无
    1 I. g( h6 r# ?$ u1 g
  28. 返回参数:int4 S/ l$ M/ ?, s9 M$ _
  29. 开发作者:-----
    - g9 v; C, L; G. k" s6 j! k) U2 y
  30. *****************************************************************/
    $ r  W. d) M5 B" ^2 x
  31. int main(void)+ a" [8 C( |8 W; M$ z/ d0 g8 B
  32. {  - k. b- S2 V8 B$ g7 ^: W: Z" E
  33. u8 key;$ V, S* W% l. Y9 i/ B
  34. u16 i=0;$ R# \" W: I  F& v
  35. u8 datatemp[SIZE];" w) o. A+ _0 j) I  r9 P8 l
  36. delay_init();                                                               /* 延时函数初始化 */   + V$ J' e0 E4 n
  37. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);   /* 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级 */* r+ b! g+ Y2 ~! o0 B& {
  38. uart_init(115200);                                                  /* 串口初始化为115200 */
    + Q  r; a: g8 d4 K! F2 E/ c$ }
  39. LED_Init();                                                           /* 初始化LED管脚接口 */, W5 b: F! c/ G0 Q- r
  40. KEY_Init();                                                                  /* 初始化KEY管脚接口 */
      k2 M, j7 ?$ u5 B
  41. printf("KEY1:Write  KEY0:Read");  
    9 }- d* b4 @( J7 h& i8 R
  42. while(1)( v  n) S2 X5 \( h8 x8 ]
  43. {. ~. Q- |: Z0 ]) t
  44.   key=KEY_Scan(0);                                 /* 0表示不支持连按 */
    1 h) ~) Q1 c  s$ S- f) R1 o
  45.   if(key==KEY1_PRES)                               /* KEY1按下,写入STM32 FLASH */
    6 C9 w: @4 ]- E2 V8 C
  46.   {  ^  {& A' f7 I/ E
  47.       printf("Start Write FLASH....\r\n");
    7 S! V" ?& J  c' F5 e7 [
  48.    STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);' v( Q, o" \3 h1 Y
  49.    printf("FLASH Write Finished!\r\n");            /* 提示传送完成 */: B' {2 j% e  J- E# q$ @
  50.   }/ k# h% o: ~$ i
  51.   if(key==KEY0_PRES)                                               /* KEY0按下,读取字符串并显示 */
    9 Z2 |) Y4 |* j: y
  52.   {: U9 r, v, h& |, k2 j# ?
  53.    printf("Start Read FLASH....\r\n");' ^# E! }" J- u8 V* b9 b
  54.    STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);
    ( t( G+ l8 a# s2 f+ {9 c
  55.    printf("The Data Readed Is:%s\r\n",datatemp);  /* 提示传送完成 */! A1 U" q! S2 @# l% R
  56.   }9 i/ Q8 |% T2 @' ]& [
  57.   i++;
    , j5 H/ s  ]- M* h; X
  58.   delay_ms(10);  
    : T4 b* b( @; o# U$ D# G- |
  59.   if(i==20)
    ( M- y7 L; J. Y; ]! V7 v6 ~
  60.   {) o  u" F  S- ]! g# P7 x. W
  61.    LED0=!LED0;                                                            /* 提示系统正在运行 */
    % H( i7 ]$ J6 r! m0 t6 p& ^- P& K$ f
  62.    i=0;
    8 F6 ?& [% ^; R' o- c
  63.   }     
    5 H$ R% E/ j1 f5 N7 d/ L
  64. }
    1 s) c0 j3 E% Q: E
  65. }5 c4 l! N/ v: l4 j7 D/ z' X7 E( r
  66. /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/
    % p! i5 B- @& Z) A, `3 L
复制代码
& W0 A0 f3 z$ i; M
3. 实验结果% y" j$ r. e6 ?6 m7 n2 x$ c2 p
: d+ q; v( ?2 D2 V1 ], h1 l0 M
20200915140953835.jpg
+ F( ?6 j8 V% a/ S& K! e9 i" E; s6 u; x/ K
: e5 Y, V1 K% @# v- l( N- ~————————————————+ H/ u  d% x& G  E& `8 K! p( b
版权声明:天亮继续睡- }! h: g# w$ E7 j5 w+ s4 N! Y  _# F
7 p. `) o! H$ ?7 [5 g! W

  P) Y( J' b; R* z# Q& ^3 V6 L
收藏 评论0 发布时间:2022-11-22 14:15

举报

0个回答

所属标签

相似分享

官网相关资源

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