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

【经验分享】STM32H7的QSPI 总线应用之QSPI Flash的STM32CubeProg下载算法制作

[复制链接]
STMCU小助手 发布时间:2021-11-4 23:27
81.1 初学者重要提示% @0 s. s6 ~0 x! F5 Y/ p
  QSPI Flash的相关知识点可以看第78章和79章。: t  J( r( c- M# w! ^
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。( j) x) |4 e9 a* k5 V
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样3 S  a. J8 G; a3 [6 Y" }* P
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。; G$ N6 [/ M/ ^1 I, k' E" I+ R
81.2 STM32CubeProg简介, [% B! C0 |7 M' g; M
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
6 y: C* ~" O. a% Z7 o8 [4 n0 @* H; T4 I" W- t8 K
1bbabf500126f58ff1439b6bc36014be.png
9 s$ A8 V; c$ \' G" i! o5 `
, c3 K: O& a$ a+ t
81.3 STM32CubeProg下载算法基础知识
, [3 m! d" J9 o% C. mSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。* W; n7 r, H" a  A2 n

, o2 {6 D0 A" k( l0 T& P81.3.1 程序能够通过下载算法下载到芯片的核心思想
# I# \  ]% e( L; ~! b认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。9 ?6 c" o# h% b% j. Y
/ O- b0 s: H0 e7 Y6 o4 Q
81.3.2 算法程序中擦除操作执行流程$ j6 ]9 g: @* M
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。$ k1 U. h" Z) \5 S. L) m  Q# i) C; g5 q
2 D; p( q/ i7 n+ ]
擦除操作大致流程:
6 O% b$ g/ _. Z( c
$ t. ~' f5 Y/ k
161f92ebd9b1dbf9febba815fa091b8b.png
6 h: L1 L" a2 l( q

9 M+ a1 Z7 T8 u7 R  加载算法到芯片RAM。$ F1 d8 u# t: R4 U# l
  执行初始化函数Init。
6 b# ~, o# d& a, I0 B  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
- ^/ Q  N2 w5 g, c- M, }! t7 w  执行Uinit函数。
3 q5 y' ?7 C' j2 @+ A! |& A& i$ |  操作完毕。# _5 c1 p# d0 f1 i# y) o: l  c  W
81.3.3 算法程序中编程操作执行流程
! s4 ?8 S3 S' Z2 X) Q; A注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。& `4 d, q3 K4 x8 ]6 B0 r. o. T

3 p& v  A  J4 C9 |8 ]编程操作大致流程:
7 c. t7 Y/ L: I4 F+ z! X0 \' `9 z7 Q# B* j* h  H7 a4 H
d80dfd755411aa0d0565f1c56f10e66a.png

7 d9 N/ O5 |( F5 \5 G& u/ u1 W5 b- B/ \% K
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
9 j) k1 y7 S4 o/ h* l  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:# I* z, U1 U8 I% L  X; G
  加载算法到RAM。# f) O5 c9 A* s6 _; k% I) y
  执行Init函数。
8 t: C2 s1 W" ?! Y  加载用户到RAM缓冲。* w# ?$ R, @9 `2 h
  执行Program Page页编程函数。
$ Z9 _. M3 |: l) g- x  执行Uninit函数。! V+ Y& g; f* t; l
  操作完毕。
3 d9 p) H; G7 x/ \) ]+ F! t* u81.3.4 算法程序中校验操作执行流程
- `4 t  g. L2 `注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。' o! V' d  O, o- d+ U: s8 K* d- _

8 @+ V9 n! v# V1 c0 D( T# H校验操作大致流程:
3 `; K: r9 q5 W) ^+ ?# `& u
2 r" u7 i# \6 r( Y% J! h
f15191090ab169d48a203ea3617af9b4.png
  q; U. a" g- g( ?) t" a* [7 D
0 P% ]* ~: q: B. q1 |: `* z! s
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。: n* `/ M" r$ h
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
& H3 @1 e3 U* F% v' n* H# y8 e  加载算法到RAM。
* V. l: F0 k" a2 N  执行Init函数。8 S8 `  B/ u9 N/ i! p) k$ f
  查看校验算法是否存在# ~3 F- K: l9 I  e
  如果有,加载应用程序到RAM并执行校验。
' a) Q* W% N. U2 a3 y  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。$ d& |8 J& n. v# O! H4 R" Z0 `
  执行Uninit函数。. @. Y3 V' ?7 i7 v/ X) ]: j. n) x$ s
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
( u# f) [0 s* b4 E% a  执行RecoverySupportStop,回复支持停止。2 l  e1 [9 R3 J8 T6 o& H
  执行DebugCoreStop,调试内核停止。
) |/ p" ?" ]* }  运行应用:
" J6 [; W6 I' E( W  执行失败
7 g, H( \- A) [& z( N" U  执行成功,再执行硬件复位。
( E5 T& }- E; P* S/ l* p9 j9 S  操作完毕,停止调试端口。; `/ Z1 L4 `! h3 [! X: Q: G
81.4 创建STM32CubeProg下载算法通用流程6 m1 ]% @* H: C5 W$ b: a' g
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。4 M- y% X0 O' F& P0 x% t" I% |: h

5 f; K! m1 X! G% x# A% C81.4.1 第1步,使用STM32CubeProg提供好的程序模板
1 ?) V+ ]# T3 _2 _/ e7 P位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
6 }9 z8 X4 }3 U* E' g$ B
( I' V% ?3 m6 a+ @
4fbfb0047e29fe23083224607059d97f.png

# \2 {6 V& \- O; B) q3 N3 S3 E" N% u- n0 @; T
以M25P64为例(注,其余步骤就以这个为例子进行说明):
* _' B. T- b& n
  t- p. o" q6 B4 i8 k5 f' |: l
d1b1a340fdc655c1030330a7dad2abba.png

8 q& R8 m, p5 B  Y$ z$ h
9 d  A/ l) j* o" B# q81.4.2 第2步,修改工程名
1 o9 N( a# H/ y1 u% V. q3 e$ xSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
0 o+ k5 K! t  X* Y! _7 E+ R& \* M' g
81.4.3 第3步,修改使用的器件$ B+ A' k8 P  x$ u
在MDK的Option选项里面设置使用的器件。  |& B7 m6 U+ n3 H8 N4 T
0 ^" @" O8 c8 w! g
092174ad976a8ce9c7fca533049e2dd0.png

5 X! n* g: \0 A( i3 w' W+ S" P, m9 F5 d* A) m& m8 g3 \2 c* I3 `- G
81.4.4 第4步,修改输出算法文件的名字2 x6 \& X9 M& P4 y' I# [2 }
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。" \. p1 C+ Z% ?3 v! D# w
# K, w6 `7 c. \* \' d* {
79c42251311dd3892fc7d6b8ddde68b8.png

7 I8 M! O' o9 O0 J% U# ~
; ]/ f4 w. }  }' \! j7 F注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
2 n3 T* G! s* c1 A4 G
e2fe61fa7ea99b25b178341b7350558f.png

