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

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

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
Flash" n& O. c- Z- u. ]$ K6 e% _
闪存模块
9 j! B7 V7 M) E
) D% h2 ]: R( @4 ^0 ]. o
bc19fd5ce8ca4f108678471a9994d1d1.png

8 @. o/ Y$ A' T- ~, q% o# n3 h- _+ W0 Y
模块组织表如上,可见STM32F767IGT6由:主存储器、系统存储器、OPT区域、选项字节4部分组成。
4 p  R& v/ ^6 v0 Q+ GSTM32F767的Flash访问路径有两条:AXIM和ICTM,一般使用AXIM接口访问Falsh,其其实地址为0X08000000
+ ?0 Y& s$ }- ]& ^; a; ?  ~8 N$ }( \: J# a
主存储器
; B# o* w; @3 ]5 f+ ]; X存放代码和数据常量(const常量数据)。它可以分为1个Bank(默认)和2个Bank,可以通过选项字节nDBank位来设置。5 A: u7 `+ |, q8 ^; i9 C) o
在单Bank模式下,STM32F767的主存储器被分为8个扇区,前4个扇区为32kb大小,第五个山扇区为128kb大小,剩下的3个扇区都是256kb大小,共1MB5 X) ?) r6 q" p) I/ D

  X  ^3 O, S% g/ A% J: }系统存储器
" Z* ^6 e' e  q6 L8 V' m8 f存放Bootloader代码,此代码是出厂时就固化的,用来给主存储器下载代码,当B0接3.3v时默认从系统存储器启动: u' E) h$ {& ^1 O# \6 Y- e3 Z

; e, L$ T6 X9 ]( R9 D( N4 ^OTP区域6 J! A3 a1 o# e8 v
一次性可编程区域,共1056byte,被划分为16个64字节的OTP数据块和1个16字节的OTP锁定块。锁定块决定了数据块是否可编程。
+ G# y0 n& n- D* c% j) _! c9 Z; A6 x) r8 b8 C+ k% }
选项字节
0 \- {  {, b; G5 w; k  Y3 m
/ P4 a0 O1 }) A$ v闪存的读取: J; u& L2 u' N  ]/ |$ V5 o$ q
为了准确的读取Flash数据,需要根据CPU时钟(HCLK)的频率和器件电源电压设置FLSH的存取控制寄存器(FLASH_ACR)中的等待周期数(LATENCY),CPU频率与FLASH等待周期数的对应关系如图:
, z. f6 M3 ~. m. j
, ~! m& |3 Q3 m3 V5 v& q8 V4 h, ^
8a2f2b2dd84e4a57a4f63ee69b5dbe6b.png

$ G/ `  b: h- c7 v8 C; ^6 l! ~
' T, k6 r7 C0 g* ~等待周期通过FLASH_ACR寄存器的LATENCY[3:0]来设置,系统复位后,系统时钟为内部的16MRC振荡器,LATENCY默认是0(1个等待周期)。供电电压一般默认为3.3,所以在设置216Mhz频率作为CPU时钟之前,必须先设置LATENCY为7(8个CPU周期),否则FLASH读写可能会出错导致死机。. n8 N: D3 K- h% Y1 u; K
STM23F7的 FLASH 读取是很简单的。例如,我们要从地址 addr ,读取一个字(一个字为32 位),可以通过如下的语句读取:/ z- I" }9 b2 M4 I6 Z$ a% w
              data=(vu32)addr;
. ?+ J2 K8 i/ |! {3 Y- R. F! Z' T将addr 强制转换为 vu32 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。
* ~3 U* w! d  f8 U6 u: l类似的,将上面的 vu32 改为 vu8 ,即可读取指定地址的一个字节。
7 A1 }2 `, \& W
! t5 ^) O  a- n: s" g5 v7 q
闪存的编程和擦除
7 s9 p* T5 P) M执行任何Flash的编程操作(擦除或编程)时,CPU时钟频率(HCLK)不能低于1Mhz。如果在Flash操作期间期间器件发生复位,无法保证Flash的内容。
$ y" S& i0 j7 |; N7 V在对Flash执行写入或擦除操作期间,任何读取Flash的尝试都会导致总线阻塞。只有在完成编程操作后才能正确处理读操作。' M- \7 ~. O( {1 V8 N; ~' Y
STM32F767的闪存编程由7个32位寄存器控制,如下
/ `- t0 g* `" B  J! e9 y. K
1 _/ @% n5 _6 ?8 c' G! I
T85`2XMT@0~([S{862MVQMV.png
. O3 T+ A9 i$ `7 L: q9 H' j7 Y' U/ J& f
3 Y5 a2 S; j* f2 j8 B# M6 b
STM32F767复位后,Flash的编程操作是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列(0X45670123 和 0XCDEF89AB)到FLASH_KEYR寄存器才可解除写保护,只有在写保护被解除后,才能操作相关寄存器。5 M* P! m2 S; N
STM32F767的编程位数通过FLASH_CR的PSIZE字段配置,PSIZE的设置必须和电源电压匹配,见表,使用3.3v供电时,PSIZE必须设置为10,擦除护着编程都必须以32位为基础。
0 I* V% ?' d- s3 y3 ?- y3 U5 s9 [  @, ?
5d10e1e0f2de471fa642a60b4893a091.png
$ K7 @' f4 U7 A8 u7 @1 G, T& {

