一、Flash基本知识0 h$ }8 o8 r4 a) g4 D7 x9 P
1. Flash容量
7 S6 e' N: p6 {9 Q( NFlash根据容量大小可以分为以下三种:
: d! G' x3 `4 M1 O; ^3 \2 R) t+ M: h" x- f- o
1、小容量产品:Flash大小为1-32KB(STM32F10X_LD): }0 b0 B% {+ x6 h/ g) f7 x0 L; K
2、中容量产品:Flash大小为64-128KB(STM32F10X_MD)
: X/ G( v0 b, O3、大容量产品:Flash大小为256KB以上(STM32F10X_HD)7 h) y* ?2 h/ I1 H1 o
2. ST库函数
% e" M+ s ]# X, T$ `ST库中对Flash操作主要提供了以下几类操作API函数:1 l! R$ H6 {$ R6 h' V1 s. m! E7 N
9 f' S* o7 j$ @; O2.1 Flash解锁、锁定函数
& G% m `8 z7 w! m0 h$ K- void FLASH_Unlock(void); //解锁函数:在对Flash操作之前必须解锁( N/ g% | V* z, B& L$ _
- void FLASH_Lock(void); //锁定函数:同理,操作完Flash之后必须重新上锁
复制代码 3 E4 n+ D f" g& w A
2.2 Flash写操作函数; ?4 x8 b) W& B9 w/ I) S; S
- FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data); //32位字写入函数8 }5 u) T: F# i$ B) v* U
- FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); //16位半字写入函数
( W4 x/ R9 B, t1 e - FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data); //用户选择字节写入函数
复制代码
% C I k; [, `2 ]注:这里需要说明,32 位字节写入实际上是写入的两次 16 位数据,写完第一次后地址+2,这与我们前面讲解的 STM32 闪存的编程每次必须写入 16 位并不矛盾。写入 8位实际也是占用的两个地址了,跟写入 16 位基本上没啥区别。
: J2 b, j8 D6 ~, a& K/ y* e& M/ Y
2.3 Flash擦除函数
+ m& z$ \5 c! M( E( x/ }. |- FLASH_Status FLASH_ErasePage(uint32_t Page_Address); //擦除一页! J: Z$ k& \7 L- S# i: a! O
- FLASH_Status FLASH_EraseAllPages(void); //擦除所有页
复制代码 - ]1 Q" ~1 ~5 p( S8 k! r/ `# i2 m
2.4 获取Flash状态5 j; J# \/ D+ D9 p2 q& m9 r
- FLASH_Status FLASH_GetStatus(void);
复制代码
% W0 T ~- h. D( p: I) u获取Flash状态函数,主要是为了获取Flash的状态,以便于根据状态对Flash进行操作。该函数返回值是通过枚举类型定义的,在代码中可以看到FLASH_Status类型定义如下(具体含义看注释即可):
1 n: x9 E# O/ R+ z' s( s: \) x
3 y, n: ]+ m4 K/ _& }: y) L# @- typedef enum {. W' T* C- ?4 l2 m( i( K b9 J9 {
- FLASH_BUSY = 1, //忙
! e4 z8 y8 T( [+ @ - FLASH_ERROR_PG, //编程错误$ F( G( S. A1 @4 p7 a! o! H. n2 ?
- FLASH_ERROR_WRP, //写保护错误
2 x: Y4 t1 e5 M! L9 q4 b# e, F - FLASH_COMPLETE, //操作完成; v# P9 |$ S, U( {; N
- FLASH_TIMEOUT //操作超时
1 _0 X$ i4 }% h! k, D- z0 N+ j - }FLASH_Status;
复制代码
# m7 B3 X& ^. W: M2.5 等待操作完成函数
: O3 P4 @& u( x" E& q" p7 k0 a$ }- FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
复制代码 5 S: O/ Q6 l- E% L; S
注:在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。8 F# {/ w, q( V; Z' r! a0 i3 e
3 N" @; D1 ~, W( {二、Flash的写入和读取* N6 n9 E6 Z, T: \
此次操作Flash使用的MCU是STM32F103C8T6,在数据手册中,可以看到STM32F103C8T6的flash起始地址是0x0800 0000,而STM32F103C8T6的Flash大小为64K,可以计算出STM32F103C8T6的Flash地址范围是:0x0800 0000——0x0800 FFFF。这里选取0x0800 F000作为读写操作的起始地址,对于C8T6这款MCU,操作这个起始地址应该算是很安全的范围了。
5 a8 {5 o2 P2 p' c2 _! e+ ]3 o0 H" m0 d, V4 r7 T
1.Flash的写入
9 V$ [: A+ W% ?; p" k根据ST库提供的上述函数,我们可以自己编写Flash的写入操作代码如下:
0 K$ }) u; l2 D3 s
2 G8 g+ b" q. k- #define STARTADDR 0x0800F000 //STM32F103C8T6适用
, K$ g* Q% \# y2 R3 l1 W - ) N9 v- l G$ M1 E8 V9 J9 H
- /*
/ F* o/ G b% g* M# z( |( d$ } - * Name: WriteFlashData* u e1 N# t) ~) {$ U
- * Function: 向内部Flash写入数据9 |+ n- N8 g7 g
- * Input: WriteAddress:数据要写入的目标地址(偏移地址)
' H+ j: y( L8 Q" q7 | h - * data[]: 写入的数据首地址
8 O3 _; e: W8 G6 `0 k! L - * num: 写入数据的个数; U y0 _9 M. a0 G; N
- */3 f6 S! d) x9 Q" i$ O
- void WriteFlashData(uint32_t WriteAddress, uint16_t data, uint32_t num)" M" }! d7 S" l2 B& S) o" B2 s/ n
- {4 a' w5 I2 d! _/ y2 `3 J5 W
- uint16_t sign = 0; //标志位
( b- C" [# r: ]2 s& [& B$ @ - 6 @8 c- r3 D# P6 z9 Q6 p
- FLASH_Unlock(); //解锁Flash; h7 G, ^% x) X& A# g! S9 G5 q1 i
- FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 清除所有标志( Y" A$ I! N; ^; G* e7 }- O
- sign = FLASH_ErasePage(STARTADDR); //擦除整页
$ j0 F* F0 Z2 M5 x2 x - if(sign == FLASH_COMPLETE) //Flash操作完成
' m! y9 ] V( b, T( i/ n9 h - {1 m$ P' q7 I) u0 s0 M0 G- f( w4 d
- for(uint32_t i=0;i<num;i++)
" J F( p4 y5 K4 m$ ` - {# L; J/ l) B# n7 R U
- FLASH_ProgramHalfWord(STARTADDR+WriteAddress+i*2, data<i>); //写入数据0 L/ c; ^4 \; ^; w
- }
' V7 O# ~( W/ i% b - }
5 _& d" p$ U2 q- `9 z7 ~- \$ q% E - FLASH_Lock(); //重新锁定Flash* T8 D1 W5 q/ u
- }</i>
复制代码
& |, h8 B, Y2 r% r- q2.Flash的读取+ U- g3 Y0 \, [+ Q; ]
根据ST库提供的上述函数,我们可以自己编写Flash的读取操作代码如下: J' U) a: }6 o& D0 r
" G: x0 \( L8 L/ E3 F8 _6 ]! J- #define STARTADDR 0x0800F000 //STM32F103C8T6适用
8 H8 m2 |: d! j* M' x7 y - /*
- m( R J) L+ U, U5 g' e4 } - * Name: ReadFlashData& l0 ?3 H- Q3 r: A% z3 ]2 Z
- * Function: 从内部Flash读取num字节数据
# R" D" g, t& |$ k( z - * Input: ReadAddress:数据地址(偏移地址)" i% _) o2 s \
- * dest_Data: 读取到的数据存放位置指针
! |4 ?' }. T4 Y: H5 I! i. z! N - * num: 读取字节个数. e4 V( U$ r7 X: C. ~# p
- */0 N$ \; {1 l S/ A
- void ReadFlashData(uint32_t ReadAddress, uint8_t *dest_Data, uint32_t num)
8 M" D4 [9 G) {) d6 R - {
# g6 I2 |) g* P6 j - for(uint32_t i=0;i<num;i++)
6 Z7 w: |' j( {5 G2 N4 l5 \6 `% P - {
& H( M6 r- a+ p: x+ S y - <i> dest_Data=*(uint8_t*)(STARTADDR+ReadAddress+i); //读取数据
, `0 F% X o) q5 ] ?/ C - }7 N5 l, I+ {, y
- }
4 w1 ]4 Y- {2 Q. D2 | N - </i>
复制代码
1 j9 Z& ]) v' s2 n+ x9 e
3 k# ?$ ^* a1 h! q( A* \4 Y9 l# o |