8 p" q7 q/ u) J6 K% D; h$ F
8 d. m8 _+ k( M! G  Z4 V81.4.5 第5步,修改使用的库文件和头文件路径
' o, n8 V9 u" R5 r1 a# H& F* A根据大家使用的主控芯片,添加相应的库文件和头文件路径。
( c; ]6 v: F$ T: v# Z: b0 @' ^: b; D% p% t- n. w
81.4.6 第5步,修改编程算法文件Loader_Src.c( {+ d- Y5 z3 [  }) Q( i" J# J
模板工程里面提供的是M25P64,部分代码如下:7 d5 i- i$ Q' H. f1 k' D1 X

7 v) r6 J; f0 N$ I
  1. /**
    # X, d$ G9 F) b0 Q, a
  2.   * Description :
    4 B" y' C# p/ m/ B' `: z
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the% ^: }3 F2 G' y3 \2 `
  4.   * device and initilize the FSMC with the chosen configuration ; w& G6 E- R. k& o! C6 i. j
  5.   * Inputs    :0 H/ X" v+ M2 e" \* a) k4 c
  6.   *      None+ _% X4 x  X4 u: [3 g( C
  7.   * outputs   :8 V7 ~; O2 ^, I% u- j$ k
  8.   *      R0             : "1"             : Operation succeeded
    / K9 L6 k" _$ }, {" t  d
  9.   *               "0"             : Operation failure; P5 x0 K5 G) H8 y
  10.   * Note: Mandatory for all types of device % ]' c0 W6 o5 Z
  11.   */7 T/ {  H9 `& d' y
  12. int Init (void), `8 [1 V, a$ e; O
  13. {  " Y# s3 o" E9 F3 F# r, A; a% v
  14.   SystemInit();% Y% n" T/ s" c- C7 \
  15.   sFLASH_Init();
    " @) d; j" K5 n! ^6 i& W4 D3 F3 X
  16.   return 1;2 ~) i  Q3 G( ?; }. Q
  17. }( `+ M$ r; `' ]/ g8 _2 k: W$ l
  18. " c" }# }0 x: ^
  19. , y, ?( h( X1 E- w- E  Y1 ~" N/ f+ Q
  20. /**
    $ R, i# r" A3 X& H- b. d
  21.   * Description :
    # Z9 Q% A2 M, T
  22.   * Read data from the device
    5 p5 {4 c9 m/ o
  23.   * Inputs    :
    ( @8 k; U: @4 u; I+ b$ m: Q
  24.   *      Address       : Write location
    ) J7 |4 D- ?* ?  C! s: l% B
  25.   *      Size          : Length in bytes  : X% X5 ?1 r6 m9 a: \
  26.   *      buffer        : Address where to get the data to write
    # ]- Y/ k3 k2 ?- f$ v: w& n" {: }
  27.   * outputs   :
    ( v% ^8 F) e) ?: r" H
  28.   *      R0             : "1"             : Operation succeeded- F: ]; z' u8 C' @
  29.   *               "0"             : Operation failure) a, u+ g$ X+ \0 v* C- p
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    ) B: S9 n( B. F
  31.   */
    9 z+ p& ?2 b% }) F/ \1 ~; i' O
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)6 x5 u7 m  R1 v2 v4 y6 p* j
  33. {
    4 m% y8 A/ ~; S) {
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    ) i, _7 c" |* y8 q- d
  35.   return 1;: S- \% {) |" j% M8 K$ ^2 c
  36. }
    # F' a+ u- B. C; d8 C

  37. / u" b2 X3 F7 K2 |. I  \. R# N7 l
  38. " t+ I# C7 l. e; f% k- q3 Y& f3 u
  39. /**
    % f, b% d- D! [  T$ ~
  40.   * Description :. m3 g+ u, J0 Y5 `
  41.   * Write data from the device
    / U- l- F  p: p+ t$ x9 U2 T- y
  42.   * Inputs    :
    / g" p" |, K( O$ K
  43.   *      Address       : Write location
    3 D. R( k- v3 [9 ?8 U# o
  44.   *      Size          : Length in bytes    h6 T/ A& j0 l' K
  45.   *      buffer        : Address where to get the data to write1 v2 A- ^  M! B% {8 T8 H
  46.   * outputs   :
    ( z8 h3 i, v. g
  47.   *      R0           : "1"             : Operation succeeded
    $ n, ^( K9 E6 f! T& B( `, u% v
  48.   *                     "0"             : Operation failure$ J" u; C6 d) a# f; G5 p, w
  49.   * Note: Mandatory for all types except SRAM and PSRAM    1 ]# L) |5 C' T7 t6 b
  50.   */& g5 ^# f7 V: `  r
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)  b& t3 V  J+ y3 A) t* v
  52. {2 [8 m4 a) k; F) E' Y
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    # ?# T( H2 v- g9 E* C5 Y0 V8 [# S
  54.   return 1;
    # \9 S0 W- f4 _4 b0 G/ \5 }
  55. }
      d# q4 z- C" i. W

  56. 5 d' j3 y1 d% B8 D3 y  `

  57. 7 g* U6 H: @; p- t' E6 Z
  58. /**; ]/ u$ c  {' |" a$ s
  59.   * Description :
    * P: s- ]% O$ g; P6 W
  60.   * Erase a full sector in the device
    8 r/ X6 @2 R- M$ Q/ ^; [4 ]( [5 f
  61.   * Inputs    :
    & p* m! b+ L: |; X6 |% X6 s
  62.   *     None$ ~+ e; Q% P/ p" l
  63.   * outputs   :
    ) V+ w6 b; T" g3 n+ z, V# s
  64.   *     R0             : "1" : Operation succeeded7 R, @+ [2 R8 f# H1 u0 T
  65.   *              "0" : Operation failure6 y- ]. b+ k' Y4 |
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    7 S: H) L7 |0 {, y+ _% A8 L" N
  67.   */; t" q7 m) \  p7 |
  68. int MassErase (void), Q9 D3 L9 m9 {! X) J
  69. {  % P: E+ D# z# _1 i
  70.   sFLASH_EraseBulk();- a/ ~9 ?0 m$ s7 q$ V# F5 p
  71.   return 1;   
    : W0 }: l. Q  `7 V6 s: c$ O$ J
  72. }
复制代码
: _1 W/ n2 D4 T5 r8 {
81.4.7 第6步,修改配置文件Dev_Inf.c8 [: l8 ^7 @4 T4 o
模板工程里面提供简单的配置说明:2 `9 C+ L+ ?% \

