一、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"+ f9 Q& L8 g1 X' ]& g
- #include "delay.h" ]# g( O7 f$ H7 A8 J: c+ }# \
- #include "usart.h"+ U3 r8 T$ b& Q
+ t5 f% ` o5 U7 ]8 u$ _- /***flash解锁*****/3 d* u, E; C- z q h
- void STMFLASH_Unlock(void)1 c, D; s/ |2 Q; X
- {" O; @5 R$ w4 {) |! ] V! J) {
- FLASH->KEYR=FLASH_KEY1; //写入解锁序列" d' v% M- Z$ F# n
- FLASH->KEYR=FLASH_KEY2;' K. A- s7 X* r( S' x% l. ~
- }
8 |- y' n' w2 h
/ i/ t/ B$ b- n) f0 ]. E' M) s" s- //flash上锁9 o: e7 ]; e) d5 P+ I( M0 ]
- void STMFLASH_Lock(void)/ i- U5 C# @, H
- {7 D7 \4 \# q2 y& H$ l/ P: U
- FLASH->CR|=1<<7; //上锁
& ~( l: p2 ~, _. X - }3 B! q) e" u+ O
- 4 x) t9 r9 N9 K3 ?* ]5 h8 K8 t2 V8 n, R
- //得到FLASH状态
( L( _( l1 n. _, ]# ?. a, } - u8 STMFLASH_GetStatus(void)* {# f% L- u$ a, m4 y2 k4 M
- {
' }5 Y% @/ p' M% v# }5 {- b' H& _ - u32 res; 2 `) |! k1 g! Q& G% l! }
1 X% u7 x" H1 e( c- res=FLASH->SR;' d, h' r+ k( T* _. W7 v, X1 R& L% m
- if(res&(1<<0))return 1; //忙- d' w8 G% k" h+ B- b1 D! m
- else if(res&(1<<2))return 2; //编程错误 ~; Q$ A& d8 ^0 U$ D% E
- else if(res&(1<<4))return 3; //写保护错误 T r* ]. h3 I8 E
- return 0; //操作完成: n' ]; I% r: ~8 N
- }
0 D+ c6 B* k) H2 j" Y - 3 H4 o+ R4 ?- k' P6 p* ]3 ^
- //等待操作完成
6 }6 E9 d! y2 \% h) w$ N/ Z - //time:延时长短/ O4 y' q: o& K/ ~
- //返回值:状态.
1 {' Y7 l( S8 m7 ]/ K - u8 STMFLASH_WaitDone(u16 time)
4 N/ Y8 i( X7 T- G5 ^, B$ V& {1 M! l - {0 Y8 ~2 m. E5 v+ d
- u8 res;
4 G3 B; X. p$ m8 ~' J* K - . j9 n3 s- o2 c3 t* p* b8 z
- do$ j4 W- o9 n: a' Y7 ^
- {6 g: \% W$ U6 E# s, V7 R4 b* _
- res=STMFLASH_GetStatus();
3 s& v- v+ |' e" X; ~: f - if(res!=1)break; //非忙,无需等待,直接退出.
; \' Y' F/ C% K" M! f1 q" I$ D' [ - Delay_us(1); time--;
, ~2 f& Y1 i- R9 G; C - }while(time);4 q( @( a& Z1 {) U* e% v+ p
- if(time==0)res=0xff; //TIMEOUT
' H$ F- d$ S* k - return res;
# H/ U& K0 t5 s9 i: V6 `/ b - }
! e* e) S, }- Z4 `# W- S* r/ G7 v - $ e- ]; p0 I! B
- //擦除页
4 ?( Y. R0 }/ Y4 y - //paddr:页地址& [! ?8 T! [: Z, @9 z
- //返回值:执行情况% |5 l0 R) ?6 N: w
- u8 STMFLASH_ErasePage(u32 paddr)- T5 m* H; S, m9 A+ D% A# s3 k
- : e0 k! M4 W* E% o2 w
- {
4 x8 x1 @1 t& H4 A# B5 h+ ` - u8 res=0;
' P# j+ U6 ]+ _8 O, H% R - 5 m4 J9 p0 ]; j5 u3 v
- res=STMFLASH_WaitDone(0X5FFF); //等待上次操作结束,>20ms
3 z6 ^- {/ y4 |- M# u - if(res==0)
; G. K. I/ |7 \ Y2 R" l& T - {/ i1 E4 C$ _0 v- V* O/ p
- FLASH->CR|=1<<1; //页擦除
0 M$ [! W" G% E4 | - FLASH->AR=paddr; //设置页地址( Y" h4 \, A" y! _' u4 b6 Z$ f
- FLASH->CR|=1<<6; //开始擦除 ) _6 `5 ?2 N0 `6 N; h. B
- res=STMFLASH_WaitDone(0X5FFF); //等待操作结束,>20ms 2 b) q& V* ?1 D& ~# P2 ~! `
- if(res!=1) //非忙6 W3 s- C% y4 A5 K4 q
- {& B4 a: D2 p: C/ r, G
- FLASH->CR&=~(1<<1); //清除页擦除标志.
" d9 z9 D% s7 o7 G2 v - }9 P% @) c2 W: I, t
- }, S) Y7 G n, y, k1 H6 Y% n* V
- return res;
" w; t5 T3 r9 q5 g q1 I1 v% X - }4 f8 o6 r1 t5 r
- & q3 |+ ?3 e( V/ R- ]
- //读出指定地址的半字(16位数据)5 S* Y+ q9 E/ w8 L, H8 P
- //faddr:读地址(此地址必须为2的倍数!!)3 P% q) F: ~ g- R7 e
- //返回值:对应数据.
' S& t: I9 C2 g% `, N8 ]) ?7 R- L- _ - u16 STMFLASH_ReadHalfWord(u32 faddr)
9 Y; P5 Z9 ]2 ~" K8 Y" | - {
# r# h) `* Q4 Y - return *(vu16*)faddr;
, q6 W, J, k. o$ ? - }
3 [7 }2 `, O7 }% Y - #if STM32_FLASH_WREN //如果使能了写 4 Y# R' \: b3 t4 d3 q/ k
- //不检查的写入
# i+ {( R+ b& P - //WriteAddr:起始地址8 o1 q% K' x \3 r8 ~
- //pBuffer:数据指针
! k4 F% ^, J$ s- C - //NumToWrite:半字(16位)数
* X. ~0 {5 V4 H( F9 K) j - void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
0 r! O N9 v8 m L( `/ H - { # K0 f+ h! z" [0 R$ J
- u16 i;
* G8 a" G) C1 {) t: p - for(i=0;i<NumToWrite;i++)7 N* Q5 R! k" S+ j# ~ t/ q
- {9 X* g' R P) m+ k- G& U# J
- FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);7 ~: q* Z: F! c9 w
- WriteAddr+=2; //地址增加2.
+ Q$ D& o+ V$ [ - } ( G& \8 i% J M, p; l- Q
- }
3 A% n1 F8 G4 P3 w% H- z - //从指定地址开始写入指定长度的数据
6 L1 Y. u ?- z; m - //WriteAddr:起始地址(此地址必须为2的倍数!!)
' T/ o! |' L( F3 h7 s - //pBuffer:数据指针
; s. I5 x' w2 J9 V4 r - //NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
) S( w' ]; i$ m2 W" U7 Y/ Q3 r - #if STM32_FLASH_SIZE<256- \" ~1 s9 l2 O4 |* O
- #define STM_SECTOR_SIZE 1024 //字节7 n$ e; X; S( k R% n
- #else E y6 j; Y2 [' j* H
- #define STM_SECTOR_SIZE 2048
n+ [# F0 j& j0 M: f - #endif
* I0 a" _& A, J. I6 i8 S - u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; //最多是2K字节
; n _9 L1 ~+ ] - # d+ `# U% U' m9 |3 v- X! T
- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
2 b0 z; H9 Q$ w0 p - {
( L" ]# k, E) m8 n; {2 v; f - u32 secpos; //扇区地址
: I; S5 Y+ C% n$ ? - u16 secoff; //扇区内偏移地址(16位字计算)
/ P% H6 {+ S; v$ W4 z7 X: t - u16 secremain; //扇区内剩余地址(16位字计算) 7 A6 Y \8 A3 U7 l t6 t
- u16 i; - P/ |+ {) d1 ~" r
- u32 offaddr; //去掉0X08000000后的地址
) D6 ~9 |0 [# S r# _ - % S& {7 W" a1 y9 a f) R
- if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
# L" Z' g( S- i3 S; r0 t - FLASH_Unlock(); //解锁+ g5 A! l6 z: E; E5 l
- offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址., c9 w5 O/ L) p8 |6 l
- secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT64 o5 h9 t1 Q" }, D( c/ `
- secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)
8 C* E5 [& p/ Q* l - secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小
$ m7 \3 h8 y# |4 u# a: J - if(NumToWrite<=secremain)secremain=NumToWrite; //不大于该扇区范围4 P& }- W' \8 U$ u, x
- while(1) 0 ?) z% J; h4 ^
- { - c* A5 ?+ c( p O& i
- STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
7 |, h, e/ z- `; J/ p - for(i=0;i<secremain;i++) //校验数据
9 ~& K) k+ }2 m4 O+ p$ ?+ E - { V3 F! [: H( H2 i
- if(STMFLASH_BUF[secoff+i]!=0XFFFF)break; //需要擦除
5 r* O# @1 K8 [2 O - }/ {/ L/ {- V2 h8 [! ^) _
- if(i<secremain) //需要擦除1 R+ @, e" I% c6 E# s8 u4 p" x
- {
' f, s' o4 c3 `9 d- B% [ - FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); //擦除这个扇区4 O2 i" r+ [: i& l, Q
- for(i=0;i<secremain;i++) //复制
. Z( b8 V' T- U+ y" d - {& K' j( k( K9 ^( y
- STMFLASH_BUF[i+secoff]=pBuffer[i]; & z+ A* j3 P% a! O
- }
$ l U6 N/ Q/ R" r. R+ O! b$ C - STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区
# w- I/ u3 z; b! n; _3 M" O0 ] - }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain); //写已经擦除了的,直接写入扇区剩余区间.
5 m* ?9 v+ r3 T - if(NumToWrite==secremain)break; //写入结束了
* `1 [8 F( R2 F0 F$ i V - else //写入未结束 C8 e% A" B& Z
- {
5 H& r2 r, M2 R' y, M - secpos++; //扇区地址增1
8 X G0 G7 e9 D w! v3 y - secoff=0; //偏移位置为0
1 n; ?6 ]( ^6 W2 h# l - pBuffer+=secremain; //指针偏移. c! ?: l. h) h
- WriteAddr+=secremain; //写地址偏移 ! t8 w3 r5 ^7 T1 q* e' o2 j6 B
- NumToWrite-=secremain; //字节(16位)数递减1 x! L0 A% W& N, l
- if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完2 o- r2 r0 A6 O; m* t
- else secremain=NumToWrite; //下一个扇区可以写完了" V; {" w7 `8 R' t; X6 h
- }
3 q+ F# Z0 U, }- v0 F. E - };
: P* L/ t# n0 Y - FLASH_Lock(); //上锁
: F+ E; k' Y g - }
& E9 c- x4 p3 Q5 M0 O- d - #endif% a& e6 J1 ]% H Y
) i) P+ c* b; q, X' w: c+ U- //从指定地址开始读取指定长度的数据: F# z( o, N0 V' R; N
- //ReadAddr:起始地址
; g) ^( Q9 S1 R, q/ d9 k- E - //pBuffer:数据指针
% A5 G: X& J+ Q! X+ x0 Q1 | - //NumToWrite:半字(16位)数$ ~3 \4 K7 Q; D
- void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
* k/ ~6 a& m9 j7 T - {# m! ^2 k9 r! \/ N: C/ v
- u16 i;9 [2 o' q5 C/ {3 u" @
- for(i=0;i<NumToRead;i++)7 k' k# N4 P0 Q% X% c. D
- {1 N9 o- u, r. e6 x
- pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr); //读取2个字节." G7 [; T7 r: k, y
- ReadAddr+=2; //偏移2个字节
' H9 ]7 ` P' i- ~- [! e1 T - }
|2 b n1 H" h! N9 o - }" ]' y, E, K( M, D; B, L8 N
- - w1 G! W& ?7 O' ?* L
- //////////////////////////////////////////////////////////////////////////////////////////////////////
5 c$ B6 k l1 l# O. j - //WriteAddr:起始地址
! P; J) Y. ^- X' G2 a - //WriteData:要写入的数据% t* M0 i5 K! N$ J" ^7 h
- void Test_Write(u32 WriteAddr,u16 WriteData) - p9 W3 M1 b* M4 P
- {3 ~/ K, D1 A! J/ q6 L$ u0 i% s6 P
- STMFLASH_Write(WriteAddr,&WriteData,1); //写入一个字
5 T, |7 l C! T5 X% U% l - }
复制代码
! p+ @4 r" ^, w$ v2 u& { flash.h文件 - #ifndef __FLASH_H__
! C$ d- O; d# m* X$ g+ Q. U4 a) R - #define __FLASH_H__# W$ e# A2 Y1 a' t+ f
- #include <stm32f10x.h>) _) q. ~0 E! J0 D
- 1 @8 I" B$ N+ C# y
- #define FLASH_KEY1 0X45670123
) L) x! m$ f Z+ w* O% b - #define FLASH_KEY2 0XCDEF89AB
$ Z" o* f) A- b, R0 r9 E7 x9 T
( o2 ? o$ P8 I- #define STM32_FLASH_SIZE 128 //所选STM32的FLASH容量大小(单位为K)9 o( R. \1 a/ b- ?7 m& ]# s
- #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不使能;1,使能) h C- a6 s2 S% W" _1 N, Q
- ////////////////////////////////////////////////////////////////////////////////////////////////////// `; p; R) O" Y2 A
- 7 O+ D2 B. ]( h. J
- //FLASH起始地址
# {. V6 g/ f+ r! Z- b4 F4 W - #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址 \+ ^" d* {. W3 T
v2 Y9 c4 {4 g' c8 i, D/ L5 f
- a: p9 D8 \& O+ z9 q- void STMFLASH_Unlock(void); //解锁
4 L0 P% e1 Q3 ]2 h' a; I - void STMFLASH_Lock(void); //上锁
! u$ K/ l$ d! ?& X# P# l - u8 STMFLASH_GetStatus(void); //获得状态
6 R: k/ q: a N: p7 u - u8 STMFLASH_WaitDone(u16 time); //等待操作结束# Y7 [! x$ d+ n* U9 T2 ]/ b0 _* B
- u8 STMFLASH_ErasePage(u32 paddr); //擦除页" T: f. N# k8 ?7 q3 x
- u16 STMFLASH_ReadHalfWord(u32 faddr); //读出半字 9 [2 B& b9 h& p: i% a& i% ^ k
- void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据8 ~9 {' ^9 b7 o" k" ?
- u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址开始读取指定长度的数据. Y& E5 T# k7 e
- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据+ k3 b' N8 A' c3 H9 v+ l
- void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //从指定地址开始读取指定长度的数据8 g2 s1 U' Y0 z, x @# @+ ?
7 m- W3 o) H* V7 u9 c# u5 _- //测试写入
, J* ]6 G* c- |3 u - void Test_Write(u32 WriteAddr,u16 WriteData);
: D. ]: R: U4 ~" \8 z3 E# ~0 b - #endif
复制代码
& O0 V& R ?7 o) j. [, r / Y) O l( i3 o$ y8 V, t
main.c文件 - #include "flash.h"
. Y5 G& Q L C) X1 c" q
; ^7 O6 _( N9 q- V- #define SIZE sizeof(TEXT_Buffer) ///数组长度1 v0 c( w0 x/ P, D; b+ g
- #define FLASH_SAVE_ADDR 0X08070000 //设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)9 v8 R% o% w# s% ^" [& G6 \) T; u
. {& e' `9 |0 _" n- const u8 TEXT_Buffer[]={"STM32 FLASH TEST"};4 P( m1 t9 r. M0 I
- u8 datatemp[SIZE];4 u% V7 I" o: Z( E$ S
- 6 M5 o8 ^2 Q( }! A$ q& f! A
- int main(void)! @ t a. Y- D7 [/ e; W/ A
- {
1 \. X. @# W% I3 h. `# R - while(1): [6 ]4 o9 m9 Z2 C# o/ u
- {
9 I) M* O9 m* L; B: k - if(掉电)
* `6 _. N3 D; D2 O9 x) g) F - {
# a1 D ]) p; }& ?' P8 N - STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);//flash写函数1 e; @. m) o: _# R2 l8 P: A+ k
- }
! D* m6 [% X+ l) k) M4 n7 } - STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE); //flash读函数 7 d6 ~% |. `7 W/ q i0 A3 n3 i
- }
4 b- K1 c9 r2 A G0 z' x- |1 {1 B - }
复制代码
( Y' @: @( T: x; _ Z9 D P! b5 {6 u! }1 @
以上就是flash在掉电的时候进行读写的程序,项目用到了就记录一下,方便以后查阅学习。其中的大部分代码在库函数中都能够找到参考。 ! r1 \1 r9 ?+ B6 @" p# U# x) [+ l3 a
|