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

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

[复制链接]
STMCU小助手 发布时间:2021-12-11 12:00
Flash
+ Z" `2 C  E% p4 @6 q闪存模块
+ A' x, S  Z9 P2 p

2 W: }8 X3 o+ e1 e% x  d
bc19fd5ce8ca4f108678471a9994d1d1.png
8 m' C: v5 b( `3 ^# J7 }$ V

  K9 \# N7 x+ {$ y$ }+ ~+ c+ `模块组织表如上,可见STM32F767IGT6由:主存储器、系统存储器、OPT区域、选项字节4部分组成。
( m3 j( S* V  l/ U/ h7 SSTM32F767的Flash访问路径有两条:AXIM和ICTM,一般使用AXIM接口访问Falsh,其其实地址为0X08000000  u5 G- {  p; z9 W
, g9 r- Q  {/ z" h# G
主存储器0 r5 I* a  }. \! ?( @% W5 e
存放代码和数据常量(const常量数据)。它可以分为1个Bank(默认)和2个Bank,可以通过选项字节nDBank位来设置。* P8 X1 U, S0 o5 z; Y
在单Bank模式下,STM32F767的主存储器被分为8个扇区,前4个扇区为32kb大小,第五个山扇区为128kb大小,剩下的3个扇区都是256kb大小,共1MB. r1 R) l7 v* R/ |) I, G

5 [* f# [8 X% W  b  }* H系统存储器
/ a( O: [2 D7 f存放Bootloader代码,此代码是出厂时就固化的,用来给主存储器下载代码,当B0接3.3v时默认从系统存储器启动1 ?+ G. V7 V+ M2 ?
/ Q0 n' @6 u/ h7 _7 c% J
OTP区域
* R& ?0 I! c7 ~! [  S* X一次性可编程区域,共1056byte,被划分为16个64字节的OTP数据块和1个16字节的OTP锁定块。锁定块决定了数据块是否可编程。
! c! Z( q+ V' I. z- U) y3 F* X: z1 U! k4 t+ }+ g
选项字节% ~9 b: z0 V- V- Y

! o6 ?+ Q7 A2 M0 G# y) V; W闪存的读取
5 J$ }7 o% ?* z& c! z. N# i为了准确的读取Flash数据,需要根据CPU时钟(HCLK)的频率和器件电源电压设置FLSH的存取控制寄存器(FLASH_ACR)中的等待周期数(LATENCY),CPU频率与FLASH等待周期数的对应关系如图:
& ^5 [$ n7 H# ?/ W( q' ]7 s2 o3 X# E# C8 h/ g; B3 c
8a2f2b2dd84e4a57a4f63ee69b5dbe6b.png
5 K+ ~) M- K0 |0 F$ O
: @. O- N6 u; t; j  ~5 ^1 F% e
等待周期通过FLASH_ACR寄存器的LATENCY[3:0]来设置,系统复位后,系统时钟为内部的16MRC振荡器,LATENCY默认是0(1个等待周期)。供电电压一般默认为3.3,所以在设置216Mhz频率作为CPU时钟之前,必须先设置LATENCY为7(8个CPU周期),否则FLASH读写可能会出错导致死机。5 n: C1 X; F  `) F
STM23F7的 FLASH 读取是很简单的。例如,我们要从地址 addr ,读取一个字(一个字为32 位),可以通过如下的语句读取:
$ _9 K3 _1 D5 a, w# {6 p4 V              data=(vu32)addr;8 a$ o! `$ v( u$ \4 k
将addr 强制转换为 vu32 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。
) U! i3 \! Z; E7 l  B; z类似的,将上面的 vu32 改为 vu8 ,即可读取指定地址的一个字节。
8 }. J4 B) l( k2 U! V5 i
$ x0 S0 v9 t' D, u2 q0 G
闪存的编程和擦除4 `; z2 z2 j: B$ u
执行任何Flash的编程操作(擦除或编程)时,CPU时钟频率(HCLK)不能低于1Mhz。如果在Flash操作期间期间器件发生复位,无法保证Flash的内容。
; x* f* }2 h: Q5 V在对Flash执行写入或擦除操作期间,任何读取Flash的尝试都会导致总线阻塞。只有在完成编程操作后才能正确处理读操作。( \( v+ c) o, X9 U
STM32F767的闪存编程由7个32位寄存器控制,如下6 O8 ]# d3 V' F3 Z) \  `

  a+ L) O: J3 Y# ~7 @! @
T85`2XMT@0~([S{862MVQMV.png
$ n+ u/ [, k3 j& x, ?/ U8 y
1 v2 `" }$ m; b3 `+ s& z
STM32F767复位后,Flash的编程操作是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列(0X45670123 和 0XCDEF89AB)到FLASH_KEYR寄存器才可解除写保护,只有在写保护被解除后,才能操作相关寄存器。- v, U0 g  S2 e$ F- U/ N/ F' p% _
STM32F767的编程位数通过FLASH_CR的PSIZE字段配置,PSIZE的设置必须和电源电压匹配,见表,使用3.3v供电时,PSIZE必须设置为10,擦除护着编程都必须以32位为基础。' x# ^" V' D) j
- \, r7 x# ~; T! j( t
5d10e1e0f2de471fa642a60b4893a091.png

2 ~1 q  D! v+ N# D+ L! k2 W! i' Z; `, ?
FLASH在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(值必须是0XFFFFFFFF),否则无法写入。9 f; |1 P( Q! S+ u2 ?0 \
3 C' ^3 m4 P3 H* W& D
STM32F767的标准编程步骤9 w5 s  J/ w( |
检查Flash_SR的BSY位,确定当前未执行任何Flash操作;: i4 ]/ T1 [' ?/ B- @7 _
将Flash_CR寄存器中的PG位置1,激活Flash编程;
9 v- r9 [' S4 G! X针对所需存储器地址(主存储器块或OTP区域内)执行数据写入操作(PSIZE需要已经设置)+ M3 c, D& I$ |: w) ?6 Q
等待BSY位清,完成一次编程。
5 J* d5 K, D) R7 z- }) t) `6 J' F  E4 h# c以上四步操作就可以完成一次FLASH编程操作,需要注意几点:
! C3 F* ~2 k  a1 m0 ^确保编程地址已擦除# _" B( b& [7 w! f
需要先解锁FLASH_CR,操作完后要上锁1 ?# q# K5 z( G$ c, g
变成操作对OPT区域也一样
) b& y7 K) a  t" |6 S* g) x4 M+ A! \
% h/ R3 U5 c+ x& w4 E6 M) \
STM32F767的擦除
: W- Y) m0 S3 n4 m: {) \  d扇区擦除
5 }5 F$ l& M8 D4 F9 G1 a. ~! R! _2 n检查FLASH_CR是否解锁,未解锁先解锁8 [9 q2 C$ h1 ^9 {) d  W7 p, Z1 H
检查FLASH_SR的BSY位,确保当前未执行任何FLASH操作
# @9 L& ^* l' s2 {将FLASH_CR的SER位置1,并从主存储块的12个扇区中选择要擦除的扇区(SNB)  j1 U. G' c( B1 B1 Q# D9 V
将FLASH_CR的STRT置1,触发擦除操作2 d* u& q, Z5 E$ O  I* e
等待BSY位清零1 q& Y7 n* _# o/ D+ B
整片擦除
# c$ u- i8 X; v6 B4 }附录
$ b: R  D' t" k代码
" Q- w! @8 M: H$ S" o6 R6 B' \+ [依赖于HAL库" X9 Z3 w! P' P3 Y3 s

  x$ R4 m1 e+ s+ C
  1. #include "stmflash.h"
    ' t( @  S7 I: f+ Z, }0 q. P7 \
  2. #include "delay.h"
    9 k! S% E  L, p- ]4 x7 y0 x
  3. 0 d) W; E& r7 j( V, ^9 M
  4. //读取指定地址的字(32位数据) . }3 D& [% Q1 H  K
  5. //faddr:读地址 ) n8 w4 @2 \% Z# c  H, S
  6. //返回值:对应数据.! y, b. d: J' m6 b' W$ P- N' v
  7. u32 STMFLASH_ReadWord(u32 faddr)
    , X  v9 W% j* S/ _  \8 Q
  8. {1 N5 f1 O% ~2 z$ D" D
  9.     return *(__IO uint32_t *)faddr; 9 ]( i/ ]1 K: P' y! C" H# O9 O4 S
  10. }- I+ Y. H/ N8 q3 l& ^
  11. * {3 i" E% c6 @
  12. //获取某个地址所在的flash扇区
    9 \! M0 m4 H0 `3 ?
  13. //addr:flash地址
    8 K/ V& X' p, T+ x/ p
  14. //返回值:0~11,即addr所在的扇区
    5 Y7 q, [& e+ y7 X" m
  15. uint16_t STMFLASH_GetFlashSector(u32 addr)
    " C5 O: z- Z6 X1 s, l$ B
  16. {! C- h8 O$ c) T' r
  17.     if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;
    % F+ f2 l# m* _; j
  18.     else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;) e* K$ e; i: C& k* C) ]6 K/ P1 N
  19.     else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;
    3 W  J) q. N* ~
  20.     else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;+ V+ X3 L7 o4 p* V% m* E0 Y; x( E" l. y
  21.     else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;1 n) s2 V: ^" ]6 A- L
  22.     else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;
    ) S8 ^1 l5 p; ^8 ]: R
  23.     else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;; n+ f! C, A0 m! e6 U* y
  24.     return FLASH_SECTOR_7;    7 c5 W$ A; r, \2 n; H
  25. }
    ) ?# `% p# N, s$ u/ c4 L6 o
  26. 4 f# H% {+ j* w/ n/ E9 t( i5 z4 y  `
  27. //从指定地址开始写入指定长度的数据
    6 d+ \4 V7 I4 M% D
  28. //特别注意:因为STM32F7的扇区实在太大,没办法本地保存扇区数据,所以本函数% V! i2 i7 s# k! K% l# P4 ?
  29. //         写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以! k4 L; p2 B: Z, f
  30. //         写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
    1 q! J/ {) Z' x* ^
  31. //         没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写. 2 m1 A7 G- V2 v! j( K& n; }
  32. //该函数对OTP区域也有效!可以用来写OTP区!
    : M' T; @7 P" Z) _5 d- s7 H
  33. //OTP区域地址范围:0X1FF0F000~0X1FF0F41F6 s/ x6 `% W; t* p4 W# J! p, a  ]
  34. //WriteAddr:起始地址(此地址必须为4的倍数!!)& L+ V' e: q0 O
  35. //pBuffer:数据指针
    . U7 O  i( F! S" i1 ?, e, w5 j1 z
  36. //NumToWrite:字(32位)数(就是要写入的32位数据的个数.) 6 d7 t! P7 v$ i  b* X
  37. void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)   
    ; t, B6 k, u, Q. @1 ]2 A( \0 a
  38. { # z+ P% ^$ D& q- }: e5 C% l: S
  39.     FLASH_EraseInitTypeDef FlashEraseInit;) |) W5 n4 \) T2 v! I
  40.     HAL_StatusTypeDef FlashStatus=HAL_OK;" I, I1 a& {, B7 v) j
  41.     u32 SectorError=0;
    + c9 Z) A: P& _" U2 I) A
  42.     u32 addrx=0;
    1 g9 ]* }8 z, Y" ^* f! E
  43.     u32 endaddr=0;    ! d7 i8 }8 c8 \( x2 J$ B+ j+ R
  44.     if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;    //非法地址
    ( ~8 m" m5 J& x& Q, f) c  V' J: h

  45. & h& n/ w/ Y7 D% L6 w3 T: [3 h; G
  46.     HAL_FLASH_Unlock();             //解锁% c: y# d1 w, c
  47.     addrx=WriteAddr;                //写入的起始地址0 K4 t7 B' r' J3 b; o) F' t
  48.     endaddr=WriteAddr+NumToWrite*4;    //写入的结束地址
    & V- c2 T9 x& b
  49. 0 P- ~# y! _- k$ F+ b% q- V$ q& X
  50.     if(addrx<0X1FF00000)
    6 Y! j, \% [$ G+ f4 r' e
  51.     {
    ! y) I, ^  R4 C& }
  52.         while(addrx<endaddr)        //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
    9 l' @1 Y+ a- `, _5 ^4 G0 J  d. D+ [
  53.         {! y7 E- H2 S/ q0 f: n" C6 ]3 G. w
  54.             if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区/ A9 E: Y+ _* x) A
  55.             {   
    ' t$ M9 p0 z" V$ ?9 i
  56.                 FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS;       //擦除类型,扇区擦除
    : {9 Q( F9 }2 y0 d
  57.                 FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx);   //要擦除的扇区
    1 ~; k% p8 Y4 o0 X; H& _
  58.                 FlashEraseInit.NbSectors=1;                             //一次只擦除一个扇区. S( f3 s# N, L* o0 q
  59.                 FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3;      //电压范围,VCC=2.7~3.6V之间!!. W. B# U& k" I  l
  60.                 if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK) 2 q" j0 W0 W/ K
  61.                 {& r6 P# a% J1 h4 @$ ~5 d0 E. \( _1 e
  62.                     break;//发生错误了   
    , }( _9 H, F% W
  63.                 }* ~" i, x  z8 P
  64.                 SCB_CleanInvalidateDCache();                            //清除无效的D-Cache
    3 y% w! k1 A0 C5 O
  65.             }else addrx+=4;" o# |' ~+ u0 M) i
  66.             FLASH_WaitForLastOperation(FLASH_WAITETIME);                //等待上次操作完成
    / x/ z  M) U% E# i  P
  67.         }+ S! x0 f. o3 B/ A: g, }5 Z
  68.     }/ b4 n# m* b: D+ d
  69.     FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME);            //等待上次操作完成
    4 W- R4 }9 z. K+ h- t- S
  70.     if(FlashStatus==HAL_OK). q8 |8 S6 q+ `& x+ v  g/ [! Z
  71.     {
    6 f7 }8 O0 H, @
  72.         while(WriteAddr<endaddr)//写数据
    ) m$ X4 C4 I- v% A! B
  73.         {
    % M- x( K' [  x) M+ q- F
  74.             if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据6 O9 x* U$ N! T0 U9 \# r7 X
  75.             {
    9 b) K% r& ?% B8 V2 }
  76.                 break;    //写入异常
    . s4 N: {% b0 ?. d
  77.             }7 k8 R' b# n0 X
  78.             WriteAddr+=4;
      j2 _5 F1 _; `
  79.             pBuffer++;
    % C8 u4 w* F9 z* x0 }
  80.         }
    / B& r7 u% {! H5 \
  81.     }
    0 X" Z% ~- G$ @1 ^/ D" v
  82.     HAL_FLASH_Lock();           //上锁4 E- G+ z8 x/ j) L% x
  83. } # |% t- X8 L8 j; X- B

  84. 6 v4 O9 F/ |7 Y- z2 U
  85. //从指定地址开始读出指定长度的数据
    # v9 i& L) U5 W: U# ?. f
  86. //ReadAddr:起始地址
    7 w7 Y6 X: N! G  K9 ^
  87. //pBuffer:数据指针
    # r; z% H2 L. N+ V9 S8 ]
  88. //NumToRead:字(32位)数
    6 G& B& W7 [- c! X
  89. void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)      
    5 t* i$ X" M  I
  90. {
    $ i9 m, j+ `# W
  91.     u32 i;
    ! T% r% a6 f" L# D( g2 ?8 X: `
  92.     for(i=0;i<NumToRead;i++); z$ z4 U# t- m8 N( e' [( w
  93.     {7 h; r* b5 U! p" Q4 z% r. {7 k
  94.         pBuffer<i>=STMFLASH_ReadWord(ReadAddr);//读取4个字节.# O$ h/ C  _$ S5 s0 q0 m8 W
  95.         ReadAddr+=4;//偏移4个字节.    9 k  k/ K4 T  p3 q0 q9 X
  96.     }
    . s2 u& B: ~; V: ?
  97. }( D2 B) V& {6 S  y/ ]8 D
  98. 9 b5 }6 [' Y3 i5 }& o
  99. </i>
复制代码
; F( K9 t' U; q: T
/ K0 c( v( q+ H) ^6 x
收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答

所属标签

相似技术帖

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