主要内容:
1 [2 @8 l) A% A1) STM32 Flash操作介绍;
7 @ p8 S! a- m6 N2 p2 @2) 寄存器和库函数介绍;
# p2 |' e- U& M) y3) 相关实验代码解读。
+ _' a% S! {9 i+ ~9 d# y0 {' p) w, a) M+ ?1 z
实验功能:利用 STM32 内部的 FLASH 来实现类似I2C通讯实验的效果(参考I2C通讯实验),不过这次是将数据直接存放在STM32内部,而不是存放在W25Q128。, c3 }5 j& S/ i' r
1. STM32 Flash操作介绍
/ M e B) m5 w- C& L1 }1.1 STM32编程方式
! j# Y1 ]" l1 Q5 D( k( O, ^( i, F1) 在线编程(ICP,In-Circuit Programming)
) ~" U) f1 @) z4 ?6 r通过JTAG/SWD协议(用JLINK或ST LINK下载程序)或者系统加载程序(Bootloader)下载用户应用程序(用串口下载)到微控制器中。
! k8 A1 C& M% [6 s$ ~* m2) 在程序中编程(IAP,In Application Programming)
: k% Y. ^5 ?8 B( g2 D; m通过任何一种通信接口(如IO端口,USB,CAN,UART,I2C,SPI等)下载程序或者应用数据到存储器中。也就是说,STM32允许用户在应用程序中重新烧写闪存存储器中的内容。然而,IAP需要至少有一部分程序已经使用ICP方式烧到闪存存储器中(Bootloader)(与系统加载程序的Bootloaer不同)。; e* S# v! I* @! W
3 |8 }7 Q, f- k
1.2 闪存模块存储器组织; ~. {/ |3 L2 y! j6 e- a. Z7 U
按照不同容量,存储器组织成32个1K字节/页(小容量),128个1K字节/页(中容量),256个2K字节/页(大容量)的主存储器和一些信息块等。战舰/精英板FLASH容量为512K,所以一共有256页(0-255)。( O, M; ~; X. x+ h9 H$ d) Y' X
9 h# ?8 z7 E; a5 T; k w
2 D& o3 G: ~( H* ?- m8 `$ V
- u$ g( E `& N, ~6 z. OSTM32的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等3部分组成。
3 {8 Y0 P U: ?+ K' c3 |* R! e1 f) {1) 主存储器,该部分用来存放代码和数据常数(如const类型的数据)。对于大容量产品,其被划分为256页,每页2K字节。注意,小容量和中容量产品则每页只有1K字节。从上图可以看出主存储器的起始地址就是0X08000000, B0、B1都接GND的时候,就是从0X08000000开始运行代码的。
4 \& C) _2 i+ E# H5 n" ]/ H- n4 d2) 信息块,该部分分为2个小部分,其中启动程序代码(系统存储器),是用来存储ST自带的启动程序,用于串口下载代码,当B0接V3.3,B1接GND的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能。
$ H3 V0 T y2 c, ?6 e+ ^$ n3) 闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。
) G% Z( B: B! C) {# A" [, z对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理。编程与擦除的高电压由内部产生。
5 T5 Y: _) f F: ?& Z# a" S& b0 I6 B R9 R( ` Z+ @
1.3 Flash闪存的读取
" V' \1 m( G, v% \2 O6 C内置闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相应的数据。例如,我们要从地址addr,读取一个半字(半字为16位,1个字为32位),可以通过如下的语句读取: m4 f4 ~. c0 F6 d
9 V( d8 O+ ?! V M) w" d
4 g- L3 d9 U6 ]7 P. S/ T+ L将addr强制转换为vu16指针,然后取该指针所指向的地址的值,即得到了addr地址的值。类似的,将上面的vu16改为vu8,即可读取指定地址的一个字节。5 G3 C- {! h8 h1 q* M8 g
, j8 k: @" t) P. @' `1.4 Flash闪存的编程(写)和擦除操作
9 M" ?% Y0 m+ PSTM32的闪存编程是由FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含7个32位寄存器,他们分别是:; T1 F; w1 `9 i
1) FPEC键寄存器 (FLASH_KEYR)
4 i1 G1 ?$ l( M, S% Q8 f7 ?4 P0 I其中FPEC总共有3个键值:RDPRT键=0X000000A5、KEY1=0X45670123、KEY2=0XCDEF89AB。% j% `' ^: F2 b" q+ [4 i
2) 选择字节键寄存器 (FLASH_OPTKEYR)
& s# e1 ?* Q' t$ T2 O" ?, @3) 闪存控制寄存器 (FLASH_CR)- E2 K) y" i8 z! w. h3 e6 y/ V9 y
4) 闪存状态寄存器 (FLASH_SR)* ~4 h" R! Z9 O0 [; R* P
5) 闪存地址寄存器 (FLASH_AR)
O" u) j. H* E' e8 ^, r: `% @6) 选择字节寄存器 (FLASH_OBR)
, y- u" [- [9 g$ B$ O7) 写保护寄存器 (FLASH_WRPR)
/ C: t! X7 s7 F% u( ~7 F5 m9 J6 w
1.5 Flash编程注意事项& Q" @8 I5 H k1 s8 R& _
1.5.1 STM32复位后,FPEC模块是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列到FLASH_KEYR寄存器可以打开FPEC模块(即写入KEY1和KEY2),只有在写保护被解除后,我们才能操作相关寄存器。 叫Unlock。键值不正确会产生总线错误。
8 V5 E5 R: f _6 ]3 s+ P0 @1.5.2 STM32闪存的编程每次必须写入16位(不能单纯的写入8位数据!),当FLASH_CR寄存器的PG位为‘1’时,在一个闪存地址写入一个半字将启动一次编程。写入任何非半字的数据,FPEC都会产生总线错误。
1 x" K$ ~, T; d2 ?$ m1.5.3 在编程过程中(FALSH_SR的BSY位为‘1’),任何读写闪存的操作都会使CPU暂停,直到此次闪存编程结束。. E4 `2 J9 O+ \: _- P' h% J
1.5.4 STM32的FLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(也就是其值必须是0XFFFF),否则无法写入,在FLASH_SR寄存器的PGERR位将得到一个警告。
) W* J- Q8 _3 k' _/ F
0 W) |1 E! n5 s1.6 STM32 Falsh编程过程
8 @( d% O5 F, p, g b1.6.1 检查FLASH_CR的LOCK是否解锁,如果没有则先解锁;
) m, r% Q- U9 i1.6.2 检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的编程操作;
( ?4 }! y" D2 D: ]6 G0 Q1.6.3 设置FLASH_CR寄存器的PG位为‘1’在指定的地址写入要编程的半字;
7 e" e# @" a& Q7 H: E1.6.4 等待BSY位变为‘0’;
- _+ I$ O; T% }8 e9 s$ i1.6.5 读出写入的地址并验证数据。 j9 r' p+ p$ a5 R$ Q( J
5 y' n! E, [4 \8 e+ l1.7 STM32 Flash擦除过程
1 j, s2 W W5 o fSTM32 FLASH编程的时候,要先判断所写地址是否被擦除了。闪存擦除分为两种:页擦除和整片擦除。
, q4 e }, g) [* ?9 q1.7.1 Flash页擦除过程# h# I$ J. W% `4 F) s
1) 检查FLASH_CR的LOCK是否解锁,如果没有则先解锁;
5 ^+ `: r. J4 G1 m2) 检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的闪存操作;
: A7 R7 p6 a+ U: R; q7 M6 H4 }3) 设置FLASH_CR寄存器的PER位为‘1’;5 Q9 c9 J& e# ?! y7 X8 ?
4) 用FLASH_AR寄存器选择要擦除的页;
5 B! ?+ U/ m+ f- b" J5) 设置FLASH_CR寄存器的STRT位为‘1’;! W8 \5 j1 o% K: Z
6) 等待BSY位变为‘0’;
) y o. w; P* S' e% J) Y/ J+ ]7) 读出被擦除的页并做验证。& G+ E# J) z9 L
1.7.2 Flash整片擦除过程( A; G% ?5 C% f
1) 检查FLASH_SR寄存器的BSY位,以确定没有其他操作在进行;8 B2 [4 H8 F0 @% u. n
2) 设置FLASH_CR寄存器的MER为1;
* j" n* {1 [: C3) 设置FLASH_CR寄存器的STRT为1;2 L- c6 j6 g' ^, t- A
4) 等待BSY为0;
v; Z% K" V; P9 ^2 i5) 读出所有页并验证。0 S) { }2 p6 B& ^
z9 {# k; w; G5 {6 R
1.8 Flash操作相关寄存器
3 j7 q3 i- P, q$ b) S1) FPEC键寄存器(FLASH_KEYR);, c7 `/ a) q/ |
, k5 A5 E, W$ J
, C1 B: @2 X! p( k5 V" b
; f: B/ R" V' ], Y9 h/ P* L- M2) 闪存控制寄存器(FLASH_CR);7 {2 B8 o: ` I" S9 P, s
3 u4 ]: s) y6 m! @: b& o
" ?4 C1 b# U4 ~, S2 V3 U/ L- Y
5 i9 c P- U9 p3) 闪存状态寄存器(FLASH_SR);
) |! s& y! Z! |+ R
* O7 `' |/ \ Z* j# q' C
! B6 {/ }7 r( p/ e4 q% J n! H! L
; w0 E+ T' |1 T- u8 X4) 闪存地址寄存器(FLASH_AR)。0 v$ Y& k3 F) j Z) X: ~
) L* y0 S! c/ B M: \3 x0 Y
6 Z1 [5 h+ z" ^5 x+ \
& a' }: A9 G! D' }1 W2 X8 G2 Q3 i) s1.9 相关库函数
6 p# v7 `9 i* ?6 ?2 o% e官方给出的库函数位于:stm32f10x_flash.c/stm32f10x_flash.h。
- \6 f% f0 s' u+ L; H$ G+ X0 M; z5 O: x
1.9.1 闪存操作常用库函数. [; c1 }* m& i; j5 q9 t) _
- void FLASH_Unlock(void); //检查 FLASH_CR寄存器的LOCK位//
+ J5 @, A" M& {$ x* ^ - void FLASH_Lock(void); //检查 FLASH_CR寄存器的LOCK位//& C0 O4 E; W& Z# v& S+ f, t. [
- FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data); //对主存储区编程//" d. @6 a: L5 s- l; D2 R
- FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); //对主存储区编程//# G& F! e# d z2 a; n2 M6 o# ^: ~' }
- FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
* T/ `, z/ u! l6 j9 e( J7 b - FLASH_Status FLASH_ErasePage(uint32_t Page_Address); //对FLASH_AR寄存器进行操作//& K" k; y1 b2 Y: C& E7 ^
- FLASH_Status FLASH_EraseAllPages(void);
) |4 ~# n9 A$ J, l/ f/ p) F - FLASH_Status FLASH_EraseOptionBytes(void);% H" G2 O& g# q; `, M+ u: q
- FLASH_Status FLASH_GetStatus(void);
3 q& k: {# {. c3 M# g* D - FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout); //等待FLASH_SR寄存器的BSY位变0//
复制代码
3 K. B4 S1 K& C/ i5 f- V1.10 Flash操作总结
8 x* _5 e! d! w- J1.10.1 锁定解锁函数+ |" O, i7 Y& ?& d8 e3 }6 a" w
在对FLASH进行写操作前必须先解锁,解锁操作也就是必须在FLASH_KEYR寄存器写入特定的序列(KEY1和KEY2),固件库函数实现:
: A8 Z; V" G+ q( M% y% c5 _$ S) O
# a( ?# o5 m% m+ y( x/ e7 v; n$ n: H( I* A% T+ v
同理,在对FLASH写操作完成之后,我们要锁定FLASH:! z& |" Z( V! i; o/ z+ m/ R- g
0 h$ U1 Q6 M" C2 H+ \/ P! p
& y" J) M) i: L- P1.10.2 写操作函数
# M% e6 ^) V) o& O; m' N6 S固件库提供了三个FLASH写函数:- D- ~1 V( w: J) P' ]( A
7 {% t: D" {6 {' r- FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);; r* c; C! D( M3 u+ E y4 p) w6 a" h
- FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
& c, A9 B$ F2 y8 _- t( i - FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
复制代码
* d# u& H5 k D& l顾名思义:FLASH_ProgramWord为 32位字写入函数,其他分别为16位半字写入和用户选择字节写入函数。这里需要说明,32位字节写入实际上是写入的两次16位数据,写完第一次后地址+2,这与我们前面讲解的STM32闪存的编程每次必须写入16位并不矛盾。写入8位实际也是占用的两个地址了,跟写入16位基本上没啥区别。 R7 ]: e8 \# R4 m
+ D" k! c5 v9 Q& i5 w1.10.3 擦除函数8 J! d& x1 z/ l2 o2 k: ~
固件库提供三个FLASH擦除函数:% Z* Y' o1 P, Q% B
1 b+ G& N3 e1 X0 s' u `
- FLASH_Status FLASH_ErasePage(uint32_t Page_Address);$ C8 L& p( W& q) m
- FLASH_Status FLASH_EraseAllPages(void);
) G2 A$ c1 A& Q. T9 g - FLASH_Status FLASH_EraseOptionBytes(void);
复制代码 . F0 Q3 h$ a+ v, Z
顾名思义:第一个函数是页擦除函数,根据页地址擦除特定的页数据,第二个函数是擦除所有的页数据,第三个函数是擦除用户选择字节数据。这三个函数使用非常简单。7 W% a) x: K/ t' ^' s4 z; I
! R: U" f1 z4 z" _0 W
1.10.4 获取状态函数# M; c( J7 V1 m9 f4 h8 c/ K
主要是用的函数是:
% h& ^; ~$ f! F8 p2 n' w8 R5 g4 h% }* J6 b6 i
- FLASH_Status FLASH_GetStatus(void);
复制代码 ) A6 k" o9 U% T
返回值是通过枚举类型定义的:1 J0 T7 ` G3 F% |# N( Y
6 h, K5 F+ | D2 j* N8 L- typedef enum
: J, H) ~' f8 q. H - {
5 x" {7 T/ H0 b) i - FLASH_BUSY = 1, //忙2 D8 }# Q. Y& U& ?- [
- FLASH_ERROR_PG, //编程错误8 u8 u | v4 j+ K
- FLASH_ERROR_WRP, //写保护错误) {. z3 t% V4 O! W
- FLASH_COMPLETE, //操作完成
- t5 I' s8 J' ~9 \( E - FLASH_TIMEOUT //操作超时
7 g( i ^0 J9 s; Q1 ^ - }FLASH_Status;
复制代码 6 L$ \2 U5 g0 e( V
1.10.5 等待操作完成函数7 ^8 w2 C: H9 P! G6 r: |
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。使用的函数是:
: s% ~9 f2 o0 a$ @4 l [- U) w. _5 l7 N8 o4 f
- FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
复制代码 % E" f, M1 a, c% D9 M. R$ A
入口参数为等待时间,返回值是FLASH的状态,这个很容易理解,这个函数本身我们在固件库中使用得不多,但是在固件库函数体中间可以多次看到。" q( x( q3 u, n- Q) r2 a; {
* a$ O Y2 B; _ j3 [5 k1.10.6 读FLASH特定地址数据函数$ k% K6 p- P' r$ T
有写就必定有读,而读取FLASH指定地址的半字的函数固件库并没有给出来,这里我们自己写的一个函数:' N; N7 u+ O2 i! a; O3 u' N4 s7 M
) l' y! d" b" I8 q- u16 STMFLASH_ReadHalfWord(u32 faddr)
/ x. K# @2 M( j - {
# O1 s7 d$ N; u" t - return *(vu16*)faddr;
7 g; [# t, g; F. `! p9 w3 W ?+ m- o - }
复制代码 + k, V# @: R+ F4 K; O/ w1 a2 R& n7 ~- ~
2. 相关实验代码解读
( `( j, r! {3 m) i2.1 stmflash.h头文件代码解读
3 a4 H( i' Y% e0 [- p$ r( }
1 b% p5 B B/ {0 x. S4 o1 j; i' n- /**9 u* W4 A+ c' Q! b8 C
- ******************************** STM32F10x *********************************
. o/ p, k1 G8 I& k3 m8 B/ J - * @文件名称: STMFLASH.h
6 j$ o5 t z6 I2 F1 e0 M - * @作者名称: -----
7 M) R8 x5 |, I! h- |6 e - * @库版本号: V3.5.0" y% O1 _- h4 D. p4 z) \4 x# Z
- * @工程版本: V1.0.0# P* G0 Q$ q& K/ r9 l: K; _
- * @开发日期: 2020年9月15日! G6 L& k8 i- g) r" O6 u
- * @摘要简述: STMFLASH头文件
. V6 z) k8 N0 q5 y! J. X - ******************************************************************************/* H9 f9 L) H: B3 d& F
- /*----------------------------------------------------------------------------' ^; V$ b0 @8 x1 d9 Z
- * @更新日志:
6 @. j1 p( H8 a) S7 g! z - * @无3 \8 \4 K! ^/ D4 A( b. U
- * ---------------------------------------------------------------------------*/0 `8 n- }2 ~7 {( d5 S! d
- #ifndef __STMFLASH_H__
t# k l ^. S4 M. H) c; n - #define __STMFLASH_H__
8 h- w' |) ?* U; g% o( A$ e7 _ - /* 包含的头文件 ---------------------------------------------------------------*/' u7 K: m c7 L' w- R
- #include "stm32f10x.h"
: J* d9 {- Q9 }3 P) f% a( p g+ w - /* 常量 ----------------------------------------------------------------------*/
: r/ H0 c2 W( K, v5 @; `: C6 |0 I/ N3 J+ o - #define STM32_FLASH_SIZE 512 /* STM32F103ZET6的FLASH是512K */
! `0 J" |% A' H3 {$ K( } v - #define STM32_FLASH_WREN 1 /* 使能FLASH写入(0,不是能;1,使能) */
1 {$ h8 o+ t b6 z - #define STM32_FLASH_BASE 0x08000000 /* STM32 FLASH的起始地址 */7 k& x V- j" f$ w. e
- /* 函数申明 ------------------------------------------------------------------*/4 @/ T$ F# p9 o8 ]6 }1 V. s# B
- u16 STMFLASH_ReadHalfWord(u32 faddr); /* 读出半字 */
* B1 I5 ]- a& V# ^: n - u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); /* 指定地址开始读取指定长度数据 */
- a0 U0 h7 h& ` - void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); /* 指定地址开始写入指定长度的数据 */0 r6 l% @0 Q' [% y3 o. [. c( r1 Q
- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); /* 从指定地址开始写入指定长度的数据 */* ]; [+ D* r: C! k7 T- N0 C
- void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); /* 从指定地址开始读出指定长度的数据 */
% M# S; `# g& c - #endif /* __STMFLASH_H */
+ E6 @8 f& V' B. y0 X4 @ t" ` - /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/
复制代码 4 K2 B! Z( J. U$ ]: a: N
2.2 stmflash.c文件代码解读
# O! Y' ^, Y9 r& r
* ~9 T, i' [' T& v% A3 S- /**
7 n8 c) }0 G7 T1 w' | - ******************************** STM32F10x *********************************
% W( z7 E+ D( r4 [, a - * @文件名称: stmflash.c! F& k: w$ o' D# C4 h2 G- ]
- * @作者名称: -----1 R: Q E/ v* m( L1 b
- * @库版本号: V3.5.0 C( l3 t' x+ m. J
- * @工程版本: V1.0.0
* H* l8 N! d' Y) w - * @开发日期: 2020年9月15日. F0 L7 y6 ^: v
- * @摘要简述: stmflash源文件
3 R, Q' c# ~3 ^' P- w4 k Y0 [" m - ******************************************************************************/
{4 a( {( s1 p% Y - /*-----------------------------------------------------------------------------, q( _; _, I% l4 x1 t# o# [) ?
- * @更新日志:
- q/ |5 j! n- h/ k6 _5 S - * @无
- d0 n, b( \( z s/ p- d - * ---------------------------------------------------------------------------*/
0 `% h! x: l; B# t& B4 h - /* 包含的头文件 ---------------------------------------------------------------*/9 ~- I b7 Z7 b1 ]1 `5 q$ ~2 }, |
- #include "stmflash.h"+ n+ h. s8 K! G2 U. N
- #include "delay.h"% t4 Y {8 |9 V: |5 h
- #include "usart.h"
2 J% v% v7 W. d7 E3 M8 J! o - /************************************************1 z4 O. }: ?0 A/ `
- 函数名称:u16 STMFLASH_ReadHalfWord(u32 faddr)
! v' Q/ ?7 {1 {. o J; C - 函数功能:读取指定地址的半字(16位数据)
4 M& `$ q/ V. A! Z# p' y; } - 入口参数:u32 faddr:读地址(此地址必须为2的倍数!!)
% r; A: g1 {; F+ n- l - 返回参数:对应数据
7 d* ^0 B" v- V2 n3 s! i - 开发作者:------ {4 l% H w j+ z; x9 x, P; M- X
- *************************************************/+ O' j/ z$ s" f& w
- u16 STMFLASH_ReadHalfWord(u32 faddr)
7 q% M, x7 ]! r1 J% I4 x8 u" o - {
- D9 i j, q2 e7 y3 e - return *(vu16*)faddr;
: T4 C3 ?" [( |* s - }0 t3 N5 N* ?/ k; P7 |; G, e% K6 S
- #if STM32_FLASH_WREN /* 如何使能写入,默认使能 */ 1 G# |! n8 t$ [) H- l
- /***************************************************************************
# T( R% O! H+ S; i3 X; z - 函数名称:STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) 4 |/ M9 m! Z9 m% d! L
- 函数功能:不检查的写入) P9 ~6 K/ X' }' K! O1 a8 q8 d
- 入口参数:WriteAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数
2 s/ m* U/ L' T% E4 N( ]+ e g - 返回参数:无
0 _4 J. E; o7 O( F" T [ _1 `4 @ - 开发作者:-----
9 ?. ~1 w [- f# d7 Z - ****************************************************************************/* ?1 X* u, t) ~3 N& {
- void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
! @2 b- }: \5 B% K7 I- g - { 0 W7 s$ v+ t3 {0 J1 B& F; ?
- u16 i;: E& Z4 E5 ^8 @2 G; x
- for(i=0;i<NumToWrite;i++)
% e: q8 v+ ]1 }. |3 S& g9 R$ Y - {0 D! E U! D/ m- w- d3 t
- FLASH_ProgramHalfWord(WriteAddr,pBuffer<i>);% u6 q2 X- | o3 N2 h6 f" |
- WriteAddr+=2; /* 地址增加2 */
6 e" ?' s0 E& H+ T+ h0 Q, d - }
+ G$ }* K p5 e6 { - } 5 g% d S. \3 O6 a) _
- /***************************************************************************6 j/ Z M4 v8 h z: G/ }( i9 Y
- 函数名称:STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
3 m6 h6 h7 R6 _9 a4 m3 e - 函数功能:从指定地址开始写入指定长度的数据6 x; R7 m( k6 D" c* P$ V+ a
- 入口参数:WriteAddr:起始地址(此地址必须为2的倍数!!);! `2 _% `5 S$ V: S
- pBuffer:数据指针;
$ }7 D0 \6 T+ ^5 q& J - NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)/ m ^' h, A( S, ^* r3 i8 D: U
- 返回参数:无
* d5 H S& `7 u7 O9 [5 l - 开发作者:-----
Q: D6 U& D. M6 e - ****************************************************************************/
9 F9 v! I- a7 | X8 C6 M - #if STM32_FLASH_SIZE<256
! K$ a) ^ V% \7 h2 ?. `8 m4 l( a1 L+ ?$ m - #define STM_SECTOR_SIZE 1024 /* 字节 */1 o- \- r J1 d) r( B; k; s3 v* D
- #else
9 G& |! s) r7 V$ E. w; { - #define STM_SECTOR_SIZE 20484 l& L X$ c. v
- #endif
% p; y2 }" G+ `2 W$ v8 Z - u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; /* 最多是2K字节 */
6 o9 h# {7 {& e- B) l3 ^ - void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) 3 w! W) c( v% B
- {3 _! ?+ o' N* Y: @. [
- u32 secpos; /* 扇区地址 */& [9 Y0 }1 n. B% `5 Q+ T m
- u16 secoff; /* 扇区内偏移地址(16位字计算) *// {& G- S9 S+ E* y+ o% v, h
- u16 secremain; /* 扇区内剩余地址(16位字计算) */ 8 r/ x) G v- x2 E; i. r9 Y
- u16 i; # V$ S0 j' Q0 R& g/ H# ]
- u32 offaddr; /* 去掉0X08000000后的地址 */
) H; M$ H+ ~1 p' h0 } - if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return; /* 非法地址 */, u+ D7 D! y% s8 k7 y
- FLASH_Unlock(); /* 解锁 */7 y* }; C! X$ _2 A `; T
- offaddr=WriteAddr-STM32_FLASH_BASE; /* 实际偏移地址 */
4 w6 R3 |# a9 f0 [! F& W2 N; C - secpos=offaddr/STM_SECTOR_SIZE; /* 扇区地址 0~127 for STM32F103RBT6 */9 k2 L: Q* O: `0 a
- secoff=(offaddr%STM_SECTOR_SIZE)/2; /* 在扇区内的偏移(2个字节为基本单位.) */2 ?* q. t, @1 S7 u) m3 E' z
- secremain=STM_SECTOR_SIZE/2-secoff; /* 扇区剩余空间大小 */
; x3 }/ [3 s; h* Y U/ R: t% l7 @ - if(NumToWrite<=secremain)secremain=NumToWrite; /* 不大于该扇区范围 */- D0 K4 n7 d3 \$ t( B. F& l
- while(1)
6 F( G( ?; U4 V% e( E - { - Y4 u2 R, |8 ~" h C6 o
- STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2); /* 读出整个扇区的内容 */7 j! Y1 F: z- Y6 x
- for(i=0;i<secremain;i++) /* 校验数据 */
" n1 I4 U+ y1 c, w - {
A) i9 L: v2 Q5 r$ f0 F$ ]2 s - if(STMFLASH_BUF[secoff+i]!=0XFFFF)break; /* 需要擦除 */ & M+ p3 d% t! T2 Y
- }
' ]* N% m9 ], ~: Q# [6 ^3 F - if(i<secremain) /* 需要擦除 */3 K* t7 P) C. f1 M( z! ] T
- {
( Y$ _- ]1 R( \, u. J - FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); /* 擦除这个扇区 */3 T% Q( S4 o3 X! H6 e& C K
- for(i=0;i<secremain;i++) /* 复制 */# ` y2 B' }% b$ }" z6 ~1 R# \
- {
' F9 Y1 L# H* Q: r# c5 a; \5 h - STMFLASH_BUF[i+secoff]=pBuffer<i>;
v; I* v' a; P+ l9 j' v7 T: ~- {- ? - }7 g+ ]3 t2 v( {( ~
- STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2); /* 写入整个扇区 */
8 t8 T4 ?6 E" d* {! I$ S) q3 m - }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain); /* 写已经擦除了的,直接写入扇区剩余区间 */ 2 f {3 A* S2 B- b3 c' Z2 w. e4 M1 D& M
- if(NumToWrite==secremain)break; /* 写入结束了 */# b' s( P, p2 _$ F3 F/ \2 b; ]
- else /* 写入未结束 */2 Y2 ]7 g" Y' l! x, ^
- {& `# z! R% z* o" a
- secpos++; /* 扇区地址增1 */2 R8 D& m* P: H! f! p0 R0 _/ q
- secoff=0; /* 偏移位置为0 */ . o0 [5 u7 j+ P# Q
- pBuffer+=secremain; /* 指针偏移 */
8 {* }6 e6 n1 f$ B( W0 C% Q- B - WriteAddr+=secremain; /* 写地址偏移 */
( q% S& r& p, Z/ t5 V - NumToWrite-=secremain; /* 字节(16位)数递减 */ T6 p2 M6 {* z- c0 h/ Y* P) ]
- if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2; /* 下一个扇区还是写不完 */' x6 Y6 @* G5 l) d% t0 l2 z
- else secremain=NumToWrite; /* 下一个扇区可以写完了 */
, Q9 s, `7 W% F - }
9 ~; f3 A1 u2 N# i0 o) Z- ?% t! p - }; ! `1 N5 X6 [, m( }
- FLASH_Lock(); /* 上锁 */
4 ]5 w# k; {& ^& M r - }( \. F M( Z; M9 ] ?
- #endif# M% U/ |) ~. @% n6 c& h
- /****************************************************************************** D/ ^) F7 G2 Y" z" m) O
- 函数名称:STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
2 ~ S- f) E8 ^ N2 \ - 函数功能:从指定地址开始读出指定长度的数据1 d( H$ P8 ?8 g7 L
- 入口参数:ReadAddr:起始地址;pBuffer:数据指针;NumToWrite:半字(16位)数
* J- Z$ w# a) Q - 返回参数:无" U" M0 l6 t3 v3 A8 e* z
- 开发作者:-----
4 g0 J2 }6 a0 |7 e( ] - *******************************************************************************/
( A/ k6 _1 @: i2 D0 D$ { - void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead) . S6 S. g% `1 ~
- { q6 d! \* {/ N Z% a
- u16 i;$ @$ H9 Y5 x4 `" v# I; ^* H- ^
- for(i=0;i<NumToRead;i++)( |1 E' |5 J d# R" Q0 g+ b8 n* g
- {6 |+ L6 F5 Z& w! Z; }
- pBuffer<i>=STMFLASH_ReadHalfWord(ReadAddr); /* 读取2个字节 */
, r; ?$ ]8 h7 t* y1 |2 _2 h - ReadAddr+=2; /* 偏移2个字节 */+ ?5 \# [* ?! }5 a9 ^; k
- }# Z) c0 V; {, F: H4 _' W
- }
" ?: i* r& p# @8 E) K! q1 Z2 ] - /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/' S4 C" {* o Y6 x O, o
- </i></i></i>
复制代码 6 K E, }& z' g6 \, R& W: }$ t
2.3 main.c文件代码解读* e) n3 \" D0 e/ T. B/ e# F) k
1 U8 J* S0 o7 {( J/ A/ s- /**
: Z( u7 X7 J" |/ t1 Y3 | - ******************************** STM32F10x *********************************% z5 ?4 E, J: ^4 u( x
- * @文件名称: main.c
4 @0 \8 t" L* `- G - * @作者名称: -----/ y, L/ I! q: G! j
- * @库版本号: V3.5.0$ F( Y9 P0 k0 E& h
- * @工程版本: V1.0.0
a: z! h$ H; g' \1 g) _ - * @开发日期: 2020年9月15日% m1 z# P- X6 J6 l- G# Y+ J
- * @摘要简述: main源文件 `6 z% y3 I) k) K) m3 `- n
- ******************************************************************************/2 T3 l6 w% }4 Z* z. Y
- /*-----------------------------------------------------------------------------
9 q! F8 L, a' z( @ - * @更新日志:3 ~" O, j0 J7 `& Z7 a8 n5 r# p2 ?
- * @无
: E' }0 f: q& U+ u* c3 l8 s2 f* Z - * ---------------------------------------------------------------------------*/3 f7 y9 y6 p' T+ h) v& o) Q
- /* 包含的头文件 ---------------------------------------------------------------*/
" r( y1 L- _# g; D i* i& k K - #include "led.h"4 Y: e7 v" ` a$ c8 O. ^' o0 C7 B* {
- #include "key.h"- m% ~0 W( J2 ?7 L, O2 b2 B2 ?
- #include "delay.h"
, A. E7 n6 B% Z* I; o( I; a - #include "usart.h" 0 M' u* m, _, }: ^6 b. X
- #include "stmflash.h"6 @ g- _6 I& s7 w9 E
- /* 包含的头文件 ---------------------------------------------------------------*/0 X/ W4 G! t0 X6 c
- const u8 TEXT_Buffer[]={"STM32F103 FLASH TEST"}; /* 要写入到STM32 FLASH的字符串数组 */3 O$ M$ a& z2 O: k* v
- #define SIZE sizeof(TEXT_Buffer) /* 数组长度 */
% @5 Z2 H& e' W4 m' | - #define FLASH_SAVE_ADDR 0X08070000 /* 设置FLASH保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000) */
4 ]3 U" @/ M0 a- S% i - /****************************************************************
% f* ]+ K0 U4 V& X. z2 x+ a; S - 函数名称:int main()5 N; L8 }& F" d+ f. _0 s
- 函数功能:主函数5 s" l ]. B2 R
- 入口参数:无
+ ?4 o3 `! c( |( z' q - 返回参数:int# ^& y! `" }" u. _8 M/ J' Y
- 开发作者:-----4 i$ l. Q0 ?$ W \* O2 P6 h: w
- *****************************************************************/
( F" v. r( O- q - int main(void)6 b. w" P" n, C8 K
- {
, X8 b* O& x$ F/ v J+ |. c- y( V - u8 key;: N7 j( d+ l+ z u
- u16 i=0;
7 C; o- T( e& p1 L4 h7 U - u8 datatemp[SIZE];: ^+ k1 ^$ @+ p6 p7 @+ q% Z
- delay_init(); /* 延时函数初始化 */ 6 V# K+ p' `6 m) g) q* {/ Y v
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级 */0 U4 q0 H) S- |8 v7 p1 w
- uart_init(115200); /* 串口初始化为115200 */
, M+ R- D1 l9 T H' T/ x - LED_Init(); /* 初始化LED管脚接口 */
% U8 f2 x' k( Z8 d' m& m - KEY_Init(); /* 初始化KEY管脚接口 */
7 k; N, f- K; U$ g: u - printf("KEY1:Write KEY0:Read"); 2 c' Q# | [# {1 m! P
- while(1)* q0 g0 l) {5 ?9 V8 {
- {
1 D8 c2 d, a" j2 `" d3 F - key=KEY_Scan(0); /* 0表示不支持连按 */
' y6 [9 L2 e2 t/ i( P: s' S" k - if(key==KEY1_PRES) /* KEY1按下,写入STM32 FLASH */' m6 A, T( ]) V* _! e- M4 {
- {) y% X0 Y% I: y. S' U6 T
- printf("Start Write FLASH....\r\n");
- G3 I2 M+ M: p4 g4 m1 i6 k - STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);4 M% g8 k8 a( @
- printf("FLASH Write Finished!\r\n"); /* 提示传送完成 */. A: e: }, x8 |: W
- }0 X) n( u9 N7 N- z
- if(key==KEY0_PRES) /* KEY0按下,读取字符串并显示 */
4 v |" x6 S5 G& o7 K2 s6 c - {
( | g! j/ C" F" K3 P - printf("Start Read FLASH....\r\n");
8 p$ |# b+ D. [8 | - STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);& Y% a( C- C& G4 v! z; i! x$ F
- printf("The Data Readed Is:%s\r\n",datatemp); /* 提示传送完成 */
. y/ }% L/ v; K1 } - }, V" n8 u. ~* t( [# x; K+ B3 Q
- i++;0 V' H9 R4 D, b$ A4 _
- delay_ms(10);
" D. Z: M* F% H - if(i==20)
; K3 n6 m4 v# \4 Q4 t) P - {
D, ? u) ^5 K. {, ]* ` - LED0=!LED0; /* 提示系统正在运行 */$ E$ X g8 a. I K
- i=0;0 H" `5 t1 P1 W( m1 o
- } + ]4 P p0 U; n; _. i
- }
4 U4 z$ w" ~. E* ?1 T - }
2 S0 Y0 o$ u: Y! ]4 g - /****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/
) o& g$ {$ z7 e5 m
复制代码
# v2 h8 m0 X6 S, b2 C- q3 r3. 实验结果: `" D7 D- x3 |
. a& T. ?- |* l: v" N
! B5 Y6 p! l5 v; x. V' E
: B# c3 z. d/ x) L————————————————
8 t/ e6 o5 d/ H6 X# S版权声明:天亮继续睡! T! K, M" t) @' i& e* K# J& ^
/ w1 H& m0 ~; n2 v* n! Y! ^
9 W1 ]2 Z2 @9 p- f( e0 S+ {( i( g |