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

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

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
Flash
( K7 {' z9 F* ^闪存模块
* Z  o0 L0 N0 H/ O  L! |

! @: V* u$ q- G. u
bc19fd5ce8ca4f108678471a9994d1d1.png
6 _# ?2 W5 m2 x! g* q1 y. b+ F
9 B  a4 p: v( a8 K
模块组织表如上,可见STM32F767IGT6由:主存储器、系统存储器、OPT区域、选项字节4部分组成。
  I' h6 T/ w- ?5 qSTM32F767的Flash访问路径有两条:AXIM和ICTM,一般使用AXIM接口访问Falsh,其其实地址为0X08000000
. D" u+ t* M% i# P) b) |
) f7 S$ v2 w0 J" a* [# {主存储器3 ~: o8 M8 Q, S. _4 u- L7 v8 L
存放代码和数据常量(const常量数据)。它可以分为1个Bank(默认)和2个Bank,可以通过选项字节nDBank位来设置。
0 `) d7 j$ o( Z6 }6 g; I在单Bank模式下,STM32F767的主存储器被分为8个扇区,前4个扇区为32kb大小,第五个山扇区为128kb大小,剩下的3个扇区都是256kb大小,共1MB
# I# y3 Y6 \$ B9 u4 R
& f$ T5 O' v" d% r2 l: T( w! l8 m系统存储器
  ?9 G2 e+ }1 J! d存放Bootloader代码,此代码是出厂时就固化的,用来给主存储器下载代码,当B0接3.3v时默认从系统存储器启动
$ [5 j% [- h! s1 U- T1 H
6 @0 W* b: L+ Y9 p  L+ \OTP区域* O) {( n- d3 K
一次性可编程区域,共1056byte,被划分为16个64字节的OTP数据块和1个16字节的OTP锁定块。锁定块决定了数据块是否可编程。7 v  |( G6 N- ^

  C2 I5 k- J/ C9 ~) R+ J% K& _选项字节
3 {: {; Q& v6 O  y7 D& W7 `# z& a; Y  C1 W
闪存的读取
) ^* m% ~1 N, z# z0 L6 X为了准确的读取Flash数据,需要根据CPU时钟(HCLK)的频率和器件电源电压设置FLSH的存取控制寄存器(FLASH_ACR)中的等待周期数(LATENCY),CPU频率与FLASH等待周期数的对应关系如图:
+ T3 ?: G+ {5 j; g1 l7 w
9 Q( l4 o8 O' I1 e' W
8a2f2b2dd84e4a57a4f63ee69b5dbe6b.png
  k7 r4 o& k' R4 @
! h+ I  _2 `* ~& v: A- q! _
等待周期通过FLASH_ACR寄存器的LATENCY[3:0]来设置,系统复位后,系统时钟为内部的16MRC振荡器,LATENCY默认是0(1个等待周期)。供电电压一般默认为3.3,所以在设置216Mhz频率作为CPU时钟之前,必须先设置LATENCY为7(8个CPU周期),否则FLASH读写可能会出错导致死机。# Z0 h6 {! Z7 O
STM23F7的 FLASH 读取是很简单的。例如,我们要从地址 addr ,读取一个字(一个字为32 位),可以通过如下的语句读取:
- O7 Z7 _, {- J# ~              data=(vu32)addr;/ Q% _" R' S" |/ ~
将addr 强制转换为 vu32 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。/ k, V# g0 h1 M  ~
类似的,将上面的 vu32 改为 vu8 ,即可读取指定地址的一个字节。
1 g0 @8 N3 i0 E/ [, @& j1 P: F/ Z: i
: Z3 `1 T& O) }3 q  g4 p, r
闪存的编程和擦除1 s- p* W  ~  D7 E( }& A% c
执行任何Flash的编程操作(擦除或编程)时,CPU时钟频率(HCLK)不能低于1Mhz。如果在Flash操作期间期间器件发生复位,无法保证Flash的内容。* R, f& J3 _# J* K1 j1 y* f$ N: Y; i) q
在对Flash执行写入或擦除操作期间,任何读取Flash的尝试都会导致总线阻塞。只有在完成编程操作后才能正确处理读操作。
' W) R+ n2 h# Q6 R, ASTM32F767的闪存编程由7个32位寄存器控制,如下
6 V( R# Y! \+ U$ _% U' m* }: ?6 i/ W5 W+ v
T85`2XMT@0~([S{862MVQMV.png

6 a5 Z' Y' ^% g! L! k
- U& r( k- I5 i: _5 ~1 `  SSTM32F767复位后,Flash的编程操作是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列(0X45670123 和 0XCDEF89AB)到FLASH_KEYR寄存器才可解除写保护,只有在写保护被解除后,才能操作相关寄存器。' K2 ?) w4 e) e, B. w0 q
STM32F767的编程位数通过FLASH_CR的PSIZE字段配置,PSIZE的设置必须和电源电压匹配,见表,使用3.3v供电时,PSIZE必须设置为10,擦除护着编程都必须以32位为基础。
9 r/ J* y; ^* Y: K3 ?. b3 l
3 E' @! g# p  w8 @
5d10e1e0f2de471fa642a60b4893a091.png
- v# I$ w* T/ N6 `/ z' h
- T" K& u! k4 P; V) C) B
FLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(值必须是0XFFFFFFFF),否则无法写入。1 f. n9 r  G( f3 e& x2 a, w

1 t) i( Q2 @& q4 \3 dSTM32F767的标准编程步骤
6 o) \) v+ @" k$ m; @1 _1 \* s检查Flash_SR的BSY位,确定当前未执行任何Flash操作;( D/ x' z! Y8 d( L; y( z' k6 @- v
将Flash_CR寄存器中的PG位置1,激活Flash编程;/ }  P) Z" |9 V4 j( P5 m
针对所需存储器地址(主存储器块或OTP区域内)执行数据写入操作(PSIZE需要已经设置)4 ~0 C4 h) w- Y6 o
等待BSY位清,完成一次编程。
( ~8 V. s' X* g以上四步操作就可以完成一次FLASH编程操作,需要注意几点:
  _/ O6 y2 d  T, {+ R确保编程地址已擦除
9 G, z) C$ {; X/ l1 ?需要先解锁FLASH_CR,操作完后要上锁
9 i! l" s/ b# k' ~. I+ B+ T! d  |变成操作对OPT区域也一样
& S5 |) d9 m/ P
0 q, v+ w5 }/ W! M" P& Y( q% E1 r& m! J: @! V$ Z, C3 u
STM32F767的擦除
8 A* V1 W* U" V3 Q: a扇区擦除$ ?. I, d1 Z& [$ x7 r
检查FLASH_CR是否解锁,未解锁先解锁
- W6 \% g+ T3 r1 U, }5 h% ]检查FLASH_SR的BSY位,确保当前未执行任何FLASH操作6 ]3 [6 D- o0 j; ?9 R! l- ?
将FLASH_CR的SER位置1,并从主存储块的12个扇区中选择要擦除的扇区(SNB)) |& L) H- `: Q. X
将FLASH_CR的STRT置1,触发擦除操作
8 U, w5 P) X' L9 P等待BSY位清零
; @0 G8 G$ [$ T) \/ \  d整片擦除' R* E# T: |) C: d3 S
附录
6 a, l8 [9 j2 _# ?' n0 A2 ^代码7 N& [) O, X! {6 P/ ]( m) W
依赖于HAL库
  _) q# B4 Q" f% e  q
& H! {$ H0 L8 i
  1. #include "stmflash.h"
    0 c4 g, e) X% r  S" H
  2. #include "delay.h"! J' w! u1 _4 c( d- s% }4 V8 s# ^

  3. & ]' x2 V, Q2 Z4 J7 x7 }- f' o+ C
  4. //读取指定地址的字(32位数据)
      z# R9 L5 L3 M; ^8 U0 O- A
  5. //faddr:读地址
    ) ^+ ~. k- V; ?; k3 f- c+ X
  6. //返回值:对应数据.9 a" d( _& E, s; U, H6 J6 f% d
  7. u32 STMFLASH_ReadWord(u32 faddr)( s  ]; f  E, e" x
  8. {& ?7 \" b- E2 w
  9.     return *(__IO uint32_t *)faddr;
    , N5 k' E" v, G* ~& z! {" |  `' Q
  10. }& u+ N8 W; |8 K- p. z1 U1 q$ }

  11. 5 q: M  r! }# M
  12. //获取某个地址所在的flash扇区& ]4 u2 \, k" c5 r% A1 r
  13. //addr:flash地址
    - S- S1 }. Y/ e& z8 f* v3 Q
  14. //返回值:0~11,即addr所在的扇区
    7 \2 r1 r4 a& D0 @0 D% l: _
  15. uint16_t STMFLASH_GetFlashSector(u32 addr)
    4 S  L& H" Q) f9 M+ A1 ~  _) A3 ?2 a
  16. {4 _1 ]0 Q, K" h5 T; L
  17.     if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;
    ! z$ j; K6 N4 _6 \) |, g
  18.     else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;
    : u5 B3 q/ c% N- I- ^
  19.     else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;0 C5 @9 d4 l" d; G/ `6 u. ]5 l
  20.     else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;" Q6 T) n- {" F  H& l8 n! t& f: S- z
  21.     else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;
    ; J2 Q2 J3 P2 ?9 g& }4 |5 R5 A
  22.     else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;5 m- ^. v2 X* Z: G& k
  23.     else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;
    . z$ e0 q1 b4 y
  24.     return FLASH_SECTOR_7;    ; y, L* r1 @/ j9 M
  25. }
    ' d! K1 O4 N! w/ {6 }
  26. # G1 |% O5 x' @" D# w, K7 g
  27. //从指定地址开始写入指定长度的数据! w2 D6 ~% I- @4 h; |% Y
  28. //特别注意:因为STM32F7的扇区实在太大,没办法本地保存扇区数据,所以本函数! y( z! C6 j) i1 R, q9 X! X; m$ H
  29. //         写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
    ; i7 ?0 @* q, b+ S+ t$ H- i. I4 [
  30. //         写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里7 ^* g. z, ^5 I* Q
  31. //         没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写. 0 ~* S5 \+ ?: f3 k0 I- s; Y9 M
  32. //该函数对OTP区域也有效!可以用来写OTP区!
    - p5 I* F& y" U2 N2 d* _$ z0 z
  33. //OTP区域地址范围:0X1FF0F000~0X1FF0F41F
    & j/ m8 G! \% v
  34. //WriteAddr:起始地址(此地址必须为4的倍数!!)
    ; \! d: Z% K: Y8 c. H
  35. //pBuffer:数据指针/ B; \( `2 R$ q/ I& M; O5 x+ D4 a
  36. //NumToWrite:字(32位)数(就是要写入的32位数据的个数.) 7 h4 E5 s3 t+ m9 n7 o
  37. void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)   
    % {1 @, u# L- ^" L9 Q/ J. @% K
  38. {
    8 c: x5 L7 O9 V- t  L. u$ Q! M
  39.     FLASH_EraseInitTypeDef FlashEraseInit;1 z2 L& u4 ?" H. b& C
  40.     HAL_StatusTypeDef FlashStatus=HAL_OK;# R6 e7 g* C+ C8 E! G+ V& L
  41.     u32 SectorError=0;
    ) P( @! D# p$ q
  42.     u32 addrx=0;8 Y9 h. ?3 [# f. Y  w2 N+ D  ]
  43.     u32 endaddr=0;   
    ' _% u1 }: p6 w0 u
  44.     if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;    //非法地址
      K1 t! C; a+ D) j7 S* {5 t0 q
  45. / O6 L0 ^$ s, L
  46.     HAL_FLASH_Unlock();             //解锁6 b9 l* o, M' D( x0 h) q% ~2 O
  47.     addrx=WriteAddr;                //写入的起始地址
    % y- H) t# [- c
  48.     endaddr=WriteAddr+NumToWrite*4;    //写入的结束地址
    9 E3 C* L6 Z  s0 m
  49. ; E7 c1 s! I% n, I' d8 Z5 T
  50.     if(addrx<0X1FF00000)( V( Q5 m2 K  d  o9 }& E, P
  51.     {5 W" y/ u. V" c; k
  52.         while(addrx<endaddr)        //扫清一切障碍.(对非FFFFFFFF的地方,先擦除); ?: I# d8 w6 P" s& @7 r
  53.         {
    . S& P. O( b. \) a" C3 D! w- y9 R  }
  54.             if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
    4 Y# s+ e1 G0 x6 h$ T- |
  55.             {   / M% U8 C1 x4 P
  56.                 FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS;       //擦除类型,扇区擦除 - ]8 ^: m* p4 o9 B! o* L% a
  57.                 FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx);   //要擦除的扇区+ q6 K; w' o" s' X( B
  58.                 FlashEraseInit.NbSectors=1;                             //一次只擦除一个扇区
    & G. H1 m* ]' t7 R+ a- V, Z. T
  59.                 FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3;      //电压范围,VCC=2.7~3.6V之间!!. `8 K8 [. q  y; D; t
  60.                 if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK) $ Q' z% h& K  a3 s
  61.                 {5 x; P! g1 E8 g! w2 e& K0 E
  62.                     break;//发生错误了    ( l, p9 ^$ w2 _' D# i, Y
  63.                 }8 [6 D1 A2 _; M; e
  64.                 SCB_CleanInvalidateDCache();                            //清除无效的D-Cache, {2 y6 S5 r  b8 K; ?( R
  65.             }else addrx+=4;& k3 M  h% N- b
  66.             FLASH_WaitForLastOperation(FLASH_WAITETIME);                //等待上次操作完成% |0 F. z0 c. e* r
  67.         }
    4 ^, `* ?+ T. v) t; {/ D4 b" j8 V
  68.     }, e, u( y" P2 G2 ]2 t* c
  69.     FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
    + @$ L" F7 z1 R( B1 w
  70.     if(FlashStatus==HAL_OK)
    & ?1 F% g( l4 ?* |
  71.     {
    6 i3 X1 _) D1 P+ C0 @( L9 {0 Y
  72.         while(WriteAddr<endaddr)//写数据& N- L8 r2 o8 g& G: \$ Z0 x. t
  73.         {
    " h$ c+ Y9 X: L6 x/ u
  74.             if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据
    " S$ f5 ?' }4 F) w. X
  75.             { ) G" W  }. q& n+ H9 y/ w
  76.                 break;    //写入异常
    . D8 Y& r5 @5 V
  77.             }
    % d4 T' G% o7 j5 r  P& W
  78.             WriteAddr+=4;: w6 b. }0 e0 R1 k4 q# U
  79.             pBuffer++;  l8 z) K; W) M# S$ P
  80.         }
    ( q+ P6 r4 d, w
  81.     }: Q7 U. l4 K7 h) L
  82.     HAL_FLASH_Lock();           //上锁8 l& S  }: R' O
  83. } + ^- q& v6 c- r5 U
  84. 5 p$ K( x, a/ J9 u3 A. L4 T1 t
  85. //从指定地址开始读出指定长度的数据
    * D" |  E! b1 q) B
  86. //ReadAddr:起始地址" x6 L9 Q+ G7 y2 _. G! d' ~
  87. //pBuffer:数据指针
    9 O0 y3 e: X9 b' e8 B# a: v# Y+ \
  88. //NumToRead:字(32位)数
    7 g0 }+ ^" Z2 v4 r4 I+ \
  89. void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)       ! J0 ]# s' y. M
  90. {9 e" |* C& e  I% J  b; w
  91.     u32 i;
    # |  x1 s& B1 }3 _) o2 C( a& H
  92.     for(i=0;i<NumToRead;i++)2 K- g1 D2 @& s3 c9 Z4 f
  93.     {% y/ ]. Z& l/ v# L9 X4 l4 F4 g1 ?; b
  94.         pBuffer<i>=STMFLASH_ReadWord(ReadAddr);//读取4个字节.
    3 S2 A5 J5 O$ Y" w# I9 K2 G
  95.         ReadAddr+=4;//偏移4个字节.   
    ! [( t* U, Q% `; `! b
  96.     }
    , Z0 W% e7 I2 h
  97. }
    1 G  s  M. p$ H7 s, i
  98. 8 c0 L  K+ N$ p
  99. </i>
复制代码

/ c. C7 d% I6 C9 B
" Y0 P! O( y0 ]/ X( I) ?- r" O9 e) o
收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版