: S0 r# j9 a' b% S
  1. #if defined (__ICCARM__)
    - Q! m; `2 Q" K7 W. F
  2. __root struct StorageInfo const StorageInfo  =  {  C7 l3 y2 Q+ y6 W1 E7 l: [
  3. #else& `2 f) X" R/ x# |5 }4 ^9 q
  4. struct StorageInfo const StorageInfo  =  {' _& {, P, {8 V/ r- J4 |1 }0 I& q
  5. #endif
    4 ]/ O7 [  [6 p" Q  G, s9 W0 d4 O1 F
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    8 e% Y* S7 M0 r9 l6 n( F9 S
  7.    SPI_FLASH,                       // Device Type
    " _8 E" m- E/ m1 @
  8.    0x00000000,                     // Device Start Address' e9 w0 U8 S& X8 \9 k% O
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    ' ~+ `4 c% b; M" {; t4 I% \
  10.    0x00000100,                      // Programming Page Size 16Bytes: J8 _# a- e) i6 K0 v1 c
  11.    0xFF,                            // Initial Content of Erased Memory
    8 Q* E( |& z8 ]3 M" a
  12. // Specify Size and Address of Sectors (view example below)
    9 r2 m6 J" U6 G3 ~" A
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    6 X4 o% D0 A# q3 C/ _
  14.    0x00000000, 0x00000000,
    * v" W4 r' t. O1 g! V+ r# W6 G% v
  15. };
复制代码

0 n  w" r7 \6 k, j; c- V7 _注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
% }  e- S3 T3 j5 `9 O& U" c3 C  F" Q4 ?4 B; k  @

0 t; J8 Q$ O1 g# m' A5 H& h) J! x* ^/ @$ i- n% l$ B2 P( A
81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
& u6 Y8 `! c* P" N" X/ M4 o+ |C和汇编的配置都勾选上:, y+ A, M  l1 A+ g
; D1 O$ O- g+ J4 q8 i! f/ O
8f2ba38db4c1f685f131e7f1f4e98551.png
5 u* L7 Z* `; d1 G; f

* L* g* h; o7 E, z! y汇编:
1 p9 T& H" P$ r6 @& l  z7 N  E& N, Z5 A0 k1 S8 ?, e- y. L0 A  n
0d264153b167afa2303144bdd378d501.png
' W' c2 H% \* h! @

$ [0 L1 k" }9 G% [# M, @, d- k如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
4 k1 y  L* g0 I3 Q: ^% D! g$ |3 G# C. C% Y2 ?7 D
(1)加载以响应运行事件。: ]8 d# r0 ?% j, \' R
8 |6 F6 B% r+ T0 ^. N: O0 q3 |
(2)在不同情况下使用其他例程的不同组合加载到内存中。
# ~" I: {" B" O& x, p7 T( F3 s- |* _* n! `8 U
(3)在执行期间映射到不同的地址。% L/ z0 `, x: m# a3 a

, @3 x0 e/ |. b) i使用Read-Write position independence同理,表示的可读可写数据段。& [" \# |! C6 H8 ~, m) g
0 C9 `4 Z6 G5 R& W
81.4.9 第8步,将程序可执行文件axf修改为stldr格式+ ^* H: }( m3 v7 R1 V& H& z# C
通过下面的命令就可以将生成的axf可执行文件修改为stldr。0 r, W4 [( v9 l, _& @

" O( z% t  X7 m8 z) Z1 J$ U, z- V
f50b1594f3741e77b4fbd5523564d3af.png

4 {' v/ A+ Q# t; ]5 \4 k2 f
! G0 p. J1 c" C81.4.10   第9步,分散加载设置, k, W1 f; f4 |# W* l' c1 [
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。8 s. l0 e' V$ _+ T/ S6 i% ~8 ^1 v! B

& |4 M( `; a7 r# p
8a01cb7c9be38519d4d0510a218447e2.png
) U6 M6 a4 d" K* d( S
& E# C  ], l; e
分散加载文件中的内容如下:4 K0 D6 U2 j1 n0 S8 Y+ B
: A# x  \+ s" C$ O
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions' m, j) ]+ F# H+ h( h
  2. {
    , G7 H$ i6 O* b! Y
  3.   PrgCode +0           ; Code5 T5 f! O5 v. b. z" l& L  }/ {
  4.   {
    $ {+ a9 J2 O) a* I8 p+ T! X5 [
  5.     * (+RO)
    4 J3 |" T& H! V7 O  R: B( G
  6.   }3 t! G# Z& _3 c1 p5 T# y
  7.   PrgData +0           ; Data
    * ~8 u- P( y' t+ c! S
  8.   {0 k& {3 g7 F! V$ h
  9.     * (+RW,+ZI)% r5 X& Y+ w, ]3 n8 O
  10.   }
    ) g& @( `& g# ?( W4 Q
  11. }
    3 O8 m: n2 `, E# j2 b: D
  12. . F) e$ @9 |' T' ^
  13. DEVICE_INFO +0               ; Device Info
    5 u7 h, [0 L8 L7 \# H
  14. {4 r0 \0 x6 j8 s- ^( U! H7 x9 L& J
  15.   DevInfo +0           ; Info structure
      Y( c5 ^; U: ~3 T. `+ _
  16.   {& h" G! y9 e6 W: H
  17.     dev_inf.o% d$ }! X- ]5 f; \! q% _' E; k. D# q
  18.   }7 G0 A. w* y* ?+ V+ V  ^
  19. }
复制代码
" R! F) ]% C9 u! M$ O* C
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
" ]( |5 ~7 G2 \: h0 X$ G
# R9 k5 e- p" {: f* P3 T: B--diag_suppress L6305用于屏蔽L6503类型警告信息。
" Q" i3 Q0 r! ^5 q- ^* N$ p/ u# b" q/ g& h# @
特别注意,设置了分散加载后,此处的配置就不再起作用了:( N# b1 ~# T4 ?0 P4 H/ S9 p
- B9 Y: b7 ]4 R1 _
13b022190bd5115ed249129fc3886a38.png
* z) G6 u  B7 E9 |' O7 n1 T+ d/ {' ^
/ ?9 c* l1 B* w' V4 G
81.5 QSPI Flash的STM32CubeProg下载算法制作
! M& d/ Y$ U4 t下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。- I, K  H* J. E( b- p
: L/ k: _; C2 o( g
81.5.1 第1步,制作前重要提示
7 R$ N$ m6 l. y7 j: v0 N这两点非常重要:6 x5 M: G0 u5 h" t
6 x% [- V% o( o4 @
  程序里面不要开启任何中断,全部查询方式。
9 ]( j7 P0 d! z$ G8 I; N  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
5 P7 G- q/ ^4 s$ R; l) C/ l81.5.2 第2步,准备一个工程模板& g5 ]* |4 u( L) |7 N' R
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
& A% ?1 ]3 V3 e* ~0 [1 t. Y# R
3 Z* A! W  h0 p2 \5 g
f46a8d2276d0ffb7beee251cdb0809a4.png

! y% U5 p& w" W  z* m& F+ u+ J
. @- V6 Y# \1 _5 K3 h9 N" o! n9 S/ @81.5.3 第3步,修改HAL库
) A: k' k7 f4 G. R4 j* ]0 i这一步比较重要,主要修改了以下三个文件:+ {+ V/ c- K# g; y: B7 O: D6 u
8 Z; \# O- k  T2 u
1efb78256b9465df406e3e9e95ae30e5.png
- {, ]2 J1 m6 J* Q
1 Z  d+ I- y, p
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
4 E; C, h- D) d+ s: d
( P2 B/ _% |% g5 B: V7 |. F: b81.5.4 第4步,时钟初始化/ P/ p" T  M5 Z) q* _! p  p
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:4 t9 t$ s+ [  B

" l7 N( Z- r0 U6 \+ p
  1. /*
    ! [$ Y9 S4 p9 P
  2. *********************************************************************************************************  ?2 l, `  l6 ], |: ]& q# _
  3. *    函 数 名: HAL_InitTick# ~# P5 a4 C! k1 W' A( q
  4. *    功能说明: 重定向,不使用, Y# A0 s/ ~2 J7 k, T6 j$ j
  5. *    形    参: TickPriority* ]) J7 T' _4 X
  6. *    返 回 值: 无
    4 C* G, `1 f0 Q8 u
  7. *********************************************************************************************************
    ( x3 N9 P3 S9 H
  8. */
    0 C+ |( n3 |6 x7 Y
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    : k4 y4 x- ^+ j; l" E. H2 a
  10. {$ P: z2 a7 l/ f' s3 D+ d
  11.     return HAL_OK;$ c2 o- j7 p0 G
  12. }
复制代码
6 G( z3 o% g6 Z
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:1 a4 X. L& H0 v: j% f+ f& P
; ?3 s8 C' @  z! H0 S5 A, V7 v, [
  1. #if !defined  (HSE_VALUE)
    7 C) E  @. |/ l# B3 l7 Q
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    0 ^0 O9 W6 k8 u& z  ^
  3. #endif /* HSE_VALUE */
复制代码
# t5 \* |. A. L5 u! z" v
最后修改PLL:- i. `7 A6 a0 |+ V+ r7 x

( w) I  C8 i$ D
  1. /*
    5 c/ y9 ^: F* x
  2. *********************************************************************************************************
    6 ]  G2 @- v: p  K3 H' Z# H
  3. *    函 数 名: SystemClock_Config
    & l' n/ C& K/ P8 Y+ a1 a( ?6 R
  4. *    功能说明: 初始化系统时钟+ e( D; }  X5 |5 }/ E. {1 d9 V
  5. *                System Clock source            = PLL (HSE)
    ; Q8 W' F+ c& Z6 U
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)- {: r) G. ?% D  P8 j. e
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    ; T% L1 r, {' ?  a- K9 ?
  8. *                AHB Prescaler                  = 2
    0 R5 G7 I& ?0 n; s
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)6 R7 C, K: p1 f
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    . \/ D$ I) J3 e- T$ P6 Q. a; O( {0 H2 U
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    / g) x5 J; C& r$ |( [* [# C3 s
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)8 c2 X8 K: I) C! K  ~# j; O% l
  13. *                HSE Frequency(Hz)              = 25000000
    2 G9 ~8 C" V# @. t9 n- P6 I
  14. *               PLL_M                          = 5
    8 y# i: `1 c& S. x4 k
  15. *                PLL_N                          = 160
    / U9 `7 ]' B2 T& F' [( ~
  16. *                PLL_P                          = 2
    1 h6 W# {, N% ]. D
  17. *                PLL_Q                          = 4
    ! i0 W9 e; b2 `
  18. *                PLL_R                          = 20 e& g7 P+ D' p% j( Y% `. e" V
  19. *                VDD(V)                         = 3.36 y8 M$ d& K  u+ Y9 C6 T4 i
  20. *                Flash Latency(WS)              = 4
    % X8 X& X' }4 V* U
  21. *    形    参: 无
    . \# T$ X. E2 y1 p! U0 \- ~
  22. *    返 回 值: 1 表示失败,0 表示成功" p4 S* T( N- o; W% f8 {/ X( }
  23. *********************************************************************************************************
    ' m5 M% b# d6 r* `7 q
  24. */, k  q; B- U. r. F6 |8 b+ e
  25. int SystemClock_Config(void)- ]5 R6 A" ?: {: l6 p
  26. {% l9 ?, B' l5 l$ T5 y
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    3 m8 T2 x! ]6 R$ j4 q
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};+ C+ x3 \. q3 O, ]7 _5 {4 q) f) ~
  29.     HAL_StatusTypeDef ret = HAL_OK;
    $ K. B0 T6 |( Y( O9 T6 U
  30. - b2 x8 h8 S+ z5 z$ q- q
  31.     /* 锁住SCU(Supply configuration update) */
    $ y& g: F, I! k" j. S' |
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);+ q: L0 h: d( _+ k( m" v
  33. & Y1 T( G) `/ L6 z
  34.     /*
复制代码

3 ~) n3 B9 S0 ]5 o4 b* g$ _' A      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,- P1 X  M! p7 i# P6 I; d8 c
         详情看参考手册的Table 12的表格。1 D. B2 R: E$ }8 m7 |
      2、这里选择使用VOS1,电压范围1.15V - 1.26V。
- {/ j7 |) M1 E( k   
  1. */( J$ K6 M' l: D; ~2 g0 M
  2.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    4 W( n3 P% B% U+ J

  3. + B7 A' i5 T3 g. S! S% [
  4.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}5 C* _$ L3 Z2 l4 D' v6 M* F! T' j

  5. ) M2 n( z1 `3 q; M; b5 K
  6.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    + _$ z0 J! v' x9 {  u- A
  7.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    # y, J1 l/ c( _8 Z8 s7 U
  8.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    ! w" b; y+ I3 ]: |
  9.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;2 `8 \/ h& [5 ^/ ]  f
  10.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;& I* h, n, b3 f" H3 Y# |; R
  11.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    3 L# L  M0 Z0 }6 C
  12.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;& p9 U( r3 ~1 p6 C. H5 ]. \, f
  13. 2 [0 F% z. b3 q4 r
  14.     RCC_OscInitStruct.PLL.PLLM = 5;# s2 m& l9 p/ _& r( U! H
  15.     RCC_OscInitStruct.PLL.PLLN = 160;' ?& M9 [6 X2 c, D, l) h  ?
  16.     RCC_OscInitStruct.PLL.PLLP = 2;
    1 t7 X+ `: R9 W) I
  17.     RCC_OscInitStruct.PLL.PLLR = 2;
    * i- j3 V6 L8 h' Z% P/ Y  \
  18.     RCC_OscInitStruct.PLL.PLLQ = 4;        * k) _, u8 O- m( H
  19. / g4 R% f- ^' c
  20.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    : I; k+ a+ j2 B# ~
  21.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    3 ]- F& x# H. \. V/ I! }
  22.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);! e4 X7 p' P8 F
  23.     if(ret != HAL_OK): u( `$ y; z& [" {: L8 a! t
  24.     {( e( O5 Z# q6 l4 i. w
  25.         return 1;        : E5 \% M  _0 g7 V+ Z5 U
  26.     }
    % Y; J9 D/ J4 f, W; \' y: ^. g$ q) J8 O

  27. / m, k1 h$ r8 T8 O. d
  28.     /*
    ! D) {7 E6 i/ `) V# ^3 K; P6 P' j6 t# W
  29.        选择PLL的输出作为系统时钟
    * r- P4 u- ^. A, o# y
  30.        配置RCC_CLOCKTYPE_SYSCLK系统时钟; j$ z# m6 D& O' z* V2 E5 p
  31.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线/ j& W1 y6 t( Q; i* _2 w
  32.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线# {" ]. B* R# }) X: ]4 E% I0 q
  33.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    . L& P1 [1 j% Y1 [  h
  34.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线& b: J) _3 {0 n% N% i% G& O* c
  35.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     & E9 y2 u) l' U) m0 E
  36.     */
    % q( R5 z  W+ K$ \& w
  37.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \7 F3 }: b- t1 O( K
  38.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    ) a) g. H  X% k' w: O9 m1 |4 j( v3 c

  39. 8 e7 z( t* X% g, `5 O
  40.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    8 }4 ]% ]$ Z0 S' f
  41.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;- v- A! i& W) ~  J
  42.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;- y( j8 ?/ o' v, d3 n5 ~, r3 q/ ~
  43.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  - _- V, L0 b6 b; t5 k: @: x
  44.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; ; W& ^' y! A; d1 G
  45.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; # E5 u9 E; S' n. I5 s; X9 Z. Q
  46.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; $ w+ H! P! H2 r+ |6 I5 [3 S/ r% U8 g

  47. 2 [/ L! m0 h$ `' ^5 K0 X- x
  48.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    + N; _2 v) I( D* ^2 u9 N" Q1 G
  49.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);& w/ ?3 f4 ?0 @6 D: `
  50.     if(ret != HAL_OK)
      g  L, W# {5 V5 b) G6 n
  51.     {" X3 j  e# R& B% `* Q9 [6 ?
  52.         return 1;9 J, M7 [" Z1 k4 x5 J/ }
  53.     }+ Q8 {6 R; l' g3 [* Y& r, K

  54. 3 T" c( i5 B9 ]! \+ ^
  55.     /*
    $ F( _. O- a5 x
  56.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    + K$ @! q/ Q6 \# [5 O, s; W; _3 q
  57.       (1)使能CSI clock# b5 m  n" z: P# S. b  }
  58.       (2)使能SYSCFG clock& q# i. m4 W) d+ B* V- \
  59.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    ( S# ^& |1 Y  k8 y9 H, u
  60.     */
    * v; b! O8 O0 M) L
  61.     __HAL_RCC_CSI_ENABLE() ;: K! x$ b/ J# N5 S

  62. 5 s0 I! g- x1 K, n( m! N  D
  63.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    3 R- G, D& ^7 w, ~) M: |; K5 l
  64. ' Q& @6 c$ ?  M* ]5 J, X
  65.     HAL_EnableCompensationCell();
    $ Z. t+ i+ n, ^

  66. $ h% n* C  [7 c0 }
  67.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
      c" I/ u# u+ n: n$ h  M- Q
  68.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    / W/ G8 _" P4 S7 F' V$ p
  69.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    0 Y  C/ r5 n" C3 R# @
  70. * E  z1 Z$ E& M' M1 h
  71.     return 0;
    2 p) n2 |1 g# w+ y
  72. }
