Flash2 w! W, V* m) F. ~
闪存模块, {/ n' A+ Z" x/ R0 ?! c
8 p1 Q4 v( ~( j# g# ~! m5 ~
$ ]4 h# A# b" ~( k9 B* w& V( P$ q% Y0 }7 a+ W* ?6 \7 T
模块组织表如上,可见STM32F767IGT6由:主存储器、系统存储器、OPT区域、选项字节4部分组成。$ s) K0 H* H" V% z
STM32F767的Flash访问路径有两条:AXIM和ICTM,一般使用AXIM接口访问Falsh,其其实地址为0X08000000
% I, }2 l( o" m7 e$ B. K$ ]! S m! W9 z# }6 m
主存储器
9 S. I; F2 B8 c! z1 D3 f存放代码和数据常量(const常量数据)。它可以分为1个Bank(默认)和2个Bank,可以通过选项字节nDBank位来设置。
3 l( p) R! p' ^/ c5 @, l0 O在单Bank模式下,STM32F767的主存储器被分为8个扇区,前4个扇区为32kb大小,第五个山扇区为128kb大小,剩下的3个扇区都是256kb大小,共1MB
( t$ a) ?8 Z" t+ p" u: P) z/ v& S; ~1 b6 D
系统存储器' r3 f" A# Z( K; @3 x0 p- s
存放Bootloader代码,此代码是出厂时就固化的,用来给主存储器下载代码,当B0接3.3v时默认从系统存储器启动
% o6 R2 e$ ?, ^. ^2 V9 R* W: W8 h( L( g: K8 ^9 l( `
OTP区域7 K1 w v( D+ V
一次性可编程区域,共1056byte,被划分为16个64字节的OTP数据块和1个16字节的OTP锁定块。锁定块决定了数据块是否可编程。. y% A5 v+ r& {& W9 O
: ~5 h" o" i8 W% x选项字节
" t% g' {6 Z, m4 D8 y8 f, R! M* j- y+ [4 |( t& N' f
闪存的读取
8 `/ r9 u7 ~! V为了准确的读取Flash数据,需要根据CPU时钟(HCLK)的频率和器件电源电压设置FLSH的存取控制寄存器(FLASH_ACR)中的等待周期数(LATENCY),CPU频率与FLASH等待周期数的对应关系如图:- G' _( R" N& ?
4 d2 i. Q7 `9 C; n1 B$ t
! M2 B& t9 a, x/ b6 q3 s
$ V1 d' `# I& _9 O5 n) y9 S等待周期通过FLASH_ACR寄存器的LATENCY[3:0]来设置,系统复位后,系统时钟为内部的16MRC振荡器,LATENCY默认是0(1个等待周期)。供电电压一般默认为3.3,所以在设置216Mhz频率作为CPU时钟之前,必须先设置LATENCY为7(8个CPU周期),否则FLASH读写可能会出错导致死机。- V5 j' x3 C3 u) \6 x5 w T
STM23F7的 FLASH 读取是很简单的。例如,我们要从地址 addr ,读取一个字(一个字为32 位),可以通过如下的语句读取:
8 y9 v0 K: r5 \0 T0 z1 c3 _ data=(vu32)addr;
) C" Q0 r$ Q' ~将addr 强制转换为 vu32 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。
& `" J3 g" E; ~) L' J+ l! F& G" o类似的,将上面的 vu32 改为 vu8 ,即可读取指定地址的一个字节。* k+ R2 [# U; {% c6 {( M
, z, r- C9 `% {9 W" x闪存的编程和擦除
6 E. Y* y3 u i( g执行任何Flash的编程操作(擦除或编程)时,CPU时钟频率(HCLK)不能低于1Mhz。如果在Flash操作期间期间器件发生复位,无法保证Flash的内容。
2 V" q* C# e8 L b9 O; n4 r5 u: X在对Flash执行写入或擦除操作期间,任何读取Flash的尝试都会导致总线阻塞。只有在完成编程操作后才能正确处理读操作。. O/ k0 }8 d& [+ _
STM32F767的闪存编程由7个32位寄存器控制,如下, L# A. r$ w3 Q3 O5 T. }
) _; u) f9 Y; U- x" t0 }- j
$ ~( }8 g, h9 h& f" |# H2 V
5 u/ ^# Q2 a6 G# m7 x0 cSTM32F767复位后,Flash的编程操作是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列(0X45670123 和 0XCDEF89AB)到FLASH_KEYR寄存器才可解除写保护,只有在写保护被解除后,才能操作相关寄存器。
7 |& d! s9 w' \; BSTM32F767的编程位数通过FLASH_CR的PSIZE字段配置,PSIZE的设置必须和电源电压匹配,见表,使用3.3v供电时,PSIZE必须设置为10,擦除护着编程都必须以32位为基础。
; |, h f& H" c ?
$ `( t8 Z6 ?" m& G2 h' c
: ]8 h3 o" `, x5 ?: N
; E$ {6 r9 H7 J: `( EFLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(值必须是0XFFFFFFFF),否则无法写入。0 Y% M0 |9 }6 l& M x7 ^0 S: U
* D, M4 |4 c/ H. N, j. hSTM32F767的标准编程步骤
& R: k$ x6 ^0 L2 H检查Flash_SR的BSY位,确定当前未执行任何Flash操作;$ M8 S$ b& e" l8 Q; H+ H% b$ d, x
将Flash_CR寄存器中的PG位置1,激活Flash编程;
7 |; y- F7 x1 w+ t6 Z& i针对所需存储器地址(主存储器块或OTP区域内)执行数据写入操作(PSIZE需要已经设置)
* X' j" F8 K2 a' A4 {; d等待BSY位清,完成一次编程。* t( o! W" i) m
以上四步操作就可以完成一次FLASH编程操作,需要注意几点:
1 ?& u5 A9 X- ~: B3 F确保编程地址已擦除) ^: n2 Y( f0 a: f# H4 z
需要先解锁FLASH_CR,操作完后要上锁 j$ l9 n9 V9 @- k. @' [
变成操作对OPT区域也一样
, c2 q" q f- l3 K, W* t# k2 R9 D
7 j. C4 }+ [3 L: ]: i0 V: V
{% ~: |2 a2 i/ U7 S8 G! f9 T7 S/ TSTM32F767的擦除
. o5 \* V c; b8 _扇区擦除$ f3 T" V9 A, N; y- K. ~
检查FLASH_CR是否解锁,未解锁先解锁
. B+ j1 g0 R- `! E B4 X检查FLASH_SR的BSY位,确保当前未执行任何FLASH操作( q2 w: W/ K4 I6 r
将FLASH_CR的SER位置1,并从主存储块的12个扇区中选择要擦除的扇区(SNB)2 u: E) }: _% e N/ ]/ `
将FLASH_CR的STRT置1,触发擦除操作/ [4 Y: j) f) Q! z% d. x0 V
等待BSY位清零/ X2 s- I6 O' q1 q
整片擦除- v# Y# }" t4 u) E
附录
; g8 x8 R8 y% R, U5 c2 P代码( H9 v+ P8 h, p B+ x! D
依赖于HAL库
0 z4 U; B) e9 M7 b% {1 y8 h, M2 Y. ^" K8 d8 `
- #include "stmflash.h"4 M6 m5 ~6 g# v2 b7 j, `
- #include "delay.h"
: D4 h& e: x" x* J$ k# h* a0 V - , n& Q; B, }! p1 o* z! d2 {
- //读取指定地址的字(32位数据)
. L8 p9 f: f( e" f! Z; [8 m/ ~: h7 x - //faddr:读地址 6 R- j* a# @ r1 h5 o
- //返回值:对应数据.$ U2 C7 c( N' e- l' q) k9 \6 b
- u32 STMFLASH_ReadWord(u32 faddr)& A8 I4 y- _3 f7 H' X2 l
- {, b+ F5 B! x1 E# M/ n
- return *(__IO uint32_t *)faddr; # t9 H, q' ]% U3 K! i% l
- }
' [, B# _3 [/ E" H - ! L$ u3 F" j4 M5 y G
- //获取某个地址所在的flash扇区+ e: l. v: |8 _7 P
- //addr:flash地址/ h5 W1 y5 _1 Q9 T1 H$ _$ c1 @
- //返回值:0~11,即addr所在的扇区
2 ~" _; H$ E% P1 S6 h& J. X { - uint16_t STMFLASH_GetFlashSector(u32 addr)
7 d% ^% f" G, t; k2 E; O- l - {/ {9 n. n. B* r! y7 q
- if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;) n) K8 E- y5 a
- else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;$ N" r4 S% k9 Z
- else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;4 `! }. X* c# y2 H. h
- else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;4 b! g+ R- g! `9 j8 c0 B: U% T4 v( d$ i
- else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;
) O/ O: Y; H" a5 K6 B: `8 h4 r - else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;
) n: J; C/ c3 i, @+ X - else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;
. L" |" ?- ^1 n% ]* y' p - return FLASH_SECTOR_7;
. V; O, M" ~6 S. B - }! Y6 n. ?3 E8 _- _
: t# D; R; d& h S5 N. j0 k- //从指定地址开始写入指定长度的数据' `/ A; y, N; i" T; J0 i
- //特别注意:因为STM32F7的扇区实在太大,没办法本地保存扇区数据,所以本函数7 i& h( U! m# \
- // 写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以' X, }' w4 n+ S+ q3 D4 S; o
- // 写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
" }+ N6 s7 Q& Y- v2 W2 q - // 没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
8 e( Y3 Z( g Y3 Q: { - //该函数对OTP区域也有效!可以用来写OTP区!3 U% R6 J0 L" v% d* S/ j% P+ E
- //OTP区域地址范围:0X1FF0F000~0X1FF0F41F- Q( c! ?, ^' v+ [5 F& a6 R4 X
- //WriteAddr:起始地址(此地址必须为4的倍数!!) R; B! C: z# M3 G' O
- //pBuffer:数据指针
9 W3 L# M* n% z2 g - //NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
0 L) Z+ `- H2 u: V, q - void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite) 8 v6 @' s2 k: f6 k. y% z2 L5 z3 C
- { 5 u* I, q& W) Y- ?* k* Y9 j) S( Y
- FLASH_EraseInitTypeDef FlashEraseInit;
5 b+ i: U8 C* o$ F/ E1 y - HAL_StatusTypeDef FlashStatus=HAL_OK;
& s' G0 l7 ]; U& u8 [ - u32 SectorError=0;
4 q8 i- D2 [! `& z p# s - u32 addrx=0;8 o, c7 {7 i! P
- u32 endaddr=0;
3 z& ~. f& E% }! Z& q6 e/ W0 L - if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //非法地址
" N2 g& R n9 D# p& b - 5 e& i Z1 s9 g, a( i
- HAL_FLASH_Unlock(); //解锁
1 e9 |" e# e) W1 h! x7 x - addrx=WriteAddr; //写入的起始地址+ t8 C5 T/ K; d
- endaddr=WriteAddr+NumToWrite*4; //写入的结束地址& w$ w* ]" a: [3 P+ a! `. P
- 8 @/ z7 o7 b8 `. \- o0 ~% b& t
- if(addrx<0X1FF00000)
: d3 q, R- J! k. W* d& |* X3 o - {
o) H- p& U% b; `4 d& `. X - while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)1 i7 _2 C6 M' x! a) y
- {" }6 I; h% N ]9 U0 S; i; v
- if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区/ L1 o, K6 U. ?) ?1 V9 h; e8 G
- {
, n+ s, [: n, n4 f3 t; U) l+ ~ - FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //擦除类型,扇区擦除 3 w- r; F K) L$ x; x# q
- FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx); //要擦除的扇区. Z, D0 ?9 g2 P8 C; j
- FlashEraseInit.NbSectors=1; //一次只擦除一个扇区
6 a% ^ i( V# @" {6 j - FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围,VCC=2.7~3.6V之间!!
/ {9 i \2 K# B# C" K - if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
/ b2 z6 z3 Z# |1 n - {$ _3 n& \5 u3 a0 V/ W2 y# \
- break;//发生错误了
- U. v$ V$ {7 @0 L - }/ {" b- s3 K) _3 r( w
- SCB_CleanInvalidateDCache(); //清除无效的D-Cache
/ ]' r2 Q" }, @( }. ` - }else addrx+=4;
2 e5 T7 `6 [( E - FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成9 D# c3 A! I. X2 Y! A
- }
/ B H1 Q+ l& p - }* z7 L/ O: r/ Q O* ]
- FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成% @& T; Z6 `' w, N( ?8 P* t/ _' U
- if(FlashStatus==HAL_OK)7 e9 t& U3 \2 I3 m% ?/ z$ U
- {4 A: h! }* S% P" ]- |
- while(WriteAddr<endaddr)//写数据
2 c% k' \, T4 C2 r7 a- ^ - {* P- W. ~/ D8 w' n; t5 ]
- if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据
& e& o! i! N- @ g0 k" K6 I% h; | - {
8 W( A ^. |) P* N6 a - break; //写入异常 @* ]/ k# X A4 k5 v L3 t
- }/ s, h: f* o2 e$ [* {: l0 {; k
- WriteAddr+=4;
! Y& O% u Z# `4 o8 `/ { - pBuffer++;0 n2 V4 |; k- w) K0 c! e) W
- } , P c$ Q% K, s5 U' N' b; g
- }1 l. c% b. R" _
- HAL_FLASH_Lock(); //上锁3 U& Y8 b; q1 f! x5 ]
- }
$ G0 o- f% y; \8 M) C* F6 r# z* f, w - ( A3 D- p4 z$ T2 [7 u
- //从指定地址开始读出指定长度的数据) e, F+ |7 b+ L) ]1 e, I/ q+ u
- //ReadAddr:起始地址
# |' {! o1 M' U6 m- k - //pBuffer:数据指针
+ i' ^1 |* `7 e - //NumToRead:字(32位)数5 u$ y% L! f3 T" F1 ^2 k q' c( |
- void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead) & U4 P- ]( |2 N1 y
- {' f; ]6 T- b7 _. U8 h _( D
- u32 i;# t7 V7 b7 s Q5 j
- for(i=0;i<NumToRead;i++)6 ~" R& \, d4 P6 m5 S, {" U
- {
) `8 s3 T; a" n2 V5 }/ f - pBuffer<i>=STMFLASH_ReadWord(ReadAddr);//读取4个字节.& E) c5 h e6 ~3 n- C7 f
- ReadAddr+=4;//偏移4个字节.
3 ]6 l4 M# v, E2 a - }, k4 Y- H% h6 Z0 p) d; N: w
- }' w* q5 O V& N6 H, J0 V. k3 `
" z3 F, K$ d$ m1 \' ]. v2 m8 X9 N- n' v- </i>
复制代码
8 M& z- _* C+ D. e" F' c- ^5 Z
- @# t2 ]2 P# t. I7 N1 j5 _ |