一、Flash基本知识6 ` b9 x, ?* s: I/ k( v
1. Flash容量9 P8 D" F; Q* O8 j6 o5 \
Flash根据容量大小可以分为以下三种:6 S* b, ^2 n5 j0 U+ Y
0 Q! S1 z; d, R" |! `
1、小容量产品:Flash大小为1-32KB(STM32F10X_LD)* U, Q. m5 ?1 U6 n7 u
2、中容量产品:Flash大小为64-128KB(STM32F10X_MD)
# U$ v, I# s* J! o7 B3、大容量产品:Flash大小为256KB以上(STM32F10X_HD)$ e" g+ Q$ t0 ^# W
2. ST库函数! U7 ?( X5 W0 U1 T
ST库中对Flash操作主要提供了以下几类操作API函数:
; m: g' }9 r* d; U) D
4 [. W% i+ t+ Y7 k" n# }2.1 Flash解锁、锁定函数: E$ u8 P& [1 o5 p9 L$ b; T
- void FLASH_Unlock(void); //解锁函数:在对Flash操作之前必须解锁+ l7 N9 f9 G6 O. Y# r
- void FLASH_Lock(void); //锁定函数:同理,操作完Flash之后必须重新上锁
复制代码
! P# e1 ]) T- z" S% b0 P9 E2.2 Flash写操作函数
; g* _" m) w: d0 [+ ]- FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data); //32位字写入函数
/ {, \0 O) k$ Q/ }/ |( N - FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); //16位半字写入函数" I$ M5 V- d! R, a, K
- FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data); //用户选择字节写入函数
复制代码 ; P R* _* C" _) ~4 G. r1 _8 c
注:这里需要说明,32 位字节写入实际上是写入的两次 16 位数据,写完第一次后地址+2,这与我们前面讲解的 STM32 闪存的编程每次必须写入 16 位并不矛盾。写入 8位实际也是占用的两个地址了,跟写入 16 位基本上没啥区别。0 v) N. ?1 Q( M s2 b2 U H
6 Y, v) r" O0 K- M/ j
2.3 Flash擦除函数$ T) w6 P1 C* e3 x. m* ^
- FLASH_Status FLASH_ErasePage(uint32_t Page_Address); //擦除一页
$ Y; g' g: y6 t, I8 A - FLASH_Status FLASH_EraseAllPages(void); //擦除所有页
复制代码
; s- v% G- Y$ t6 V2.4 获取Flash状态/ U5 @3 A& E% k/ Y7 s
- FLASH_Status FLASH_GetStatus(void);
复制代码
- Y( Q& m% M/ q5 m! Z% O获取Flash状态函数,主要是为了获取Flash的状态,以便于根据状态对Flash进行操作。该函数返回值是通过枚举类型定义的,在代码中可以看到FLASH_Status类型定义如下(具体含义看注释即可):
, Q4 I, N5 K% Z
) U3 F4 d& @/ L" i. o' V0 }1 K- typedef enum {& b) ~2 g0 Q$ \
- FLASH_BUSY = 1, //忙
, x8 _: o5 H7 y; A - FLASH_ERROR_PG, //编程错误
8 E0 p: o4 j, L* a% e - FLASH_ERROR_WRP, //写保护错误
+ L7 \" h) j' Q& `0 ]9 y - FLASH_COMPLETE, //操作完成( D) e+ ?) u$ B, K: P! h F' U
- FLASH_TIMEOUT //操作超时5 }% U8 i' n6 B
- }FLASH_Status;
复制代码
3 m( R+ I5 g0 U2.5 等待操作完成函数
: ]4 B6 k0 _' a: p# @" ?- ?& K& V- FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
复制代码
+ x8 L2 g4 a R# y+ s注:在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。
6 S S7 E) `2 C- w5 [/ q: ~4 M, f f5 R7 p& L" \
二、Flash的写入和读取
; b4 N$ k2 L% t1 q2 s此次操作Flash使用的MCU是STM32F103C8T6,在数据手册中,可以看到STM32F103C8T6的flash起始地址是0x0800 0000,而STM32F103C8T6的Flash大小为64K,可以计算出STM32F103C8T6的Flash地址范围是:0x0800 0000——0x0800 FFFF。这里选取0x0800 F000作为读写操作的起始地址,对于C8T6这款MCU,操作这个起始地址应该算是很安全的范围了。
2 N' u7 t8 D) F( U1 ~# P5 i) H Y( J
1.Flash的写入5 q8 }/ E0 f* g8 t4 d( l8 d, |, H
根据ST库提供的上述函数,我们可以自己编写Flash的写入操作代码如下:
1 c, f7 L* _' Q3 y! @3 ~; a' g8 w7 T* b
- #define STARTADDR 0x0800F000 //STM32F103C8T6适用; Y; `. o% c/ [' m; \/ O6 R# k- d
( p8 ]0 ?& r' M0 w m* P% C- /*
* J+ d# W7 g \4 m1 [ - * Name: WriteFlashData& B6 I3 W3 p" j5 N1 M4 c
- * Function: 向内部Flash写入数据1 x7 H, }/ T6 V0 h# r( M1 S1 p/ K
- * Input: WriteAddress:数据要写入的目标地址(偏移地址)
* c ~4 P3 O* M( ` - * data[]: 写入的数据首地址
+ M( [% @0 ~4 S: H* U7 U" L E1 u: R ^ - * num: 写入数据的个数- H$ r5 V8 x" p6 A6 K2 Q
- */( H, Y& J0 O+ ^& f
- void WriteFlashData(uint32_t WriteAddress, uint16_t data, uint32_t num)
( `$ u h; ^) O: @ - {$ r& @ p5 @) `0 |+ }7 f) k
- uint16_t sign = 0; //标志位
% D' }2 o0 W1 L. F% w8 c -
/ D7 P9 D* w I* S& r - FLASH_Unlock(); //解锁Flash9 [, {" b* M7 ?$ y- ~
- FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 清除所有标志9 K! Z/ g7 W9 I$ ]! [" M
- sign = FLASH_ErasePage(STARTADDR); //擦除整页
% y4 F3 s! K- }- P" k l - if(sign == FLASH_COMPLETE) //Flash操作完成
& Y! F" I2 X& U7 R# D) B: r - {* {$ z$ Q; ^! p# u' P: P8 g
- for(uint32_t i=0;i<num;i++)
/ k8 O- x- d7 S: q6 S - {9 G( u. c) }4 A6 V7 u$ Q
- FLASH_ProgramHalfWord(STARTADDR+WriteAddress+i*2, data<i>); //写入数据7 U% q. l+ D0 u* ]) f2 S% Z4 W- L
- }
: c: ]% q/ e4 \. G- a& M - }
4 [) T* c4 P3 J7 k- B- | - FLASH_Lock(); //重新锁定Flash* \, r! m9 f3 h, o$ {
- }</i>
复制代码
0 }' _; R% s M: j/ D2.Flash的读取
2 ~. d8 l ^; g根据ST库提供的上述函数,我们可以自己编写Flash的读取操作代码如下: g1 ~ C$ i6 \# y P) l
; R1 @6 e9 |( D- #define STARTADDR 0x0800F000 //STM32F103C8T6适用7 {3 a8 |9 k' j2 t' {
- /*/ b, a- N/ y0 C0 V( A7 N
- * Name: ReadFlashData
" d: I) n8 h+ _& Y9 Z - * Function: 从内部Flash读取num字节数据! X, C& u9 H1 a
- * Input: ReadAddress:数据地址(偏移地址)
+ q9 T7 X8 F. } - * dest_Data: 读取到的数据存放位置指针1 G' m4 ]8 s1 E) d/ @
- * num: 读取字节个数
0 z0 w4 y! ]% b& ~) B6 v) F - */
/ O" \( o$ c) N2 Z! r - void ReadFlashData(uint32_t ReadAddress, uint8_t *dest_Data, uint32_t num)
/ Y" J+ Q3 i, Y( T - {
/ C+ ^4 [9 q1 l( M1 v - for(uint32_t i=0;i<num;i++)" A% u9 I7 A1 m' _% @1 w( y
- {
- n. q7 b7 X" A2 n0 Y2 @4 F" t7 l - <i> dest_Data=*(uint8_t*)(STARTADDR+ReadAddress+i); //读取数据
3 h5 q6 f7 J& C! V7 x1 A - }0 Y* o9 |; x E' G1 J2 _
- }
2 Q ^& d" B1 t% K* Z5 L9 a - </i>
复制代码
6 u- [4 W" {3 e6 X8 j
" M9 `/ T2 X3 q" O |