复制代码

9 c9 d/ d) J, t) M" e81.5.5 第5步,配置文件Dev_Inf.c的实现" L3 ]+ O. I8 p* t
配置如下:
* N8 h6 T( ?7 s) \6 {# s; y- L0 {2 v1 x! ~# S
  1. #if defined (__ICCARM__)  T. T; a, b! I1 W7 [0 J' l! N
  2. __root struct StorageInfo const StorageInfo  =  {
    9 Y7 O4 n6 A2 }1 Y: W6 b4 j: Q& e# B
  3. #else
    " G  V3 w/ A/ p7 y+ Y9 F/ T. i
  4. struct StorageInfo const StorageInfo =  {
      {5 }1 z1 m$ a1 v3 ^
  5. #endif
    6 W/ R% i' k. V* W
  6.     "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */5 A) ^0 _1 C  B6 b7 V" j, O
  7.     NOR_FLASH,                      /* 设备类型 */* w' ~6 o# w& h2 B0 ~
  8.     0x90000000,                     /* Flash起始地址 */
    ( Q6 `( Z* m5 t+ |% ~. A
  9.     32 * 1024 * 1024,               /* Flash大小,32MB */, L( v& L# j9 {' n  Z
  10.     4*1024,                         /* 编程页大小 */& v. R0 l0 t. l% ]
  11.     0xFF,                           /* 擦除后的数值 */6 }* s8 r! e3 U9 Y# a
  12.     512 , 64 * 1024,                /* 块个数和块大小 */0 A  W5 z/ S$ L' |
  13.     0x00000000, 0x00000000,
    6 |6 l# F# B( U3 x0 o/ \4 A
  14. };
复制代码

. l& s- J6 E4 e! q0 Z" V注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:1 B% v% ?9 i& S0 l* e* b- r* `; T
" t, o/ T& _5 i2 g, T2 m
bc495adf8d970e6e1fb6822b88b94e2f.png

0 s) d4 A" J$ \8 T7 t: A% r" p0 R- W
81.5.6 第6步,编程文件Loader_Src.c的实现4 U( x: `4 \; v( f- Y' R3 x
下面将变成文件中实现的几个函数为大家做个说明:# Q% }) \0 J1 R  z  M7 G
9 C" U# ~0 c3 m" h
  初始化函数Init
3 w/ g- X& p  i( S9 X+ Q
  1. /*3 h9 J" h, i' @6 H. v" t, k3 Q, }
  2. *********************************************************************************************************9 Z! R, a+ N+ X8 y
  3. *    函 数 名: Init3 H9 ?" H6 k5 {  z
  4. *    功能说明: Flash编程初始化
    $ J9 x$ i& _( ?. R
  5. *    形    参: 无6 M+ N# i- [: B
  6. *    返 回 值: 0 表示失败, 1表示成功# W6 J9 x' a4 [# y+ ]- [0 _/ `
  7. *********************************************************************************************************
    5 G$ C1 c5 {. ^- |5 q
  8. */
      L# D3 T0 w: i0 w* G
  9. int Init(void)
    7 T# m; k2 g# I8 [: j! a" B
  10. {   1 z9 j2 |% {( ]% z4 G
  11.     int result = 1;+ d' K3 q; K5 d1 i5 W. K

  12. * V; l# ^6 I+ C; ]1 _8 \, g" W
  13.     /* 系统初始化 */8 I7 N" E8 v5 N$ _' H- K& \3 ]8 @
  14.     SystemInit();
    6 `# v5 N' `; R1 y. R; J9 q

  15. 5 j+ J- N/ J, B% Z5 e' Y$ U' s
  16.     /* 时钟初始化 */; [0 l) G% Q& s" y. X
  17.     result = SystemClock_Config();& s7 @, A- {% z
  18.     if (result == 1); r! F" M4 W# K1 l" v9 n  I
  19.     {
    / [  k4 r4 J( d: x9 L4 {3 R4 W
  20.         return 0;        # O7 l4 g5 x6 h; ?* M
  21.     }
    . G0 |  j; Z/ N/ @* |

  22. ( W" X7 A* o6 _
  23.     /* W25Q256初始化 */
    1 h; X* c( I$ g$ d# ~' f
  24.     result = bsp_InitQSPI_W25Q256();5 G! |/ o. ~) }& j3 ^7 x& u
  25.     if (result == 1)
    " B3 [/ w3 z, Y, O, {) @" q9 d
  26.     {
    3 H, S1 h) o) X
  27.         return 0;
    $ a, b$ b& j; Y3 z: N
  28.     }
    / {2 n9 O6 Q* r- x+ ^- }+ U

  29. , J" K$ T9 I  X. F; [. _! Z6 t
  30.     /* 内存映射 */   
    , w( B! ^- p9 x8 g; U
  31.     result = QSPI_MemoryMapped(); 7 }, S* N( }7 S, E3 _
  32.     if (result == 1)
    0 Z' c/ i, x8 s& S7 R% b
  33.     {% d/ P1 S9 t, Z
  34.         return 0;
    9 e' K& {: G9 Q
  35.     }% b8 b9 T0 ]7 g2 E2 d1 c. Q0 K
  36. 2 a$ S! v- U! v- K
  37.     return 1;
    4 ~; D5 U6 g% F# s" v
  38. }
复制代码
; C7 ]9 Z! b( @
初始化完毕后将其设置为内存映射模式。
2 Q1 l0 F6 I1 a8 ?: L4 Q
* G1 ^! n- i' P& R- R  [4 C! @1 a4 k  整个芯片擦除函数MassErase: }7 J' ]7 s; n
整个芯片的擦除实现如下:' R8 X8 t! W- \- G7 ^2 F4 m

* B- _: L( i  i- u' v5 S+ h6 ~
  1. /*
    0 B1 X8 m3 Q$ s) N: s2 K" }
  2. *********************************************************************************************************
      B4 Y0 v$ y# N9 \: Z
  3. *    函 数 名: MassErase
    * P5 [/ D# P: D9 U- V, L
  4. *    功能说明: 整个芯片擦除
    / S  c# f  i3 V  t; K! b
  5. *    形    参: 无
    # z1 \, J: _3 n2 h! j' n5 O
  6. *    返 回 值: 1 表示成功,0表示失败
    $ `% m* }. v3 o4 s5 h8 P% |! u/ ?
  7. *********************************************************************************************************
    " w2 u: @8 i. a3 Z  u6 J. v
  8. */% D5 K' y5 s4 t$ V
  9. int MassErase(void)
    * q! U- K7 |) X* N* X: c( S
  10. {
    9 x& z, y7 Z% X8 d  c5 t; l( Q0 x
  11.     int result = 1;
    0 V3 b* E+ L2 A, f; @9 `- F

  12. % C) V- Z, L- N! @' _
  13.     /* W25Q256初始化 */
    & N* K" `5 V( S$ t
  14.     result = bsp_InitQSPI_W25Q256();
    9 Y4 r! M" u8 }; m+ [& H% r
  15.     if (result == 1)! u2 e! J/ J# a4 A( O
  16.     {
    ( [$ W$ v6 `$ J- {# y/ _7 R( p
  17.         return 0;1 c# r& j4 B& J' S8 b
  18.     }
    4 M  R/ j* B! {- i4 H! F
  19. 2 c* a4 o3 x) G5 Q
  20.     result = QSPI_EraseChip(); . y( J1 u. B' @& o+ l- F$ G
  21.     if (result == 1)8 N+ O( Q' ?4 z$ s/ K7 @2 y
  22.     {, |2 C9 h- {& T
  23.         return 0;" H! Y; L! o6 C. A. v  \5 R& A0 Q
  24.     }    , ]4 _! i# i$ s+ C+ o, V. T

  25. 2 X& b' U( T' X4 f6 B  N
  26.     /* 内存映射 */   
    ; r8 Q6 P8 e& T
  27.     result = QSPI_MemoryMapped(); : R) _* B2 |; ^; I
  28.     if (result == 1)$ k' W. y% N$ y# `
  29.     {, T9 H1 ]" S' c" j7 N5 T: b  g
  30.         return 0;/ O8 x  [( q9 {+ r# o: N
  31.     }
    . s  B  m5 Q3 [9 C/ H9 u7 f# S
  32. ( N+ v9 d6 }* o2 ?7 z+ V
  33.     return result;          ' Z& l) [  k9 K- K+ c8 R. b
  34. }
复制代码

  F) O: N7 V* u) G5 e1 b. r' @4 A  扇区擦除函数SectorErase
* u6 k3 _# h" R# h: [扇区擦除实现:
: r7 s! T, [5 ]( K2 t
6 P! [/ P, F9 h
  1. /*- Q# O) u: Y- n: n
  2. *********************************************************************************************************6 k/ |2 e' o, V, O+ G/ w1 {1 @
  3. *    函 数 名: SectorErase: K+ u2 n% ]& g& L, j5 m
  4. *    功能说明: EraseStartAddress 擦除起始地址+ f+ h- r5 J0 ^& u9 G. R$ F
  5. *             EraseEndAddress   擦除结束地址
    0 O; D3 X3 H0 R$ I" m, A
  6. *    形    参: adr 擦除地址
    ! Y/ `9 G( {! s% \& e  G! d- R
  7. *    返 回 值: 1 表示成功,0表示失败5 |( ]* ]; H/ ]1 l8 _
  8. *********************************************************************************************************$ s. A! z! H3 E7 b6 K- I* m) N
  9. */7 c4 W" `# b3 C- G3 M& \6 h% M
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    / z$ [5 V& K& D9 t* E
  11. {/ f6 j% i+ D( |: e1 y( N
  12.     uint32_t BlockAddr, result;
    7 r, J! Z) g2 W& I, r' J7 Y( o

  13. * }7 e) F9 J& w* G4 M$ D6 n: Y
  14.     EraseStartAddress -= QSPI_FLASH_MEM_ADDR;
    - m3 P1 e) Z: g' [1 W2 U3 N1 r
  15.     EraseEndAddress -= QSPI_FLASH_MEM_ADDR;
    % @8 o- y5 s  r+ k$ C/ G  G  K
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x10000; /* 64KB首地址 */5 b5 P' ?# V- r7 \/ X- V& R

  17. 0 x& n- M' J4 g
  18.     /* W25Q256初始化 */# |* N% s$ z/ ]+ h: b1 p+ J5 }
  19.     result = bsp_InitQSPI_W25Q256();" p4 w: \* Y$ d6 f$ t- J
  20.     if (result == 1)
    4 ]4 W0 C1 t& g/ S( X5 b
  21.     {# l4 O& p4 `1 n! G* `- w
  22.         return 0;        
    ' r, C" @9 y9 V5 P/ |
  23.     }; ?# q7 H' R5 T$ c
  24. , a' R3 v: j" u& h' @+ @
  25.     while (EraseEndAddress >= EraseStartAddress)
    6 k( G5 l3 D6 ]% t
  26.     {
    + p9 b2 j( z( Q: e) z  x) u
  27.         /* 防止超出256MB空间 */% X$ q% r; [; d) t) s1 Z, [9 W
  28.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    3 V& M- ~6 a! d) g

  29. " u8 V# G4 t5 r+ `: |# A
  30.         result = QSPI_EraseSector(BlockAddr);   
    ! P, p9 b  i+ \4 [1 J
  31.         if (result == 1)
    " ~5 E7 b1 P6 _6 L
  32.         {
    6 W; b% Q! F- z
  33.             QSPI_MemoryMapped();
    : b/ a! ~" X5 k8 s5 I
  34.             return 0;        
    7 i+ K2 b. |2 T& C& s; U1 ~
  35.         }8 k5 ]8 _1 W' V1 N

  36. 6 ?8 [; w2 v  r5 [- x- _
  37.         EraseStartAddress += 0x10000;  Q  b+ q# v7 V( {3 Z: [; s  j
  38.     }0 \- ?  H' E2 f" m( ^: m! W

  39. * p, \$ o" J% [( d9 {! B
  40.     /* 内存映射 */    5 U# \" C7 `. h% U- g
  41.     result = QSPI_MemoryMapped();
    0 y  a' _: d4 |2 n
  42.     if (result == 1), J) D0 i; [2 k# t9 O+ ]1 ^
  43.     {. U, W( p0 \! z) `9 W
  44.         return 0;
    4 p3 d0 Z8 D0 P1 }) u6 A6 z
  45.     }9 q) `. M3 o; [6 u+ Z5 O4 l3 `
  46. 3 f& ^! N; x7 y5 J5 `1 E
  47.     return 1;
    % V" i) n1 \; L. S9 ~
  48. }
复制代码

' J3 A: |) |0 h- F这里要注意两点:
, G; |% l( w$ b" ]* r
1 w  j0 D9 W' s(1)     程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
6 w( C, S( [) v9 b6 |, A/ a9 o7 d# J/ k, G
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。; p+ c4 X+ V/ J2 I" E/ r

& R& ?* H  {- |9 s0 ?  页编程函数Write
2 Q& d- I/ U7 x页编程函数实现如下:
1 ~8 J! p9 o. |; S% Y0 o7 i* c+ ]% T* X
  1. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)8 X% e! d' Q; [* A% |; p
  2. { - V) a' E; \* q! H' i
  3.     int result = 0;5 ^: Z+ _9 D9 I* \' ?. o8 T
  4.     uint32_t end_addr, current_size, current_addr;+ f; u7 R4 e, a6 i8 `# Y/ E
  5. % N5 I+ D  f8 ?6 @/ l

  6. ; Y2 b* @% U/ P0 J7 P8 P; I# T; m7 R/ {
  7.     if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES): s5 k* P6 `2 h2 Z
  8.     {, H  g. |) @( Y1 p
  9.         return 0;3 @# P. o; w! s: C! g
  10.     }
    " O( R" {4 W6 k& E8 w, P

  11. : V. x, N' f' V$ x; M% m+ _( {
  12.     Address -= QSPI_FLASH_MEM_ADDR;* w7 E/ ^4 ~  `
  13. 7 n" O! T* r/ r, x+ S* e: v
  14.     /* 计算写入地址和页末的字节大小 */
    3 L. l' C' O6 k) D8 l( A9 S
  15.     current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);1 g$ m2 r; Q2 W, t& F
  16. 3 y/ s2 U1 ~; }2 H, o
  17.     /* 检测页剩余空间是否够用 */
    7 u6 r0 E/ Q* I7 m1 q3 g0 a5 F$ l, J5 l
  18.     if (current_size > Size)
    ; J/ F4 h( f: _# y
  19.     {. p6 {9 m! o2 d
  20.         current_size = Size;
    4 U& \7 A- X1 _# r& s
  21.     }
    ; Q/ l1 G& ]' Y- J$ k; ~5 ]

  22. , u  o3 ~7 h8 Y, c; R9 p9 `
  23.     /* W25Q256初始化 */
    - z- o9 l) A* w
  24.     result = bsp_InitQSPI_W25Q256();9 [, E$ \  g) L0 x" b) D2 {
  25.     if (result == 1)
    * |( K2 ^* \0 x
  26.     {; ^  G* S$ G* T) B' U
  27.         return 0;
    0 N9 u/ p, q+ U, [. ~3 v' j
  28.     }) @4 h% l7 q' F# ^
  29. 2 o4 ~* n5 a2 z$ a: q4 S' K
  30.     current_addr = Address;
    # g5 E' Q2 _. s* N; U4 s8 t
  31.     end_addr = Address + Size;& H/ F. B5 D! [
  32. # j3 z" e  w+ S' P
  33.     do{# h: Q7 ~. q# z8 s3 ^2 L" O
  34.         if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)
    ; W( Q% g% i3 d$ J9 U# a
  35.         {1 e. j/ Z! t% f9 H4 F
  36.             QSPI_MemoryMapped();    c5 f  Z) P; L3 L% h4 ^, r
  37.             return 0;  
    5 }" J7 s2 s* N3 q9 ]- [/ H' V
  38.         }
    9 J2 v1 n$ [) r
  39. ( J& V; U" A( ~* n( n
  40.         /* 更新地址 */) Q1 Q/ l+ K/ T6 |& Y6 _' q7 K8 ~$ }
  41.         current_addr += current_size;
    ! h. b: H: i% a6 V0 C  d2 c
  42.         buffer += current_size;
    % c# c3 X; |, A+ L( _: k4 l
  43.         current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;$ G" j6 z& ~' R5 F
  44. : \9 W2 L, z; L5 N" l& {( v
  45.     }while(current_addr < end_addr);
    + A# ]1 C  ~, _4 A- z' ^

  46. $ T! w/ [& F, Q  x0 \+ z7 K3 T
  47.     /* 内存映射 */   
      a- |! S+ u: ]. G2 o+ B: [* ^6 ?
  48.     result = QSPI_MemoryMapped(); 1 _6 ^' |" F; H2 l
  49.     if (result == 1)
    ' y0 ~  u7 e' y; S: h
  50.     {
    / v/ R. f- P0 W, b9 z8 l$ f: R/ ~
  51.         return 0;; K( d" m4 l4 |3 e6 ?& o2 D0 R. N
  52.     }
    % u( J( k3 i. h0 H: [

  53. 2 \) t8 S! g% F1 m/ ]* V" b, F" ^
  54.     return (1);  ' q) X7 q: D& w: A- f- r
  55. }
复制代码
/ A9 q% w; ?  v+ D1 ~) S* m: s
这里注意两点:
* Q5 C) k' o0 G0 r) p; w& y3 q1 S+ s1 {' z' R9 w# N" ^. {
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。$ Z6 H$ L. k' E* R' ~  W: N- v* [
( l$ p3 D/ v" c! S3 B0 b- @2 |# f4 G
(2)     程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。: E- L1 B# M( V( b# K0 N

! x- H! Y, L5 C4 ~1 v  读取和校验函数& |( v: I8 C% o% {, v
我们程序中未做读取和校验函数。  s2 U# p6 n1 p, i

" `# u, M0 j) d: C. v(1)     如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
% _+ z/ S1 Q. }1 o8 w" r& T- ?4 ^& y, p- }( B8 J  T4 H
(2)     如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
5 r6 K% a, ]& X1 O
: C2 Y( x  m9 U81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
' @8 m- c1 p1 y) o2 m# o- v最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
4 r$ Z* x- I' W4 S# l
: d* ?. i5 k1 l8 j' e
  1. /* / n2 n- q4 k$ W& c
  2.     STM32-V7开发板接线
    ( \8 Q' p, J6 c4 T2 Q
  3. # V5 b4 i6 s7 @% L  ~) `; y
  4.     PG6/QUADSPI_BK1_NCS     AF10
    * O4 U7 y9 v$ W- U
  5.     PF10/QUADSPI_CLK        AF9
    ( y) `1 G9 l5 O0 \6 W( i0 }
  6.     PF8/QUADSPI_BK1_IO0     AF10& \- z/ o5 Z" q. f( Y2 H9 g
  7.     PF9/QUADSPI_BK1_IO1     AF10- {3 u' V, Z9 u1 T3 b, O
  8.     PF7/QUADSPI_BK1_IO2     AF93 o. {0 r$ P- e0 P& G
  9.     PF6/QUADSPI_BK1_IO3     AF9
    ! p- K4 d4 ]& p8 ?4 y7 I' l

  10. % E7 b- ^. F4 u9 B8 {* i, H
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
    , j; z" N9 J% K1 E" C

  12. 1 s8 A7 ~# r& [+ c
  13.     H7-TOOL开发板接线
    . c. P  D, e) y8 O+ R% }
  14. . w6 b! \8 B3 _4 c
  15.     PG6/QUADSPI_BK1_NCS     AF10; @2 z9 E6 Q' J
  16.     PB2/QUADSPI_CLK         AF9) w- r9 m, b7 L; N7 M3 f5 @
  17.     PD11/QUADSPI_BK1_IO0    AF100 @/ n7 a3 p$ X# }$ g& Z5 t
  18.     PD12/QUADSPI_BK1_IO1    AF10) H1 v9 ?9 Y9 @
  19.     PF7/QUADSPI_BK1_IO2     AF97 p7 b- w1 t2 R$ H& I9 ^
  20.     PD13/QUADSPI_BK1_IO3    AF9
    ( C# M0 F7 M8 u( s1 K9 ^
  21. */, V, Q1 x4 ~) P; ~+ {
  22. 4 A9 ]) {. \7 p) L7 e0 w
  23. /* QSPI引脚和时钟相关配置宏定义 */
    ( s0 A. [# o* a* U
  24. #if 0' A' ~7 T; p. v
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    4 ]: A4 G1 |& L* j# Q2 O
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()8 i% O- l9 p; Q2 S
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    " }# s; y* ?7 [) `0 I- m
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE(). d# d# k: f8 ^. R
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    2 z2 s0 e6 D+ v0 |- H) C5 w
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    , O! t/ f8 p( g  w* s
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    $ O4 a, D8 p( o  _$ j- A( W- F
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()1 t- l- z/ K% u# v; c6 o
  33. 5 V2 l' K/ Q2 n. u
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    : m+ Q2 ?6 T+ s) J4 v
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    9 d* g5 h9 {: i( w) C+ f( v
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()/ K, r, J3 |5 H- ^" z. f

  37. & \6 E; z- c* d8 ^
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    + x' q' a7 k3 f/ d- K" U
  39. #define QSPI_CS_GPIO_PORT               GPIOG8 P4 n. o1 s2 x2 L6 I
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI- {5 j. U8 z. a' C* m, \
  41. / f6 K9 \; C* o) w% p+ D2 m
  42. #define QSPI_CLK_PIN                    GPIO_PIN_29 f; N7 F- n/ i9 u4 @
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    : M3 |3 l; H6 o: r1 P; d- \) O+ X! H, D1 O
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    . @+ C  K/ m/ ?
  45. ) r9 o) V; [( X
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
    # P/ i' i9 s( [# Y7 u( D
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD! S9 ^, w# o9 L: e
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI
      j4 q. u. ^8 F6 h$ U6 T4 `1 s

  49. $ A, U+ ]2 h* i' X2 P5 {1 x0 T
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_121 M- i: }8 P  R1 o9 ]* e. C
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD4 N6 j$ C4 `/ }# G8 R
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI9 u# n; X* }" R6 [3 T! I6 E
  53. 3 h- q# Y$ k) m  d1 H5 g3 m
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    6 D8 Y: F' t3 A3 ]
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    " z+ M/ J4 x* i5 x( W
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI9 }- j7 l1 s9 n0 _; m( [

  57. , W/ m. a! I9 u/ P# Z
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13
    6 H/ _4 g3 z; z' F/ I% q
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD
    7 x, c& R/ n1 z7 s+ D8 z
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI, \6 W% W5 q  x  D. O' O
  61. #else- t# @  o$ Q" H8 L- ]
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()  x6 w  n% B2 `. D
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()6 x8 A7 y: f3 @& [& }
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()% D! a3 v  i8 s* }0 H& A0 @* p
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()) w7 {8 N. j8 h7 T1 \& `
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()4 \+ ^4 d4 G. E, @6 [; y
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()/ C% h+ J" ^$ I& Z8 ?' F4 v3 E" B
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()' N9 S8 q8 {7 P4 f! I1 i/ g
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    . N9 }$ G+ \3 c0 B/ }5 Y% e; ~
  70. ! q# I4 I# M" |% k
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    0 p, ~( K* N, L3 _, S: c, _, [
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    , d% X% P8 [% v1 u) W
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    2 F9 s# y; f# H3 ?

  74. . d% u$ I# I0 m1 |' J: w# J
  75. #define QSPI_CS_PIN                     GPIO_PIN_6$ N9 A% B- v2 c% U
  76. #define QSPI_CS_GPIO_PORT               GPIOG
      ^# Y9 p/ t5 J- Z5 o5 u- |
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    7 L/ t' I9 x  J: f6 l

  78. - y7 A7 p2 |8 X7 A* Q
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    ) y( [8 g# b) }0 Q$ P6 Q
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
    9 h5 D7 r5 J. f6 F6 p, G
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI, a  k' S. X( p  h% |6 E

  82. - ]+ y8 Q( D, k+ f1 J1 ~
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8
    , y0 r0 p8 ?& ]! X. H9 ?! v
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF+ h# `9 A6 F9 y' ?  `4 [
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI
    $ t6 M( B0 L6 g+ c4 T& D/ C
  86. 7 p& H: H6 a/ j" Y$ Q8 I3 N* n7 O
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    + J4 _, i- m7 h$ _; Z8 f1 B* i7 d7 Q
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF
    & ?. ~& @; p2 S# `
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI" }- }0 r3 V* [2 D: X$ z% f
  90. 6 o' ]( J% F# ^! G
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7* D1 D6 c: `$ [: t; ~+ J! |6 h& X
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    4 I6 _& y- e1 b8 B# R2 E9 p0 X
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI$ k: u8 G; q& F$ W0 Y& \; K
  94. ! U2 `* n) @2 g* {% k
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    9 b9 H6 r- W3 X6 L: k' q7 D
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF
    % ]. h9 }- X2 u% J0 v) Y
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    2 n+ Z  r6 `* s9 t: ?
  98. #endif
复制代码

" W( c' `' x  n1 }硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
. ]1 v1 `  ^7 S; L) J! O1 ]/ G% Z) E
主要是下面这几个:
4 V8 B7 N7 _8 U) R2 C/ @4 ~& H8 ^  I  U% F6 _" W2 [  E
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000' L$ W2 M* x1 ]" H$ `

  2. - ~" B+ r7 g( x* D% W9 N
  3. /* W25Q256JV基本信息 */7 Y- g8 X0 _% D( h
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/' s# Q/ M1 x" U" O5 \
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */
      ^3 m# i: w' o$ P" ]+ V: l/ i
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */6 G2 I- [9 v! q% T' g/ J. b
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    ( n( D0 b: R* z2 d3 A, N" U
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/; F& T( g8 O' Z, ]7 t

  9. 8 n3 u6 ?0 U* D* M
  10. /* W25Q256JV相关命令 */7 Y: P2 U6 r( Y8 [
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */
    " L8 o: a. Y6 \6 x. n) }
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */. C1 p6 p/ ?. x
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */2 r6 O6 ^  J' g% r" G
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    6 u) B* a3 R9 J( q$ T
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */
    8 ~% h. W1 q# f* c
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */" F4 ?$ e9 m5 P
  17. 8 z! r9 d* U* f0 P
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    ' u3 Y4 A, h# V

  19. $ E. d' X- o$ K+ s+ q+ }
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

8 P, {7 @7 A6 G81.6 QSPI Flash的STM32CubeProg下载算法使用方法
4 s& r* c/ m8 C0 A- N4 {编译本章教程配套的例子,生成的算法文件位于此路径下:
. a1 k5 T+ b" n! k. [7 O4 N8 z6 w; l3 G; \( E# z& P) M9 x
d45f9274d8762b095790b3e6778b0a93.png

0 j9 }, C7 Q" f3 r( }+ }  V  H, C* D; d' |- H0 W; Y# }. G8 }  ~* F
81.6.1 下载算法存放位置
) L: z& [8 G6 Z6 O* Q7 a2 X' _) B生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:+ k) ]* h7 H$ R0 G( h
# s8 p( I3 c0 e; a' V3 `  |
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
- y0 v5 o5 i' L$ ^3 M$ C  @' [, I  }
60cce5a15415e27e9d5b75b05028d1c0.png

1 ^5 E. _3 y1 k9 V# o* N& i, p: d# E/ I. s( t5 C! w1 Z% z
81.6.2 STM32CubeProg下载配置5 l1 O2 f- y: |0 R8 w+ T
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
' e* v, K: J$ Y/ i
3 B$ l" K% T! m" \3 `
9cc179641e765ea43cb38d268bf734df.png
+ x) F& J$ I7 q$ ?/ j
: H- h" k# v; X* n
点击Connect后效果如下:& c. L/ Z9 ^9 E* k4 l0 `

2 z/ C9 s0 t8 X' \' b- I* s
b39ce8a738163430db2119bdd87e3fbb.png

  x8 b% J! c" ^  o& Z! Z2 f5 |* {6 t5 R, o
在这里选择我们制作的下载算法:
& z" N- r' z. i( I) }6 w3 n4 W0 _( G, L  f& |- ?
17f6e0f50a18ae818faaa1399daef12f.png
$ a4 c3 b8 o+ B7 v; m3 e/ L  @
6 a) j: Y" e0 t; X5 o
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
2 i9 z0 s1 N( \8 c0 t. ]; D0 E3 e* u
43a0d90faf1d392f5a74eaa7777e4178.png
, u" H7 q) I2 X4 U5 X/ F, E1 B5 A
% d. K9 e& v5 a
下载完成后的效果如下:: J. x# v6 L! n9 ?+ H
  f0 ]9 q& R0 Z6 O
a1d6662baa9e7be00f7e67e02b5c9b79.png
% a- D( H3 y! y* _1 Z! j! a

( }, J4 m; M( [: X81.6.3 验证算法文件是否可以正常使用% H) `- D  P+ ?, Z6 D$ U  Z
为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:6 {2 ?3 u9 m6 ~7 `0 J

0 q$ Z3 v1 l; W3 Z
3f2eefeef3db68623cb8969974c40a20.png

- ?/ g& g  ^: `) ^2 u% Y6 z0 o$ p3 i- E
81.7 实验例程说明
: j: H# y8 k  S/ i& r7 @本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。& o; |! ^8 l% v$ X% R
; j8 q# i+ n+ ~
编译本章教程配套的例子,生成的算法文件位于此路径下:
: t/ F' f: w# q2 a: y, I
5 v! |+ v- Y+ D' U0 C8 u
b7028271136c8053d7300063ed4aec3a.png
0 S8 Q2 O& e$ D5 I/ S0 Y4 U% e
7 P9 H0 D8 Z; x0 k7 Z
81.8 总结
' S# F' @% o4 V) y( L3 ~$ W本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
/ a# Q0 o# U7 n, I3 j0 Z. P+ v0 {$ _+ {/ w% K4 U0 T* q
* L% s- M4 `! t2 W- L
) }9 o6 `/ `" S6 D/ Z1 k4 k5 w7 E
收藏 评论0 发布时间:2021-11-4 23:27

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版