. r" G, a0 B' l+ _& Z% c- a  j) fFLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(值必须是0XFFFFFFFF),否则无法写入。$ G; d* x) f$ c! f. ]# q

  i6 a" P! a/ U- B# ?, {5 u; _1 w, KSTM32F767的标准编程步骤7 F1 {+ M2 Y" [, Q
检查Flash_SR的BSY位,确定当前未执行任何Flash操作;4 V6 r  ~; k4 r# x
将Flash_CR寄存器中的PG位置1,激活Flash编程;' x, K4 R# p- f6 \, w( R
针对所需存储器地址(主存储器块或OTP区域内)执行数据写入操作(PSIZE需要已经设置)  _& z3 ~% L3 }& a. i0 q
等待BSY位清,完成一次编程。/ h% T4 ^0 r  Q0 O
以上四步操作就可以完成一次FLASH编程操作,需要注意几点:
0 k4 F* W8 A) r+ _+ p! M( i$ V* [确保编程地址已擦除4 S9 V2 _: K- U
需要先解锁FLASH_CR,操作完后要上锁: p9 X7 z% ^1 U5 @0 ^% N+ W
变成操作对OPT区域也一样6 w' T- [9 C( d9 M4 L% `; G' L- M
2 [8 ]+ h, C8 T$ y8 E
+ J/ o& [$ f+ Z7 n  {2 S* N* v
STM32F767的擦除
. W8 v. ~) O/ y# }1 q7 K1 |扇区擦除5 K7 w1 \2 G8 y; J3 j0 q/ \& v
检查FLASH_CR是否解锁,未解锁先解锁$ n5 @4 L( y1 J) e2 U$ V# j( v
检查FLASH_SR的BSY位,确保当前未执行任何FLASH操作& [# [' w: |; R- h; w
将FLASH_CR的SER位置1,并从主存储块的12个扇区中选择要擦除的扇区(SNB)
! E. t/ O, y; C! {将FLASH_CR的STRT置1,触发擦除操作
! ]% I0 d, y" f& z9 g等待BSY位清零. q. S9 Y! Y9 K6 A/ M- N& o: g
整片擦除
( `* `2 e1 K5 ^  P! s$ [附录5 y' t2 ]% e7 k5 ^* R/ ^
代码0 M; ^2 m# C" k$ O$ l
依赖于HAL库! L( A5 U! H0 y( ]. P1 q

8 Q6 W; W- Z, g. l, n
  1. #include "stmflash.h"$ T# u  [# M1 O0 p$ t, h& @$ G
  2. #include "delay.h"
    1 Q: K  S& h1 Y: l2 E, G& d( e( }3 X
  3. 8 {, f( ?! x  K- g
  4. //读取指定地址的字(32位数据)
    5 b) j/ j( s( F, J( c: y3 t7 y1 c+ X
  5. //faddr:读地址 6 b8 C6 P6 ?4 g
  6. //返回值:对应数据.8 [. U0 e, V" n. [1 q* n
  7. u32 STMFLASH_ReadWord(u32 faddr)
    * l1 S: A, i: M, `' t
  8. {
    9 z, f: ?: O/ S/ G* @
  9.     return *(__IO uint32_t *)faddr; 5 S* M" o" h3 O
  10. }* B; `9 n6 s8 k, c5 o

  11. ( j$ I: }: O" @
  12. //获取某个地址所在的flash扇区
    " q# W  ?/ F/ c  P/ C
  13. //addr:flash地址
    8 I# ]2 {5 j! d# S! Q
  14. //返回值:0~11,即addr所在的扇区& D1 r9 Q$ H4 _) g' Y: B
  15. uint16_t STMFLASH_GetFlashSector(u32 addr)) c; g# |, U5 D5 T5 K8 F6 v7 _
  16. {
    9 D1 b! b  v! g; i
  17.     if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;& t( U1 B2 t* w$ j. R3 m
  18.     else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;
    ; y6 y& Y& W* J2 q3 b3 i5 V5 J
  19.     else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;2 x" l: O3 v* x0 Q. C
  20.     else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;5 C( W$ g% ^5 D4 B
  21.     else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;
    ( u/ P& \7 y9 E" ?+ G% M* R: M
  22.     else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;
    . w2 I) M/ A( t
  23.     else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;
    * s/ p9 s# ]; @& W! P+ y! T
  24.     return FLASH_SECTOR_7;    + f, }, U- p: |
  25. }- I1 Z5 Q4 R3 ?- O/ ^, F9 z5 A  |
  26. 5 F- W; W/ b& }+ S: Z
  27. //从指定地址开始写入指定长度的数据
    ! N/ r0 P) @" R' ^2 }0 I0 H3 F
  28. //特别注意:因为STM32F7的扇区实在太大,没办法本地保存扇区数据,所以本函数0 V" a- l2 }! _6 J" h* O
  29. //         写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以8 B1 \9 {+ x1 R0 ]6 o9 K
  30. //         写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
    ) u( m3 @: t- R6 C' k+ O
  31. //         没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
    : t+ l0 p3 Q/ U. s& u4 k7 k
  32. //该函数对OTP区域也有效!可以用来写OTP区!/ O2 r0 ]* D% E
  33. //OTP区域地址范围:0X1FF0F000~0X1FF0F41F1 v$ Q4 }( p0 `; P
  34. //WriteAddr:起始地址(此地址必须为4的倍数!!)( J2 {& |" ^! b, c5 k! |7 {
  35. //pBuffer:数据指针
    ( Y  k$ F& I- ~2 d' |7 v
  36. //NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
    & \/ K6 ?, p& k" {2 p% ]
  37. void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)   
    ! M' T% q( W, \
  38. {
    ! w  H: n7 p2 u/ [( T3 A
  39.     FLASH_EraseInitTypeDef FlashEraseInit;0 G, Y) O& t" ?" B
  40.     HAL_StatusTypeDef FlashStatus=HAL_OK;
    ' p, n% y$ w3 O8 d6 k4 Y* k  T
  41.     u32 SectorError=0;- E6 U- d7 w  i0 M) Z
  42.     u32 addrx=0;
    ) ~0 f2 Q3 m+ ]1 j1 r' L
  43.     u32 endaddr=0;    % `0 i( ?% x4 C3 {
  44.     if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;    //非法地址
    ! m9 Y. ^0 T4 e& a6 i2 g

  45. 1 D9 h2 |; ~2 i) ?9 y) ]
  46.     HAL_FLASH_Unlock();             //解锁
    % M, {! l5 g" g9 T
  47.     addrx=WriteAddr;                //写入的起始地址6 V; Z1 \; N/ x# e/ R% n" F
  48.     endaddr=WriteAddr+NumToWrite*4;    //写入的结束地址
    + l, O* @4 O  w! P& S2 E

  49. ) A- ]1 n8 d! y0 o3 G
  50.     if(addrx<0X1FF00000)0 H5 [5 F. Y8 }9 R8 e
  51.     {6 s3 d* x: m4 T
  52.         while(addrx<endaddr)        //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
    0 ]8 v9 H# w& \0 v1 v2 J3 h- e
  53.         {: @& Y1 h9 b. T: ~4 s2 L
  54.             if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
    % E! S5 F# V: O# _5 n
  55.             {   . h; `* q2 l. Q  f- V- m
  56.                 FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS;       //擦除类型,扇区擦除
    6 w( i% A, i, ~6 \
  57.                 FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx);   //要擦除的扇区, W& x2 J0 ^8 J  p
  58.                 FlashEraseInit.NbSectors=1;                             //一次只擦除一个扇区" c5 f+ {8 K' P4 T( k
  59.                 FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3;      //电压范围,VCC=2.7~3.6V之间!!' K& S( L' |0 R
  60.                 if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK) - T7 e0 C) |- \, F# l
  61.                 {
    + z; W, i! x" F- p
  62.                     break;//发生错误了    # H  \7 [0 I; b
  63.                 }5 K+ Y; ]" J4 D9 l# Y
  64.                 SCB_CleanInvalidateDCache();                            //清除无效的D-Cache
    * X9 T& v' s: _- N. i7 z6 |
  65.             }else addrx+=4;' Y/ v7 a. B& X! F; z6 s  y. s
  66.             FLASH_WaitForLastOperation(FLASH_WAITETIME);                //等待上次操作完成3 D5 Y7 ~1 M  Z* ?4 w2 s( _
  67.         }$ ?# w: p& J+ u
  68.     }
      Y$ ^$ @! R/ z4 }. G7 Z! R+ ?& m9 U
  69.     FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
    . p1 ?" D$ V0 p0 `1 W
  70.     if(FlashStatus==HAL_OK). w7 Z/ I- a! {4 Y
  71.     {" G$ U4 N. D' Z+ q3 S
  72.         while(WriteAddr<endaddr)//写数据3 B7 L* a' y; q# J6 }2 }
  73.         {% [+ C# j, y8 o. y3 \1 T( I: R0 h
  74.             if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据
    7 q& I% \7 [7 ?! V
  75.             {
    " R- v& V/ \5 c( c( A. ?
  76.                 break;    //写入异常
      j+ t% ^. a" z% I8 `& \  A$ I% L
  77.             }4 |: z( [0 c3 x0 [7 X
  78.             WriteAddr+=4;
    * l1 E4 ]& `- n( k$ B
  79.             pBuffer++;
    * T! H( _1 U  \4 j8 Y; ~
  80.         }   o$ a/ k, d& ]5 h9 I
  81.     }
    2 \$ @! _1 t6 ^/ c4 x2 y' K
  82.     HAL_FLASH_Lock();           //上锁4 m- D/ R; e' m7 I8 @: n9 u* |- L
  83. } - F. i$ K; c2 r# n  o( x

  84. 2 i/ \# f% N9 u- y) O
  85. //从指定地址开始读出指定长度的数据
    4 N' Q3 Y6 y; X1 ?2 K1 s2 B/ a
  86. //ReadAddr:起始地址0 Z& w& _3 [! _
  87. //pBuffer:数据指针, K0 \* y5 e. [. b6 L( T' @6 J/ t
  88. //NumToRead:字(32位)数
    1 F& P' c& A5 V- X- {1 O) W
  89. void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)       % d5 J6 O9 V; w& p
  90. {
    2 i1 L8 \5 I# r
  91.     u32 i;
    1 G; U5 R3 r1 a( A+ l0 p' D
  92.     for(i=0;i<NumToRead;i++)2 I& X* ?; A: S% [
  93.     {
    3 ^0 c# L3 M4 I1 R( a, j4 j* N
  94.         pBuffer<i>=STMFLASH_ReadWord(ReadAddr);//读取4个字节.
    # O' N2 V4 T; w+ b+ l( ]# Z
  95.         ReadAddr+=4;//偏移4个字节.   
    : R- U, f$ ?* R4 L
  96.     }
    2 U3 u( K3 M" q4 m9 N$ b
  97. }. t/ y5 m* D3 e3 s9 n" D4 L6 X. j

  98. 9 f5 Z$ j' a$ c6 h( k
  99. </i>
复制代码

, z: W4 y8 A. ?- ?
' k' M5 T  l$ y; i7 J& ?7 ]
收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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