主要内容:
( 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
% 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 |/ {
! ~& 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
& 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
. 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" @
' 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
$ ]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- void FLASH_Unlock(void); //检查 FLASH_CR寄存器的LOCK位//
% t* v, r0 o3 g% ~3 F - void FLASH_Lock(void); //检查 FLASH_CR寄存器的LOCK位//
4 x8 B) T9 _0 O - FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data); //对主存储区编程//
: `8 r# k0 v# R - FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); //对主存储区编程/// \4 E; n8 x* Q% ^' ]: z& r
- FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
! A' B1 j4 Q! Y4 Z$ j; s0 x0 F/ X - FLASH_Status FLASH_ErasePage(uint32_t Page_Address); //对FLASH_AR寄存器进行操作//- d1 i0 N$ t1 u0 d5 L' W
- FLASH_Status FLASH_EraseAllPages(void);. @1 }+ r2 V& x1 `5 s8 G
- FLASH_Status FLASH_EraseOptionBytes(void);
% @5 v: @: a5 J5 \, c2 t - FLASH_Status FLASH_GetStatus(void);7 q5 B o/ \" ~2 [6 G/ H' x
- 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( }
) `- c6 m7 C( r
同理,在对FLASH写操作完成之后,我们要锁定FLASH:$ @3 h7 s* \/ F$ Q: i& D
" x( R) I0 E3 J) l- \/ Z8 @
; 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
- FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
- J7 \$ R4 X$ X% W6 {0 a% r4 h/ m/ {: O2 f. _ - FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);* D: J; [; G6 x4 W3 i! c! ~
- 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+ ~- FLASH_Status FLASH_ErasePage(uint32_t Page_Address);6 S( y( m$ g2 U$ w& Y4 O7 P
- FLASH_Status FLASH_EraseAllPages(void);
; {5 U/ B" ~" f0 y% |2 { - 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- FLASH_Status FLASH_GetStatus(void);
复制代码
7 ^' m( |9 T. ~返回值是通过枚举类型定义的:) i; L/ w, @* ` D1 i4 T
! m: S% I* [% e+ S/ \6 K- typedef enum
' z( B' |. E+ T& l, M9 B - {
2 }5 c; [4 Y. B$ L - FLASH_BUSY = 1, //忙
/ H; d, t+ U3 `! ~9 s2 l - FLASH_ERROR_PG, //编程错误$ h6 I: Z; y+ r C0 W
- FLASH_ERROR_WRP, //写保护错误
: c6 w- [/ L( q6 T8 X+ }* C9 Q - FLASH_COMPLETE, //操作完成1 m3 C' I) M& k! }
- FLASH_TIMEOUT //操作超时
/ c3 @! {/ I3 K7 t% ]. c - }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- 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- u16 STMFLASH_ReadHalfWord(u32 faddr)# S1 e! R" j1 x4 t) Z! D9 R
- {
3 R# v0 M. ?" k9 z- l8 E1 r; I# c - return *(vu16*)faddr;
: Y# k! A) f! h - }
复制代码
( 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- /**
4 i' W! a/ {3 b) p/ e0 r2 C6 x - ******************************** STM32F10x *********************************
% \) v |6 ]" \ i+ T: o3 o - * @文件名称: STMFLASH.h
+ y A5 p$ z6 {: s6 _ - * @作者名称: -----( |$ r) C- E4 S7 }. h; J
- * @库版本号: V3.5.0
& U$ N0 H# p) A4 U3 R7 S5 [: r - * @工程版本: V1.0.0! Y9 [1 l- ^! d9 m7 D& @
- * @开发日期: 2020年9月15日2 B( e/ D3 g1 Z$ F" K3 d
- * @摘要简述: STMFLASH头文件: x( L( i* V* c3 h
- ******************************************************************************/
6 l* P- u; u5 b- C( F- {& D# M - /*----------------------------------------------------------------------------
8 I# c ~5 f& i2 U, b7 l - * @更新日志:
0 Y* i( L; w _! F - * @无
X" L8 w# X9 Z4 e - * ---------------------------------------------------------------------------*/
0 g# @& I3 h% G! ^- s( k& F - #ifndef __STMFLASH_H__
" N3 t6 F( g, ~" z4 M0 h" {& x - #define __STMFLASH_H__# c) ^' N4 z# N$ T! Q
- /* 包含的头文件 ---------------------------------------------------------------*/- P( {/ [; _, H& ^
- #include "stm32f10x.h"! B) D' r$ e' s9 \9 j7 A
- /* 常量 ----------------------------------------------------------------------*/
# N- H6 K# u0 { |# i" I) t5 } - #define STM32_FLASH_SIZE 512 /* STM32F103ZET6的FLASH是512K */% S+ b; o/ f4 n+ x0 [5 i
- #define STM32_FLASH_WREN 1 /* 使能FLASH写入(0,不是能;1,使能) */
* a# D- T( I2 G$ z* W$ L( b6 K - #define STM32_FLASH_BASE 0x08000000 /* STM32 FLASH的起始地址 */
+ y/ z! L$ }0 }9 U q/ v6 w. U+ j - /* 函数申明 ------------------------------------------------------------------*/! [$ V2 F* e/ U; e' _) {
- u16 STMFLASH_ReadHalfWord(u32 faddr); /* 读出半字 */ " C; p+ k' M" K+ Q+ Q
- u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); /* 指定地址开始读取指定长度数据 */
+ U6 k1 f6 h8 `4 I" U - void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); /* 指定地址开始写入指定长度的数据 */
- t) \# E& p* `( @. T - void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); /* 从指定地址开始写入指定长度的数据 *// I# Q& F/ Z# a i- g5 D& `
- void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); /* 从指定地址开始读出指定长度的数据 */ 7 N0 @% | r6 W1 u! Z
- #endif /* __STMFLASH_H */, _. L1 d! W( D% Q( b3 ]' D
- /****** 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- /**2 ], C, L! e! N$ q6 L
- ******************************** STM32F10x ********************************** i$ h; |! Y8 \& |9 r% s
- * @文件名称: stmflash.c) U3 Q5 A9 h3 I
- * @作者名称: -----) C% A4 e5 i8 W3 x9 ~1 q
- * @库版本号: V3.5.07 b! d9 `( u% E& U+ J7 u# |- B
- * @工程版本: V1.0.0
4 Y/ j3 s0 D+ N; J3 Y T - * @开发日期: 2020年9月15日
$ L/ |$ B R3 C - * @摘要简述: stmflash源文件8 b8 N# w! \. ~3 M A( q
- ******************************************************************************/
6 M/ N- A5 ~5 r# ]2 a2 v/ U! j - /*-----------------------------------------------------------------------------# L. c N# x* n# X i7 r
- * @更新日志:& t+ Y9 r, s; W9 r1 n9 M0 X+ n1 Y
- * @无0 o1 Q; D7 m( x9 d& f) a
- * ---------------------------------------------------------------------------*/
$ n' Y& A% p+ A5 U - /* 包含的头文件 ---------------------------------------------------------------*/
5 e4 `$ f. S9 m2 S0 H - #include "stmflash.h"
$ X% n& y; G2 @5 D - #include "delay.h"
6 w7 M1 F t3 J; Z; [$ ]+ m - #include "usart.h"
4 c* K# h5 w; j - /************************************************
5 k% @) f9 Y+ N; M b1 p9 w - 函数名称:u16 STMFLASH_ReadHalfWord(u32 faddr)
: s8 f: U. U: C3 i2 \3 [+ R - 函数功能:读取指定地址的半字(16位数据)
! e+ k5 ~6 ~/ y$ w' o4 ?" g - 入口参数:u32 faddr:读地址(此地址必须为2的倍数!!)6 l. \0 y2 C# V. t7 [5 B
- 返回参数:对应数据
. q2 \0 Q' @* [& x+ R - 开发作者:-----) O- e4 X5 K+ Z1 }# Q/ G9 \. }9 F
- *************************************************/8 a; f9 _% _ k" g
- u16 STMFLASH_ReadHalfWord(u32 faddr)" v8 B! l+ i1 f, q
- {/ l3 V5 w* o7 B
- return *(vu16*)faddr;
+ u: A! I6 |0 F3 r6 n - }
& S8 z) a0 e$ D& R: J - #if STM32_FLASH_WREN /* 如何使能写入,默认使能 */ # V% k3 W( w4 J2 r/ Z! o
- /***************************************************************************0 ?% R/ p+ X1 U. x) q! n
- 函数名称:STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) 4 H# p2 X% a' P$ p3 X
- 函数功能:不检查的写入
k' Q( ]: o! \ - 入口参数:WriteAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数 t( ], l% A( J- z: E9 A
- 返回参数:无 m( ]0 i" l# T' L4 [
- 开发作者:-----
+ v# f/ w$ ^7 P/ H8 m6 X - ****************************************************************************/
& k& \. D: J" i Y, U( ]4 R - void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
" h( a( x' r7 g+ h - {
4 C( T; e7 n Z; M0 ]1 K - u16 i;+ z7 m7 A/ l1 D# e* p9 N/ W6 D O: f
- for(i=0;i<NumToWrite;i++)* v2 J/ f- s% ~
- {4 F% f- n( }% s
- FLASH_ProgramHalfWord(WriteAddr,pBuffer<i>);4 z& a- Z( P; I. j- }# x `
- WriteAddr+=2; /* 地址增加2 */
- @! u4 G: t1 o/ } - }
7 i$ d! { b5 U( E - } . J& s3 Z" ?1 [# k8 u+ X* f
- /***************************************************************************: q/ V1 A! v# a# I6 ]/ q2 U
- 函数名称:STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
; B! z& R, e6 q - 函数功能:从指定地址开始写入指定长度的数据- v/ Y0 s! O+ V: T1 s
- 入口参数:WriteAddr:起始地址(此地址必须为2的倍数!!);, C0 b% c" k! W, @3 D5 P
- pBuffer:数据指针;
. e% [- `( [) U: ]& I - NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
M2 q' A# Z# z6 ]$ j, a - 返回参数:无4 u' e$ p% D/ J: ?8 Q- `* X
- 开发作者:-----
6 G8 x7 D9 _4 ? - ****************************************************************************/. k; ?, }( J6 R; p0 V
- #if STM32_FLASH_SIZE<256
& t9 ~/ e4 a: n/ ?: u - #define STM_SECTOR_SIZE 1024 /* 字节 */
# K2 Y3 |' Q( Y/ E3 j O6 ~6 y - #else
+ G) M% A0 d: r& B" ?4 O! p c2 R - #define STM_SECTOR_SIZE 2048/ \3 g) S( [! k# U# y
- #endif
' e6 O- r" h$ _% j - u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; /* 最多是2K字节 */6 z8 y4 X h, b; ~
- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) 4 X6 m2 E" t. e2 H$ j" d" T5 S# }
- {3 Z2 p* x5 {- f5 z2 g8 v
- u32 secpos; /* 扇区地址 */
5 B# V# A u; U6 L - u16 secoff; /* 扇区内偏移地址(16位字计算) */
- W/ ?& C9 ~) }' r. a; G) G& f7 d2 j - u16 secremain; /* 扇区内剩余地址(16位字计算) */ 2 U: B6 N: |, V- M$ P9 G$ r$ t
- u16 i;
* A, i8 |+ H9 s1 U+ K - u32 offaddr; /* 去掉0X08000000后的地址 */( ^( B6 F. {/ t+ g# l
- 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
- FLASH_Unlock(); /* 解锁 */
, `/ q# r' e- y& k1 C2 t - offaddr=WriteAddr-STM32_FLASH_BASE; /* 实际偏移地址 */
' W/ { j, W5 J8 u, e9 G, @ - secpos=offaddr/STM_SECTOR_SIZE; /* 扇区地址 0~127 for STM32F103RBT6 */
1 e/ ]2 C: O9 |! D - secoff=(offaddr%STM_SECTOR_SIZE)/2; /* 在扇区内的偏移(2个字节为基本单位.) */
! r0 Y }! A/ }7 e - secremain=STM_SECTOR_SIZE/2-secoff; /* 扇区剩余空间大小 */ / c4 [4 B+ V8 X; B7 v
- if(NumToWrite<=secremain)secremain=NumToWrite; /* 不大于该扇区范围 */8 k% N% A5 Q* z0 j; K, W' y) h
- while(1) 1 B; ]3 f- ` G; I
- {
: \# T4 \5 m3 l0 f5 b - STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2); /* 读出整个扇区的内容 */
! @6 l' P* L' y2 J# C% s: P - for(i=0;i<secremain;i++) /* 校验数据 */
$ N8 X7 j8 u; U: c - {
4 m3 e, b& [9 u2 Y& O. S3 { - if(STMFLASH_BUF[secoff+i]!=0XFFFF)break; /* 需要擦除 */ - I; o$ h5 I1 E2 P: z/ c
- }
1 T, F5 J- t5 Z. d+ y5 }- B - if(i<secremain) /* 需要擦除 */) r* L: R6 a4 Q9 o3 F
- {- f+ A# L! D1 u" F' Z8 W! h+ w
- FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); /* 擦除这个扇区 */
8 U( Y# l; s X S& U - for(i=0;i<secremain;i++) /* 复制 */
8 v9 X5 P& f# ? - {
% t( J* v1 Q5 t - STMFLASH_BUF[i+secoff]=pBuffer<i>;
* M) ^" p6 r/ d; k- e9 E - }
5 h' U1 {4 M1 @9 f - STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2); /* 写入整个扇区 */ $ _- i3 D3 O& t) ^
- }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain); /* 写已经擦除了的,直接写入扇区剩余区间 */ ! n# Q( K$ W& X# Y2 j5 m9 v9 x
- if(NumToWrite==secremain)break; /* 写入结束了 */
6 f2 _* k7 r/ A8 ]5 ^ - else /* 写入未结束 */* e [. V2 g( `/ @
- {
+ U7 Z S4 p! s6 B - secpos++; /* 扇区地址增1 */) Y4 ]( i+ f F0 Q6 K5 Y4 U
- secoff=0; /* 偏移位置为0 */
. c" b( s9 P5 z! z3 @/ K: \' h - pBuffer+=secremain; /* 指针偏移 */7 f0 g) m% a- t
- WriteAddr+=secremain; /* 写地址偏移 */ ( Q- p3 Y. W$ V1 B; T; g
- NumToWrite-=secremain; /* 字节(16位)数递减 *// M9 G2 ]. ^1 W# C1 u5 I8 C" v2 p
- if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2; /* 下一个扇区还是写不完 */
3 O# ]# O9 _# l' `8 V - else secremain=NumToWrite; /* 下一个扇区可以写完了 */$ b5 E( k3 T+ i1 H
- }
6 U _2 ^* P3 v - };
+ y6 F M% T; M+ f5 u6 R+ L" R' d - FLASH_Lock(); /* 上锁 */
2 z+ A9 W* G+ R, X" w1 p/ [3 W( X3 ^ - }
; o/ ~; n3 p5 s7 e; | - #endif
5 e1 t; q& }! j* G& c - /******************************************************************************; l4 Q2 C* h/ r5 ]3 ?) \% w5 G
- 函数名称:STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
4 S( B p5 t" E" t: {3 P5 A. c - 函数功能:从指定地址开始读出指定长度的数据
5 B p. I" U1 {9 g - 入口参数:ReadAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数/ S# N% G. _3 s+ c, p2 {6 c7 v: [
- 返回参数:无
( h' v( }$ p% F8 i - 开发作者:-----3 k' w N8 K; _1 {7 B$ E
- *******************************************************************************/
* W2 z+ R1 o2 O$ M4 b3 q - void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
& q7 O; L. m5 }- _ - {
_) }9 K: e3 @+ z; l - u16 i;
8 Q1 y8 E' U3 M* s+ A - for(i=0;i<NumToRead;i++)
$ M" o1 K+ |& v" Z - {; K {/ @0 v6 V! A# ~) H6 X8 ^
- pBuffer<i>=STMFLASH_ReadHalfWord(ReadAddr); /* 读取2个字节 */4 H' J0 t O P; e) \3 K
- ReadAddr+=2; /* 偏移2个字节 */4 a. v- z" l: [: U: Q% Q7 M4 k
- }
4 l0 _, L9 k1 w) S8 D - }4 c' r5 e: W* q) z
- /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/% p, T# l. A# D
- </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* \
- /**& G5 W l3 F7 ~( T' w
- ******************************** STM32F10x *********************************, C6 J8 H: i3 D
- * @文件名称: main.c: x: f! E! l7 U2 ?* ~
- * @作者名称: -----; A+ A# @$ ^% i$ T
- * @库版本号: V3.5.0/ X, I- q+ b. Q& F4 ]
- * @工程版本: V1.0.0
& P, A( u) A4 M& _8 E8 ~6 D( p - * @开发日期: 2020年9月15日5 [4 h8 \. R7 I. w6 O1 n9 y7 \3 T
- * @摘要简述: main源文件
; H( a) q4 K/ H% F6 T2 r; U - ******************************************************************************/, b8 _; K1 s/ ^; E7 ~/ a
- /*-----------------------------------------------------------------------------
6 {. x/ F/ @1 ?; r3 n1 {9 A" d - * @更新日志:6 i3 g4 h% P; P( i: u5 q
- * @无
. `/ N4 ?" i. t; M7 ]9 o - * ---------------------------------------------------------------------------*/6 n$ C/ `1 D% r& j; ?
- /* 包含的头文件 ---------------------------------------------------------------*/$ d: t6 u: `6 h4 h
- #include "led.h"% Q' ^- K* w( m4 J
- #include "key.h"0 g" n ]% G& f3 |2 s$ o
- #include "delay.h"
0 V2 d& X B' A$ E - #include "usart.h" 1 R) J+ l7 X2 Y1 l9 f7 d" r
- #include "stmflash.h"
; e5 E; l2 Z% N. l/ I - /* 包含的头文件 ---------------------------------------------------------------*/
6 d5 o; m* t$ A* n/ Y; m - const u8 TEXT_Buffer[]={"STM32F103 FLASH TEST"}; /* 要写入到STM32 FLASH的字符串数组 */+ s% R" g8 P5 [4 `& o/ X
- #define SIZE sizeof(TEXT_Buffer) /* 数组长度 */' P% q" L( Q7 L9 N/ L: h$ T
- #define FLASH_SAVE_ADDR 0X08070000 /* 设置FLASH保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000) */" ^3 k3 y1 ?+ X: Y# U9 t2 Q0 l
- /****************************************************************
% a0 T4 ^4 T, S O+ ]8 V9 X - 函数名称:int main()$ U1 o$ \2 z0 O E" ~2 Q4 z, x6 ?
- 函数功能:主函数0 w2 A( w( r+ o. ^. A1 ~& {+ k
- 入口参数:无
( h, z9 s2 [1 b" M. ?! Y) ^' ? - 返回参数:int# b7 u! X8 v2 Y: \: L7 n5 {0 n
- 开发作者:-----
% k6 C$ T) a1 ]) N - *****************************************************************/
r; P, }. z+ o# P - int main(void)
9 i. n# K2 d& x F# u - {
2 @6 m# ^7 U- q, G. M - u8 key;
8 z: S- q# J3 S6 |: o - u16 i=0;
5 g* G6 O5 u& h* v4 Y - u8 datatemp[SIZE];7 z7 K6 z' m A3 {0 g4 T' E
- delay_init(); /* 延时函数初始化 */ " N2 n8 I* j4 ] H2 C8 c+ R
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级 */6 Q0 U$ J4 z" k6 Q* F3 ~
- uart_init(115200); /* 串口初始化为115200 */
8 w: J1 e/ c, h5 H: M* g - LED_Init(); /* 初始化LED管脚接口 */
4 N0 C* \5 S }3 q1 E6 H: o& ~ - KEY_Init(); /* 初始化KEY管脚接口 */
& l6 v* Z$ K4 O5 T - printf("KEY1:Write KEY0:Read");
& g6 S4 P; y. Q9 F4 e. u, J- D - while(1)
6 \5 B% J8 n0 r- a [( r - { D7 m' K8 D8 U2 @
- key=KEY_Scan(0); /* 0表示不支持连按 */2 p, G8 h2 v2 b
- if(key==KEY1_PRES) /* KEY1按下,写入STM32 FLASH */! v$ Y1 A0 g, H! {
- {/ g1 |5 s9 G% ~# U" H# `4 ~; w
- printf("Start Write FLASH....\r\n"); R# W- M8 c, O& o: d2 H' I
- STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);- z1 Q. u4 `8 f1 i; a J8 N
- printf("FLASH Write Finished!\r\n"); /* 提示传送完成 */* a: H* p8 s9 [8 f$ a9 o+ ]
- }
+ ]& B& C8 c' l) R7 y - if(key==KEY0_PRES) /* KEY0按下,读取字符串并显示 */
C8 \3 ?; H. J, {( M - {
. t& S) r% y+ z% }- G+ [, S - printf("Start Read FLASH....\r\n");
7 a$ r% i- S6 Q - STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);' M" o' C6 b& C [* x
- printf("The Data Readed Is:%s\r\n",datatemp); /* 提示传送完成 */
5 t2 ?0 h7 e5 i% |0 I - }
- G5 \! ~8 f7 z' Q+ u" E/ x/ Q3 K - i++;
) E$ p9 j W5 E - delay_ms(10); , r- d/ `, @5 P5 t- `0 N6 B6 ]8 N
- if(i==20)
% ^" m; {3 Z( O4 ]: ] - {4 n8 J2 |4 P5 H" f: g2 `
- LED0=!LED0; /* 提示系统正在运行 */
( Q. `8 i! q8 g- @* U - i=0;
3 ?! m6 s0 a9 Q/ J: d+ N - }
9 a; G: a4 `4 ^/ P7 Z - } ; v, t) ~+ U5 `# V7 c( g
- }
5 L8 I% ?! O$ J! A q - /****** 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
; 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) ] |