一、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": O7 h" H1 l1 ~
- #include "delay.h"- I( `4 g; n, X6 p% l& O9 i& z
- #include "usart.h"
3 {: x5 C( [9 H9 R - " y2 y2 |$ P3 d2 M! |
- /***flash解锁*****/
4 A; C( B1 m2 q - void STMFLASH_Unlock(void)* z( j4 G$ R* T* X' ^
- {
$ J& w% ], W$ F+ u - FLASH->KEYR=FLASH_KEY1; //写入解锁序列6 p U* r9 h0 i' A3 t+ m
- FLASH->KEYR=FLASH_KEY2;5 e3 O' W' i5 m# M1 r
- }" G* K3 A( n2 B
0 a1 ]) B; @) [1 E9 a% i( Y& R- //flash上锁
9 T, X3 T% ^. o; P+ _ - void STMFLASH_Lock(void)
' m; l0 X" k- f7 P. D' u - {
`6 M- k# [: @; o; _% o( w5 { - FLASH->CR|=1<<7; //上锁
+ s( u" J8 ~1 c9 u. B2 f - }
# V3 M* A5 R, m2 F* Z9 W" F% y/ ^
( X6 C# A- b% r/ [- //得到FLASH状态) {4 [) h, w8 w/ T x
- u8 STMFLASH_GetStatus(void)
7 @% [4 F: |* C& j2 H$ _ - {
( u% k; Q7 u9 u+ \ - u32 res;
9 Q$ ?! X1 k ^) P, B6 A - " u9 E6 U/ S& G$ o n! J' P* e9 D6 ~
- res=FLASH->SR;
, E5 z( o: g; _6 G" h# B. I - if(res&(1<<0))return 1; //忙4 X! t3 ]% S4 K7 g+ W/ \ s
- else if(res&(1<<2))return 2; //编程错误
, @# \; i6 L" h - else if(res&(1<<4))return 3; //写保护错误: X0 b* @" c% w3 c5 v' ?
- return 0; //操作完成3 n' j) |0 F: X; U) a6 h; ^
- }- Z- ~8 M8 R& h8 t: ?4 F3 U
9 A" [: @$ T/ E3 a, a. N- //等待操作完成. ^! H9 u* R) I
- //time:延时长短
" g. w! H0 F% _ - //返回值:状态.# M# J% k& ~& m& ?. V
- u8 STMFLASH_WaitDone(u16 time)
, C# Q. u& c# z3 a- G, W5 q; \& @ - {
+ M0 [" g0 P) @% E - u8 res;
$ P6 n5 G; @- v' t
- r9 P! I8 \/ r$ M" g( w4 j5 b- do
`/ G3 v* H8 n4 b+ g - {
k+ u& D0 I" f5 A* x2 J - res=STMFLASH_GetStatus();: o1 m4 C3 p! [- \
- if(res!=1)break; //非忙,无需等待,直接退出.
; |" v7 r* L" s+ e6 C( r) k - Delay_us(1); time--; : ]1 a2 f! _ C# g# X/ M |+ q
- }while(time);" v" q% w( o* u) {' q
- if(time==0)res=0xff; //TIMEOUT( z+ F+ O7 U. w k! ] }
- return res;
( B# {7 O* F$ u$ z& u: c" y - }$ A" v/ k" @5 N& k4 j& Q' j
- , |6 L" ?) _# i: G
- //擦除页- g0 e, [) N7 J* i
- //paddr:页地址6 D' i: \# M, V& x6 {
- //返回值:执行情况
$ ?/ d6 s# k8 X; k6 ? - u8 STMFLASH_ErasePage(u32 paddr)
. |7 Q: ]& A6 H% T: v3 r( o
! T& u9 }/ k" r5 K- {9 T1 h& ~+ Q2 C: M9 q' k+ J
- u8 res=0;# C0 Q+ H" O- p9 z% L
( N3 s5 p& x4 A# ~- res=STMFLASH_WaitDone(0X5FFF); //等待上次操作结束,>20ms - p1 d7 q: p7 T
- if(res==0)7 N7 E$ `7 _6 K! W! z& G
- {
) F- x5 [# l/ K' y1 z& x - FLASH->CR|=1<<1; //页擦除
) K& o. K' u( S% _- P* t7 n) u - FLASH->AR=paddr; //设置页地址
' w4 @7 @' B/ @8 { F( | - FLASH->CR|=1<<6; //开始擦除
+ o9 W. {* Y2 W& Y& D; i$ Q - res=STMFLASH_WaitDone(0X5FFF); //等待操作结束,>20ms
$ Q5 e8 t; Z9 _3 X - if(res!=1) //非忙( \9 ?: T+ }2 y: G! L
- {. }, B# L0 Q( }7 d& n
- FLASH->CR&=~(1<<1); //清除页擦除标志.
# X1 q8 c1 P. f# O3 A% {8 j0 Q( m; r/ B4 F; T - }
/ e$ z1 @ d9 W' u* a2 [ - }) a6 U2 [3 ~9 D' t& M" L
- return res;' a8 h8 w- l. o$ y5 [
- }
, B2 J7 q* y* t' c, c; C
0 b- ?7 U8 c& z5 N- //读出指定地址的半字(16位数据)
1 k! f0 m/ u: D2 j - //faddr:读地址(此地址必须为2的倍数!!)2 L) i/ t& s+ o) p% j4 s
- //返回值:对应数据.& \& Q k* M6 U: }+ j: X: s
- u16 STMFLASH_ReadHalfWord(u32 faddr)
9 {) H9 Y) {% V - {
/ U' w6 U* K) \7 H - return *(vu16*)faddr;
, P: `. m( ]5 J7 L" J9 J$ O - }. B' E2 z- i% ~" n
- #if STM32_FLASH_WREN //如果使能了写 ( ?5 d, `( M0 [) {2 b5 h
- //不检查的写入
# a$ a0 d& x, z" K* _ - //WriteAddr:起始地址6 S- c7 G* ^# T7 p7 i2 ^
- //pBuffer:数据指针
F; z. F$ i6 a1 W - //NumToWrite:半字(16位)数
2 I" r0 w% c$ F. @1 x5 j, ^. y# z - void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
! g) ]! m0 O# |5 w# P - {
# H7 ^3 O8 P# S5 a - u16 i;- v9 S2 h! ]% y3 `7 @/ e' d
- for(i=0;i<NumToWrite;i++)* S" L6 ?3 ~# q- p! p1 n8 h
- {
- d9 ^3 C8 o' V/ d) ]. t- a8 D - FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
! h. \5 ~ e# O) `0 f- O- C - WriteAddr+=2; //地址增加2.
5 o" w3 ]- n4 x/ W2 F: ^4 s& v1 o - }
' ~7 b9 T J* F8 N - }
# p& c; I V1 V' n$ P - //从指定地址开始写入指定长度的数据
7 Z) R2 ~1 }: S Q3 H* w, ` - //WriteAddr:起始地址(此地址必须为2的倍数!!)
& ` U4 D3 l. o6 A, \) v. L4 C - //pBuffer:数据指针
, o( e* ]) S! c! T/ a - //NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
+ X0 ^! b% V! I6 c7 ` P - #if STM32_FLASH_SIZE<256
2 G* T" h$ \2 D C3 f; j - #define STM_SECTOR_SIZE 1024 //字节
$ X* \3 f; P) c. C3 ^9 v, _ - #else
0 D- }( F' @ j - #define STM_SECTOR_SIZE 2048
; [# S& i" W s }. w5 W8 | - #endif
3 o3 G8 \8 }& M/ t S, C3 k" u: A - u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; //最多是2K字节6 b& k4 g- r* \. h5 c0 t5 _- g
0 Y6 B% m3 ^; R I% \1 g$ l- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
* ]' a% s. J6 k' D- A' } - {3 [ ~+ ^# n' z' ?
- u32 secpos; //扇区地址
1 X$ H; S3 @: N - u16 secoff; //扇区内偏移地址(16位字计算)/ B3 M8 t; v5 R i5 I) n4 O; O
- u16 secremain; //扇区内剩余地址(16位字计算) 9 K/ x$ C9 u/ ^
- u16 i;
/ _( `& D1 k8 k2 c/ ]" C - u32 offaddr; //去掉0X08000000后的地址
& h% z# S7 e( w& j -
: a- b! s* T% f4 T. @ - if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址; e0 _4 M" P) i7 q
- FLASH_Unlock(); //解锁
: S+ l. M4 v; v2 p" z - offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址.% G% ]& S* o) \
- secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6
" C+ ]9 Y% T) i" [$ q- }! M" _; a - secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)
% s) {3 x1 b* Q( {7 W4 O$ g7 V - secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小 5 }4 Z7 W, v% m
- if(NumToWrite<=secremain)secremain=NumToWrite; //不大于该扇区范围1 @' ]: d% g) F4 T" j7 B9 Y
- while(1) 4 [. c: V" q; p+ c/ D
- { 8 W/ r4 ~. s4 t+ y; b! s
- STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
?* X) J; l, _7 \' Z - for(i=0;i<secremain;i++) //校验数据
# k0 a1 W, G% P) ^, M - {9 C" D5 t- S3 [. z/ \
- if(STMFLASH_BUF[secoff+i]!=0XFFFF)break; //需要擦除
* S4 }$ @' v, V+ a: O1 [$ [ - }
4 B u1 @6 W# e9 @ - if(i<secremain) //需要擦除5 Y7 i1 Z8 J8 v$ ]% ]7 f! Z0 C
- {' T/ Y# n1 i6 ]( u9 b4 l! j
- FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); //擦除这个扇区& N, Z D$ p) I! ~% B
- for(i=0;i<secremain;i++) //复制' K( u. z/ O9 s0 d1 _$ _
- {
. v) E0 @5 {! V - STMFLASH_BUF[i+secoff]=pBuffer[i];
2 ?2 d) M- \% ^ - }1 S' E. v# T( ^7 h2 G
- STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区
3 p2 C& _' C* H - }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain); //写已经擦除了的,直接写入扇区剩余区间.
# x$ G7 a7 B9 H' d8 x& G - if(NumToWrite==secremain)break; //写入结束了6 U: o8 H2 i& b# l
- else //写入未结束: w% `. M0 P+ {% H- o6 {6 [
- {/ g' t) D; \" e2 v" V+ |
- secpos++; //扇区地址增1
1 z+ e& t+ E* ~% E/ S) c6 O7 @5 C - secoff=0; //偏移位置为0
v C5 Y% ?4 d+ R, t) d, V - pBuffer+=secremain; //指针偏移
. E, |; v% a4 P+ T7 M0 u5 o% r - WriteAddr+=secremain; //写地址偏移 , l6 S _; Z9 \
- NumToWrite-=secremain; //字节(16位)数递减
0 m* ?+ ?0 ?0 {( l8 J S: c - if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
+ _0 ] V7 _/ p* W* J/ d5 z% @7 L - else secremain=NumToWrite; //下一个扇区可以写完了* K; \+ n7 _1 g5 Y+ J
- } N3 e* ]- C+ q+ i
- };
8 L+ D- J" l4 n9 d) L/ b+ p0 o$ k - FLASH_Lock(); //上锁4 K( p* C( ]! d- R* j) b; t" u
- }
5 E2 W- Q' d9 f1 D2 D! X9 X6 K: b - #endif% J( [, L, b! _" h, z# I
- . t' O9 D0 |3 d- P. p
- //从指定地址开始读取指定长度的数据
( z3 Q L- D$ P: l6 B- G, z/ F' X# k - //ReadAddr:起始地址
, c* x" _# [* m" Y& s# |. W6 K - //pBuffer:数据指针
$ b! r% J& D2 t& @/ }) ~ - //NumToWrite:半字(16位)数0 |! O$ z0 r, {& C. K5 n; d
- void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
4 y9 ~* W. a, ~, G S5 o - {$ D2 X2 u* ` L1 W9 q% M
- u16 i;
; j4 o0 p$ J& ` - for(i=0;i<NumToRead;i++)
/ d4 X0 P7 I1 ?( U$ J/ P: Y2 B - { c k' L/ a( U; n# ~
- pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr); //读取2个字节.
9 }- I; |* c" c: [8 T - ReadAddr+=2; //偏移2个字节$ G: x4 m# g" }, v Z6 W
- }0 z2 {6 h4 m1 J1 x" n* A6 ]
- }
3 V+ Z/ i- ?2 `2 y& K$ i& H$ w - * T1 T. }$ u8 Q6 S0 i* D [0 {. O
- //////////////////////////////////////////////////////////////////////////////////////////////////////
/ X0 u x5 p, C1 W' s$ g! H5 s* G, W - //WriteAddr:起始地址 A5 r, n4 {- {: R: W) j; [3 _
- //WriteData:要写入的数据
4 i) O8 P; t# K" s+ t - void Test_Write(u32 WriteAddr,u16 WriteData)
6 I# _4 L' ~6 V# P0 k$ Y* V - {
3 ~ A: q3 R; B- M. l: ?4 r - STMFLASH_Write(WriteAddr,&WriteData,1); //写入一个字 " u8 W2 T) h0 ~" w! I: h
- }
复制代码 7 c# l& o( n8 X( d6 Z
flash.h文件 - #ifndef __FLASH_H__ G8 E! T% z4 T7 E" H
- #define __FLASH_H__
1 m" Q2 [! G1 }7 g - #include <stm32f10x.h>
" U$ c7 B6 G6 D! G
9 W# g3 Q6 T5 o4 D) e- #define FLASH_KEY1 0X45670123
6 p) S7 H: C9 l6 U - #define FLASH_KEY2 0XCDEF89AB
) ], c+ s8 C& t3 J6 D) ] - % ^- V/ m5 w: P" W7 C
- #define STM32_FLASH_SIZE 128 //所选STM32的FLASH容量大小(单位为K)+ Z+ g4 |' v# n. D! v7 t/ W
- #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不使能;1,使能): G5 n2 m% V! g, F! z0 _
- //////////////////////////////////////////////////////////////////////////////////////////////////////
3 Y4 C; M3 x# I( c! u; h" S0 B8 u
6 L; H P: ~5 a- L! E. x2 d! Y- //FLASH起始地址
& N* A# q9 M4 I$ H, g6 d( T - #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址0 E3 c3 C. H2 j1 ^- O; \, t
- ; a) T( I. C. P* Z: j& I7 D
. Y7 ?( E- I( n1 K% n2 m- void STMFLASH_Unlock(void); //解锁
8 C V1 r7 ~9 U$ R/ k: B& M. f - void STMFLASH_Lock(void); //上锁
" L5 [& B, L: T! H. ]! f - u8 STMFLASH_GetStatus(void); //获得状态
, K0 Y( n9 i% d, @) B3 u' v - u8 STMFLASH_WaitDone(u16 time); //等待操作结束
3 \9 e# o5 m4 K; F* @ - u8 STMFLASH_ErasePage(u32 paddr); //擦除页
) W; c3 j5 O9 ?! q& U - u16 STMFLASH_ReadHalfWord(u32 faddr); //读出半字 . H: r0 s" f3 r) w
- void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据5 O+ h: \ U) z3 e% I
- u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址开始读取指定长度的数据. K+ X# L5 w5 n: d4 C( E- U
- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据
: @4 c9 [- Q" Z8 p& D; D, y - void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //从指定地址开始读取指定长度的数据/ h$ E3 X2 k) s4 p8 L. H/ ~
- 9 D" l: X5 [4 O4 S! D# g
- //测试写入7 I" q ]( I9 K5 E5 t
- void Test_Write(u32 WriteAddr,u16 WriteData); 3 c1 O! M! b8 V: h5 q& b) k
- #endif
复制代码
7 m) q, e# K4 N6 D3 M% U" j
/ o9 F# @) N+ a s9 t: R main.c文件 - #include "flash.h"
- N" V$ z6 o4 X" X8 i3 ^ a
; ^+ @/ ]9 d+ \/ _- #define SIZE sizeof(TEXT_Buffer) ///数组长度, E6 j6 J1 V: ?- V
- #define FLASH_SAVE_ADDR 0X08070000 //设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)9 d& F+ U' Z0 N3 }9 ]+ t$ `, T
- 2 q; O" g5 m7 n% [4 C% [1 ?# r6 w2 n4 Y
- const u8 TEXT_Buffer[]={"STM32 FLASH TEST"};
`& u* D- w& H: _ v2 ` - u8 datatemp[SIZE];3 d% p8 G1 d# N
$ k8 s' ]9 W- Y- ^( {4 `" H9 x- int main(void)
7 |5 V& ^0 z8 e. U+ s$ Z! [# L) o! z+ I$ B - {
4 @3 o! N% i6 @5 `- D. n( l% ~: f - while(1)% v1 R0 h8 q2 ^6 D! b
- {
% m: B" l% k$ B. ^ - if(掉电)
6 v5 i. W: C) H8 L! v+ W" W8 A- A - {/ v' B" }% ]- q9 ` k. W
- STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);//flash写函数5 [1 j t. G& C8 v/ Z
- }
0 g( k: ~& r% y% ] - STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE); //flash读函数
. ]: v) F6 F0 L9 [: R - }. h. d% g7 g4 p5 }) Y' O
- }
复制代码
3 T, F' C2 F# T, @; _" y) W" d! r8 c. e9 p
以上就是flash在掉电的时候进行读写的程序,项目用到了就记录一下,方便以后查阅学习。其中的大部分代码在库函数中都能够找到参考。
& g/ [ f, h* p- v% K- K |