一、Flash基本知识7 X, Q$ b* C" O$ J8 c3 U
1. Flash容量+ y- Q: K0 |% a
Flash根据容量大小可以分为以下三种:4 [* z, W, R+ H8 c# J, b
( M/ t8 D: P W- K
1、小容量产品:Flash大小为1-32KB(STM32F10X_LD)$ }2 T# t) r1 d
2、中容量产品:Flash大小为64-128KB(STM32F10X_MD), h+ q4 G; }$ |2 {& I. |8 I
3、大容量产品:Flash大小为256KB以上(STM32F10X_HD)# x( g$ ~% M2 D. D
2. ST库函数
5 T' r8 Y8 U$ xST库中对Flash操作主要提供了以下几类操作API函数:. V7 t1 s! A6 d- |" Y5 i
$ R) C: l+ g% N
2.1 Flash解锁、锁定函数# Q e+ K: U/ v- [2 i3 s
- void FLASH_Unlock(void); //解锁函数:在对Flash操作之前必须解锁
' A3 [; m. A! W - void FLASH_Lock(void); //锁定函数:同理,操作完Flash之后必须重新上锁
复制代码
, A) c$ x+ X8 f- e. I: m* w+ e% B2.2 Flash写操作函数3 \' M& c3 G$ ?& u5 v
- FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data); //32位字写入函数
2 N) {. _! G* _5 d6 Z, }$ Z - FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); //16位半字写入函数7 T7 _) `% N2 ]9 a! ^
- FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data); //用户选择字节写入函数
复制代码
4 c$ B) V0 ~& l! m. s; T" T# C注:这里需要说明,32 位字节写入实际上是写入的两次 16 位数据,写完第一次后地址+2,这与我们前面讲解的 STM32 闪存的编程每次必须写入 16 位并不矛盾。写入 8位实际也是占用的两个地址了,跟写入 16 位基本上没啥区别。 ~0 @* c; ]* K0 C+ t- G
( I d. h4 p' L5 p% H8 f
2.3 Flash擦除函数% j+ I0 m3 ]' v9 ~
- FLASH_Status FLASH_ErasePage(uint32_t Page_Address); //擦除一页. u- B) i' Y; c& P: X+ e
- FLASH_Status FLASH_EraseAllPages(void); //擦除所有页
复制代码
) B' e6 g5 w: _/ v* Q7 a& ?2.4 获取Flash状态
, T { P+ M' s- FLASH_Status FLASH_GetStatus(void);
复制代码 8 Z& q5 L' C9 s" u1 ^4 ~+ P
获取Flash状态函数,主要是为了获取Flash的状态,以便于根据状态对Flash进行操作。该函数返回值是通过枚举类型定义的,在代码中可以看到FLASH_Status类型定义如下(具体含义看注释即可):) I$ {8 |) |8 q# ?/ K: }
) {* }8 j- h9 b3 Y& g$ J! W+ N9 x- typedef enum {
/ l5 A, B) k/ w1 q2 |% B7 N - FLASH_BUSY = 1, //忙
# Z- F: K1 E, H: s' } - FLASH_ERROR_PG, //编程错误6 O d: `3 c; f, x+ I
- FLASH_ERROR_WRP, //写保护错误& J8 h4 V2 {, X7 v; [9 x# Q
- FLASH_COMPLETE, //操作完成
X' W& P3 ~2 _, v - FLASH_TIMEOUT //操作超时; v2 X" O3 H0 s# v4 S
- }FLASH_Status;
复制代码 & o" a/ {' n0 f B4 B- Z$ M
2.5 等待操作完成函数2 p3 l$ D8 H5 \7 {4 ~* K
- FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
复制代码
5 H3 h" |3 b% `! L& A9 P注:在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。
& E5 k+ U4 @# v& \& N4 _7 X5 H; _' U; \- Z0 X
二、Flash的写入和读取
7 |# R& t. Q0 g5 f( n此次操作Flash使用的MCU是STM32F103C8T6,在数据手册中,可以看到STM32F103C8T6的flash起始地址是0x0800 0000,而STM32F103C8T6的Flash大小为64K,可以计算出STM32F103C8T6的Flash地址范围是:0x0800 0000——0x0800 FFFF。这里选取0x0800 F000作为读写操作的起始地址,对于C8T6这款MCU,操作这个起始地址应该算是很安全的范围了。
, z) h1 m! g/ k
4 E' C* S" V9 ?* a4 v' Y* k1.Flash的写入& b" z$ H; i) K
根据ST库提供的上述函数,我们可以自己编写Flash的写入操作代码如下:( @6 e6 |/ U5 b: N$ s f
! c7 z' @4 k+ {' @0 g9 b. G
- #define STARTADDR 0x0800F000 //STM32F103C8T6适用
; E- l( V- E/ c& F+ r/ y, q/ G
$ d# d# Q- [2 ^- K4 j7 w% b- /*% ?; H% ~6 y6 r# o% ~# r3 W
- * Name: WriteFlashData
! S7 e# ~( W; T% x* W" Q/ z - * Function: 向内部Flash写入数据8 g. M h. D6 j$ A& Z5 N: Q$ G
- * Input: WriteAddress:数据要写入的目标地址(偏移地址)4 P F5 i' e7 r8 Q O* G, u8 Y
- * data[]: 写入的数据首地址( i" {& _. u, U+ u- S, B
- * num: 写入数据的个数
5 m3 o' j( `8 S6 h& r w) @ - */
2 x2 G) q) g) m& ` - void WriteFlashData(uint32_t WriteAddress, uint16_t data, uint32_t num)
( |5 M" W# P5 d" r7 X. ^$ I$ g - {3 N, R8 F6 N: s2 a: G( L
- uint16_t sign = 0; //标志位1 G2 h/ w& A1 P0 C/ [
-
' Q5 ?# b t. U7 a - FLASH_Unlock(); //解锁Flash L* s* Q5 F5 ~6 z6 v/ Q& R% i
- FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 清除所有标志8 e# z; P! A) Z, X9 N/ ^* R$ W
- sign = FLASH_ErasePage(STARTADDR); //擦除整页
5 D1 ~+ C }1 a5 t j - if(sign == FLASH_COMPLETE) //Flash操作完成
- h9 Y/ y( d& {! L - {' ^+ ^, e& [( g- B$ ?$ T
- for(uint32_t i=0;i<num;i++)+ d) l$ M: q3 W
- {, W1 g% }# v3 c+ L- U
- FLASH_ProgramHalfWord(STARTADDR+WriteAddress+i*2, data<i>); //写入数据
8 T3 _. _6 x# }" a' G, T9 U5 Z - }
6 a: k/ Q t8 p; f' X( y1 \ - }
3 z: {/ \( z: O+ K: F - FLASH_Lock(); //重新锁定Flash9 K7 x* i- [5 t
- }</i>
复制代码 3 g: P) W7 g2 M, G% K* H
2.Flash的读取
1 T8 s6 j, b8 \* W1 F# c: v, V. l根据ST库提供的上述函数,我们可以自己编写Flash的读取操作代码如下:
0 R: S8 K% m% L$ ]/ g' x; `* E1 H/ j
8 [' G* M1 U- ?) ]5 J, U- #define STARTADDR 0x0800F000 //STM32F103C8T6适用$ I, l8 ?+ C3 `% M/ B0 |
- /*- ]7 C% E! @6 a% ?" ?
- * Name: ReadFlashData
; n( m" }. A6 o6 t: t; b% D - * Function: 从内部Flash读取num字节数据
! L V; V4 s2 \& m$ `" K& F - * Input: ReadAddress:数据地址(偏移地址)
( O6 y( B8 C; w+ x( e - * dest_Data: 读取到的数据存放位置指针
9 p% N2 Q) \( H' b5 t2 d$ O - * num: 读取字节个数
, u+ I6 B- r' d$ d# Z5 W% @ - */
6 x* p' f9 m% ]; ]$ b" k% E - void ReadFlashData(uint32_t ReadAddress, uint8_t *dest_Data, uint32_t num)
3 o" l$ [2 p9 d* F+ L7 p - {5 x# C; J' U: d5 C1 W& _* c- [8 T
- for(uint32_t i=0;i<num;i++)) a. _* `# t) v/ l' y! {2 C: ~
- {6 J* c5 @ L& t- t6 {+ g
- <i> dest_Data=*(uint8_t*)(STARTADDR+ReadAddress+i); //读取数据
3 p# L8 ^# Q5 X, u9 `$ ^. G0 L - }
% z4 A. u! t9 d6 N3 N/ j - }2 d- v1 g# c" U, Q; g0 |9 c
- </i>
复制代码 7 o2 b* R# q/ [! O* i
) d$ b! i2 q1 @0 I: F5 h+ ^
|