请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
Flash
: r7 [. B4 i4 C2 m8 |闪存模块

9 M9 I9 `: v# p, [. G. ^; z
: _# ]7 ?- r# m" y( S+ U
bc19fd5ce8ca4f108678471a9994d1d1.png
$ n; m1 {1 X0 j9 h! l" E9 L

- K# R$ N; \+ k模块组织表如上,可见STM32F767IGT6由:主存储器、系统存储器、OPT区域、选项字节4部分组成。
; S  g% ]1 o: H6 ESTM32F767的Flash访问路径有两条:AXIM和ICTM,一般使用AXIM接口访问Falsh,其其实地址为0X08000000! V0 m7 |3 [7 c! T: O, ^8 Z6 `
$ O) `7 o; G) @1 U/ u; {
主存储器8 ?- Q2 C8 X) y% S: k- l
存放代码和数据常量(const常量数据)。它可以分为1个Bank(默认)和2个Bank,可以通过选项字节nDBank位来设置。
* {% \& v4 S  x: N- Q" g+ D# J; N在单Bank模式下,STM32F767的主存储器被分为8个扇区,前4个扇区为32kb大小,第五个山扇区为128kb大小,剩下的3个扇区都是256kb大小,共1MB4 \* `1 q( G! ?

( V' b: h+ Y3 L, J7 c系统存储器6 i% b& w2 R* a$ j+ g
存放Bootloader代码,此代码是出厂时就固化的,用来给主存储器下载代码,当B0接3.3v时默认从系统存储器启动
2 G; K& q$ D0 `" E1 p. B( I& q0 [7 D
OTP区域0 z* a3 g9 d& W
一次性可编程区域,共1056byte,被划分为16个64字节的OTP数据块和1个16字节的OTP锁定块。锁定块决定了数据块是否可编程。
( L$ h8 M( V6 W& z; @; B6 z9 X! Q" `$ _) X
选项字节
& m& ^$ C6 X' k* c
" _8 B$ Q$ w$ x  k  J% H/ U4 `闪存的读取
; m: B  c! D3 c# \为了准确的读取Flash数据,需要根据CPU时钟(HCLK)的频率和器件电源电压设置FLSH的存取控制寄存器(FLASH_ACR)中的等待周期数(LATENCY),CPU频率与FLASH等待周期数的对应关系如图:
& o1 {$ B; X5 j- q. e( b% P% ~, p" ?/ d& H8 |6 g
8a2f2b2dd84e4a57a4f63ee69b5dbe6b.png
3 p$ ?, P5 b$ v* L. u
* V/ R) `: X) L+ W
等待周期通过FLASH_ACR寄存器的LATENCY[3:0]来设置,系统复位后,系统时钟为内部的16MRC振荡器,LATENCY默认是0(1个等待周期)。供电电压一般默认为3.3,所以在设置216Mhz频率作为CPU时钟之前,必须先设置LATENCY为7(8个CPU周期),否则FLASH读写可能会出错导致死机。
! U; {  w8 ~$ Y! tSTM23F7的 FLASH 读取是很简单的。例如,我们要从地址 addr ,读取一个字(一个字为32 位),可以通过如下的语句读取:
  ~6 x( Q' U* D+ E              data=(vu32)addr;
( k0 f9 F' j2 C' f将addr 强制转换为 vu32 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。
$ F8 ]- W9 H8 C" L5 }% j类似的,将上面的 vu32 改为 vu8 ,即可读取指定地址的一个字节。% S4 a4 v0 w; z2 i$ `

# i1 B* |! ?0 }$ l; z% w8 u
闪存的编程和擦除
: T0 U* D) Z/ o. e9 E. E/ a执行任何Flash的编程操作(擦除或编程)时,CPU时钟频率(HCLK)不能低于1Mhz。如果在Flash操作期间期间器件发生复位,无法保证Flash的内容。
9 Y* q: X4 p( r9 l) a1 X在对Flash执行写入或擦除操作期间,任何读取Flash的尝试都会导致总线阻塞。只有在完成编程操作后才能正确处理读操作。
& q1 V: u6 z0 y+ p# o) CSTM32F767的闪存编程由7个32位寄存器控制,如下
" {9 Y# ^% ?. E1 }
5 \& A0 g- E8 W2 A" u
T85`2XMT@0~([S{862MVQMV.png

# F2 V( G9 G# p  D3 D5 B1 ^5 e2 t
; K" ?; Y1 E' y8 r, I) i& LSTM32F767复位后,Flash的编程操作是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列(0X45670123 和 0XCDEF89AB)到FLASH_KEYR寄存器才可解除写保护,只有在写保护被解除后,才能操作相关寄存器。6 L; j- k1 l7 b$ I/ ^" E9 S6 U
STM32F767的编程位数通过FLASH_CR的PSIZE字段配置,PSIZE的设置必须和电源电压匹配,见表,使用3.3v供电时,PSIZE必须设置为10,擦除护着编程都必须以32位为基础。5 ?0 E' J! }: A8 e) _3 q

& Z6 P# R# e  g* z8 D
5d10e1e0f2de471fa642a60b4893a091.png

! W3 Z8 N" L9 l% X8 w  z4 A: Y- C
, W& b  K. U+ ]% @6 J- GFLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(值必须是0XFFFFFFFF),否则无法写入。0 ?9 e! C. M4 X) X* ~7 Z
' w4 X. _8 t0 f+ D9 J, [3 C
STM32F767的标准编程步骤# D+ A* Y/ f% @
检查Flash_SR的BSY位,确定当前未执行任何Flash操作;
0 i6 R5 e7 f' s$ [' T' r2 b将Flash_CR寄存器中的PG位置1,激活Flash编程;, m# K/ J6 F+ J  h+ H
针对所需存储器地址(主存储器块或OTP区域内)执行数据写入操作(PSIZE需要已经设置); k+ S; h0 j9 H# V# E: D6 l
等待BSY位清,完成一次编程。
+ Z% b7 e2 `: f; `/ ^" O; v1 U; A以上四步操作就可以完成一次FLASH编程操作,需要注意几点:
9 M6 r3 j6 h4 R: C确保编程地址已擦除; i; Y' e9 B7 n* p7 S; I7 s
需要先解锁FLASH_CR,操作完后要上锁
! C& [' {9 y9 Q变成操作对OPT区域也一样
; q9 W$ T: m& o  J
, p5 M. K, u" D( m7 M8 L
' M$ s7 z7 T. ?7 R* ^+ `STM32F767的擦除
9 W/ X; c+ B& q- a5 l: @扇区擦除! J8 ^/ G9 o/ S+ H3 S* o3 W
检查FLASH_CR是否解锁,未解锁先解锁
9 t+ I, _9 ]3 i0 C检查FLASH_SR的BSY位,确保当前未执行任何FLASH操作  H7 m3 d8 E4 C, T6 v" x& {
将FLASH_CR的SER位置1,并从主存储块的12个扇区中选择要擦除的扇区(SNB)( S5 ?! g6 H1 A8 z. |
将FLASH_CR的STRT置1,触发擦除操作  o& {) }- f+ y5 t
等待BSY位清零# \1 N' v0 k  l
整片擦除) u) H/ W- |8 V: g
附录! h0 }  [' y5 I
代码/ a$ R3 b; V% f" `2 v
依赖于HAL库
4 U, O0 r/ `$ c( b
! s$ m7 ^8 k9 @+ V
  1. #include "stmflash.h"
    3 ]- G) v1 [1 _) x1 T
  2. #include "delay.h"- h# }. n* }: b# x! g
  3. 2 P1 v% o" W0 O9 d. J$ D
  4. //读取指定地址的字(32位数据) , U, p0 B7 z) @# L+ }
  5. //faddr:读地址
    8 f7 W6 X: U6 R; U" V9 d! M3 @
  6. //返回值:对应数据.
    5 o+ a& f# L$ y0 N* w5 W. y# H, L) o1 G
  7. u32 STMFLASH_ReadWord(u32 faddr)
    / u- l0 F. a& m6 q) w9 N8 Y
  8. {5 A8 X6 }" X4 @& S8 R9 c: f
  9.     return *(__IO uint32_t *)faddr; 6 W* ~" P& K5 [2 e: E, X+ B
  10. }* ~8 I& V7 Q$ a! b5 o( }9 K( L7 H
  11. " t& `6 n3 Z$ F& n  C% k) |
  12. //获取某个地址所在的flash扇区2 [( D  s$ _+ e) b; H% z
  13. //addr:flash地址
    0 u+ Y- X8 I$ `: A2 N
  14. //返回值:0~11,即addr所在的扇区# n! E. I# ]4 t$ i6 O
  15. uint16_t STMFLASH_GetFlashSector(u32 addr)* H( X$ p# o( U& ?" b
  16. {
      q3 |8 {5 S7 v( O! w2 N
  17.     if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;) P: D* @! E, y! E; z
  18.     else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;0 S9 k; _0 y- J% r: \4 A5 V" T* U
  19.     else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;
    ' F, g2 X4 K2 f
  20.     else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;/ F. D" X( z' v
  21.     else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;
      r" G* O0 L9 k/ H  ]
  22.     else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;/ o: {2 ~0 K- R& a; r0 e( V& K
  23.     else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;. t: u( W; Q) H2 v* b4 X
  24.     return FLASH_SECTOR_7;    9 e& t" E9 }- j; ^/ u
  25. }( Y3 u2 U. S) h2 H& |

  26. - J8 _: C8 B3 A1 o% G* Y* c3 N
  27. //从指定地址开始写入指定长度的数据
    - A% p: L! s) d" \" W# n! r
  28. //特别注意:因为STM32F7的扇区实在太大,没办法本地保存扇区数据,所以本函数: o8 N2 ]  S5 B' u: g
  29. //         写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
    ! r, D; _! I9 u6 ]' T& ~2 o
  30. //         写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
    . t$ P/ L* ]- v' {  V
  31. //         没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写. 4 N" A+ z& U# z* P
  32. //该函数对OTP区域也有效!可以用来写OTP区!
    7 w$ P0 D% l& v3 J
  33. //OTP区域地址范围:0X1FF0F000~0X1FF0F41F, C0 E( b2 A  G3 A. ?" K' i
  34. //WriteAddr:起始地址(此地址必须为4的倍数!!)
    % L3 ^1 p! M; v! d3 q2 a* \
  35. //pBuffer:数据指针$ I; Q9 S" `! J! U( P
  36. //NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
    . q) I& D/ G% ~' C
  37. void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)    ; _* J# i2 n& g/ [/ h* G5 S
  38. {
    $ S+ }# o" R6 I1 ]: B  V% w
  39.     FLASH_EraseInitTypeDef FlashEraseInit;
    & ^  Q7 {0 M, b' x9 C# P
  40.     HAL_StatusTypeDef FlashStatus=HAL_OK;& B) b; A, J  V6 t9 u( ]
  41.     u32 SectorError=0;$ t6 W9 ~4 ~' w2 Y
  42.     u32 addrx=0;% _7 n6 N% N# \! e
  43.     u32 endaddr=0;   
    # R  u* X& T+ n$ o; G& J
  44.     if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;    //非法地址
    # O$ l: }$ k6 A6 {
  45. ' c9 \5 y2 n+ p4 ~5 q
  46.     HAL_FLASH_Unlock();             //解锁
    ( b" w7 J! }  A/ u+ B0 O: |
  47.     addrx=WriteAddr;                //写入的起始地址0 c$ q$ W; C5 t
  48.     endaddr=WriteAddr+NumToWrite*4;    //写入的结束地址
    ) i# B& U& a+ T+ h; t# C5 `4 r
  49. $ @1 t$ z) w% K) T$ h+ x
  50.     if(addrx<0X1FF00000)
    $ h2 d5 T& e+ V
  51.     {7 J- [% a8 X$ r5 ]% X* _
  52.         while(addrx<endaddr)        //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)# H# N7 |0 }/ j' X8 x
  53.         {7 e, l  h/ ?6 L6 j/ G$ u/ ~8 ]
  54.             if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
    , N" W! I, ?7 U5 B5 _
  55.             {   
    ( ~! H$ \7 O& R: U# ^1 K2 e0 o
  56.                 FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS;       //擦除类型,扇区擦除
    6 p( T; J! C$ |
  57.                 FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx);   //要擦除的扇区
    5 b3 G. D) @9 f( }; [
  58.                 FlashEraseInit.NbSectors=1;                             //一次只擦除一个扇区
    # v6 O% |9 D+ I' e0 N
  59.                 FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3;      //电压范围,VCC=2.7~3.6V之间!!
    6 f# F/ C6 F1 L6 D! A; T
  60.                 if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
    ; B  p2 v, H( ?& Y9 F- J
  61.                 {
    3 r6 V( }6 m9 Y+ n  g- O
  62.                     break;//发生错误了    : S" M0 r7 f- P0 l
  63.                 }
    8 b# N7 m5 ?8 {0 I+ t
  64.                 SCB_CleanInvalidateDCache();                            //清除无效的D-Cache
    ! N& K8 F; C2 a* s( e
  65.             }else addrx+=4;" A' p1 |0 b1 |! m8 G
  66.             FLASH_WaitForLastOperation(FLASH_WAITETIME);                //等待上次操作完成
    7 c. G. T) T! N4 A/ g
  67.         }! B3 v! D  K# L5 }
  68.     }
    ; X" ?* ?7 E$ w* i8 e" Z
  69.     FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成/ X( R- N; K9 n: w- z
  70.     if(FlashStatus==HAL_OK)0 Z& F+ @7 `1 r' f+ ]- y8 D
  71.     {
    . |# O, {- |; t" u2 S* z
  72.         while(WriteAddr<endaddr)//写数据3 `0 f& @7 p" c( B6 m' g
  73.         {
    & W; ?* t) G. N" l
  74.             if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据
    + z4 R7 J4 u: }5 k8 [
  75.             { 7 l; N" f. j$ j; l- i
  76.                 break;    //写入异常$ O3 [- X, R7 s' K# C" X
  77.             }) b, f8 |* ?1 B* u5 U
  78.             WriteAddr+=4;
    6 U, V" }' E+ i0 ^3 F
  79.             pBuffer++;
    9 G. o  d8 D" r7 J6 Z+ L
  80.         }
    7 B% f7 ~3 k9 c, t
  81.     }
    $ i: x( w6 W2 J7 x! I
  82.     HAL_FLASH_Lock();           //上锁8 O: n* t- h3 d3 A
  83. } + C+ G. Z. T5 ?; ]  i8 b
  84. / D1 ~) q5 G0 B
  85. //从指定地址开始读出指定长度的数据6 C0 j8 }: I8 v* j$ y" z5 ~4 L
  86. //ReadAddr:起始地址
    8 B: v) J8 j5 A
  87. //pBuffer:数据指针5 j. ~2 U" P1 |  g2 ]
  88. //NumToRead:字(32位)数
    . K9 D$ e  R0 z  ?% ^* u! ^0 P$ W( Y
  89. void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)      
    % R7 z) ^: ?! D/ o
  90. {
    9 Y' r$ g9 `, A! ]$ W: T3 B
  91.     u32 i;; ?. o( l3 \2 S3 @/ v. w
  92.     for(i=0;i<NumToRead;i++)! i; \- M9 u- f4 }
  93.     {
    3 _4 S6 K+ _3 H! }* S( ~" M' b
  94.         pBuffer<i>=STMFLASH_ReadWord(ReadAddr);//读取4个字节.$ y% l' n, N2 K8 Y. K) u
  95.         ReadAddr+=4;//偏移4个字节.    + v* M" X: u  U8 E  V! w6 _2 b4 ~
  96.     }! @. u  ^* z" ~* |
  97. }
    8 u: q& {2 P* N& ?, X

  98. " H. B: d8 J3 Q
  99. </i>
复制代码

5 K6 ?8 {/ u; p# Y/ F
. }" p1 j* F% |( A* F2 g- G5 B8 q
收藏 评论0 发布时间:2021-12-11 12:00

举报

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