你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F7--->Internal Flash

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
Flash1 {8 [8 A; Q& w/ f5 Z
闪存模块
" g! z% Y# O5 J% `) q" n
. ^7 l: }8 D. n& S8 S
bc19fd5ce8ca4f108678471a9994d1d1.png

! M+ \1 [% L! X6 k! [
) p/ y/ V3 _( `3 u模块组织表如上,可见STM32F767IGT6由:主存储器、系统存储器、OPT区域、选项字节4部分组成。
  C3 e% F" K$ T+ I" _STM32F767的Flash访问路径有两条:AXIM和ICTM,一般使用AXIM接口访问Falsh,其其实地址为0X08000000
# M/ G: }3 t* L4 M4 b7 Q3 d, P1 `' v9 l+ y
主存储器' ~) r# y5 q6 m  q* b3 A
存放代码和数据常量(const常量数据)。它可以分为1个Bank(默认)和2个Bank,可以通过选项字节nDBank位来设置。8 |! i& e! r2 _$ f. d+ n
在单Bank模式下,STM32F767的主存储器被分为8个扇区,前4个扇区为32kb大小,第五个山扇区为128kb大小,剩下的3个扇区都是256kb大小,共1MB! ]: {+ o' h. n. n" H

& @$ y8 P. u5 L  u系统存储器7 D, ?) c/ @. s! J2 R% _' G
存放Bootloader代码,此代码是出厂时就固化的,用来给主存储器下载代码,当B0接3.3v时默认从系统存储器启动
' x6 _. W: R! c( H# L6 |- s  U6 p9 q) i* K  t5 ~3 d
OTP区域
/ U- r  J( H+ X3 I) c一次性可编程区域,共1056byte,被划分为16个64字节的OTP数据块和1个16字节的OTP锁定块。锁定块决定了数据块是否可编程。: V0 U) g0 H5 F5 F
  g) P$ ~0 _- Z* t) ], }
