在我们应用开发时,经常会有一些程序运行参数需要保存,如一些修正系数。这些数据的特点是:数量少而且不需要经常修改,但又不能定义为常量,因为每台设备可能不一样而且在以后还有修改的可能。将这类数据存在指定的位置,需要修改时直接修改存储位置的数值,需要使用时则直接读取,会是一种方便的做法。考虑到这些数据量比较少,使用专门的存储单元既不经济,也没有必要,而STM32F103内部的Flash容量较大,而且ST的库函数中还提供了基本的Flash操作函数,实现起来也比较方便。- f: n1 {0 g' y
; ?+ h8 ]% @3 s) s$ P+ O
以大容量产品STM32F103VE为例,其Flash容量达到512K,可以将其中一部分用作数据存储。如下是大容量的Flash组织模式:0 v* M, w7 L+ v
9 `5 i5 t- I! _' C" a& {
. Q* A2 O+ O }( H+ ]1 z0 V( W& H) o9 o
根据上面的Flash组织模式,我们可以根据自己的使用方便来作相应的定义。因为大容量每个扇区定义为2K,而小容量和中容量都定义为1K,所以我们做如下宏定义:3 H& h; Y& _6 f# z$ |
- #define FLASH_SIZE 512 //所选MCU的FLASH容量大小(单位为K)
8 N- F$ R( C1 N, |2 D! s# e+ ^
$ ~# C7 |: e' q) O+ I- i3 P- #if FLASH_SIZE<256
0 `3 ], C4 z7 c; j6 n( F - #define SECTOR_SIZE 1024 //字节5 }' w# U8 ]9 l5 U& t$ n5 ] I Y- a
- #else
+ [( V T P6 B; ] - #define SECTOR_SIZE 2048 //字节
0 F6 X, T# o3 v' E - #endif
复制代码 虽然ST的库函数比较全面,但都是基本操作,为了使用方面,根据我们自己的需要对其进行再次封装。7 Z6 V' _) u. A: P1 ]
6 C; t1 M9 H @ Y4 V 对于读操作相对比较简单,内置闪存模块可以在通用地址空间直接寻址,就像读取变量一样。+ ~; m$ b& W4 Z F
- //从指定地址开始读取多个数据& G8 l L- i* Q9 l, Q( O5 R* Z2 J
- void FLASH_ReadMoreData(uint32_t startAddress,uint16_t *readData,uint16_t countToRead)
6 I* `- V- y5 s2 B - {4 D! {& x k9 }' @/ {0 `, n, l* [
- uint16_t dataIndex;1 V2 t' n/ o3 }5 G$ H
- for(dataIndex=0;dataIndex<countToRead;dataIndex++)
$ z" t4 q0 u2 N" {$ R - {# _1 b+ u2 y0 K
- readData[dataIndex]=FLASH_ReadHalfWord(startAddress+dataIndex*2);
8 g7 O( n$ C2 [% d3 s - }7 u4 l3 n$ ~- t3 `- v* D: ~ {
- }
1 a8 W8 }" S3 R6 A! u* u: F
; D0 A$ R3 `# ]# B- //读取指定地址的半字(16位数据)6 N a9 o/ k) }. g5 K
- uint16_t FLASH_ReadHalfWord(uint32_t address)
. |6 s3 e" y! V; G0 J" D# \6 m3 k - { H0 C# @ K5 }. x( O
- return *(__IO uint16_t*)address;
" v1 d! k$ R& [* V$ i( I. u+ F - }0 f5 p0 W9 U7 |, q- ?
- 2 j* K* k5 W: _* n
- //读取指定地址的全字(32位数据), i: q6 j: I9 u: G( m2 J
- uint32_t FLASH_ReadWord(uint32_t address)
' U& z( V3 L) b! |8 ^ - {
, X" H* s- p) n- d - uint32_t temp1,temp2;
6 l% `$ n X/ \) h6 m0 B4 D - temp1=*(__IO uint16_t*)address;
8 L: r' d0 f6 q+ p! @+ S- g - temp2=*(__IO uint16_t*)(address+2);
8 ~5 N( r% j) E8 M - return (temp2<<16)+temp1;; k8 h k8 n! Z" y' h7 T/ y
- }
复制代码 对于写操作相对来说要复杂得多,写操作包括对用户数据的写入和擦除。为了防止误操作还有写保护锁。但这些基本的操作ST的库函数已经为我们写好了,我们只需要调用即可。8 T& [2 n& p# ^7 Z! h4 V, z
) o) ~% i, K; j! u1 E# U7 n6 u STM32复位后,FPEC模块是被保护的,只有在写保护被解除后,我们才能操作相关寄存器。STM32闪存的编程每次必须写入16位,任何不是半字的操作都会造成错误。如下图是Flash写的过程:
& e& b3 u' \+ k+ o' j
( {' O- Q' q: T
" T3 B! m& P& @) {
3 j/ G6 s7 T, x2 N& v- } STM32的FLASH在编程的时候,也必须要求其写入地址的FLASH 是被擦除了的(也就是其值必须是0XFFFF),否则无法写入。Flash的擦除要求必须整页擦除,所以也必须整页写入,否则可能会丢失数据。如下图是Flash页擦除过程:% @9 N5 `& f0 y7 n& J+ X
5 c2 H$ C" M7 v4 H
% k3 o& s* ~6 J# l8 Q9 A
/ I' N+ O- ]1 z5 a% A5 q
如下为Flash全擦除过程,
( l7 p8 \* [; L! }6 f! ~4 c; P' e, X+ c+ j( D4 B! U
1 `5 q3 ~5 I1 i% }' f8 `- u$ Y8 v5 Q
2 O$ W' r6 p5 q) B4 J
根据以上图示我们便写数据写入函数如下:7 R! ~" S7 u- E/ v- Q! j
- void FLASH_WriteMoreData(uint32_t startAddress,uint16_t *writeData,uint16_t countToWrite)' W- D- K: z' u% \
- {7 V* T! [- l& ]7 M
- if(startAddress<FLASH_BASE||((startAddress+countToWrite*2)>=(FLASH_BASE+1024*FLASH_SIZE)))7 F" o; Q; q, p( i3 Z' O7 V# m
- {" S) V2 U F" ~& ]
- return;//非法地址: V6 D( ~) _/ ~9 @6 X9 c
- }* V- A u6 k8 E
- FLASH_Unlock(); //解锁写保护
* [! [: M, w' W S. ] - uint32_t offsetAddress=startAddress-FLASH_BASE; //计算去掉0X08000000后的实际偏移地址
/ D/ Y4 _5 ]. } - uint32_t sectorPosition=offsetAddress/SECTOR_SIZE; //计算扇区地址,对于STM32F103VET6为0~255
5 @' @5 R5 r7 [ -
. W, a, J' F& p+ ^- }" ^# ?' x+ d* _) F - uint32_t sectorStartAddress=sectorPosition*SECTOR_SIZE+FLASH_BASE; //对应扇区的首地址& F# h! O/ O4 _; M! q
- % N/ U M/ D. {; J. D$ x9 E
- FLASH_ErasePage(sectorStartAddress);//擦除这个扇区
* b" _' }( X( G( b - ! L; W# j% A! s0 t, T6 |: i% t$ c
- uint16_t dataIndex; C9 X7 i5 F! l- S/ } Y
- for(dataIndex=0;dataIndex<countToWrite;dataIndex++)
7 W9 H- r' _/ A$ R - {
' f) i" ]1 m a7 G - FLASH_ProgramHalfWord(startAddress+dataIndex*2,writeData[dataIndex]);
, l# K& l/ o9 @2 @ - }" d! [% F9 `) y! D4 M
-
/ P" j2 g1 O( ^+ a! R) T) n - FLASH_Lock();//上锁写保护
! f3 S8 o: T; ]3 \) W% L" O. s - }
复制代码 在擦除之前应该将页面上的数据读取出来与要写入的数据合并,待擦除后再写入,但这样数据量很大(大容量是2K一个扇区),所以考虑到是少量数据存储,所以每次都将全部数据同时写入,简化操作,也减少数据处理量。经测试以上程序写入和读出数据均正确,可以实现内部Flash的读写操作。2 Z" E8 T3 @% e7 @8 V
/ ?" z& [% ^ v
, x: R; I. z! @9 a4 t9 `2 W$ g, A* \
8 E+ G# x- t% w, G' } k转载自JawSow. |6 H- K% A5 Q8 @; G$ p) _
) z, E8 A6 U6 F
|