一、芯片FLASH容量分类: 
; S& Z( h7 Z( Q. | G3 O: |
可以看到我们今天介绍的这款芯片的flash大小是64K的,网上也有人说它可以支持到128K,但是官方给出的解释是前64K是有保证的,后面的无法保证,所以想要使用的小伙伴需要慎重。% N+ k# s' F! g4 X
现在芯片的flash大小我们知道了,下面就可以看看这个flash是怎么划分的了,通过芯片数据手册,我们能看到今天说的STM32F103C8T6是属于中等容量的设备。 
既然是中等容量的设备了,那我们就来看看flash划分吧,在STM32的闪存编程手册中有这样一段话:按照不同容量,
$ Z! [( [" [2 r) @* W$ H$ b4 h存储器组织成:5 B* w8 B& z6 \4 ]! d. w/ ^/ w& i
32个1K字节/页(小容量)0 G6 x, i' r8 T! U8 [. L
128个1K字节/页(中容量)2 |' F |5 |5 j; q8 S
256个2K字节/页(大容量)' j/ }6 g" V Z. f
这段话怎么理解呢,就是告诉我们小容量的设备(内存是6K和32K)的设备是由1K字节每页组成的,
0 }9 ~1 N2 {$ I! O' K6 L) ]1 N中容量的设备(内存是64K和128K)的设备是由1K字节每页组成的,
' Y3 L5 e/ v9 n0 a/ t大容量的设备(内存是256K、384K和512K)的设备是由2K字节每页组成的,5 t6 F% l. Z/ P* Y2 W5 E0 M' L
举个例子吧:0 |; P0 n/ M% t0 h! I6 ~/ q" [ M
一个芯片的存储容量是64K,这64K是什么呢,就是64*1024个BYTE,一个BYTE是由8位0或1组成的,(比如0000 1111 这8个二进制数组成了一个字节,用十进制来说就是15)
, L+ d, a+ e1 K# w5 d小结一下:64K的flash可以存储64*1024个字节的数据。
6 i( T7 `# ~2 {8 c, \4 w咱们继续说,这64K的数据怎么划分,存储是按照页为单位进行存储的,一页1K的容量,也就说一页可以存储1024个字节
0 ?: {8 Y% Q+ M$ n6 [0 Q* l一共是多少页?% }* m% G7 X$ N" h6 r: T! o6 Q$ B
答案是:64页,我们看一下官方是不是这么说的 
在闪存编程手册里确实是这么说的,所以我们刚才说是64页是正确的 + J- v7 e' F! _4 c8 Q; S
二、 读写步骤:
& l9 h' |2 }* Q! T9 ^上面我们知道了芯片是怎么分类的,下面我们就重点来讲解一下芯片是怎么读写的。) k# {8 f& Q: s7 ^
内部flash我们参照HAL库或者标准库,直接调用ST公司给我们封装好的库进行编程就可以了,这里我用的是标准库,有兴趣的小伙伴可以去看看HAL库
9 f. _3 b @+ R, w9 U% s是不是有小伙伴会疑问什么是标准库,什么是HAL库?
% L3 L3 D3 e. o. d. A在这里给大家解释一下,这两个库都是ST公司,直接把寄存器封装成函数,供大家直接调用某一个函数,就可以完成各种寄存器的配置,不容大家直面芯片的寄存器,方便阅读和使用,因为每个函数的名称功能都是不一样的,在调用前可以参考函数的注释,在F0和F4的标准库里甚至有每个函数的用法,不知道为什么在F1的库里把使用步骤去掉了。5 W% ^3 @3 t! @% b6 g
咱们继续,读写的话库函数分为: - /*------------ Functions used for all STM32F10x devices -----*/& c3 H [, m5 v) B- P
- # J6 ^0 _: d4 u! c
- void FLASH_SetLatency(uint32_t FLASH_Latency);, E {3 W9 z* w2 ~& F& `
-
2 T( y0 W3 h ?* C1 x - void FLASH_HalfCycleAccessCmd(uint32_t FLASH_HalfCycleAccess);
+ B& f3 b4 _( z0 W. z% f - - p! x: H. H7 Y/ t" }3 E
- void FLASH_PrefetchBufferCmd(uint32_t FLASH_PrefetchBuffer);
; R, D/ i! H+ p3 z( h& S" a - D/ ?& t2 Q* q2 b; e9 w* @$ m/ ^
- void FLASH_Unlock(void);9 t/ M9 J7 E z- l6 M3 F8 n3 Y; ~
-
0 ^1 ^' Q h# u3 e - void FLASH_Lock(void);) M9 A9 V, H- \( h, ~: u
- 9 d9 M' j3 f/ q1 e J
- FLASH_Status FLASH_ErasePage(uint32_t Page_Address);" E' W9 n' I* }- o7 k/ R( w
-
; A0 i3 W$ k3 E" t1 `0 R- y7 f - FLASH_Status FLASH_EraseAllPages(void);. o! s: l+ G6 b* b! A
-
9 Z+ k( o. Q) p) f5 {1 V - FLASH_Status FLASH_EraseOptionBytes(void);
8 h3 X# w6 m; x9 I -
. ?9 v4 K/ ]3 K w$ A1 [% M - FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
' [5 S/ l! q6 V8 p n - O; g6 Z9 i8 D, s' Y+ G$ S
- FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
& L8 x4 U) e6 ~9 S -
$ x( Z0 y, Q6 z - FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);& s, b7 c# Z$ h" b0 e7 e1 v
- $ d' @/ p$ B# W4 u- Y" R9 E8 O
- FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages);
- a. Y/ X, Y' T2 V) f, `' x8 `9 ]) H: v - $ C% {) m ^" n( \
- FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState);- p$ B* N: ~ ^) R
-
2 v+ c( u/ c) ?- x2 u$ e6 b+ H! Q, B, V - FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY);
1 t+ V. b; \6 k7 n' ~ -
* Z' u' ]9 i# g6 E3 F" u- } - uint32_t FLASH_GetUserOptionByte(void);1 V( m; g" }1 B8 n
- 1 q; C3 X3 I* E& o+ J, p% d' N) P
- uint32_t FLASH_GetWriteProtectionOptionByte(void);/ m2 U" ~, L x9 C
-
9 a1 y* u, ?$ p1 o# P - FlagStatus FLASH_GetReadOutProtectionStatus(void);+ A' a! m1 s* h
-
$ l' K9 \0 T& T' W" a% w - FlagStatus FLASH_GetPrefetchBufferStatus(void);
' v: K$ `1 I" Z ?# c. @, I" S -
, M- a; r( L! }3 [4 F2 i! p1 w - void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState);
9 L2 B: v6 a0 q. C' e -
, [4 Z8 a1 b5 D - FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG);
9 X) b' Z9 g B - , D$ \4 j: N% i) y& C% a6 W
- void FLASH_ClearFlag(uint32_t FLASH_FLAG);
( P3 W' [- O( |& O9 @6 Z0 O0 S -
8 K9 I( }' V+ Y' y/ H3 p8 \ - FLASH_Status FLASH_GetStatus(void);
8 B5 x4 }' V/ u( b6 W6 J7 o - : _+ h0 D2 f$ L8 t# \
- FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
- c0 F$ j* z5 M - $ I" E2 b4 ?" R) V$ Y
-
# h2 b0 I1 \5 R& t -
/ `, H1 ]! z3 I B$ b2 ?6 j4 } - /*------------ New function used for all STM32F10x devices -----*/
/ t. o- {6 I. ?5 O: e -
) X' o$ t2 Q6 h/ L8 [ - void FLASH_UnlockBank1(void);
p' z9 F1 W+ v x -
9 F) M* E$ c1 R6 j - void FLASH_LockBank1(void);
, @5 |1 [4 P7 C4 S5 u: @ - - i7 s! \4 m6 h& Z
- FLASH_Status FLASH_EraseAllBank1Pages(void);: Z' t& g$ X( ~6 a
-
3 m# i9 A7 G# {( a& Q# h - FLASH_Status FLASH_GetBank1Status(void);4 R5 b2 Q6 f8 e9 X ]' g
- - V; z$ f, ?6 y7 d- Z# g0 o
- FLASH_Status FLASH_WaitForLastBank1Operation(uint32_t Timeout);
复制代码 6 q9 H8 v& [/ R; G( i! R; j9 G% ]
2 J% H1 }, }4 O/ a. S. J( r* W) C. L
在这里就不一个一个的详细说了,我们说一下常用的就行
3 g0 R5 Z; C7 G1 K1. 解锁 void FLASH_Unlock(void);$ P$ C" U* q7 c- T0 {2 S
H6 P/ m, E7 G, S0 y& V; d0 s
2. 上锁 void FLASH_Lock(void);3 t6 D4 ~5 Y3 j3 Q5 A) I$ \
# E Y0 c5 g }& U. @% H3. 页擦除 FLASH_Status FLASH_ErasePage(uint32_t Page_Address);4. 半字写入% w) E; ?* @4 ^. [' i6 J" G. j
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);上面这4个函数就是我们最常用的。下面说一下数据写入的步骤,第一步:解锁第二步:判断写入的数据是否被擦除过,也就是判断写入的地址内存放的是不是0xFFFF 这里要重点说一下,为什么要判断是不是0xFFFF而不是判断是不是0xFF呢?因为我们每次写入数据都要写入半字,也就是两个字节的数据才行,而且写入的地址只能是2的整数倍,不能是奇数。这里大家注意一下。第三步:写入数据 STM32F103C8T6只能按照半字的方式进行数据写入,写入前的数据必须是0XFFFF,因为FLASH数据写入,只能写0,不能写1,这也就是为什么我们要先确保写入前的数据是被擦除了的原因。第四步:上锁第五步:验证写入是否正确其实第五步可以省略。我们看看官方给的写入过6 R( I4 I+ d- J/ Z! M7 `, r! q

$ ~- u3 p6 o$ K+ c8 o0 j! S好了,其实是一样的。
4 v* X" c) u" Y7 u+ H下面我就和大家来分享一下(百分之九十九参考的正点原子的例程)
 - //不检查的写入$ @+ O$ }! X# ]0 s# r
-
8 n6 Z6 [, I9 c6 [ - //WriteAddr:起始地址
, C6 J! F8 F) t/ H+ r E( T - 5 U' d% P: U3 T( L# C
- //pBuffer:数据指针2 F: T( p5 P& C: C/ F0 W
-
: ^' ^: G! w, p2 E4 ?. M3 T - //NumToWrite:半字(16位)数
2 F: |, [! R% f - 9 [' a5 u0 e/ F# u; O
- void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) 7 T/ \8 W* j: P+ E
- $ @8 L1 U. d$ n$ _9 E
- { 5 W, i. a% y* z1 v1 s9 q3 }$ r
- 4 j1 P0 D. ?4 i9 L+ f0 O- s: Y
- u16 i;
, m- ] \% y- x0 c0 J - / C9 h |6 H$ Q( R
- for(i=0;i<NumToWrite;i++)
9 o7 z4 R8 ?) j -
5 ]- u1 |4 D2 d, Z {* x - {/ C9 b/ D& p3 N; i: i( ]1 A
- * V6 D& U y( w( s5 q9 w! v' T2 X: `4 \: O
- FLASH_ProgramHalfWord(WriteAddr,pBuffer);
8 m/ A X N! r5 W -
3 E* k# o& q9 D7 W' J - WriteAddr+=2;//地址增加2.
) J, D4 [8 m" A; K -
+ f& d$ z; M% I5 U8 U. | - }
/ p3 @+ d' B4 h - ' U2 ~( @7 ~$ M0 }
- }
复制代码- //从指定地址开始写入指定长度的数据
+ ~5 j `2 b2 j -
' W. V. v& j! Z. [; y2 S! v - //WriteAddr:起始地址(此地址必须为2的倍数!!)* `1 _! p) X0 K* \. a
- ! y$ N& A. p: U! N1 W/ } q I% |
- //pBuffer:数据指针' q2 |- u3 J+ g# h
-
- p: J. k; S. U4 ]' A1 `$ d* q - //NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)( c6 }" m& e6 \
-
# E" G5 K+ n; d7 a) O -
: F% ^7 `% g: f - ! x& M# J+ y3 h1 Z
- u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
' v$ W2 A i5 `0 G* L0 b% F$ Q: m - ' D5 |. v* o5 H% ~% S) Q+ i
- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) 4 d$ ] c) y- l* W Q
- 8 I( I! s- I8 n, y4 W0 `3 B4 ]: Q
- {
% k/ a2 [4 V7 J) T; G) Z+ p5 ]# F -
Z0 F- x# I. o4 o3 }& ?- P - u32 secpos; //扇区地址
, W! L5 H8 G( H2 _* B -
) n$ ?, S, c8 I' }& S( S - u16 secoff; //扇区内偏移地址(16位字计算)
8 _, x8 L: q ?* K; Y - I+ h: @ N/ w6 y2 L0 a! C: T
- u16 secremain; //扇区内剩余地址(16位字计算) + o$ y; `1 j T4 z
- * _" ~* S; k1 L: d5 ^& a6 o
- u16 i; ; n$ }2 F+ K; z" Q
-
, ~9 H, A% L- R* R - u32 offaddr; //去掉0X08000000后的地址
$ F/ T+ w4 a6 Y+ y% X( W -
0 t" g. A# b' X$ H% |# x7 Y6 y - if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
1 r; m8 u( x8 f' _" d4 d4 _ - C* H! J( p# P8 J, G
- FLASH_Unlock(); //解锁
5 L- K: m5 O, ?8 v: H m8 y - " a$ r& V( \! L' _/ l
- offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址.
1 l) L' [* t4 {% D U' x. r - ; L# s5 d7 _/ j \
- secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6
: ^. C" o- g9 Y: {+ c Q - % k4 t( l- C& ` k X
- secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)5 ?' Y, l# T$ z1 N+ [7 w0 g- N$ E/ T- y
-
! |$ \/ Y3 g1 i$ A: v1 L - secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小
) M' z6 C, S: {# A, c3 i; O -
2 o, \0 S2 r9 r6 c# u9 A9 S9 h3 |/ V, S - if(NumToWrite<=secremain)
5 e6 o# _: C: y! Z( a& X -
7 r" O; s D4 R% M0 G - {$ L" O. ?3 w2 h8 x% Z
- : W( j4 }1 L9 V: o
- secremain=NumToWrite;//不大于该扇区范围9 n8 I R" u" |' C
-
0 w. ^- N$ ?0 T" `9 X) H - }4 R, r$ L5 g) c) u- r/ U
-
, V1 b( z) f$ v8 Z - while(1)
7 e' e) d5 S5 m- N9 D F6 Y9 Z - ; [5 D4 ^( g9 f$ P* E2 k3 F
- {
: J' b, M7 s5 ~5 W" y, m8 q -
. x( j) I9 d. \0 ^' o1 N - STMFLASH_Read(((secpos*STM_SECTOR_SIZE)+STM32_FLASH_BASE),STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容1 K9 x# U% Q: J1 t% t3 U/ r) }" D
- ( C* g! c* m9 r% O1 j; `
- for(i=0;i<secremain;i++)//校验数据
. _' A$ f. `! t6 ~ k, l -
; ?0 S* O* T) W, ~2 [- Y - // for(i=0;i<(STM_SECTOR_SIZE/2);i++)//校验数据
c: e5 e/ w# S$ ~/ l( q/ J: E& s - & M3 _+ h$ w. X: `; L7 i4 N- c/ _
- {
+ v+ c2 \6 }/ H, {' a0 Z1 Y) r4 F - : E9 y5 G: V% ^
- if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除
# ], K! y3 b3 Z. G - $ {2 |0 P4 g' o; `: p4 L/ P$ Y
- // if(STMFLASH_BUF!=0XFFFF)break;//需要擦除
6 F: s' m0 i% f+ e$ A( e( @- V -
) ?- Y; Z, G# M! m- e - }& {$ p/ B( d6 W% J" a- s2 i5 X
- ; U2 _% B! i# V) D2 B" ]8 b& z) Y
- FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);* t5 l: ^/ Y# s6 _! i
- 5 s! M+ E4 ?% n: E0 M' ^4 s) e
- if(i<secremain)//需要擦除
/ ~, N8 c3 h$ d3 s8 j -
$ U2 X7 X- k5 [ - // if(i<(STM_SECTOR_SIZE/2))//需要擦除& u _6 A9 T T2 F. Y3 B9 s
- . ]# ? C$ V1 J5 d, u
- {
( q/ i% q( m0 t5 v$ Y+ @ - , z5 W0 f- I E; o
- FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
0 h1 W. x4 h7 s: m -
) Q0 C$ B6 P; d' c; U6 u1 I -
3 `" D) x' W$ [: j' o7 ^ -
, {; U: ?; X, a* T( }8 v - FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
" Z9 R( b2 i5 o& r8 N- f T m -
& @9 _! P n5 ~) S) D* f - for(i=0;i<secremain;i++)//复制% W' _3 Y @* Q
-
$ R5 x6 s0 x I3 `; K - {
" D6 {3 [0 N7 [9 G7 t8 S! l -
/ M1 ]! B0 y: e# a. U _ - STMFLASH_BUF[i+secoff]=pBuffer;
7 a/ @: `0 @# L; Q: o6 Y1 g - $ L5 x& M6 L. c- B+ K4 U
- }$ v7 E( T1 q! L
-
3 K- x, _7 R$ F8 f - STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区 8 w/ Y; m; {1 p, e. I6 h% w. _
- ( R1 i3 q& r& C. J6 k# v% f3 I
- }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. % j; \: X1 x/ C/ K9 O2 m
- R& c2 I, x' ?- ^3 v; _; [
- if(NumToWrite==secremain)break;//写入结束了
1 e+ f' k* k8 r, G( a" e -
9 D" D( ~+ v2 Q& _. _# y/ q - else//写入未结束: U: V1 M _/ f7 u! P9 g
-
) L" u9 l( e. D( B, U% L - {
1 o0 A$ `( L+ g5 c, q -
; W# U; G# T* q - secpos++; //扇区地址增1
& P l/ D2 [* b+ K -
8 y, ~" ~( j( ~0 N/ | - secoff=0; //偏移位置为0
8 ]" k; R2 w* J -
0 r, U# \, m, R5 ?5 X - pBuffer+=secremain; //指针偏移' |& G, {3 m! S D& L; z
- & L3 U) \$ O/ n. m, b. T
- WriteAddr+=(secremain*2); //写地址偏移 # ^( c: P0 O. n6 U' c
- . O' J& f' ?+ l9 |
- NumToWrite-=secremain; //字节(16位)数递减
$ L1 j5 S; j, `: R- ^5 w - 6 P2 J6 ]5 D8 O% c9 p0 d( n M
- if(NumToWrite>(STM_SECTOR_SIZE/2))
! I$ e2 q; N. b# J -
9 `) n9 m; C' d) r - {' J2 B3 R- ]8 A, t- g% W6 f
- ; z: t" O; r% w- U
- secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完- ]: u6 z' r1 [+ o/ ^& `5 @2 u ^; i
- 7 H( e, A1 K8 _- |1 r; e' \
- }
. o Q) l$ W2 z5 t/ n9 t - # j* t, _( A0 Z8 X% k( a0 M, o
- else
a( k" F1 @/ A$ x5 Z -
3 q$ G$ h+ V! d3 [8 {* ]' k - {
$ _2 C* v/ J3 V - , `" o1 z; [8 u8 W+ W+ \5 l$ d
- secremain=NumToWrite;//下一个扇区可以写完了, y( j" u7 }9 b# Z/ o
-
, X! p7 Z9 g z3 t) `! N - }
* K2 i: F4 z! _. M/ |- @ ] -
9 _' \) R% t5 K# d$ B - } . M; [5 t$ J$ N- F1 |
- 8 ?3 Q- c- F# c- N* Z
- }
- C" j* m5 t1 g; @6 P1 z. I -
4 Z7 Y" ^, l/ G - FLASH_Lock();//上锁
, D2 R' A' e: D9 @; l# G! T - 5 l( C: _- _& G; {/ l# j
- }
" s( a3 d+ R1 N0 G: G: y -
复制代码
$ n6 j4 F3 `. m1 C* {+ k
$ L6 x& Y+ f, R1 T& `3 ] u最终我们调用STMFLASH_Write()函数进行数据的写入,是不是有没看懂的小伙伴,我给大家解释一下写入的过程吧
2 s3 K9 b4 c3 v( h7 j! B4 D5 {/ G) p: g1 R9 `
这个STMFLASH_Write()函数,是说给定一个写入的地址、数据和写入的个数,然后按照给定的地址开始写数据,注意红色字体。; M% U0 }1 e+ a" |
写数据是怎么做的呢; U) k, c' j/ `2 @
首先是整理一下写入的页地址和需要写入多少页,每一页写入的话起始地址是什么! T j2 O* c! R! {
然后开始一页一页的写,当遇到跨页写入的时候,把第二页的地址写进去,写的个数继续写入就行
# `( |+ w2 l1 a+ k! L% @如果还有不明白的可以在下面留言,我给大家解答
& q' i' ~& H: i V C还有一个地方很重要,就是我修改了库函数
# K9 h) Y% M+ U5 h2 b+ ?
8 M; d8 w& X3 ^: W" z6 S- /**
1 k; H4 [4 Q' e5 x' a - * [url=home.php?mod=space&uid=247401]@brief[/url] Programs a half word at a specified address.
+ b. v* ?8 W% @) I* K6 A - * [url=home.php?mod=space&uid=536309]@NOTE[/url] This function can be used for all STM32F10x devices.8 ?) b1 j5 n" F+ Q. Z2 n! a9 ^
- * @param Address: specifies the address to be programmed.! s7 R) s! c1 J2 i+ K
- * @param Data: specifies the data to be programmed.0 V4 d/ Y( H: o2 j" `) ?
- * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
% m; k6 h1 s5 r4 o - * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
* C7 t/ J$ s4 k3 u, E - */
7 v$ k; j' _- [4 E q% Q -
8 f8 p. [# M5 _1 p* d - FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
# B# t# G* e" W8 s - 2 v7 N+ C# Z* H
- {' b/ {+ J9 a0 V0 v {
- % E3 X) v9 `1 _) O
- FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
/ t% x9 ~) N9 o+ o - " s4 s$ A4 ?) N' C! M
- FLASH_Status status = FLASH_COMPLETE;
# C2 b8 `& ]1 F1 K8 U - 4 v2 ?. @) c( h8 p! J8 V/ I5 }) B
- /* Check the parameters */4 R3 D4 ~( a! a$ U: ~+ [8 [
-
" A0 f: ]0 T5 ]3 W/ f. v - assert_param(IS_FLASH_ADDRESS(Address));" r+ ^& ~2 ]! ]
-
b4 |1 Y9 k" W+ g" o( F -
- r% ~5 {1 n% f- |$ `* t - % ]1 P. b0 @: c3 @
- #ifdef STM32F10X_XL' p# I) x, G- m. s6 J: o# _) a
-
) Q4 p# W7 A7 P/ {; e5 B. y - /* Wait for last operation to be completed */
8 ^0 x9 U; N7 \) Z. t' _; e G - 7 g9 a1 @: _- u" Z1 x) p
- status = FLASH_WaitForLastOperation(ProgramTimeout); L& C& |4 B5 z: m, |' Z
- - b% D) l9 Y; U4 B4 r/ z
-
+ w$ |9 f1 Z$ H: X Q; ?% j: P( N - 8 l% x# f1 K) p6 `( N1 h
- if(Address < FLASH_BANK1_END_ADDRESS)
$ [) @7 Z- c- r: Z5 t -
, ]3 C; q% j9 {/ K! e - {+ H2 K& K+ U8 K1 y
-
* ]/ i& U/ u; V3 P; C2 P* f - if(status == FLASH_COMPLETE)4 O: Z" c- y5 P, E
- $ @: W/ W) O* d5 `# J5 K
- {
& Y9 A2 @" L: h$ J - ! L: X' s$ Y2 A7 o; i
- /* if the previous operation is completed, proceed to program the new data */
. f9 t8 [) S4 `1 S, t -
9 ?) P! Y5 D# D: H% G, ~ - FLASH->CR |= CR_PG_Set;) A# z1 ]1 @; b+ \
-
, p- I# A- }: G8 J4 h ~: K -
' V: q4 M( o* a: p1 x& J - ; c( A$ S9 ]* R! O- m: Z4 \
- *(__IO uint16_t*)Address = Data;5 M6 Z5 P0 C: ?9 M
-
# ?7 E! S, M% }: @$ V" F - /* Wait for last operation to be completed */+ ~: ?6 {3 b3 q1 U$ B, Y; Y
-
~+ F. \' H6 Y! }8 _. w# \( q, r - status = FLASH_WaitForLastBank1Operation(ProgramTimeout);
/ a- W4 W# o: g -
! N! z9 h/ q. I! v% C6 \ -
, l! q, Z% k* S& Z# B -
. T1 Z8 V3 H+ q, { J5 ?& ?6 k) _ - /* Disable the PG Bit */7 f& R' \0 A8 e* Y: d
- ( s- [/ S3 ~& T
- FLASH->CR &= CR_PG_Reset;
& u0 a7 i8 O' f8 \' x% R - ; X" ?) |6 _6 `* j1 u$ {$ {
- }
/ I; m6 x! X* e' P& V8 H3 p- R -
$ t# Z2 E* _2 ~; H+ Z - }
2 v. a$ S+ X9 X, ~' c -
7 ^1 K2 \) \! I7 S* r - else
" H0 L/ K- O& X$ q$ V0 p% G - % E; p7 o2 [7 ~6 F
- {8 `# j! j. I: _: C- K* N+ t1 i
-
8 z1 R4 |) }* v0 F- m$ v$ a- H - if(status == FLASH_COMPLETE)
$ g: x) a# }' w% U3 P8 w( W- C - + r) [4 H* e" q$ m! `0 Z' Y& a
- {$ p N7 W) s6 x3 H: t/ |* `
- 4 S9 H6 |6 {) }/ K- j
- /* if the previous operation is completed, proceed to program the new data */
O1 \1 x Z& l - ) T/ l% f8 `* K4 @. |8 c1 w4 l
- FLASH->CR2 |= CR_PG_Set;
T7 q3 y; I1 j7 D6 v' ?0 S -
* h0 _# w- i' u6 z$ g - + I3 ?) q2 \2 E
- , ~+ d( M2 q- @: n
- *(__IO uint16_t*)Address = Data;
$ c3 H/ I' ]5 c- Q+ e( m -
0 P+ i- ~: X1 {5 ]& V2 a3 I - /* Wait for last operation to be completed */
: }4 O+ O" |2 w" W7 L0 P -
- m) a5 E* X" K% o. M - status = FLASH_WaitForLastBank2Operation(ProgramTimeout);! i! @0 W, i p
- ) a$ z+ R* _) W6 M, J! b
-
3 r: r, d2 D. U) Q/ N -
2 J1 [3 P0 E# |. j: K: A5 |# W - /* Disable the PG Bit */' S9 T ~3 S: Z! @9 G2 O
-
' w: s' K1 D0 j3 z: h0 m* `# }( W - FLASH->CR2 &= CR_PG_Reset;9 \* } n3 B9 i S, w( v/ R% w
- * E R7 i/ A, `0 `6 n. n) [
- }
# _' S. \, _4 H7 ] - s, Q0 E$ y- r9 z! T F8 k
- }7 v+ I7 {3 B5 g, T
- ' J0 ?5 N3 O# p
- #else
; d* b; h8 @4 h! L - 4 Y5 Y# U2 u+ J- P) }% g" {
- /* Wait for last operation to be completed */9 n* m5 M& X. m. _1 C
-
2 C6 Y0 G' J9 E- N - status = FLASH_WaitForLastOperation(ProgramTimeout);$ i! K9 I' F& }1 n$ @+ z7 e9 Y
- / [& f- d- B( D
- - G5 b4 q& e8 n9 g$ j
- & l& l* u; \) q" C7 r. ?
- if(status == FLASH_COMPLETE)/ h+ x' f k) t, y) F+ J/ G
-
{ X ?. n% a s7 o% [9 N6 X - {/ n1 @; E6 B5 q* f4 d; g( F0 N
-
& K& M4 Q' S7 ^, l7 r - /* if the previous operation is completed, proceed to program the new data */
2 s4 G" M0 r F) H1 j+ E -
) y* J5 q8 F6 r - FLASH->CR |= CR_PG_Set;0 e" c1 c! m8 h! M' W2 s* X
-
3 h3 L6 s! H8 y V4 l- @! ?" b - 6 G3 j* b) [; N& x8 s; w
-
9 b& q7 e( F6 m7 T - *(__IO uint16_t*)Address = Data;1 }1 U% s/ v! s+ I7 @. l/ N- Q. m
- 7 n" ^. ~2 D5 t2 h* `) y2 p
- /* Wait for last operation to be completed */
. i$ w2 J( J' d0 | R2 M - 2 Z% o+ K1 |, K0 x
- status = FLASH_WaitForLastOperation(ProgramTimeout);
4 b G9 J7 X5 V -
* q% z' {5 }. Y @4 J7 P' [2 z* ? -
# G1 d# G3 G; L1 h - 9 T, }3 }* A7 ^6 j; \
- /* Disable the PG Bit */. i" |' ~8 U0 E& j4 ]
- ' U; W2 P$ k ?1 t( K1 H' x0 O
- FLASH->CR &= CR_PG_Reset;
2 a+ `8 O& x, b7 m -
: o$ O; j) `7 b' v' x T _ - }
$ d- ~8 Y+ `& Q1 W$ |9 e! [ -
1 S8 v5 t7 U0 C - #endif /* STM32F10X_XL */
1 B: |! f5 b6 X& w8 t# w0 M; y - # D8 O. {( f7 }) m( w# o$ |
- ) O3 B/ Q- A. `. r
- 2 V, F& A {- `- M0 E
- /* Return the Program Status */* H5 x" V; `: x' U
- : }: ?* J' ~6 {, S
- return status;$ I8 x; U4 l& i7 {4 M6 V% ^
-
" c- a# N5 x4 e& U4 f3 {% A - }
复制代码

: G( b7 p, q! ~$ \$ n& M M% r
' e3 o4 T, q- o! o, F大家能看出来吗?就是红色字体部分,增加了一个每次写入前清除所有异常状态。
; d1 O1 ?, e5 Y& Q; k为什么添加这个呢?5 n# G9 x O' \4 Z
因为,如果你写入的数据的地址没有擦除,你就写入的话会导致异常状态的发生,而这个异常状态时要手动清除的,如果你没有清除这个异常状态,而继续写入数据的话,那么你后面写入任何数据都会报错,均写不进去,所以我在这里增加了一个异常状态清除,如果前面写入的数据报错了,不会影响我接下来的数据写入。1 a) d2 u# p1 d# G, |4 T
这里大家就清除为什么了吧。
; j4 @0 w: T* p$ d1 J) M写数据会了,那么再说一下读数据,其实这里读数据要比外部flash读取容易的多,我们直接读取地址,返回的就是地址存放的数据,是不是很简单# R: c g& D. V5 n& C5 N/ N0 d0 e
看下面的函数
6 T4 T3 d, m7 s# h0 R7 e3 W, ~0 P
-
' [0 W% ]9 M$ U8 r - //读取指定地址的半字(16位数据)$ M" L0 L) K9 D6 i( i
- : ~# p. ^9 y: L" e" ~
- //faddr:读地址(此地址必须为2的倍数!!): y2 I9 M- E; ^, f
- % v# z* n9 j9 p( p5 ?! V. X l
- //返回值:对应数据.
8 [4 X# U+ e" q C- n7 K6 b1 _ - " F6 D" r1 C5 Q6 E
- u16 STMFLASH_ReadHalfWord(u32 faddr): W5 k0 {- e! s
- $ W; R2 q2 ^" S* c& k/ _8 T
- {
2 |, i. c* q& [ - , p# y6 Q# V, Y2 U8 F. k
- return *(vu16*)faddr;
& V; |0 v& _* E -
7 L' B$ ?$ D* g, r/ a - }; X) M: `/ b8 }" A& | P% Q; E
- " F+ j0 P7 @* u: [
- : B4 H1 `4 ]! S1 Z* W! g. e3 B' _: b
- , |" a9 H% j, [" L2 T! w0 g
- //从指定地址开始读出指定长度的数据
: M% [6 W8 A: H' k# W8 W - - f% I6 G$ U2 Q
- //ReadAddr:起始地址" P: J/ m# S/ C: w
-
: |, M4 T& n$ t, E - //pBuffer:数据指针
( @1 _3 m, H l* T- Z$ \7 z" Q% ~ - . y( h% `3 i+ g. o
- //NumToWrite:半字(16位)数2 A' u5 @$ G1 z' ^
-
! t! X3 t# g' L+ \ -
) c1 P: s2 ~: g- c+ N8 B -
! g# t* s0 x6 @9 e - void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
. N3 z7 w! n0 A: c8 f - . o: l3 V' ?5 @; T
- {3 l9 O/ _1 ^+ ?; _) f# r
- 8 T3 j# c$ G- A; B
- u16 i;
2 f6 h2 j, N9 q9 R- d -
. f. z4 d! O b - for(i=0;i<NumToRead;i++)
G5 F. z X2 d1 ]4 d -
' g1 O( }4 o8 S2 [! u1 N- K% g+ P7 b - {
, r6 h. E$ p f& X -
8 V0 N8 z4 J; g$ m' G - pBuffer=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
! c1 N2 d& p3 ^- n' X - / v3 @, g; ^% z2 b5 x
- ReadAddr+=2;//偏移2个字节. 1 z# b! c0 T6 |
-
9 _" d2 F( |; }) S - } d! L: J6 p# w/ x. W" u7 v
-
: Q3 [! j2 i p/ x - }+ S- O8 d6 i1 S" A p$ b+ {
-
复制代码* N, @% i' F2 Y% R
有没有很开心,读写数据就是这么简单就完成了。
7 O, L0 }, Q4 Z9 Q
& f9 q. ]6 ]% L8 x---------------------6 f5 H6 [( H& w8 z4 A
作者:binoo7
& j `" N& ~* X$ t O" p, M2 C |