一、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"
6 q$ m$ b, F( M' y+ T/ z( U - #include "delay.h"2 Q8 \) r9 r* K6 O* s& y2 R, [
- #include "usart.h"3 D# x) x- a* s+ A1 ^
- ) T% @1 _3 A, k9 O
- /***flash解锁*****/
( V* x* X) n. F& ^, S1 N3 D - void STMFLASH_Unlock(void)
$ R: H2 j! y/ e- {1 A8 p6 D - {3 |4 Y7 k8 U0 o1 U1 j: U% S2 p) P" ~
- FLASH->KEYR=FLASH_KEY1; //写入解锁序列
}; k p* C/ \% U' H5 _ - FLASH->KEYR=FLASH_KEY2;
% W) M Q1 W) n& C: I: s/ F - }9 @6 y3 I. i: ~/ c2 [
x6 v; y) M, D/ f# d1 n, w! P- //flash上锁0 K- Q; ?$ E3 ~# B# s
- void STMFLASH_Lock(void)' h+ o6 n6 D- ^! {! `* U6 x i0 M
- {
1 `9 a% m: f0 _, q - FLASH->CR|=1<<7; //上锁9 C; ?* d+ W5 |" O6 H6 w
- }: A( d5 v1 A* M. @
M0 _* H/ \: m z5 u" N- //得到FLASH状态
}; q( U" Z t( R, R - u8 STMFLASH_GetStatus(void)8 F5 Q; R. }+ |4 v5 U
- { 2 @. C/ Q* t- i2 I+ S4 Y; _
- u32 res; # M+ |! r8 a' j/ Y, r7 i
- 8 H. @7 w( w/ j8 X2 ^6 L
- res=FLASH->SR;' l% S- H: _9 ^4 d" g% J
- if(res&(1<<0))return 1; //忙6 J2 V) l7 R+ U4 C# O% b6 n
- else if(res&(1<<2))return 2; //编程错误
6 K/ z; k) l& u3 ^% e% e - else if(res&(1<<4))return 3; //写保护错误
" }3 p- f; D% v - return 0; //操作完成
& G! Q- i. D# D v - }
) T% W2 n1 ?( n$ G" U# L9 _ - ) v. V2 {% U$ r' c" _" \3 d
- //等待操作完成
, O- U( L% J* t" g8 V - //time:延时长短; u2 _6 ~- M- c- M( A( Z x
- //返回值:状态.
) C" F. J2 D1 m' @6 M. p - u8 STMFLASH_WaitDone(u16 time)
* f4 u3 e, z$ ^1 [/ A" n - {, w3 u' V) v. q. I" Q
- u8 res;
- c. L8 s$ H" m
7 P/ Q }" {8 E% }- do: h K7 ^8 f3 o/ q& l
- {
, v& b0 _6 b* x; v; F, y - res=STMFLASH_GetStatus();# `4 X- P! [- e2 w7 ?
- if(res!=1)break; //非忙,无需等待,直接退出.
/ Q$ H9 H: ~# ]5 k* B( [! I - Delay_us(1); time--; , u5 K H3 @7 M4 c
- }while(time);
. z4 u+ b5 S) B5 b/ s - if(time==0)res=0xff; //TIMEOUT
( \0 ?0 S. m! \7 ]4 O - return res;
1 G* j4 N0 c6 T" Y, S5 | - }/ ^0 Q* I, s+ h3 d) y! x& i& `9 R
- + O* b2 T6 J5 F; e, ?
- //擦除页' B/ [& P0 v% s% a% }. _$ m
- //paddr:页地址6 y3 k5 l. ?+ k, M! T
- //返回值:执行情况
8 i. Z, ], H! j! r - u8 STMFLASH_ErasePage(u32 paddr)
# A2 u$ G0 p8 K# K$ @- E - 7 R$ u( l1 w, G3 i' _; ~. ?( D
- {
. n# `- E$ p1 e9 o - u8 res=0;9 L; t ]# J5 W" `- T g
- ]& `2 T( [( l* B# w/ _) i- res=STMFLASH_WaitDone(0X5FFF); //等待上次操作结束,>20ms
9 A' z! F! A5 Y2 K* Y5 d! r - if(res==0)/ Z" I$ l5 j5 b1 o- M
- {( h# s5 M+ Z/ _1 [
- FLASH->CR|=1<<1; //页擦除7 R6 k: J' ]2 k7 a9 ?
- FLASH->AR=paddr; //设置页地址
8 I! E! O' N+ t$ v- [0 F - FLASH->CR|=1<<6; //开始擦除 Y9 q" j4 j/ m6 I
- res=STMFLASH_WaitDone(0X5FFF); //等待操作结束,>20ms * z; \' n$ I" R$ t& f
- if(res!=1) //非忙
: N8 H5 v9 a( K5 j W& O1 k. x7 t7 ~ - {
# V% O2 D- d5 Z J& |; [' N% D - FLASH->CR&=~(1<<1); //清除页擦除标志.
% V' W* P: ~% }( [" {( o - }
* ~& ]3 c$ A) D4 M - }5 ]+ m- P) I# b: {( e, h! j( ?
- return res;
5 ?; l2 m2 w1 v - }
( G# [, `6 r% W2 m
" `, U' d" l. d& g8 [- //读出指定地址的半字(16位数据)9 ?- \0 w& ~$ W. v% S
- //faddr:读地址(此地址必须为2的倍数!!). P0 T& y+ a2 M" B X( @
- //返回值:对应数据.4 d- G0 c# g* P" @' b
- u16 STMFLASH_ReadHalfWord(u32 faddr), [* g* [; k8 P5 d1 ^
- {' H' i* q3 o( y/ u' ^) ?2 ]
- return *(vu16*)faddr; . X- k+ R5 p! L! f }, S; j7 i
- }, i+ I! W0 I/ o$ d. T
- #if STM32_FLASH_WREN //如果使能了写
# H3 ^+ l4 o3 f' S9 _ - //不检查的写入
1 G E( O, r* w1 W ^1 } - //WriteAddr:起始地址, n5 v% d8 ?$ E- t2 j( d
- //pBuffer:数据指针
+ e, U" _/ `( B+ ]6 S) B1 o: L - //NumToWrite:半字(16位)数 8 l m5 ]4 Q }. @. Q" M6 W1 {
- void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
1 A2 x6 b& K! t: Q+ C& y - { ) G9 s1 p, O. o5 t% e) ]: Z6 V$ B
- u16 i;. ?5 g% i, M' b& b3 `* v
- for(i=0;i<NumToWrite;i++)
/ r: n4 b- y- @6 \! J - {
# e( y7 y: b( y - FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
" |" Q/ W# \8 Y0 F5 ?. _: ^ - WriteAddr+=2; //地址增加2.+ d$ X5 M) \% T% h4 h
- }
' t/ p: |" Z& I6 }& i! L - } ; k/ n0 F" K' y/ H1 f
- //从指定地址开始写入指定长度的数据
& w: A! @2 i: B- k& z1 J - //WriteAddr:起始地址(此地址必须为2的倍数!!)
- ~6 D" c6 i5 h, z5 L" e - //pBuffer:数据指针
% X e6 n1 _' j& I! a - //NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
4 d6 G7 q9 c3 m5 v+ M$ H) _+ t - #if STM32_FLASH_SIZE<256
# Q# M9 e+ j, i% r& c - #define STM_SECTOR_SIZE 1024 //字节
+ x) L U/ N. h. |' w9 g0 i# Y - #else
9 ^$ B6 ` S- R' T* ]( V1 L - #define STM_SECTOR_SIZE 2048% j1 c# g1 R- o' H
- #endif x. @; P9 k4 Z$ M% U" i& p
- u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; //最多是2K字节
" M# y! a! ?2 z' r4 F* o - ( d- U2 U* W" t# F
- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
: X+ ~9 i; ?, @, x4 j - {
9 s0 g; k4 ~5 O& v6 T2 j9 D- v! j - u32 secpos; //扇区地址
; J" K! I$ X' H - u16 secoff; //扇区内偏移地址(16位字计算)( w9 n4 f$ M% \, ]/ C5 ~
- u16 secremain; //扇区内剩余地址(16位字计算) 5 f; L: S# Y3 A
- u16 i; N" g a1 J- R4 ]% p
- u32 offaddr; //去掉0X08000000后的地址
" |, y5 _1 d# J8 V _ - ) V+ w/ E: n8 B) z2 g7 M1 b* g
- if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
& k/ r1 a. z. F B2 n( D - FLASH_Unlock(); //解锁
9 v8 r2 N6 n' j3 t8 s7 O - offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址.: C @- A- i2 f# D5 P- [
- secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6
; P6 s6 S E0 a8 T) e( D - secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.) {; W8 p5 q( z$ q Y
- secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小
! I8 R, @. N" y' n# E! g2 O N - if(NumToWrite<=secremain)secremain=NumToWrite; //不大于该扇区范围
- X+ P$ a9 e* C$ ` - while(1)
8 n" i0 ], f$ ? - {
2 y* N( I7 }* ~8 C - STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
% S2 `" P# @1 j9 d) m# ^ - for(i=0;i<secremain;i++) //校验数据5 }5 P) A/ |7 U- T$ a3 ^% V2 q
- {
' b7 b+ ^8 H- g( ^1 q5 c8 Y - if(STMFLASH_BUF[secoff+i]!=0XFFFF)break; //需要擦除 . j$ s) x0 s8 w6 t2 J
- }
9 c. @! `5 ]1 H - if(i<secremain) //需要擦除
, Q: n8 n" w- C9 N& D% K0 I* [ - {
' j/ Q7 \% ?' {4 N: @" R M - FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); //擦除这个扇区1 L! z) `$ F8 g$ ~8 h
- for(i=0;i<secremain;i++) //复制) N: o3 `$ k9 r& T! W- X; U- \# }* ~
- {
" W7 Y U9 m- {! S5 ~3 ~* j - STMFLASH_BUF[i+secoff]=pBuffer[i];
. `; o# y/ i9 x: k8 `: u. n Z - }, v6 L% \. w& h& @3 |) i# h5 p0 u
- STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区
4 g' N: I8 F% t+ D - }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain); //写已经擦除了的,直接写入扇区剩余区间.
5 i8 l R9 C4 @3 ~* E: z - if(NumToWrite==secremain)break; //写入结束了
* R7 c) d" Y0 V# L - else //写入未结束, b+ G4 ^8 ^+ G9 f+ E( ^6 j$ G
- {4 f! @8 `% _& v' T8 ]3 G
- secpos++; //扇区地址增1
f5 l$ b+ E- f: E3 n! o6 A - secoff=0; //偏移位置为0 . R& Y/ v3 j. r+ w1 m) S E+ s
- pBuffer+=secremain; //指针偏移
# A9 T# G f5 N) F - WriteAddr+=secremain; //写地址偏移
, S; C1 V6 w3 y$ j8 k - NumToWrite-=secremain; //字节(16位)数递减0 o# C+ A; ]" I$ w* k3 w
- if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完( L o' G# g/ `! f
- else secremain=NumToWrite; //下一个扇区可以写完了" Q: R3 m( Q0 M9 O/ ^! r+ E
- } * I9 T9 ^) a0 l
- }; 5 |2 n+ C1 v5 J5 z+ N$ Y5 t7 J' k1 n
- FLASH_Lock(); //上锁0 q, Q- q( Z, @% q5 d$ A
- }2 ~8 ]% Z' E0 G' y- u
- #endif1 z) C" r1 l4 c. A0 G; ^
- - O, v" r2 }1 l
- //从指定地址开始读取指定长度的数据
& w1 X# T+ w; ~3 n/ w - //ReadAddr:起始地址
( Q0 F Q- U3 u, O1 T* Z1 f - //pBuffer:数据指针( [+ h9 [$ M" D; Q
- //NumToWrite:半字(16位)数
0 `& P# b* W/ ^" i) c. Y* ` - void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
+ p& b( l/ r& l4 n( E - {; `" G5 ?, d [" U
- u16 i;8 w( t) P' L6 w/ X; x, A) H
- for(i=0;i<NumToRead;i++)
% x* A# V: F. U7 Z* c - {
( w$ K- [9 e; x - pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr); //读取2个字节.- Z' ~& h( F5 {8 A0 m
- ReadAddr+=2; //偏移2个字节
5 E3 W+ y/ R u L' r7 r - }
4 B% w5 t1 R; S2 R4 o4 I - }
0 w( i3 T. m- q6 s6 s" B - $ V2 j1 h6 g0 y* J
- ////////////////////////////////////////////////////////////////////////////////////////////////////// @- A# ]7 Y. |3 t4 i
- //WriteAddr:起始地址
/ q1 D: h. v/ l; [- p) t# J - //WriteData:要写入的数据
6 e5 i. k; P8 h# d5 C - void Test_Write(u32 WriteAddr,u16 WriteData)
. o: O8 u; ?+ w' E6 {: }$ |% I, _ - {% u- e3 u' T1 n. `& r
- STMFLASH_Write(WriteAddr,&WriteData,1); //写入一个字
) b* F- K7 ~2 r1 T2 S4 A - }
复制代码 0 R: T5 w8 M6 ^7 s" k+ T
flash.h文件 - #ifndef __FLASH_H__- V% [" b' [5 v' _& i$ U; b4 o
- #define __FLASH_H__) t2 h- Q' h8 f
- #include <stm32f10x.h># Q1 o$ E/ ]5 k0 U* }
* l w' ~& D: F$ ~- #define FLASH_KEY1 0X45670123- ? V% a z& G: j5 T, I6 \
- #define FLASH_KEY2 0XCDEF89AB0 G' j, n* u J( I' G" t/ e
- - }2 h+ ]; K w# m! X/ E" G
- #define STM32_FLASH_SIZE 128 //所选STM32的FLASH容量大小(单位为K)
; B- K/ q& C M/ s" h - #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不使能;1,使能)
) S. r+ w' F2 m* p" M3 R: c - //////////////////////////////////////////////////////////////////////////////////////////////////////2 u9 Q& w0 E) t
2 i! X u* C# z) Y. V1 o" C- //FLASH起始地址
( q6 k; L8 ~% u! I1 I0 {% t- K: j - #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址 I% i& O2 q) M" I, L1 ]
! F. c/ { o8 N# y4 {
% `/ p4 _8 L" I ?7 P) P8 ]- void STMFLASH_Unlock(void); //解锁) S- a. u- x0 ~1 Q+ L
- void STMFLASH_Lock(void); //上锁5 \6 X- }9 m! E8 Y9 {0 O: C/ r: A) N
- u8 STMFLASH_GetStatus(void); //获得状态 k w5 ?8 ~0 s4 `; x6 u8 ~
- u8 STMFLASH_WaitDone(u16 time); //等待操作结束/ B) l' X* l7 Q% t: m
- u8 STMFLASH_ErasePage(u32 paddr); //擦除页
0 {/ z( F& p# y! D2 k - u16 STMFLASH_ReadHalfWord(u32 faddr); //读出半字
+ T; b# T7 L1 o/ x - void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据
" n$ R3 j5 I. `; c - u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址开始读取指定长度的数据: V% |* i1 V; L/ t/ E' ?& I1 E
- void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据& e! L0 |, Q5 o% O# x
- void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //从指定地址开始读取指定长度的数据
2 r1 j, w+ H: q2 | - % h# z9 Z( @9 B, P0 ~: H+ j$ f( I/ U
- //测试写入
0 B# B$ j( T) `" c" s$ ^6 N, } - void Test_Write(u32 WriteAddr,u16 WriteData); 8 z6 A4 l: U7 J) _$ u3 S! o) v$ X7 N! Q
- #endif
复制代码 # Y* F, M$ M7 ]5 K* G. p0 e" P
+ o" r) ]- X/ Y, l* c8 B
main.c文件 - #include "flash.h"4 b$ i' s1 @" O9 ^
5 _) X6 c% x0 _9 h" I; w9 s- #define SIZE sizeof(TEXT_Buffer) ///数组长度2 u$ E/ g; V; F8 }) f& E- T5 F
- #define FLASH_SAVE_ADDR 0X08070000 //设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
+ v2 _& i/ x0 Y8 O
* a0 W. i D; I/ _- const u8 TEXT_Buffer[]={"STM32 FLASH TEST"};$ s6 {. |, E7 E8 f
- u8 datatemp[SIZE];& h* |8 M/ A$ b& d# P
9 _4 n- c3 [' F2 M+ g( ?, W, Q9 m- int main(void) g7 ?( c) ]/ ~1 _+ \- A# B0 ^0 t
- {. r% A3 U/ G) ]
- while(1)
4 H3 r. w! }1 m0 o; Y" Q% y - {# ^ _5 o( }& Y% `0 F* x2 `
- if(掉电)* z6 h# ^9 u* p0 T# J
- {
9 v: v+ w2 \+ E7 T6 T. | - STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);//flash写函数
2 e2 U+ D0 J$ Y J+ N+ L - }
2 X6 R. Y# H0 \1 q* l) {2 }- [ - STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE); //flash读函数 7 A+ Z" P/ f. M6 M k5 Z
- } l$ C1 [, y7 `9 P. K1 f% m
- }
复制代码
_, {3 n. i; P! z% y$ F0 z ~5 `. C9 x% H
以上就是flash在掉电的时候进行读写的程序,项目用到了就记录一下,方便以后查阅学习。其中的大部分代码在库函数中都能够找到参考。 , M1 o, A' c0 V" d5 M/ Y! f
|