选项字节
- x2 _# O, \" ]! y4 T: ]5 w: X$ t. E0 R1 V6 U! j# q
闪存的读取
( n( ?& L* l* [7 q* ]# t为了准确的读取Flash数据,需要根据CPU时钟(HCLK)的频率和器件电源电压设置FLSH的存取控制寄存器(FLASH_ACR)中的等待周期数(LATENCY),CPU频率与FLASH等待周期数的对应关系如图:9 _! ~$ @) e, c1 ~$ O

+ F9 o4 ~* C5 S1 ?2 `% h6 m  g
8a2f2b2dd84e4a57a4f63ee69b5dbe6b.png

; ]2 M+ _! x* |9 J
- L- N0 V4 X' m! Y3 C* d% ]等待周期通过FLASH_ACR寄存器的LATENCY[3:0]来设置,系统复位后,系统时钟为内部的16MRC振荡器,LATENCY默认是0(1个等待周期)。供电电压一般默认为3.3,所以在设置216Mhz频率作为CPU时钟之前,必须先设置LATENCY为7(8个CPU周期),否则FLASH读写可能会出错导致死机。
0 [! }7 M' ~% l9 o! qSTM23F7的 FLASH 读取是很简单的。例如,我们要从地址 addr ,读取一个字(一个字为32 位),可以通过如下的语句读取:. Z4 D0 f9 v, o& s3 X6 ^
              data=(vu32)addr;
( z9 K1 ?) N4 b3 Y将addr 强制转换为 vu32 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。
* {. j7 Q0 b' E: V$ _类似的,将上面的 vu32 改为 vu8 ,即可读取指定地址的一个字节。6 _! P5 l0 X; u+ P0 N

: L+ g* ~0 `6 o( D8 ^. e
闪存的编程和擦除
+ O" |5 l! l4 W  a0 \执行任何Flash的编程操作(擦除或编程)时,CPU时钟频率(HCLK)不能低于1Mhz。如果在Flash操作期间期间器件发生复位,无法保证Flash的内容。
7 H/ Y4 l0 X' D' a在对Flash执行写入或擦除操作期间,任何读取Flash的尝试都会导致总线阻塞。只有在完成编程操作后才能正确处理读操作。
2 U8 Z$ Z! X* Y- J/ }STM32F767的闪存编程由7个32位寄存器控制,如下
. Z2 D% l+ ^4 k/ p' H4 t! D, b& H/ l/ h7 [& M& L$ Z
T85`2XMT@0~([S{862MVQMV.png
, y2 _3 f0 u; g0 W

7 M; l$ r3 ^* Y4 S! iSTM32F767复位后,Flash的编程操作是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列(0X45670123 和 0XCDEF89AB)到FLASH_KEYR寄存器才可解除写保护,只有在写保护被解除后,才能操作相关寄存器。2 F9 }% V1 x3 a! V/ ]
STM32F767的编程位数通过FLASH_CR的PSIZE字段配置,PSIZE的设置必须和电源电压匹配,见表,使用3.3v供电时,PSIZE必须设置为10,擦除护着编程都必须以32位为基础。
; K* f1 [$ O: O7 c, w: ~
4 Y9 `- S6 k9 l8 x! t
5d10e1e0f2de471fa642a60b4893a091.png

9 M7 x. D6 J0 j& Y7 {5 `$ e2 y
+ @/ i7 J( k& b0 w7 f6 tFLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(值必须是0XFFFFFFFF),否则无法写入。- m, m; C& C. ~  c! Q
) H5 Y3 {6 W7 ~& ~* i- `4 l
STM32F767的标准编程步骤- p  E# N. u( _+ S
检查Flash_SR的BSY位,确定当前未执行任何Flash操作;
; `- B  k+ S! k' {  X将Flash_CR寄存器中的PG位置1,激活Flash编程;
* x* t" v; P* o# w针对所需存储器地址(主存储器块或OTP区域内)执行数据写入操作(PSIZE需要已经设置)
/ E0 T3 ~( R5 M/ v等待BSY位清,完成一次编程。6 ]' s4 H/ _! p, T; h( b) T1 z, q
以上四步操作就可以完成一次FLASH编程操作,需要注意几点:
5 J7 ^" {: b: B/ M6 d+ j  Q3 y4 o确保编程地址已擦除
3 t( w0 D# h# a  r1 i0 l' |需要先解锁FLASH_CR,操作完后要上锁
2 D; l8 [# s& n变成操作对OPT区域也一样
/ R2 K" W* D( q- x3 e4 t0 b0 c# [
  `; W& z' A8 d% K! @# Y) a
+ L& O% _. R* Q' F6 HSTM32F767的擦除
& ^- c1 Y! y3 p6 C4 ]  A; `5 a扇区擦除
1 y( Q9 `8 V" k% r: Y检查FLASH_CR是否解锁,未解锁先解锁
8 h9 p% a8 G" o' ]; Y7 q检查FLASH_SR的BSY位,确保当前未执行任何FLASH操作2 C0 d1 [3 i5 T( t& g
将FLASH_CR的SER位置1,并从主存储块的12个扇区中选择要擦除的扇区(SNB)! C/ H5 X# _2 I0 R4 r: }
将FLASH_CR的STRT置1,触发擦除操作/ C. ^" A# J& S7 t8 Y$ U5 h
等待BSY位清零3 C# P0 {( n, t* X; G: m
整片擦除' Z& Z$ {! g6 U; z& j* G
附录
8 t; R' ^8 S5 Y. Q, l2 e; s代码" g6 l6 u6 f' s+ G7 I- B
依赖于HAL库
& e# q2 y4 j, n) J% ^7 U; o/ C+ _& ~+ P- E
  1. #include "stmflash.h"
    ; i; E3 ~; Y) r8 L+ s
  2. #include "delay.h"- B/ S3 q3 W5 M: s  l

  3. * v# c! G( ^, J  g1 o/ y  p
  4. //读取指定地址的字(32位数据)
    7 C+ k* x/ j- ?  I
  5. //faddr:读地址 4 w4 ~9 d- y& F5 b! c2 ^3 f# s# C
  6. //返回值:对应数据.
    $ L( w. E, G# U  X; r
  7. u32 STMFLASH_ReadWord(u32 faddr)
    : _8 j2 J, u- x* ^6 B+ C
  8. {7 D; r+ U( E. x4 i# [0 D  `9 {; R* P, ]
  9.     return *(__IO uint32_t *)faddr; / k0 E# Q" o) b
  10. }( C9 d0 q) X, A+ P$ u
  11. / [+ S# W& g; a' a$ I
  12. //获取某个地址所在的flash扇区
      {: C$ P# b+ i1 d3 w! m
  13. //addr:flash地址
    & r5 G1 O+ j) S1 }" a9 g' B: R
  14. //返回值:0~11,即addr所在的扇区
    ; t$ l$ ~- \2 R
  15. uint16_t STMFLASH_GetFlashSector(u32 addr)( W( X% C  W& H" @
  16. {
    4 u6 `; D5 s  y& ~3 @
  17.     if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;0 X6 F% y) I1 w7 `2 I; @( o
  18.     else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;
    % @3 W9 Q1 j$ c9 z
  19.     else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;
    ) H0 D4 f, Q# u1 p! O4 v7 |+ C. Y
  20.     else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;6 Q9 ]! l/ q* z9 m; h
  21.     else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;* l8 |0 ~! D" |! A/ A6 O# ]3 T$ ]
  22.     else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;0 _2 c. X% D, u  P6 A
  23.     else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;
    - r% X) J4 M6 v, ]. ]+ _
  24.     return FLASH_SECTOR_7;    ; I9 B/ u0 w8 [8 M8 {
  25. }
    + e- q' w  H; K- A0 M
  26. . }2 M; I) k& r
  27. //从指定地址开始写入指定长度的数据
    3 S4 x. |9 f% \, h9 B0 D
  28. //特别注意:因为STM32F7的扇区实在太大,没办法本地保存扇区数据,所以本函数: r; @8 C, l4 J, U
  29. //         写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以/ @  A: R8 v: L% i# @) ~
  30. //         写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里/ H) Y0 S# n- A! Q4 E' C  B
  31. //         没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写. 8 ^+ s3 A% T6 w6 _6 p
  32. //该函数对OTP区域也有效!可以用来写OTP区!
    5 E6 ~. c, b! N5 Z( u
  33. //OTP区域地址范围:0X1FF0F000~0X1FF0F41F6 F; d! Q' i- J; c2 n( v" u( `
  34. //WriteAddr:起始地址(此地址必须为4的倍数!!)
    $ z( N$ W! p0 w9 q0 g
  35. //pBuffer:数据指针$ }# n: z- R) ^
  36. //NumToWrite:字(32位)数(就是要写入的32位数据的个数.) . a5 u2 ^: W: a+ [6 P% V
  37. void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)    * S6 F& s3 x0 |8 l0 c" w
  38. { ! m( e6 Q, E' D. {5 N2 @
  39.     FLASH_EraseInitTypeDef FlashEraseInit;. Q9 J6 f. _' i1 r2 x5 O% |
  40.     HAL_StatusTypeDef FlashStatus=HAL_OK;
    , A, r) I% x* ?' U  N
  41.     u32 SectorError=0;' o. I( p+ M$ c3 @( T2 v9 q
  42.     u32 addrx=0;
    3 F; K" X$ W+ V$ h
  43.     u32 endaddr=0;   
    ! I/ k7 W! K7 i" \9 S  b: M9 p* _
  44.     if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;    //非法地址3 m+ D! U- Q, r% \9 ?

  45. 5 ^+ O% n  r8 K% S3 I8 h
  46.     HAL_FLASH_Unlock();             //解锁' N% C. O) |/ ^0 ^, b) m6 l
  47.     addrx=WriteAddr;                //写入的起始地址6 v7 ?! W; b3 l7 o6 I. J& R/ b
  48.     endaddr=WriteAddr+NumToWrite*4;    //写入的结束地址! E; ]/ h8 s0 u8 P5 |! @" {

  49. 8 l) R( \1 R+ A6 ?" `& ~$ E' A
  50.     if(addrx<0X1FF00000)- w: l3 o: ]2 M% _1 l" i! R6 @7 w
  51.     {
    $ E0 ~9 E( o5 w5 M
  52.         while(addrx<endaddr)        //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
    8 w: |6 ?1 R. ]9 X, F+ T4 m
  53.         {
    * H6 V9 k6 X. D/ U
  54.             if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
    ; ]. H  h8 E, x2 b
  55.             {   " j. ^. N; @! j* I
  56.                 FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS;       //擦除类型,扇区擦除
    . e! e  ~1 q  q3 y: S5 d1 q) d
  57.                 FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx);   //要擦除的扇区
    5 _5 E# P2 x0 R. M8 |  s
  58.                 FlashEraseInit.NbSectors=1;                             //一次只擦除一个扇区
    3 F+ x5 ^" m  X) n1 i( e
  59.                 FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3;      //电压范围,VCC=2.7~3.6V之间!!; X( J+ S$ t- T
  60.                 if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
    , N5 `4 \2 L7 |! G
  61.                 {; V$ V! ?8 f! W) D. {
  62.                     break;//发生错误了   
    : [3 D7 h; G* t
  63.                 }
    1 l! _& \' Y4 t
  64.                 SCB_CleanInvalidateDCache();                            //清除无效的D-Cache, O1 G! b0 ~' e/ Z+ i
  65.             }else addrx+=4;+ L  t3 `- J0 }' ^5 n- E2 J
  66.             FLASH_WaitForLastOperation(FLASH_WAITETIME);                //等待上次操作完成
    3 y$ C* M3 [/ {. V
  67.         }
    - C% ?' B: v- X/ r& s1 M3 P
  68.     }! `* J' k+ P$ Z; p
  69.     FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
    $ S1 B5 ~+ q7 B# R: {
  70.     if(FlashStatus==HAL_OK)
    + _+ \$ ^5 @' T
  71.     {
    6 O  T. R0 u, A. e
  72.         while(WriteAddr<endaddr)//写数据9 w- j6 d2 @9 {# O( x
  73.         {
    + m8 L. `2 h( E; i# g; Y. D/ l
  74.             if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据
    : f& a' w& [3 A- W9 A, z. Q  P4 [% W
  75.             { 1 I0 ?2 L) s. j" q1 k
  76.                 break;    //写入异常
    & o% K) t$ \: ]6 D, u$ w6 S1 z+ l
  77.             }
    0 Y/ \$ D' o5 X4 u# {4 C  O
  78.             WriteAddr+=4;: C# P, }$ Z3 g( }8 z( S
  79.             pBuffer++;5 f$ L; E% h  z4 C/ d2 h& D
  80.         }
    5 Y3 y) f6 d2 y3 i$ p$ k9 u$ P% v! x
  81.     }; @/ P$ X9 T9 C
  82.     HAL_FLASH_Lock();           //上锁- i1 Y9 }% F! j! G- X
  83. }
    ! _% g. K. h: K7 C/ A' h9 `

  84. ' Q$ m, `6 t% m: c: |1 r
  85. //从指定地址开始读出指定长度的数据
    , {' [$ J9 O8 H+ r4 s8 o% v# @/ @
  86. //ReadAddr:起始地址
    + M3 `3 r# O2 t& C" P5 q
  87. //pBuffer:数据指针
    3 r6 d/ C, @/ m& I" b+ [
  88. //NumToRead:字(32位)数
    ! N3 j# @' l7 |8 E2 V- v6 R  h
  89. void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)       # E  D; M4 X6 f2 ?* |# Z2 L
  90. {2 y2 F; o) B! D! O
  91.     u32 i;# \$ c4 H/ P+ }7 u) ]( G: Z
  92.     for(i=0;i<NumToRead;i++)3 _3 H: Y: K5 f2 z* Y1 W% N# y2 J
  93.     {% T: B' t% H! y2 u' Q  i( K$ P
  94.         pBuffer<i>=STMFLASH_ReadWord(ReadAddr);//读取4个字节.
    * [, G; X! r9 j2 i1 h& C
  95.         ReadAddr+=4;//偏移4个字节.   
    0 A2 f$ \8 Q" {8 ~* b8 L7 s- K+ t$ \
  96.     }2 X( i' x; g. F7 ~% x
  97. }
    5 {4 T) M  N3 X) A9 j1 F# O

  98. ) A2 |& V0 `4 w- M  T
  99. </i>
复制代码
! G. `% G9 G! O/ t1 R4 X5 ?
. K+ }& [5 ^* {+ F) Q
收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版