|
一、STM32FLASH简介 不同的STM32它的FLASH大小也是不一样的,分为大、中、小容量,容量由16K到1024K不等。这次实验用的开发板FLASH容量大小为128K。 STM32的闪存模块由:主存储器、信息块和闪存存储器接口寄存器三部分组成。 主存储器:该部分主要是用来存放代码和数据常数,被划分为128页,每页1K字节(小容量产品也是每页1K字节,大容量为每页2K字节)。主存储器的起始地址就是0X08000000, B0、B1都接GND的时候,就是从0X08000000开始运行代码的。 信息块:该部分分为2个小部分,其中启动程序代码,是用来存储ST自带的启动程序,用于串口下载代码,当B0接V3.3,B1接GND的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能。 闪存存储器接口寄存器:该部分用于控制闪存读写等,是整个闪存模块的控制机构。 对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理;编程与擦除的高电压由内部产生。 在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作 闪存的读取 内置闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相应的数据。读接口在闪存端包含一个读控制器,还包含一个AHB接口与CPU衔接。这个接口的主要工作是产生读闪存的控制信号并预取CPU要求的指令块,预取指令块仅用于在I-Code总线上的取指操作,数据常量是通过D-Code总线访问的。这两条总线的访问目标是相同的闪存模块,访问D-Code将比预取指令优先级高。 这里要特别留意一个闪存等待时间,因为CPU运行速度比FLASH快得多,STM32F103的FLASH最快访问速度≤24Mhz,如果CPU频率超过这个速度,那么必须加入等待时间,比如我们一般使用72Mhz的主频,那么FLASH等待周期就必须设置为2,该设置通过FLASH_ACR寄存器设置。 闪存的编程和擦除 编程过程: ·检查FLASH_CR的LOCK是否解锁,如果没有则先解锁 ·检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的编程操作 ·设置FLASH_CR寄存器的PG位为’1’ ·在指定的地址写入要编程的半字 ·等待BSY位变为’0’ ·读出写入的地址并验证数据 擦除过程(页擦除) ·检查FLASH_CR的LOCK是否解锁,如果没有则先解锁 ·检查FLASH_SR寄存器的BSY位,以确认没有其他正在进行的闪存操作 ·设置FLASH_CR寄存器的PER位为’1’ ·用FLASH_AR寄存器选择要擦除的页 ·设置FLASH_CR寄存器的STRT位为’1’ ·等待BSY位变为’0’ ·读出被擦除的页并做验证 二、软件实现 flash.c文件 - #include "flash.h"
! {$ v9 L5 t$ z3 l' m4 T - #include "delay.h"
5 x8 c9 w8 r; g - #include "usart.h"
/ J+ }9 y! b0 s/ @; o* e7 L
; y. b4 }: w0 _; B- B" S- /***flash解锁*****/( Q/ Y$ t1 N1 y+ a. J0 W
- void STMFLASH_Unlock(void)
! I% H2 _5 P0 x - {
6 k- M( l4 v/ x9 t, o: @8 Y1 D4 [ - FLASH->KEYR=FLASH_KEY1; //写入解锁序列4 c7 J9 J9 z- a& u O! Z) t M
- FLASH->KEYR=FLASH_KEY2;
& `% q+ P! K- F8 V% p4 ` - }
2 N, [4 A8 d$ O. r4 f
% e3 u! D. z, `' B- //flash上锁' Y$ }- N2 M; h# ?( K6 ~
- void STMFLASH_Lock(void)
' p* V( E7 u" g+ z1 ^4 z - {
* d. D/ R$ t* v9 V+ m - FLASH->CR|=1<<7; //上锁
7 `3 d- R/ ?0 T% ^4 R1 o - }2 A- c% {+ E& m1 O
- " E5 m$ m1 u3 G0 Y
- //得到FLASH状态7 ?9 \! U$ X8 W; `$ L# B7 F
- u8 STMFLASH_GetStatus(void)8 ?$ `/ s, R) w$ R
- {
' V" N3 R) P% y' D: e/ s - u32 res;
- f4 j( n; h2 t( w5 r - 0 y" t+ Y6 l1 u0 M& F/ f
- res=FLASH->SR;0 U" N& M: H# A" Y/ x
- if(res&(1<<0))return 1; //忙
- y" i; \$ B% K: t8 C* a - else if(res&(1<<2))return 2; //编程错误
s5 f- O0 x/ X. p* g - else if(res&(1<<4))return 3; //写保护错误
3 i+ C8 A% _ { - return 0; //操作完成0 p9 t9 @4 a, S; M
- }0 L* t# J& s- J$ \& J
- 8 E& p6 D4 V9 z! I& }- U' ^' V/ W
- //等待操作完成: y' R3 M( i( A7 j; R
- //time:延时长短) @7 n! ^% L7 O3 g- H- b! J7 p4 ^
- //返回值:状态.
* M7 B" v3 h6 x+ _ - u8 STMFLASH_WaitDone(u16 time)/ k) X! t b& [
- {( q0 H; s+ w- c0 Q9 m. _2 m. p
- u8 res;
2 u3 L' @/ {+ R6 z; X3 }
6 \- i, H0 @/ [/ r2 \7 o2 }1 K- do
# N, m+ z+ _( Z0 i3 p { - {
5 y& V, ] s8 z; V2 Z! D - res=STMFLASH_GetStatus();2 y0 _2 d. Y1 P" Y7 ? Y
- if(res!=1)break; //非忙,无需等待,直接退出.
3 c! h+ ~/ [# W - Delay_us(1); time--;
5 a6 U+ | g2 G' }! D* O - }while(time);
$ g9 ^6 ?- m% \- ?% M- O6 S0 z8 y8 f - if(time==0)res=0xff; //TIMEOUT
; E. t- k( [' b! ~% o8 m - return res;
5 d7 e) q9 }: Y, u4 v4 q - }
$ S t) C2 q# j. p - $ i4 [' E5 I V
- //擦除页
( Q3 X( q( K. U I7 B - //paddr:页地址% u4 U) [' n) D
- //返回值:执行情况/ [# h- a) u/ u( b+ U9 h
- u8 STMFLASH_ErasePage(u32 paddr)
! F' l$ |9 N/ S' }; b - . H( ?4 y$ g+ }8 T6 N5 B6 m
- {# Y+ |3 l, c3 ]; y
- u8 res=0;
, X; l* s! e% d
u+ _' @( u- M) @1 O- res=STMFLASH_WaitDone(0X5FFF); //等待上次操作结束,>20ms
) n8 ?, O4 C0 a - if(res==0)% ?3 n) w( l& c* s! c
- {
+ V' a, e6 I) K& j9 y* T6 ~" l% S - FLASH->CR|=1<<1; //页擦除% q# K0 c1 S* \4 N& W
- FLASH->AR=paddr; //设置页地址
* T, u( M* ?: d - FLASH->CR|=1<<6; //开始擦除 & T' \* _( K, m7 i0 e
- res=STMFLASH_WaitDone(0X5FFF); //等待操作结束,>20ms
$ y2 n' E6 A5 J1 N- c# @! r - if(res!=1) //非忙( ^' q$ Q B* w2 |+ p
- {
5 Q# u3 K1 _: L2 `, q% M. w& q) @. z8 C - FLASH->CR&=~(1<<1); //清除页擦除标志.& v1 g. G. o( Q7 N9 {
- }' E+ |$ h2 y- t' }$ k: k2 M
- }2 Z) G$ T9 M& O& `
- return res;
6 M& d, a3 z+ m/ O o/ U, }$ n8 U1 F - }
& S* [( h% Z/ v0 [0 l - + b: H6 V) `" `& H* w& X5 X
- //读出指定地址的半字(16位数据)
* y3 E6 ~" I' Z - //faddr:读地址(此地址必须为2的倍数!!). _- q) Y& p8 R6 y W
- //返回值:对应数据.' o2 w \" x4 C1 N- x
- u16 STMFLASH_ReadHalfWord(u32 faddr)
! h. ~/ _. J3 M1 \* Z9 V' | - {/ a% J8 z; `) i3 {; y
- return *(vu16*)faddr; 4 a0 T" D. P8 b, u2 A
- }
9 B$ }5 e2 ~. [. X9 Z - #if STM32_FLASH_WREN //如果使能了写 & R. v( d* U* H" j! T
- //不检查的写入4 f; [! @1 P" c
- //WriteAddr:起始地址! R( S. [1 U" f) m9 z
- //pBuffer:数据指针
. V' u+ S, m' l% }! C8 r - //NumToWrite:半字(16位)数 8 ?- C4 b( X* P# D
- void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) * d" P0 F3 w, ?4 A6 Q! }! h9 r) b$ z
- { 0 j! V+ J( s" g1 |' |. j/ {
- u16 i;
- ?0 q. K0 d( B* M6 h - for(i=0;i<NumToWrite;i++)& n6 @& _' L! m
- {
9 [7 T* t5 S! d9 r9 d: i; p, T - FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
/ L1 p! s8 H! q7 s/ H. ? - WriteAddr+=2; //地址增加2.6 R% P+ H' X# J4 P( R; K
- } ) }* K: b( R( k
- } 3 J7 A) I' s4 b8 B# }2 m
- //从指定地址开始写入指定长度的数据
$ q& ?5 d2 J! t* x; ~, i6 q - //WriteAddr:起始地址(此地址必须为2的倍数!!)
7 h4 n! m3 Y, D - //pBuffer:数据指针 f9 [- F* m3 R
- //NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
0 K* b3 C0 d; G1 [3 o1 O7 c# v8 t - #if STM32_FLASH_SIZE<256' r! E4 K- B/ p
- #define STM_SECTOR_SIZE 1024 //字节5 v" Q1 `5 a# Y2 D% U4 t8 [
- #else
3 |9 _8 F" W A) L( v - #define STM_SECTOR_SIZE 2048
- t" Q7 S9 q3 g) `5 o) ~0 O( d - #endif
5 z7 k) y# N- \+ z6 _ - u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; //最多是2K字节5 Q8 N& |7 }0 n: h! T
' y: G S+ x; o+ a6 }# y- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
2 V) S' m5 M! t$ X" v" o! k - {5 V( O2 o2 r3 q {# X
- u32 secpos; //扇区地址3 i& S2 v4 X3 ]' j0 i
- u16 secoff; //扇区内偏移地址(16位字计算)
7 n. r: ~' h1 N3 e& {( t- J: I - u16 secremain; //扇区内剩余地址(16位字计算)
' j4 c, F; U6 E, G b - u16 i; ; @6 I# I) ^. o4 q# k( g9 Q9 a$ u
- u32 offaddr; //去掉0X08000000后的地址
k" W2 X" l* A! ]8 N. L -
% R5 V \9 [) s; ~9 \ - if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址6 |" v9 p3 K0 t, k. q
- FLASH_Unlock(); //解锁
$ i5 X9 I$ c4 M - offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址.
# ?4 u" n$ f0 s- R2 b- { - secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6
. i) I3 r! _' q& J - secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)% B" _. G9 i2 S6 Y" h/ D" t
- secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小
4 H0 A8 m5 \, | - if(NumToWrite<=secremain)secremain=NumToWrite; //不大于该扇区范围
' d+ C8 Y: }5 m - while(1) k2 I. J: V( D6 u2 l o
- { $ ^( k, A! T0 X0 [& ~
- STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容 Z, P c( k" e% R% K8 _1 m- t
- for(i=0;i<secremain;i++) //校验数据- `% Q) k3 E w4 o7 f
- {
3 f. z7 N) p- b4 P - if(STMFLASH_BUF[secoff+i]!=0XFFFF)break; //需要擦除 & T- \! c1 k3 N* N' W, l8 A1 M/ W( l
- }3 d$ H( b- y; u: K2 `8 H
- if(i<secremain) //需要擦除" k/ i8 I2 x5 U& K( F. K+ A/ k" r8 N
- {
2 b9 ?* G' M' Y* \+ J - FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); //擦除这个扇区
F3 R z3 u8 K, [; V - for(i=0;i<secremain;i++) //复制
" _- i6 K2 Z+ k' S - {
$ [1 C D- `. v& P" k$ l3 p - STMFLASH_BUF[i+secoff]=pBuffer[i];
: ?# D' D- t, e* c* H* i - }
5 g5 T2 j6 v, h. m. F" y: [. M! u - STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区3 N9 H+ n0 W: j6 Y) ^
- }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain); //写已经擦除了的,直接写入扇区剩余区间. : B+ n+ Y3 M' r" o4 d- C
- if(NumToWrite==secremain)break; //写入结束了
% P, U; b' z ^' T - else //写入未结束, t" i9 O) o# i7 o: z D
- {
8 m _) a5 f7 r q% p- K3 b u ^) B - secpos++; //扇区地址增1
, i% \- V% I3 H- z3 t) _ - secoff=0; //偏移位置为0
+ \1 z) p- `- h - pBuffer+=secremain; //指针偏移" g$ |; x n" Z8 c! q$ n- _
- WriteAddr+=secremain; //写地址偏移 * {( z. W2 U7 h8 G8 b/ w( U
- NumToWrite-=secremain; //字节(16位)数递减
# I6 f N" O- V7 v' A - if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完* H/ h' H! t$ `& F2 G2 J
- else secremain=NumToWrite; //下一个扇区可以写完了
; l$ T2 z6 s" s+ _8 j& c, x D, C8 [ - }
: R. A- p; g ^* H9 B( q/ `4 o8 @ - };
4 V& F0 ]6 m8 q& M( K+ T, N; \ - FLASH_Lock(); //上锁; u9 D. J3 y/ j2 v
- }
; j) z+ ?* w" \6 l - #endif# d% H+ X2 ^, C+ E. F0 E, [
! o, b; I6 F" A$ E H- //从指定地址开始读取指定长度的数据
. @4 f0 C" s( U$ ~+ h - //ReadAddr:起始地址
1 E' f$ h+ I7 y+ f( w5 t: }7 G - //pBuffer:数据指针9 r( R6 m5 P& o" W& n
- //NumToWrite:半字(16位)数
7 U: _0 b, P" R6 B) K. G7 H1 ] - void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)) o R! T! h0 I" q6 d% G) f
- {! j& H; b3 V8 r, A! @* \1 T- \
- u16 i;
0 l' d2 j3 ]( @- @! B! } - for(i=0;i<NumToRead;i++)
6 r& P$ o: ]+ K1 U+ p- I) ] - {
) a6 |7 _! g% g7 r - pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr); //读取2个字节.
# D+ ]6 R- L( O/ G$ f( ?. A% c4 c - ReadAddr+=2; //偏移2个字节. |; x* |" `3 S- L
- }
. q0 _. s7 W$ e/ x& u0 G7 y1 @ - }1 l3 m+ M0 ~8 P8 |" c; T
- 0 K6 k9 s4 C! ^9 T
- //////////////////////////////////////////////////////////////////////////////////////////////////////7 q( h8 F8 e# e
- //WriteAddr:起始地址/ o2 V# ~. J' |
- //WriteData:要写入的数据
2 Y% ?8 [6 a7 D: C% R F6 e$ I - void Test_Write(u32 WriteAddr,u16 WriteData) 2 ]: ?. j) A3 c( f. Y4 Q
- {7 r9 d$ J& a+ u& ^& k: Q
- STMFLASH_Write(WriteAddr,&WriteData,1); //写入一个字 : [8 x4 s* l# ^( g
- }
复制代码 ) K8 {5 B; B0 x; a- G7 H$ s1 s/ R' Z
flash.h文件 - #ifndef __FLASH_H__8 w5 _. T; A, R( ?$ S) j
- #define __FLASH_H__3 j9 T/ t+ Q5 i6 u6 J1 W
- #include <stm32f10x.h>
: _! W2 L' ~/ u
% G: s1 a) s% J& ?- #define FLASH_KEY1 0X45670123( g: c. g; Z' O5 F. C
- #define FLASH_KEY2 0XCDEF89AB( C4 P: {* [, S9 F9 V+ s2 G: c! |
. N- y4 U# U' r" h/ n7 h- #define STM32_FLASH_SIZE 128 //所选STM32的FLASH容量大小(单位为K)
: b" [1 O% y' d4 I, y - #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不使能;1,使能)
' H; {6 Q$ |4 ? - //////////////////////////////////////////////////////////////////////////////////////////////////////( \6 [1 f/ a4 j
- 3 j- K0 j' t2 J; P/ |
- //FLASH起始地址 n; ]! B& C; m- o( o. @
- #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址& k! s E9 z6 Z2 l ]9 b
- / _, V% ?- c! N) D, w: P
, s F, j4 \* m. S- void STMFLASH_Unlock(void); //解锁. F0 l6 {7 `: c- n. a# R
- void STMFLASH_Lock(void); //上锁8 k3 V1 x* K8 S& e V$ T
- u8 STMFLASH_GetStatus(void); //获得状态
% W* Y$ S: s( M8 v: g% Y - u8 STMFLASH_WaitDone(u16 time); //等待操作结束1 d8 F d" Y3 l' m7 d* C* z
- u8 STMFLASH_ErasePage(u32 paddr); //擦除页# @* o; i" M9 R+ b# i# e7 Q
- u16 STMFLASH_ReadHalfWord(u32 faddr); //读出半字 , S+ P/ m8 k' l7 k3 E
- void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据6 H6 p) p9 [6 w! ^4 \, O
- u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址开始读取指定长度的数据/ K z& N2 ~$ N3 ~9 t/ U' T
- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据- ?$ C7 x8 Z+ P1 b+ \. o( E; B
- void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //从指定地址开始读取指定长度的数据
; p( e7 o R; j) v7 g: F5 } - % C ?( Z9 x+ Y# ^3 O
- //测试写入
3 ^) F% o8 |4 o9 c$ _0 b$ ]8 \8 v - void Test_Write(u32 WriteAddr,u16 WriteData); - l& R. K- }( A& _
- #endif
复制代码
- } t* c q8 r5 ]" o+ B/ z
& B" |3 J9 a2 i1 X main.c文件 - #include "flash.h"
5 y8 F0 [9 m& v, m0 w! Q) I7 L) k' z
; J6 K& X; H) M" V" r# V- #define SIZE sizeof(TEXT_Buffer) ///数组长度
& y6 x2 y& |* l) `2 l - #define FLASH_SAVE_ADDR 0X08070000 //设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
' L0 J ^1 y+ T1 J
( |; E; {6 L) G- const u8 TEXT_Buffer[]={"STM32 FLASH TEST"};
1 k% [7 l0 P: l5 G' Q6 l - u8 datatemp[SIZE];
+ R& X4 f+ l- _1 U - 4 Y/ _' q% ~- o( C9 k2 k
- int main(void)
& i0 e- i% C$ [ - {7 J1 N! H- x' r4 n% }" B( N
- while(1)
0 a% P8 ^: ^6 K% o3 r& \# V - {
y( s4 R& P# v5 ~9 u& [9 |: f - if(掉电): I x' ~; U5 k0 j8 e! J9 m
- {* v# P) b: a# O
- STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);//flash写函数
`! c$ Y6 ?6 T ?& @, o - }
% O8 b! k& L. \4 p! p - STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE); //flash读函数 + B4 b+ Z K5 p. U* d! T8 i5 A
- }! a. Y! \" ~; |
- }
复制代码 ' L, k& \: Z9 {; s! \' |
+ p7 V- X& S% p 以上就是flash在掉电的时候进行读写的程序,项目用到了就记录一下,方便以后查阅学习。其中的大部分代码在库函数中都能够找到参考。
" I& u2 N* ]: V. K1 y% E9 L |