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

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

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
Flash
' s/ r1 B) R% x# I. {$ X0 i闪存模块

! z8 K3 u0 H! J9 M
! q0 Q: r' E; c" y5 h- u" ^* |% d
bc19fd5ce8ca4f108678471a9994d1d1.png
7 W, g  u2 J, D) K

% {9 ~4 r/ [; u, Y0 e% g' A模块组织表如上,可见STM32F767IGT6由:主存储器、系统存储器、OPT区域、选项字节4部分组成。* C' H6 ^' o6 _' N7 r$ ]/ e
STM32F767的Flash访问路径有两条:AXIM和ICTM,一般使用AXIM接口访问Falsh,其其实地址为0X08000000
" S) ~9 S% ]5 b5 s3 y# N
" H' r4 H- p, Z1 c" q' Y+ ^主存储器
  @8 i! g' o( H, Q6 M3 ]  z6 o存放代码和数据常量(const常量数据)。它可以分为1个Bank(默认)和2个Bank,可以通过选项字节nDBank位来设置。4 P3 I9 I9 E! d
在单Bank模式下,STM32F767的主存储器被分为8个扇区,前4个扇区为32kb大小,第五个山扇区为128kb大小,剩下的3个扇区都是256kb大小,共1MB
' ?' M$ q# a. O, ?5 E( e' g
* M$ s& E; J, H  O系统存储器) }- H. V4 ]9 ]# h
存放Bootloader代码,此代码是出厂时就固化的,用来给主存储器下载代码,当B0接3.3v时默认从系统存储器启动
2 M: ~4 D) C% z" U
2 \1 w) i0 y7 p. _  POTP区域. ~' ~4 z. `9 O" X; M- q& S0 n
一次性可编程区域,共1056byte,被划分为16个64字节的OTP数据块和1个16字节的OTP锁定块。锁定块决定了数据块是否可编程。
. z: T/ I* }' y9 F. o
" V0 P- J6 h! ?选项字节4 a" a2 y/ v0 ]6 I  G) ~& v
8 v+ d4 I- q, j7 V8 E3 C, Q" R9 e
闪存的读取6 r' A$ V4 E  M! F  s3 l
为了准确的读取Flash数据,需要根据CPU时钟(HCLK)的频率和器件电源电压设置FLSH的存取控制寄存器(FLASH_ACR)中的等待周期数(LATENCY),CPU频率与FLASH等待周期数的对应关系如图:, W* g7 N+ k; S# o3 F2 e$ f" S1 E4 ~
# R8 ^0 j. S% L1 Z: R7 f
8a2f2b2dd84e4a57a4f63ee69b5dbe6b.png
1 t% G& r# m5 e$ @# d, W' D. {
) o8 @7 X5 {" U+ z$ X* `0 @5 F0 j
等待周期通过FLASH_ACR寄存器的LATENCY[3:0]来设置,系统复位后,系统时钟为内部的16MRC振荡器,LATENCY默认是0(1个等待周期)。供电电压一般默认为3.3,所以在设置216Mhz频率作为CPU时钟之前,必须先设置LATENCY为7(8个CPU周期),否则FLASH读写可能会出错导致死机。
! S1 H  i% ^, K4 G" ~8 ZSTM23F7的 FLASH 读取是很简单的。例如,我们要从地址 addr ,读取一个字(一个字为32 位),可以通过如下的语句读取:
9 _0 x0 D( s3 Q& ?/ ?7 N              data=(vu32)addr;; c8 Q  n, R3 P) k- e& O
将addr 强制转换为 vu32 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。
( Q# ?1 s/ s2 K6 o类似的,将上面的 vu32 改为 vu8 ,即可读取指定地址的一个字节。  }, K/ X2 f% r3 B- |

! o$ x9 P. e! R" \' B* e
闪存的编程和擦除& C8 _; _; m  ]1 ^# e
执行任何Flash的编程操作(擦除或编程)时,CPU时钟频率(HCLK)不能低于1Mhz。如果在Flash操作期间期间器件发生复位,无法保证Flash的内容。) ^; @  A& Y" N5 ?2 |# O
在对Flash执行写入或擦除操作期间,任何读取Flash的尝试都会导致总线阻塞。只有在完成编程操作后才能正确处理读操作。; `# f! g) Q( j
STM32F767的闪存编程由7个32位寄存器控制,如下: \# g; R0 i2 P7 ]+ |8 ]
+ N. U' \+ H- p
T85`2XMT@0~([S{862MVQMV.png
& A6 d; v  b, w- o9 q

1 i$ J; N% s5 l6 R8 k. O* ^2 Y% uSTM32F767复位后,Flash的编程操作是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列(0X45670123 和 0XCDEF89AB)到FLASH_KEYR寄存器才可解除写保护,只有在写保护被解除后,才能操作相关寄存器。7 V- v/ r+ k: [9 x5 z5 b& o6 T
STM32F767的编程位数通过FLASH_CR的PSIZE字段配置,PSIZE的设置必须和电源电压匹配,见表,使用3.3v供电时,PSIZE必须设置为10,擦除护着编程都必须以32位为基础。
1 k: Q. _0 D; V& a$ y2 }
+ ^- [0 H8 Y: r" {4 k8 w$ m
5d10e1e0f2de471fa642a60b4893a091.png
5 ?$ B# n( n8 _7 ]6 M8 O) j; {3 s; _( ?0 i

1 K. M2 h" x+ ]8 d. v; ^FLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(值必须是0XFFFFFFFF),否则无法写入。' s) e) p; B; a

( u8 R8 u1 N3 \7 m+ k8 r4 hSTM32F767的标准编程步骤6 v; P* Q) Y& f* i" a
检查Flash_SR的BSY位,确定当前未执行任何Flash操作;
: w, X, {5 C: C( X0 \将Flash_CR寄存器中的PG位置1,激活Flash编程;
8 t8 E- s1 T" B" D2 i# h针对所需存储器地址(主存储器块或OTP区域内)执行数据写入操作(PSIZE需要已经设置)
* X1 q: R4 U2 F: T6 N; F9 f等待BSY位清,完成一次编程。
7 }- g; Z( _1 T5 e, o, o: o$ C# G: U以上四步操作就可以完成一次FLASH编程操作,需要注意几点:
  h8 g& S) i$ ]9 F5 o) v; s/ b确保编程地址已擦除
* `. x& Z- Y, y5 }需要先解锁FLASH_CR,操作完后要上锁
8 R; l% w/ B, C& Y2 U变成操作对OPT区域也一样
7 n" r! C, n% S4 B2 ~1 b0 c% I, l+ ]: x) r/ h

( F& p4 m5 @% W. g. TSTM32F767的擦除0 q8 \* O0 ^/ M) ?
扇区擦除
6 K' ~+ P2 Y5 J$ I1 l3 y检查FLASH_CR是否解锁,未解锁先解锁
2 ?2 p" f5 {# x2 |检查FLASH_SR的BSY位,确保当前未执行任何FLASH操作
. F  o, Y( e4 R. P3 W将FLASH_CR的SER位置1,并从主存储块的12个扇区中选择要擦除的扇区(SNB)# L6 A* I& T5 ?! L+ \5 X
将FLASH_CR的STRT置1,触发擦除操作
' r2 D5 b; K* K& X5 O  u% R等待BSY位清零5 c7 g( ^) Q( _5 H
整片擦除, X" n0 p- Q: O2 p6 m+ Z
附录
- |& r6 e4 E9 a* |代码8 S$ C- E7 T1 c1 Z. @9 L/ p9 G" ?
依赖于HAL库* i' e( X0 ^% ]3 j1 j' d2 x
6 z/ ~6 K0 A# {' b# B, r% `
  1. #include "stmflash.h"
      w: `7 _& K/ k: h: z- Y+ E* m8 w
  2. #include "delay.h"( U6 P& X- E2 Q
  3. , O: J: c3 f# E8 C# K$ K- @
  4. //读取指定地址的字(32位数据)
    0 V- l' q# A( x
  5. //faddr:读地址 . k+ M0 ?8 f8 e% N0 Y
  6. //返回值:对应数据.
    1 X& `. W( U8 a/ t7 ]
  7. u32 STMFLASH_ReadWord(u32 faddr)
    ! B0 U( `7 o& I1 Z
  8. {
    , h0 c% u8 @6 c
  9.     return *(__IO uint32_t *)faddr; 4 p9 w$ u: X1 w$ Z- H
  10. }
    ) w: y4 x3 ^" |1 p! ^9 s. `
  11. 6 Y" d/ p9 o! z/ M2 p$ ]4 t
  12. //获取某个地址所在的flash扇区- S- O5 D% ^3 R( g9 m8 s
  13. //addr:flash地址0 [  N3 x  ~' T2 Y& ~+ X. ~
  14. //返回值:0~11,即addr所在的扇区
    . Q& I2 e! a5 c, M
  15. uint16_t STMFLASH_GetFlashSector(u32 addr)
    ( Y$ S& D  H2 L. s
  16. {
    # }9 T# s, B* c- H
  17.     if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;; x5 ^' Y( Q  q0 r9 q0 ?5 {
  18.     else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;5 H2 H; ?' U1 l: J
  19.     else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;
    ( J0 q% l$ x+ ~; Q; E
  20.     else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;
    # x3 [7 N1 V# m5 l; D0 l* }: j
  21.     else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;3 E( j+ ^* E# `" G* ]$ C5 G
  22.     else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;# ~. Q/ f* G9 F% o* I. Y8 j
  23.     else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;. r& N* ~4 I; I. E3 l6 S, I( R. M
  24.     return FLASH_SECTOR_7;   
    $ l; `7 ~/ d! b5 V8 k
  25. }9 }" E1 t: B& h3 S$ @
  26. $ D. O- G& P0 n  Y2 O4 U/ X
  27. //从指定地址开始写入指定长度的数据3 \8 I' o* `, _% y$ `/ q
  28. //特别注意:因为STM32F7的扇区实在太大,没办法本地保存扇区数据,所以本函数$ k  N# X! \; a" w% Y4 \) K
  29. //         写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
    7 Q8 H8 N0 f; Q0 ]* M) g- h0 I( D8 K
  30. //         写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
    + Z7 ?. O" o. l3 o$ b+ R
  31. //         没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写. ! T- b4 B4 S9 g: f. l# u$ R
  32. //该函数对OTP区域也有效!可以用来写OTP区!0 S7 C# x* c( }( |
  33. //OTP区域地址范围:0X1FF0F000~0X1FF0F41F
    8 ]' W4 N$ z' I5 o
  34. //WriteAddr:起始地址(此地址必须为4的倍数!!)
    5 y( O/ A  ]  t. J6 d
  35. //pBuffer:数据指针
    1 `9 Z7 p, z( K! d+ W2 i; l( B
  36. //NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
    4 s, |! O  m% e+ \4 s
  37. void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)    ' H# ~* |5 o" w7 X  K1 r
  38. {
    6 `8 \; L. v4 h4 R5 ~% O: F
  39.     FLASH_EraseInitTypeDef FlashEraseInit;. J- k. N" [" F# D9 d* `
  40.     HAL_StatusTypeDef FlashStatus=HAL_OK;4 @4 H! ~+ n2 K/ B
  41.     u32 SectorError=0;7 u9 f: ~* N. ~' D
  42.     u32 addrx=0;
    / p( Z  I8 Z6 d) E: a; B1 a
  43.     u32 endaddr=0;   
    9 t" h( f, |0 g7 H
  44.     if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;    //非法地址( O$ @) Y  E" A1 P; z' N

  45. # o, B  ?  `* l* m( I6 q2 K. r
  46.     HAL_FLASH_Unlock();             //解锁3 k, E; x1 |# {- |7 z
  47.     addrx=WriteAddr;                //写入的起始地址% s7 w0 Y; l/ s
  48.     endaddr=WriteAddr+NumToWrite*4;    //写入的结束地址* W/ j- |& v( q* h
  49. ( v" [) b* O  A1 ]1 `9 S
  50.     if(addrx<0X1FF00000)3 ]) F- [" ?  D& K, h$ p
  51.     {+ N  _* K) V! \, B6 u6 m
  52.         while(addrx<endaddr)        //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
    5 Y- z( q8 y, Q3 z) u. O
  53.         {
    8 ^# ^9 h5 N) A' d, |+ h+ L. Z
  54.             if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区" F# F% ?. q& z. M
  55.             {   
    / q/ B+ c  E* G/ p% B  {0 ^4 d
  56.                 FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS;       //擦除类型,扇区擦除 . v% ?: N! m) @7 ^! A
  57.                 FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx);   //要擦除的扇区
    4 ~) U/ m, S5 L5 H
  58.                 FlashEraseInit.NbSectors=1;                             //一次只擦除一个扇区
    4 k2 U2 S  f- n, v- s6 q
  59.                 FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3;      //电压范围,VCC=2.7~3.6V之间!!
    * P. [( m9 l* }  m  {
  60.                 if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
    / g/ |3 F, t: E& D* S
  61.                 {5 C& N2 Z- c# C& h: x7 a1 @+ l
  62.                     break;//发生错误了    ; a* r( r4 ?9 I4 w. u8 J
  63.                 }
    ; ?& Z, w3 N0 O& ~3 @6 t1 c
  64.                 SCB_CleanInvalidateDCache();                            //清除无效的D-Cache
    ! ^$ s2 Y, M7 p$ O0 Q; Q6 s- }# ?
  65.             }else addrx+=4;. W4 |- b3 M" C; f- F
  66.             FLASH_WaitForLastOperation(FLASH_WAITETIME);                //等待上次操作完成
    * c" X/ |0 v, s0 b9 w
  67.         }3 }/ e- ?! p# B0 y; |  f( [, Z
  68.     }
    ; @: K0 U- H% w
  69.     FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
    9 X! O6 v& B* |. O" m( ]. u% q
  70.     if(FlashStatus==HAL_OK)+ I0 Q/ S% I8 I+ {( E
  71.     {$ f- R  T9 U' D6 j
  72.         while(WriteAddr<endaddr)//写数据1 C4 T* K. L4 h
  73.         {8 P: a7 L  T3 P! z0 i7 E) H
  74.             if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据
    ; L% F3 j5 ~- e$ E! v$ P, k
  75.             {
    / H; F% u" d" @$ {% a1 \$ E# r
  76.                 break;    //写入异常
    - X6 C" A3 D( b/ k7 s4 k# e
  77.             }
    " [5 |. j$ _5 I6 t: j  }1 Q  ?
  78.             WriteAddr+=4;
    $ q2 [4 S% A/ |. ^  d
  79.             pBuffer++;( `+ A/ g+ _, `" R8 h5 C: a3 O
  80.         } 8 y4 R! p6 s- C4 c
  81.     }7 @" f- I% O! z9 k' E5 o
  82.     HAL_FLASH_Lock();           //上锁
    % H: l" `) s6 o
  83. }
    6 s1 ^7 \% x' h0 q8 K2 z7 b" J5 R; R

  84. 2 l  {3 I0 Y% v. B4 o
  85. //从指定地址开始读出指定长度的数据
    # a0 `" |/ z* D0 _3 F6 M
  86. //ReadAddr:起始地址: f2 C9 D" T4 |& W* n* m6 Y
  87. //pBuffer:数据指针
      Q0 v/ @( p! A1 q/ A$ {  k
  88. //NumToRead:字(32位)数% y" {) V( j2 ?! {
  89. void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)      
    . e, X9 a2 z' c
  90. {$ l; a8 W+ |+ Z2 @1 i
  91.     u32 i;
    1 O6 h1 R" p( ]* l' e2 R
  92.     for(i=0;i<NumToRead;i++): u0 F4 E4 p9 h: s( w
  93.     {
    6 Z  e1 ^7 i& a& ^
  94.         pBuffer<i>=STMFLASH_ReadWord(ReadAddr);//读取4个字节.
    1 M; e7 V0 [. F
  95.         ReadAddr+=4;//偏移4个字节.   
    # S0 Y1 n" N, A* w2 U  y
  96.     }' [  z; O8 X9 r5 ^
  97. }
    * N' C+ [# j" Y

  98.   D6 O2 m( B6 L' U
  99. </i>
复制代码

1 a# w1 g6 c; S! V/ @- |; ]6 o2 O2 L8 [7 }
赞 收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版