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

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

[复制链接]
STMCU小助手 发布时间:2021-11-4 23:27
81.1 初学者重要提示
" D6 T- s! z9 W8 y9 `' P, K$ Y  QSPI Flash的相关知识点可以看第78章和79章。: O* k; Z1 n0 T+ w  W& C, p2 I8 c
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。9 I! D! N: M2 R. K/ \& Z2 i
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样1 o5 w8 P7 }2 y" x
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。4 t5 u4 O( i* q# H# i7 n
81.2 STM32CubeProg简介& M0 n, q, e. u
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。6 T0 J& i( i; h6 l. u
. y, ]9 Y: t: @
1bbabf500126f58ff1439b6bc36014be.png

' V2 D3 E# @& F$ C; k
- O' s2 P# j* U81.3 STM32CubeProg下载算法基础知识
( Y1 Z* S; T% _. w0 F$ PSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。- r4 `( A9 {' i2 t! V
, v( w' F0 E) e+ ^2 \0 c
81.3.1 程序能够通过下载算法下载到芯片的核心思想
! P8 K! S5 @+ W1 x; u( f8 a, ]认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。
. V+ r7 F9 {/ o' q' g7 I
  A. Z. _: l- c81.3.2 算法程序中擦除操作执行流程& ]! W- x: {3 }8 ^( U) q: I4 O; e5 j
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。# v) T7 C' y7 w9 }2 G( N% C- M6 d
1 [, z' m5 F1 y% ~1 d! W
擦除操作大致流程:- {0 Z7 u- g7 P8 g

& o; P  O- @$ Z8 x0 S& M
161f92ebd9b1dbf9febba815fa091b8b.png
! \. P# c- A. s
0 y' D1 w$ M0 f: a' D# G
  加载算法到芯片RAM。
: P" t3 f6 B" q" f1 f  执行初始化函数Init。2 R6 @' |. l5 D
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。9 B$ V* M6 V: ^9 \
  执行Uinit函数。" @* ]" E5 h# v( E; S% h
  操作完毕。& x2 p7 Y; `4 e. Z! C
81.3.3 算法程序中编程操作执行流程1 q9 A) D( ~: J, o5 |- i* L+ k& X
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
0 U0 q6 G4 Z, Z. a5 s) q* i8 |& P8 C# B# z9 u* X
编程操作大致流程:
+ K$ e8 }4 q* R; V* `5 L
- I; P, H2 L: [" @
d80dfd755411aa0d0565f1c56f10e66a.png

: q/ b* D; ~& ^% x
+ t3 w- ^2 E* f8 ]8 |  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
( n' x9 B# H' v# y4 `1 C$ I  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:8 e! J4 n) D: }4 H
  加载算法到RAM。
2 g) P9 {2 Z/ m9 z/ y. [" G  执行Init函数。% G; `" c( D, O; ~5 j3 P
  加载用户到RAM缓冲。
# q6 @$ J7 |6 H  A! l, c9 R9 o  执行Program Page页编程函数。. f2 y  {. d/ y. V
  执行Uninit函数。
: b6 k! x% t: w- J+ w( |$ |  操作完毕。/ F' U. b8 F" ~; a2 u4 F
81.3.4 算法程序中校验操作执行流程# }8 n3 _. U/ K( g  @
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。2 ~. m* U9 x; w  X9 y6 Q
( E/ B& ^+ Z( q+ B3 {
校验操作大致流程:
; {+ N3 J8 @. Q) j" d
4 S! E" {9 Y2 _# h) w( X
f15191090ab169d48a203ea3617af9b4.png
8 ?# B- L, l' U3 E; ~# z
8 m, k5 r3 P1 M2 Z, j4 q* ^
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
6 I) x  w. g6 e8 }1 b  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:" [5 ~  C: I# x8 ?! u
  加载算法到RAM。
2 O0 b1 ]" I! l) ]! a  o  执行Init函数。
$ a5 s9 d* O* i7 x  查看校验算法是否存在+ o) v0 l; R. \1 C6 H6 H
  如果有,加载应用程序到RAM并执行校验。" a2 q* J2 a) \5 d( _1 a3 d5 N
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。$ m! r: J2 n# S1 r% z8 g
  执行Uninit函数。
" @8 o) h, H' @  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。8 n/ g- f( H/ E9 F
  执行RecoverySupportStop,回复支持停止。) c/ W7 G" h$ s9 w& _" b
  执行DebugCoreStop,调试内核停止。! \2 C4 K% d: |# P9 B
  运行应用:
" l% m% }- D$ w& V  执行失败
/ p5 ]' r( B8 N- [) V0 R+ }  执行成功,再执行硬件复位。& a+ S( @! F. o, f9 y
  操作完毕,停止调试端口。
+ p7 z6 O* D; I: E9 ^81.4 创建STM32CubeProg下载算法通用流程
$ _, }5 K, @2 j$ Z; r$ f下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。. j3 B) I5 @- K1 _

) z% n4 I3 A( O7 R  x  j. U. h81.4.1 第1步,使用STM32CubeProg提供好的程序模板
' S6 {# z5 J+ D" y+ k3 B位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader  z0 K5 n' a7 l! R6 q+ F/ [
/ B4 v2 W2 l- M# M" a
4fbfb0047e29fe23083224607059d97f.png

7 D$ [) u/ i- R5 \* I6 Q" g1 e9 U/ E  g3 }. F/ q
以M25P64为例(注,其余步骤就以这个为例子进行说明):
' B' Y5 c5 P' v9 L# O; ]; a
% ?& d7 Q2 C5 x: z
d1b1a340fdc655c1030330a7dad2abba.png

. m) k  n8 Q1 T, x4 X3 g9 i; v4 C2 j6 h) K4 k
81.4.2 第2步,修改工程名
6 g& ?; v& o- Y, a3 o  NSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。/ @. i' n0 K' K, O! M/ O
0 \# O0 I- g, z3 ]
81.4.3 第3步,修改使用的器件
0 z( K( T0 _' V) r在MDK的Option选项里面设置使用的器件。
+ F' {5 \, l! z) t6 u1 ~& ]' R
+ z& z' L2 r( f7 b( [8 T7 K
092174ad976a8ce9c7fca533049e2dd0.png
) Q4 H5 i$ k6 Z/ c% ]1 l9 O
3 g5 z7 S) }& O
81.4.4 第4步,修改输出算法文件的名字  d8 `$ W7 i+ I, e' o# b, `
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。$ r; e# N* k+ t# b8 J# J

/ x* I' b( n; ~5 Z5 |2 i1 b
79c42251311dd3892fc7d6b8ddde68b8.png

8 u) z1 H, x5 @5 m, Q. [: k) I+ f: U2 L8 b
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
# C( ?4 S) t: t+ X
e2fe61fa7ea99b25b178341b7350558f.png
: H' i: u* z8 r
6 q- J, C5 A' ?$ E; \& }9 ~0 h
81.4.5 第5步,修改使用的库文件和头文件路径
! L/ }3 F4 `0 T9 H9 p7 n# T根据大家使用的主控芯片,添加相应的库文件和头文件路径。* t" y& N9 Q* h9 b
$ C: C/ G5 ^3 e" K& E/ m) O7 X- s) X
81.4.6 第5步,修改编程算法文件Loader_Src.c
4 g1 m0 g1 B: Q( }  N  Y模板工程里面提供的是M25P64,部分代码如下:2 x- E" Q" [0 y2 q" \* x
6 |% K# L7 ?! E" M5 O" x( J0 A: ~
  1. /**
    / u, O0 ^/ q' @4 \" C2 _7 B
  2.   * Description :
    ; b9 `" D) ^: C+ X
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    . w1 b' p" |/ M5 G4 q
  4.   * device and initilize the FSMC with the chosen configuration 5 H2 S' b( C8 O8 z& m
  5.   * Inputs    :' V  z6 C& g7 q$ ]# J# N' a( a; l
  6.   *      None
    , `2 B- D. m+ B
  7.   * outputs   :/ J; q# I7 A  e/ X# T6 @
  8.   *      R0             : "1"             : Operation succeeded
    ) r7 m8 }* o, Q7 R% \' {, J
  9.   *               "0"             : Operation failure3 v8 q/ g: f1 f7 L
  10.   * Note: Mandatory for all types of device 0 S# f3 c! F$ c# C3 o) p' Y) h8 J0 H
  11.   */) }$ X, X4 x# c
  12. int Init (void)
    ( y0 j. V7 l  c/ o5 Q
  13. {  $ }$ f* c8 Q; ^7 b
  14.   SystemInit();2 l0 G5 J( R# v$ H5 u& z5 u, I) c1 v
  15.   sFLASH_Init();7 ~! N  e" o. A4 w5 C1 e3 c
  16.   return 1;
    % N( g1 l* _, m! }$ A; m
  17. }/ P' }! R5 ^: W$ J

  18. 7 G0 v% M( q$ ~$ A4 F: @

  19. 9 _9 ]9 Z2 W6 @1 _/ X* Q; Q" U4 l
  20. /**
    0 g6 @9 z* M5 ?4 i0 r( W" x
  21.   * Description :" D8 K8 v: z* K0 ^1 q: `5 i! K9 J
  22.   * Read data from the device
    - r* \( h) @! J5 @4 H
  23.   * Inputs    :
    4 n0 ]" b# \5 m9 }1 b! A' g+ g( o( f
  24.   *      Address       : Write location( q2 `6 N0 c5 R  C$ z
  25.   *      Size          : Length in bytes  
    2 M2 Q  N) }. A6 O- `; L
  26.   *      buffer        : Address where to get the data to write
    0 [9 _. `) |/ f
  27.   * outputs   :
    2 ~# G# j5 l! `$ V
  28.   *      R0             : "1"             : Operation succeeded
    - @* Y5 h: K8 r0 f
  29.   *               "0"             : Operation failure
    0 F6 C3 i( _& Q: w
  30.   * Note: Mandatory for all types except SRAM and PSRAM    ) }7 [* t7 m% N" `3 E
  31.   */
    * a; c/ h2 E9 l
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    - h1 g( F0 g( g0 _
  33. {
    8 l8 ?8 l, m+ h* [
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    : _+ e9 e7 Y, i& J
  35.   return 1;
    , o+ x- B0 R) q1 k7 h4 r
  36. } 1 _1 z7 P; J+ H" ~# R7 J

  37. 3 v. L& V9 `( G3 U

  38. + z$ I$ v: t( t, s/ k' ~
  39. /**
    $ Z! T  t! P: _9 v8 V& s
  40.   * Description :" n4 w: F% L0 B" t
  41.   * Write data from the device $ f4 l; `4 S4 T, c$ |9 o  d3 t
  42.   * Inputs    :
    6 C) g* E4 j" d/ M0 L) Y, K$ u
  43.   *      Address       : Write location
    6 {0 j& N/ n9 b8 n8 `- n
  44.   *      Size          : Length in bytes  
    & o, T1 M" b* I! \
  45.   *      buffer        : Address where to get the data to write: q7 y, z' W7 I% l# `
  46.   * outputs   :
    # l* m8 v  }" |( s: k6 m
  47.   *      R0           : "1"             : Operation succeeded9 R# f2 ]6 |/ x
  48.   *                     "0"             : Operation failure$ i# f, o' c1 c& r7 \" @
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    " C8 r* o* Q0 _
  50.   */
    3 |2 @& z! _6 ?& u7 I
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    / @9 _. Z  D9 s0 U  e/ \* B
  52. {
    . g% j) g# v# v, J5 ^
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    6 V' L8 }, Y/ y6 T  P& l
  54.   return 1;4 f! I! c/ |# G6 i3 X) b* V: e9 f
  55. }
    / f& @+ g3 s% }2 n2 v3 x9 ?

  56. 5 N% k3 _( F, Y* }5 N+ x

  57. " H8 {5 q: c7 [0 `" z$ {3 u* O
  58. /**
    $ S# l: Q! `$ z  U5 ?& D- T
  59.   * Description :! s9 X3 h9 p4 ~7 K  a4 H' x
  60.   * Erase a full sector in the device
    ; e% ~# @& R0 L/ @: V2 ?$ E
  61.   * Inputs    :% B+ B5 ]6 c% w
  62.   *     None
    0 u& i. N% s3 T4 d1 H" Q
  63.   * outputs   :7 z3 X% L, ~! W6 d. b
  64.   *     R0             : "1" : Operation succeeded
    . f% O% |6 y* S/ J
  65.   *              "0" : Operation failure. }2 J1 P) k: G5 v
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    * _' {) C& `3 I% y, s: B; ^5 s! h
  67.   */
    + \* _& w6 i% P+ |
  68. int MassErase (void)" X" |! ^% R3 q; _1 U
  69. {  
    ' o/ S" ^5 ]2 p
  70.   sFLASH_EraseBulk();
    - n2 c' B9 @$ E8 T
  71.   return 1;   
    9 f9 N) Y. q  g2 E3 D) F
  72. }
复制代码

1 `2 b' K! D+ @/ Q. H% B81.4.7 第6步,修改配置文件Dev_Inf.c: R5 o- M9 O. b& c* `  u# e" c
模板工程里面提供简单的配置说明:+ n0 X9 H+ o. o0 D$ b8 D5 [) v
( ?" k# \" q0 b3 i" `2 A+ F
  1. #if defined (__ICCARM__): D6 J, x; q+ _: h; E
  2. __root struct StorageInfo const StorageInfo  =  {! a/ j& ?& |* {6 T( b# s
  3. #else
    2 X( y8 B5 R0 [9 `4 p$ d
  4. struct StorageInfo const StorageInfo  =  {2 \9 f) o1 x. N0 k
  5. #endif
    " K! W. S# [3 o2 r
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    + E' N% [" W! e" k
  7.    SPI_FLASH,                       // Device Type$ q) `* P( i! E* O+ Z
  8.    0x00000000,                     // Device Start Address1 P$ k" ]: t/ o3 W  [
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)5 V5 R: z; @& r: r4 O4 r
  10.    0x00000100,                      // Programming Page Size 16Bytes
    " v! ~& A" q* B* K
  11.    0xFF,                            // Initial Content of Erased Memory
    1 ], ]' v1 ^7 Q
  12. // Specify Size and Address of Sectors (view example below)* C4 @  h% {$ P/ t7 g# [3 t
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    + y! I/ C4 |9 ?. E7 B
  14.    0x00000000, 0x00000000,
    0 t" `+ b4 q2 g7 U5 x, h
  15. };
复制代码

5 i6 X5 E0 m# E2 f3 K$ X+ B0 P注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。1 e) q4 s9 W8 l( ?

5 ^, T% B. N# W0 R9 u% e; }
6 W. q# I4 G0 I  Y
  c6 O/ I+ m) b$ U/ u* `, D% R; q4 r0 C81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
2 a6 t  A7 @: a2 m4 a& @C和汇编的配置都勾选上:
9 K. W) z! T2 g9 B8 v! k: y# }* |. D; L1 i: u, u: a
8f2ba38db4c1f685f131e7f1f4e98551.png
- k& V" t, b( ~8 z
* E$ E* v; ^; t$ q  E  N
汇编:# G* ?2 U6 B. W. ]0 j3 V$ Y. ^, W

, r" n' W$ r& A6 {- \9 v$ Y0 i4 F6 j
0d264153b167afa2303144bdd378d501.png
# N5 H: _; a4 M, _- H# q+ m3 w

0 U# ?, t* q9 f如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:2 s9 G& B5 @$ z0 {( m

( r3 m3 ?2 c- {8 b' B(1)加载以响应运行事件。
4 g# G% ]+ V4 s6 E- u2 S$ U
9 V$ e: ?$ _4 l0 P(2)在不同情况下使用其他例程的不同组合加载到内存中。
$ p1 m0 S& d5 h6 W+ M0 I2 W7 U! h) z9 @) [+ d
(3)在执行期间映射到不同的地址。
6 S- X1 N5 i7 j; h' M3 K
+ U0 c9 e4 {# r8 \) C5 x, _* q使用Read-Write position independence同理,表示的可读可写数据段。0 W+ m: w# s, x; \6 g5 H8 j6 \! B

1 I8 j, M) s2 z' q. L81.4.9 第8步,将程序可执行文件axf修改为stldr格式
! V$ ~5 |! W( q4 }通过下面的命令就可以将生成的axf可执行文件修改为stldr。
: t# ]5 A' @2 p" J' G& k$ i
& X6 [* o. t) F& E2 m& B$ M
f50b1594f3741e77b4fbd5523564d3af.png
6 U! g% T5 d/ O/ Z  P: U* h8 Y- ]

4 G5 P8 C1 v5 y  M  s5 S81.4.10   第9步,分散加载设置8 C. B8 e$ C: Y* V/ q
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。3 Q7 g) b6 p% u; K3 F" n1 P% ?. p* [

! U, Q, P3 x" q& \  p
8a01cb7c9be38519d4d0510a218447e2.png
/ p# q, J2 p$ n6 E! L; o) d' u9 _
4 C3 R2 N# T* [
分散加载文件中的内容如下:7 T" R, b; o+ x( B  j# i

/ ^. N" N/ }7 s( b# H
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    * e# f) u7 ]7 Y2 R6 E" t9 i# |2 d
  2. {1 v( k" d: J! J) s# s
  3.   PrgCode +0           ; Code5 ^8 M9 N5 L% y9 J8 B% Y! x0 ?
  4.   {
    # }0 b" ~/ B" ]2 C1 O3 H/ {- v
  5.     * (+RO)) k# U! b( S7 H/ ]6 E! N
  6.   }7 N6 a, E5 [9 [+ j+ _9 z* h$ z: N" r
  7.   PrgData +0           ; Data
    + {$ U' x; u, P1 d. y: \
  8.   {: ^$ L' R1 @. A% u2 R7 j
  9.     * (+RW,+ZI)
    : ^/ t! f1 D. B, m. i
  10.   }8 Y* \! {  S$ ?$ x3 G- v
  11. }) l$ D1 x; e, j1 L& ]4 @
  12. : Q; t4 G, ?5 B" m
  13. DEVICE_INFO +0               ; Device Info
    4 l3 c% s/ r* V" S: n* C
  14. {
    2 T( E/ Q* Z( ]3 ^  I6 w' w
  15.   DevInfo +0           ; Info structure$ m" o. |8 B/ U& h
  16.   {0 }& k$ W+ k8 M( q4 u
  17.     dev_inf.o
    & [+ ?+ e9 l% |! r9 s8 e
  18.   }7 K; ~9 n% O8 Z
  19. }
复制代码

: _6 b3 ?; _0 ]' A# ~; O9 m这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
0 S2 [0 f, U! G2 \) D. T  ?/ l. E+ a  K  d2 q( M4 k1 ?
--diag_suppress L6305用于屏蔽L6503类型警告信息。; c( j- {$ R( C5 g9 f2 ?( `! ~# o
, c. f% e9 U3 z& \  M
特别注意,设置了分散加载后,此处的配置就不再起作用了:
5 r% x) I0 R5 ~. {0 X; r$ j+ h5 Z( |
13b022190bd5115ed249129fc3886a38.png

2 Q  K& @4 g, Z% A+ M- f( ~; Y3 y. a' h; V1 ^( [6 s+ y, q
81.5 QSPI Flash的STM32CubeProg下载算法制作
2 ], w- a8 i5 s; f) ]. e' B下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。& }0 {9 [& b5 N! V# w( S* _3 n

7 c9 R- J" j4 o81.5.1 第1步,制作前重要提示
7 Q9 A& b% e- J( C2 |这两点非常重要:
& T  x0 p; S) n
0 h9 ~2 V4 b- L: t7 J- I  程序里面不要开启任何中断,全部查询方式。7 \) M  Q$ C  j5 e, ~* G- ?/ i
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。& N6 h! v6 q' e8 c
81.5.2 第2步,准备一个工程模板
$ N" V( N- p& w# Q# Q6 L, A 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
" ~7 R- B* r! p7 A( w6 ~" C. k- K" m. L6 Z0 u
f46a8d2276d0ffb7beee251cdb0809a4.png

, F3 ^0 N' O0 x4 w( o- ~
* c. m6 W9 N+ U3 Z81.5.3 第3步,修改HAL库# F# e# J- j( l8 V1 _; G
这一步比较重要,主要修改了以下三个文件:
6 Q! Y; \1 V/ p  R' f( \* Z6 |
7 g6 m* I- y+ e# O" {$ ?: R
1efb78256b9465df406e3e9e95ae30e5.png

1 |! @, R( i% N; l) k2 _- s+ ^! a& t! k6 l- i0 K8 ~3 d
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。( F) z" x& P. O; F

7 Z# c/ a  I% R2 ?81.5.4 第4步,时钟初始化3 k: A1 [) i+ z) T- [
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
# N' r' ?8 f6 R8 F' O
2 K/ H0 {, l: M  b
  1. /*4 S, ^2 u# E/ Z3 }% V* s; M
  2. *********************************************************************************************************9 A6 b8 p2 g# {$ o! ?
  3. *    函 数 名: HAL_InitTick1 |: i* E7 A4 m2 z0 X. \
  4. *    功能说明: 重定向,不使用) b- x+ F( S5 L' C
  5. *    形    参: TickPriority' n% p6 `/ Z; C2 \' q
  6. *    返 回 值: 无  ^7 W! A$ e+ m* V
  7. *********************************************************************************************************, U$ Q; T' ]3 R0 S2 w& X9 V
  8. */
    4 M, Q) p1 B0 k. t0 c+ Y5 L3 P) i, U
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    3 [4 x5 l5 z* ]0 B/ i
  10. {& R6 ?3 g0 ?. j. Y2 i1 M8 A
  11.     return HAL_OK;; B: c# m) k% i2 J9 t
  12. }
复制代码

- F6 Q% Y) }& L7 w0 h( w$ j然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
+ G5 D" u0 y' ~1 J3 Y' o/ L
. s6 U5 v. G( ]* C
  1. #if !defined  (HSE_VALUE)
    & N4 b, I- O" d$ v- I1 O, c
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */* Z- E' y! y7 r
  3. #endif /* HSE_VALUE */
复制代码
# b. |2 }1 P1 Y9 s$ S; H$ n% v: G5 O
最后修改PLL:
1 B& ~" l4 b3 u' o; o0 M7 G4 Q9 }9 T7 {  k, J6 s( b9 H
  1. /*
    ) w: c5 f! z1 `- o
  2. *********************************************************************************************************9 y4 D5 w  s3 L# {" x4 H% C
  3. *    函 数 名: SystemClock_Config
    - v# m/ s2 D+ p% Q, [9 \4 V; L
  4. *    功能说明: 初始化系统时钟4 k0 J% `6 W0 c" S
  5. *                System Clock source            = PLL (HSE)
    $ C4 ]! Z$ I# \1 C, J: _6 I
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    2 B7 _+ _' F( s) W
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    , \# N" G& ~; }! A6 l' r
  8. *                AHB Prescaler                  = 2  b4 B: p2 q. t
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    9 U  D4 |/ W6 l- i1 d
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz), F* r4 @" `) F
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)+ x% k; n8 ~/ k7 g' A; F5 n
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)0 s0 h- ^& N9 e, e  Y
  13. *                HSE Frequency(Hz)              = 25000000; }- c9 F" `+ L8 J2 G
  14. *               PLL_M                          = 58 o4 u- S2 [9 g' _7 D, j
  15. *                PLL_N                          = 160
    : O: N6 b: s/ Y7 w( N8 _$ n; k
  16. *                PLL_P                          = 2
    % U% {+ r' O) @, ]8 C
  17. *                PLL_Q                          = 4
    0 N' e& D1 [! I- ]4 O  h  L
  18. *                PLL_R                          = 22 i# t# S* ?- ?% l6 Y
  19. *                VDD(V)                         = 3.3. \' y7 X( L1 c: k/ {8 c
  20. *                Flash Latency(WS)              = 4# y+ {" Z% g# e0 d
  21. *    形    参: 无  F: g6 B  F7 o% o* \1 |9 J) z
  22. *    返 回 值: 1 表示失败,0 表示成功
    : L+ {+ ^+ ?4 q% c# T
  23. *********************************************************************************************************  I6 ~0 I, L. Z$ ]
  24. */
    8 D' x' h" E- F1 H, m8 U+ C) n. m  h
  25. int SystemClock_Config(void)) X8 [; `" O! H+ q7 e
  26. {
    + ^1 ?7 T: o$ g& Y2 j
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};# ^* p1 P' T. }" F
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    3 h& I/ n6 v; C, f) S' q% z! S  x
  29.     HAL_StatusTypeDef ret = HAL_OK;, f5 {, r7 H% b5 e" I

  30. 5 S2 k( j, X8 u3 ]
  31.     /* 锁住SCU(Supply configuration update) */
    - V6 ?+ j1 P1 q+ J9 F! C
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    + r6 K! B8 l: y" F. d# Z/ H

  33. % b$ \0 a# Z* Q6 y. J
  34.     /*
复制代码
# p' O' d: _- F2 V+ g# ^
      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
/ f. v5 h* M4 u. s. I         详情看参考手册的Table 12的表格。3 A2 z6 }9 V, K* I# |
      2、这里选择使用VOS1,电压范围1.15V - 1.26V。* B$ W: k: a' d: x$ c% `  E
   
  1. */
    . H  Q4 r- k' O. A% e
  2.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
      t5 {% O  ]6 ?" P
  3. ) x2 v' b& N( S" d5 t; A0 ~- X0 ^
  4.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    5 K/ Z9 T& Q  P* B

  5. # O; k. ]1 n) a5 t) C( f
  6.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    ; R0 l0 |9 p( B5 f5 x6 Z9 H
  7.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;6 a0 q+ Q, \* i6 B; s( G; n
  8.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    % W0 W  O5 y$ [/ L! k
  9.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    " v$ {1 a" o6 P( Q
  10.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    $ L" E& D( ^+ v# r' d
  11.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;" b, ~, b1 B, C+ C2 e, r
  12.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    3 F, z( J1 O4 Z- f/ \

  13. 2 y$ a  y* i  Y) I. K
  14.     RCC_OscInitStruct.PLL.PLLM = 5;( v% H& p' J( O' }3 f7 F
  15.     RCC_OscInitStruct.PLL.PLLN = 160;! p( j: M% d- ~) h
  16.     RCC_OscInitStruct.PLL.PLLP = 2;- B, y( O5 @: `4 u0 i
  17.     RCC_OscInitStruct.PLL.PLLR = 2;) J  z; L9 {, @6 ^
  18.     RCC_OscInitStruct.PLL.PLLQ = 4;        # z9 B* S9 n6 ?, @0 u
  19. / |- C$ o1 x1 g
  20.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;# h' n- f) ~+ i0 u; d6 }
  21.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    + @. v2 _/ d$ {& ~$ Q
  22.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    * P9 V* s. p* P9 h/ ?
  23.     if(ret != HAL_OK)) W$ D$ N1 G1 k6 h' ?: w
  24.     {
    1 d: w# e5 }" H1 X
  25.         return 1;        
    & `" o" t$ D7 Q( H0 y; W0 R
  26.     }
    . A' }# Q4 L6 L& D5 k  G8 n
  27. $ l4 j1 ]! E, C2 G4 A+ C
  28.     /* - c! r% K5 Q; s( f! ?. M' B. X) ^
  29.        选择PLL的输出作为系统时钟
    ' ?4 ?8 T7 {" W7 ]" W! v2 }& l2 t- b
  30.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    : w/ R3 a, y: [) P; |3 G/ t
  31.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    7 J1 _' X2 u3 p+ R; T
  32.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    ' M8 S+ _- x8 t+ C
  33.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    3 g# i6 e- ]! W0 K$ A, s# ^
  34.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线* m* M7 N! v8 b& p
  35.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    : ~+ Z) g& E6 ]9 R0 \+ {+ `1 a
  36.     */
    5 w4 L+ X# e4 T. w1 x
  37.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \6 N* Y/ B# O9 K8 s! I* A
  38.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    8 [# s1 C  o8 p2 K1 N
  39. ( @( K1 g0 Y* X9 l2 `
  40.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    4 k, ~' k: \  I9 r# G, w
  41.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    5 w) ^1 s- ]* D' ]+ u  `
  42.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;1 o  A/ n$ y4 F5 [, ?& p! K
  43.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    8 Z( v; U! A7 t) h' f
  44.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    1 {6 P! T  N8 h0 t# c8 F
  45.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 9 G8 ?! X( ^' Z9 }
  46.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; ) q0 t/ ?' V+ G. u; G. a! n# p" W% O

  47. % C9 r0 F: h) l$ @8 w
  48.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    4 b" `/ y) `) D, i9 K- U
  49.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    1 y3 [# a- N' J% z* M0 k
  50.     if(ret != HAL_OK)
    , i  P* c5 l' t
  51.     {$ ^7 ^% a* Q! c' J5 k
  52.         return 1;
    5 e5 l0 |: y" ^' k
  53.     }, B- A, ^1 Y( x8 P5 ~) H+ {: y

  54. 1 [0 w+ U0 _3 w/ b6 o" l
  55.     /*; w/ P' T% L. i
  56.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    : U0 c2 s; W+ l& W
  57.       (1)使能CSI clock( a. b2 r3 y1 o( r
  58.       (2)使能SYSCFG clock
      ]- {' n# B% g6 D% M! `# B
  59.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0, u  s5 r( K. k% W
  60.     */
    7 J( X: l1 T$ h2 M# t
  61.     __HAL_RCC_CSI_ENABLE() ;
    ) Y5 d* F+ Y/ W9 m$ a3 W
  62. 9 \* c$ |1 C* u+ B, n
  63.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;4 Q  D( R% x% z1 t. ?2 r
  64. - F+ o6 D! H2 @( Z
  65.     HAL_EnableCompensationCell();0 A' g3 @1 |/ N# R0 H0 t' E! s0 n
  66. ( W3 g& g- m' A# p* ]7 Y# W
  67.     __HAL_RCC_D2SRAM1_CLK_ENABLE();* ^( Z" K; t! f, l0 @7 N/ N
  68.     __HAL_RCC_D2SRAM2_CLK_ENABLE();) H' ~6 d$ S. ~# l# w, J0 Q
  69.     __HAL_RCC_D2SRAM3_CLK_ENABLE();; {$ G8 ^3 X  l, X# S7 w
  70. 2 H& d$ Z) [  s0 M
  71.     return 0;
      }- x4 x; ]& Q- l8 a/ E" U& Q3 {: A
  72. }
复制代码

! [: f3 ~/ o/ S4 V81.5.5 第5步,配置文件Dev_Inf.c的实现
9 O( N/ X. M1 ?  L$ P6 O配置如下:
  n5 r% `  {8 E; U5 F3 i& ]0 ?6 D, H
  1. #if defined (__ICCARM__)) g" f. e  t$ T0 I# U6 H
  2. __root struct StorageInfo const StorageInfo  =  {
    8 x  C) [  z* Q9 p
  3. #else
    ' b5 Y' {6 D6 w
  4. struct StorageInfo const StorageInfo =  {; d5 ]4 o: O6 ]* d: `- u
  5. #endif
    & {* B2 ^( }6 x
  6.     "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */" T$ w2 D! ?0 Q1 ], @  @/ r
  7.     NOR_FLASH,                      /* 设备类型 */
    : r3 ^) h: y# p" }
  8.     0x90000000,                     /* Flash起始地址 */
    3 {0 M) X* T6 B& R4 I
  9.     32 * 1024 * 1024,               /* Flash大小,32MB */& g* Q0 U! J5 B" m& m
  10.     4*1024,                         /* 编程页大小 */
    7 p$ H+ X' f- x: {; d: R8 r& [
  11.     0xFF,                           /* 擦除后的数值 */( E( b$ t; E2 _  J2 Z
  12.     512 , 64 * 1024,                /* 块个数和块大小 */: Y7 L) `- U# F1 D* {4 U
  13.     0x00000000, 0x00000000,
    / t  x3 e7 i' E6 m0 J5 x
  14. };
复制代码

+ l: I* t1 k2 d注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:- j: M; W) B7 S" Y4 d

5 ~. [. l& c8 i1 k: S
bc495adf8d970e6e1fb6822b88b94e2f.png

# e0 m' F, g9 p7 z3 l' W3 I8 E+ O  q+ E" W$ G! q& T
81.5.6 第6步,编程文件Loader_Src.c的实现9 f) v0 k/ j, g2 i
下面将变成文件中实现的几个函数为大家做个说明:
' c6 G# `6 `2 w2 P
4 Y  s( T/ e4 n2 N* h  初始化函数Init
7 x3 R% ?, I. }* C0 T1 r
  1. /*. K. M7 h. T9 H( {. _
  2. *********************************************************************************************************
    + z% k( }! C3 T- q$ b" A
  3. *    函 数 名: Init) B0 B1 K: \& M5 w3 G. C) o' I( [
  4. *    功能说明: Flash编程初始化; m9 M  Q! ]  p8 e6 E: h# T
  5. *    形    参: 无8 M6 }2 c9 S8 q+ u  V
  6. *    返 回 值: 0 表示失败, 1表示成功! f9 J* }: E  L  F, J& }5 B
  7. *********************************************************************************************************
    % w* O/ s4 [% [
  8. */6 f  ?$ C* d) ?! e. a
  9. int Init(void)! u1 {' C; [$ }" L
  10. {   
    ( L8 e  m/ E# j+ s% y
  11.     int result = 1;
    / K# m( i$ k4 O. g* q4 \2 J7 W: \

  12. 2 T+ F# c  x; s: g+ ]' O
  13.     /* 系统初始化 */5 y1 q* k# [8 @3 a/ W5 {
  14.     SystemInit();
    & u5 y. c3 D& X6 L

  15. , O  ~' ]4 A( D8 T9 A% Q$ y
  16.     /* 时钟初始化 */6 \6 B2 m& Z- T
  17.     result = SystemClock_Config();. P0 i% o# p% y% q
  18.     if (result == 1)  y1 P0 D4 X( I
  19.     {) ]& A3 ^3 U9 Y; ~
  20.         return 0;        
    ' f9 B" f- Z9 ]7 [
  21.     }
    - [3 w# j% t9 E5 Z' s% ~
  22. ) r# D: X& W; ]( V' |- \- V1 M" i2 X
  23.     /* W25Q256初始化 */+ g( u% U$ x2 e  t( n# J1 Y
  24.     result = bsp_InitQSPI_W25Q256();
    ( i; l" W" t7 n1 @1 r! f$ e# Y+ f
  25.     if (result == 1)
    5 d" `6 G" D6 f/ f; |( I
  26.     {
    ; K4 e4 \/ m9 h
  27.         return 0;! Z3 t+ i4 G9 C, [
  28.     }
    ; r5 [; i7 M# G) l- B

  29. 3 E  U9 e% @4 A' t; v7 Z
  30.     /* 内存映射 */    2 j' C# l1 a  G; P: L. u+ E
  31.     result = QSPI_MemoryMapped();
      L  E( G& _3 w- i+ s6 W
  32.     if (result == 1)& t# Q* _8 `$ k# M: ^
  33.     {7 s+ W( a7 |9 H1 D( C" I5 y# }+ n! l
  34.         return 0;
    + t3 _$ w5 U" l4 R, u
  35.     }
    , P* j! y0 E' ]6 m- g
  36. ( V% a# f1 }/ [* d9 y. b; M% R) C8 j$ F
  37.     return 1;
    ) d& D$ r+ F& F! ?6 e* R! H# C- O
  38. }
复制代码

( `' n6 B" ]$ j) |. c初始化完毕后将其设置为内存映射模式。
# q# u8 V( Z/ n  v; H5 d
  D% f4 H) D3 e) J" p  整个芯片擦除函数MassErase
$ s* `5 \, @3 i. H( k0 J整个芯片的擦除实现如下:$ X3 s% x8 L* x& g

7 @  Y2 [6 p+ i
  1. /*" n$ }+ X# g( D2 A
  2. *********************************************************************************************************! r- Q3 b5 }6 c9 Z
  3. *    函 数 名: MassErase
    ! L/ ]0 |8 X" L9 N
  4. *    功能说明: 整个芯片擦除6 t* R( }* }  D. s: {
  5. *    形    参: 无
    , n* Z' p+ r; U' s: p: N
  6. *    返 回 值: 1 表示成功,0表示失败$ v' p1 A) y  `2 P; g% P" B, K4 j
  7. *********************************************************************************************************4 ]. v# g( |# q' m
  8. */
    * x, i  V/ @6 ]
  9. int MassErase(void)
    * E% M+ r' u4 ]: H9 X
  10. {
    " b1 @) X3 |: C. M2 w7 d3 h
  11.     int result = 1;
    + [5 d( U7 d$ G  a6 a- S7 m

  12. 8 I7 m6 ^. o& ?* F
  13.     /* W25Q256初始化 */
    $ Q, ?( v; y& u8 d* P3 N% Q% @
  14.     result = bsp_InitQSPI_W25Q256();
    8 ~. k  N" j8 f
  15.     if (result == 1)
    6 Q7 V9 L# B/ C7 h
  16.     {) g1 f: u6 `) ~
  17.         return 0;* A, A9 X' o4 D! Z* s
  18.     }
    7 s( U2 q& {: v; _3 G
  19. 3 p) Q9 o. i) h" W5 t" \
  20.     result = QSPI_EraseChip();
    : h2 ~2 e& l( u( y) [7 Y
  21.     if (result == 1)
    & M9 d) K; K& j' ^
  22.     {
    " |6 o+ z) _- k. \1 U0 h  D; k* f
  23.         return 0;
    * _, S  u6 X$ N2 U3 a! T
  24.     }   
    & f# Y0 e& L  ?8 ^: [* m
  25. 4 |; N. h9 K1 k* @
  26.     /* 内存映射 */    : e4 G2 ?3 g% q$ o& L1 ~- V
  27.     result = QSPI_MemoryMapped();
    3 H: w. T+ T: t, a/ W- a0 m
  28.     if (result == 1)
    : ~% t6 r7 N0 G( x) A) t
  29.     {7 e% ]: d3 k9 {+ s% i& k. i: m" G" f: l
  30.         return 0;
    - {& u9 V  D5 G) s
  31.     }
    . N/ D! p1 b# i4 d
  32. 8 s: f% m1 @$ V9 Q+ O
  33.     return result;          , ^0 V/ {/ y, c% ^* i# V4 `0 k! ^
  34. }
复制代码
$ Y1 d7 }$ J( m1 ^7 u  i/ b1 X0 }
  扇区擦除函数SectorErase' p+ [, _& A( q0 c7 N
扇区擦除实现:, N" c* z* x, x5 f) n

7 z9 e8 l; U' m2 g/ H
  1. /*
    1 K, {( _  c5 ~1 K
  2. *********************************************************************************************************
    5 g- U" [" e. c7 F& K$ }! l) Q
  3. *    函 数 名: SectorErase" c" C. R# k1 n8 l" S
  4. *    功能说明: EraseStartAddress 擦除起始地址, C- ]9 O2 [8 }; }/ R
  5. *             EraseEndAddress   擦除结束地址' t2 A1 h. H7 D
  6. *    形    参: adr 擦除地址
    3 |" ]. |1 A9 q5 A/ z7 h" [
  7. *    返 回 值: 1 表示成功,0表示失败0 x' Z; i7 p/ X5 I" ]' `
  8. *********************************************************************************************************
    % |5 w+ u3 F* |
  9. */
    $ N  u* N9 y5 o% V) z# V6 t2 W9 G
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    4 v  {8 a: Q* J
  11. {
    9 k* S# E) o! O+ j" ]( ]$ b& Y
  12.     uint32_t BlockAddr, result;
    ( \. e* b7 ^( U6 O6 g3 k7 u* A

  13.   [/ |& t; z: h7 u' R/ X
  14.     EraseStartAddress -= QSPI_FLASH_MEM_ADDR;4 K, T' a3 T" B4 z9 Y( b
  15.     EraseEndAddress -= QSPI_FLASH_MEM_ADDR;
    ; H1 |' c( T3 r! }4 T
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x10000; /* 64KB首地址 */
    $ |8 ?0 p/ Y* G) t9 m6 G% O

  17. ! x3 [1 ?& B* E6 D: D# J% D* s5 B
  18.     /* W25Q256初始化 */4 H6 n. w4 m) e1 D# C# v- g% w
  19.     result = bsp_InitQSPI_W25Q256();3 N  y, m7 \2 Q) g6 A4 ?
  20.     if (result == 1)4 r& ~, v. b' D, y- R$ d3 z
  21.     {
    5 n% J+ A2 M* Q" d( e3 k/ S5 O
  22.         return 0;        
    2 |! i: F. F/ I, `: a6 j/ E$ X
  23.     }
    5 o  z( i& Z& m$ ^0 n6 F1 Z9 _& N6 G
  24. ' N+ g' U, I/ ~
  25.     while (EraseEndAddress >= EraseStartAddress)! T: o! O) r$ T
  26.     {, f: {' t( |5 p8 G/ {/ C2 k
  27.         /* 防止超出256MB空间 */
    & y" s6 v" Z' x5 s
  28.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;% A, v& V- g) b; I* C) x

  29. . u& [% w+ C/ H7 d, u  j
  30.         result = QSPI_EraseSector(BlockAddr);   
    $ [. `- v: L, U" a4 y5 c+ Q
  31.         if (result == 1)
    6 x" @. F) K2 d7 V* v# Q- X
  32.         {
    ! t( I+ m2 i5 S3 Y" [
  33.             QSPI_MemoryMapped(); 5 x* B) P, D( K
  34.             return 0;        % }- {7 {! N3 Q+ o5 n5 N8 p6 }' O
  35.         }; U" b. E) U8 N, f/ V
  36. ; H& n/ ~7 _5 o4 Z
  37.         EraseStartAddress += 0x10000;( ], L1 R1 d2 i5 j+ G1 g
  38.     }
    9 O; I8 x0 y' _; h
  39. : v+ E! Z+ A" n5 _
  40.     /* 内存映射 */   
    : l$ L1 c' u0 @& C
  41.     result = QSPI_MemoryMapped();
    6 a2 a* u) F& a/ m, c3 z% u- c4 L5 m. X
  42.     if (result == 1)
    , V9 g7 |% J/ S1 w3 ^$ [9 k- q" ]
  43.     {
    * r# z/ R: Y# V, O, j
  44.         return 0;& X" m4 b* M) ]6 {! H% X
  45.     }7 K' L% L  p% ?: j0 O5 q- k0 f, N- K
  46. ) {# \2 ]1 g  Y4 _
  47.     return 1;
    6 g+ h9 |- V8 Y4 i7 u
  48. }
复制代码
% d% q* L- U; r  r( f- }, i' l
这里要注意两点:
! g) m' W1 v. H# Q% Z5 p
% o; Q/ G; B+ h) |(1)     程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。+ W1 e9 a; T3 O( F1 B1 a
/ n) j6 z7 ^( [' H
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。3 A9 Q' [$ t8 W+ S8 L

1 f* s; S8 R7 t6 S5 X" M  页编程函数Write
8 _  f7 j- A) g7 D: v页编程函数实现如下:
' m. l- h/ g: j8 l7 q; Q) Z  Q1 z6 t1 z( p0 r: j
  1. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)% N2 T( A9 f% D. [
  2. {
    0 g9 E5 @: J  J8 H' h' E* x
  3.     int result = 0;5 N( }- |$ q2 Q( O4 p
  4.     uint32_t end_addr, current_size, current_addr;9 j- V6 u2 A, N( R

  5. ! r2 W% @* k- S8 u* t6 r

  6. 4 y* d# J. e. N7 Q
  7.     if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)' |9 D& d7 O5 }
  8.     {# l9 G" M( W: B: }
  9.         return 0;
    " m9 c" M/ F, K
  10.     }' s9 j7 S: Z; l

  11. 6 t0 M" v: |0 k7 H. k; d
  12.     Address -= QSPI_FLASH_MEM_ADDR;4 Z/ m+ e0 o! S' q. d9 _

  13. : U6 H* L" u& [2 Z
  14.     /* 计算写入地址和页末的字节大小 */" x5 ^: c% S7 ^* _* B, E  E
  15.     current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);
    : ~: R  [& W! S% c
  16. : c1 e0 g6 {- r' ?' A
  17.     /* 检测页剩余空间是否够用 */; K/ H$ Q* ~. T$ d) {# Y5 I" _4 P
  18.     if (current_size > Size)8 C: V) R0 V: l/ R* A4 o# |
  19.     {( N  d- G9 t" l% p8 X
  20.         current_size = Size;* Y9 n$ U' ~% ?0 t, G
  21.     }: A' g: R* {' e9 E2 ^

  22. 1 R* V4 o: U- e  y
  23.     /* W25Q256初始化 */
    3 g% f/ X1 }7 z
  24.     result = bsp_InitQSPI_W25Q256();' c" L# i7 w9 p( U* s% F. L2 I
  25.     if (result == 1)
    / d' A" X2 C6 o' F. f/ j
  26.     {% m% c+ f% ~% f  r
  27.         return 0;6 O# z; ]8 B7 E* Y- C7 _0 p& u
  28.     }
    6 R( A+ D6 ~' O- K2 z3 \

  29. 9 B% v" P" m4 }8 z: x( F
  30.     current_addr = Address;
    * p( u% \& S9 @: k- p! C
  31.     end_addr = Address + Size;
    # L+ A% Y7 O2 \8 Z1 K; T

  32. % y, N% G. k3 r# K% V9 ]
  33.     do{- Y/ Q) m  c/ `& G9 ^* L/ o$ U
  34.         if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)0 ]2 M1 _9 X2 e' D1 I6 g4 L
  35.         {# k, z3 n( g- n1 `" y* W
  36.             QSPI_MemoryMapped();  % L7 c" Q& Q5 ~& i+ c3 @  J- O. @. s* X
  37.             return 0;  - C* e0 Z. a+ `7 r! ~* d! |, x; y
  38.         }" t; c+ C% a  B$ j$ F4 {% G

  39. ( p8 @9 B' o' ?; \  L; Q
  40.         /* 更新地址 */
    * q6 w4 n+ U; y% t
  41.         current_addr += current_size;; l3 L" z! Q; ?) d' m5 \& F
  42.         buffer += current_size;# k- O9 K7 I, c+ B0 K) J# \6 r5 M( j
  43.         current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;0 N$ y8 e5 F2 z8 m+ x* x9 U& |' c) H

  44. ' G; X9 ]5 B; o  W
  45.     }while(current_addr < end_addr);
    ; T7 u5 b# a1 B* u2 A
  46. % A! f3 i" s7 P7 U
  47.     /* 内存映射 */   
    ( w% E+ F2 i* ]7 J
  48.     result = QSPI_MemoryMapped();
    ! v6 a) c$ L6 L2 T* c7 g
  49.     if (result == 1)4 R: }* f- ^; N5 s% B
  50.     {
    ) E; c! t  f6 s# G7 ]
  51.         return 0;
    * H( W( M8 q9 N$ k* Q$ J
  52.     }1 x" h# ~8 ?6 B2 X+ c' N
  53. 5 |, G& Q  q; }5 `$ @
  54.     return (1);  
    ) E; ~  P" ^; }* R9 C. p
  55. }
复制代码

/ Y5 c: Y: \7 [! m5 ?这里注意两点:: {' Y+ Z! N' u+ a

* Z: @2 [6 D* L9 p(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。: K+ v0 ]; N6 l4 \# r3 Z
$ E0 C) c7 U, c/ p, b! G4 e+ d, h
(2)     程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。5 v! J7 D5 |: j6 n  `

8 d- w8 Y% X+ k3 V1 i0 y" q! s  读取和校验函数
; B! X5 r1 W0 a0 L+ ^我们程序中未做读取和校验函数。
: `4 G7 W1 Z( o! r5 l/ m
# F- S; ?1 q( ?. C/ ^$ r/ s* g; c(1)     如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
7 r9 O: H- h* g' w  K: H4 }% V% V' i! t# x* F% K
(2)     如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。: q' S+ A  D% X% n) d

  F+ k6 s. _& r- ^/ P8 a81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)4 \  L8 @8 b# r6 n& z2 \
最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):/ u. E4 H3 g1 s, H6 n

2 Q$ @+ I$ X: K/ Z, d6 Y2 r
  1. /* : l; V* R' h* j; A1 ~
  2.     STM32-V7开发板接线
    8 J9 h  V+ {. [  }

  3. 9 v5 y2 H0 H" s/ L( V* z+ z/ q
  4.     PG6/QUADSPI_BK1_NCS     AF10, f* o' R" ~; p7 V; C* _. \+ v
  5.     PF10/QUADSPI_CLK        AF98 U; K$ q7 R/ r8 e  G
  6.     PF8/QUADSPI_BK1_IO0     AF10
      C& F0 p5 `/ u" c6 p7 K+ |
  7.     PF9/QUADSPI_BK1_IO1     AF10% v  u* m* f. Z( A2 |/ S
  8.     PF7/QUADSPI_BK1_IO2     AF9
    : l! b& r1 z7 q3 d! a, \
  9.     PF6/QUADSPI_BK1_IO3     AF9
    3 L2 B2 `9 b. N- s) y* v+ g
  10. ; t; U; i( n6 P0 H
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
    ; {- t  b) _7 M) r! @# p
  12. ( O1 m! |2 E3 `: a! v( N
  13.     H7-TOOL开发板接线8 X0 _" l! _7 f+ U
  14. " U" {5 D# Y& N* k. J. J5 x
  15.     PG6/QUADSPI_BK1_NCS     AF106 i* Y( }1 F( |$ W
  16.     PB2/QUADSPI_CLK         AF9
    9 g; s" Q) J9 a4 }+ C3 ~
  17.     PD11/QUADSPI_BK1_IO0    AF10! M# T4 J% m+ |2 P4 |  W
  18.     PD12/QUADSPI_BK1_IO1    AF10) m$ c1 b' F; ~: l' c$ D$ J6 A, d
  19.     PF7/QUADSPI_BK1_IO2     AF9
    0 ^+ n0 Q8 |) B( [* j7 E4 b
  20.     PD13/QUADSPI_BK1_IO3    AF9' `- g3 N+ O4 c, R- O$ j4 A
  21. */1 r, }/ V: Y3 w9 Z3 O& G

  22. * B4 e- x' `3 f$ c9 E
  23. /* QSPI引脚和时钟相关配置宏定义 */7 ?1 X, {: N- m
  24. #if 01 M" I. V( t) Q
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()! k& W1 B6 {% X) {, l
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
      j# l. m: v2 v5 c. }* ^
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    : D' u: c# n$ S+ q
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
    / a( Y3 R) N: R6 `
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    $ p8 m; o: s3 Z( d8 u5 j0 _
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    ) D& q7 a2 f4 N; Q6 h, W5 {. Z1 R
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    & C8 X. P: \6 I# o2 Y' h
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE(): s" s/ C, l/ @6 E' r
  33. 7 ^2 c  Q4 ~+ B4 g' c/ ^
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    0 G8 T* u  ]' v2 B: c
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    8 w( Z2 D6 k$ x" \
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()  T' E3 h* _! y" E$ U1 {2 |0 j

  37. , Z8 S. f, k/ x" }8 D$ J  O
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    7 Z' b; K# g5 r. b4 @6 d& ^
  39. #define QSPI_CS_GPIO_PORT               GPIOG5 i+ w* v* }) l/ B5 J9 Z8 B, z3 |( R
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    1 {8 \# f4 y( I
  41. $ r  Y3 |2 [; S$ ?9 H
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2! i* M. G# Y4 }6 L' \
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    + ]& |* @  [2 g6 o( n
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    7 g% o9 Q: q! T! N) ]2 z. `9 i2 p5 {$ M

  45. 7 `+ J4 @# H8 L7 `1 G* V
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
      B6 ]. N0 V5 W* Q6 x4 u- {
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    0 j# F* ~9 b# x. q' r
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI
    9 C1 ^4 v7 x2 V: ?+ c

  49. 2 W. g6 w6 j5 H/ s, g1 a
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12
      u! ]/ ~' Y9 A1 ]
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD$ ?& @# z/ s; x3 T. n9 }4 {3 G
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI
    5 K6 S& `/ }! N! X6 |+ }

  53. 9 e, I* Z  b& F& F5 U8 s6 f
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    * _% n* ?: {% M3 ~* j# W, Q$ V
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF8 `3 L3 I; A6 C" W/ z' V
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    ' q3 p# [# ?& t  q4 x/ Z+ r

  57. 6 D5 B, {- P  a& q; X
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13
    8 ]+ N& I0 e# y5 R  f# P  U( B
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD
    0 {, @$ p+ i" I+ a
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    , F5 C! Z, w  I0 U8 K
  61. #else0 C1 X- S5 H% E( ^$ c
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()  _% D' w/ K, |  Y
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    ! d+ T3 Q+ n1 d( v& O
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    - N/ o/ u1 g1 T
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    + d" d4 H) f6 B0 H7 w6 i8 B
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    4 W& v1 d8 _( P* H7 }
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()5 t6 Q8 Q3 `' ^% {* @* Y) @
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()2 H. Q4 S! p8 J- l
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    ) K9 M8 U( d( Y; e1 T

  70. 4 Q; \: N0 g+ |/ n7 p
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    5 ]! n+ |4 h0 S
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    2 S  [: p! ^* x
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    1 v/ S2 o* c  e+ b
  74. ) j. V9 T7 l6 n1 f: J
  75. #define QSPI_CS_PIN                     GPIO_PIN_6/ Y1 u% I2 a7 A  j
  76. #define QSPI_CS_GPIO_PORT               GPIOG
    4 o7 L( J. v/ V+ X; T, K
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI0 |; K0 ~; Y1 A$ ^. N
  78. # ?7 Q: Q9 R% v* ]7 J$ t
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    0 K! h9 a7 ]; w! `3 l% E# j1 G
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
    + h0 i9 E  w3 n* z' @8 n( a, e9 z
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    8 m2 w; }3 M) M$ `$ ]/ c
  82. 9 X: ?9 U" e* F: q( u+ S7 u
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8# ?9 M# S" \& U6 p5 y% }( _6 F
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF( G! [1 Z. d2 p1 M5 p% J
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI% Y& @6 \! X: F, x5 O' P. }  n
  86. - K: V1 k) N. ]( `  Y4 h
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    * D/ C# E9 i: Z
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF" K2 D/ }' R! x% U! S* s# F3 b+ D
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI
    2 F# I, Y' x; a; y+ i% y+ I5 C' D( e
  90. 9 t$ _0 ^* j* y/ @6 {" ~* ~. h# ~& c
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    $ v0 Q3 g& U8 Q; c& ]# ^: K
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF* n' N) y4 f8 N8 ]' N" k! L
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    3 p* n- t4 }% j! l7 V

  94. 4 s* g/ f% J) M! T
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6+ |& k7 @$ [; m1 G
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF
    % t5 P! h' A/ R2 j+ {* x9 k
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI( m) T, V4 F( @1 \& I
  98. #endif
复制代码
. h1 x2 n0 J- v  S7 o: \4 D
硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:# V- i' f: i  [
, V3 a  x' x) _2 q4 ^" K1 Q2 T
主要是下面这几个:
+ j4 F1 i% V: x; [, C
6 B. H1 S( G  W+ o- ?
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000$ ~6 e  S$ K+ o

  2. 5 q4 v) E; X) K/ E: {. F
  3. /* W25Q256JV基本信息 */7 v( F- i0 O) @4 F: h0 y* b
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/' x5 B1 Z% D; l% U) H5 j+ i
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */3 w& F8 G  o$ N
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */
    ( u2 W; W( O  [4 G/ c
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    # c9 t) `! q. F3 F, c7 L
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    ; D! I% ?. v/ p" A5 J1 n2 Y

  9.   f( j1 }) A. a3 w
  10. /* W25Q256JV相关命令 */
    " V3 j6 M8 V2 L" _
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */  r  I' l# }- A+ H/ \0 L  Q$ F: y0 G
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */3 j8 ~1 a2 C& I% C# J' c
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */3 P. N4 _, c2 R: P3 {+ t
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    8 n* D  ?1 @+ i" h8 s# ^" y( m
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */5 x: g6 i# w4 |! k5 V7 F3 ]$ `
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */
    $ `2 p, ?( t+ U: n" M! C9 B) T7 T

  17.   O" e  {. [, P% \
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    : q$ `; M* n7 E) S5 F) @+ \* c) h

  19. / z, p* Z# U! w' z: i* {: U
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码
$ G# c' x0 s2 J0 P4 [, c: E# |0 b
81.6 QSPI Flash的STM32CubeProg下载算法使用方法
- l$ X5 r+ G" K9 ?" Y编译本章教程配套的例子,生成的算法文件位于此路径下:: `) H% ]" x- [# x9 t

* l1 o" @. _5 N6 U; F/ v: V7 Z) K
d45f9274d8762b095790b3e6778b0a93.png
( A& Y6 Z( L5 z  l. f  V

  R+ w) z1 J/ H! ]81.6.1 下载算法存放位置# [9 P! [6 q6 d
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:, D3 x9 ^- Z$ R+ j0 q/ \; ~* u& V
$ S' A+ ]+ p9 e6 G) ]( c
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader: K$ Q# A" K% o5 \0 D0 K# f

7 W. ^4 D) ?5 \
60cce5a15415e27e9d5b75b05028d1c0.png
' ^8 w( |$ m  S3 W' m4 L0 K4 U

4 _6 @, i- V) O: t) B81.6.2 STM32CubeProg下载配置1 U, D2 j' W* C5 l- B" ?( O
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
8 X8 v+ i) y/ x' I* \! o+ y; |$ C9 E( @" h9 L8 |# K7 b6 h9 W
9cc179641e765ea43cb38d268bf734df.png

" ^! g& k; o* V
* R9 n- ^2 \4 Q) b9 T点击Connect后效果如下:, i  z+ f) |* M- C* o7 ?. f
. e/ z! Q) i2 ]8 t
b39ce8a738163430db2119bdd87e3fbb.png

/ y5 m! V$ m' M% Y5 [, h7 K/ {/ G  B0 W0 u7 T
在这里选择我们制作的下载算法:( k' n* D1 u! s9 ^
5 {) W6 L* M8 x- O, k- z
17f6e0f50a18ae818faaa1399daef12f.png
6 H- X. h) Z+ }2 Q, w% R

5 `, u% ~8 Q# K' j' l$ ?" K任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
2 x1 f9 v/ L" u, I3 ~5 B
" m5 J: E; n* a
43a0d90faf1d392f5a74eaa7777e4178.png

5 O# D, F, ~$ a9 V. ?6 h- z0 Z0 U' e' Q3 S* _. F' a$ O! m
下载完成后的效果如下:4 F- s1 B2 D* o4 Q5 \
+ ~& O& T3 o2 I" x( b' u
a1d6662baa9e7be00f7e67e02b5c9b79.png
( K6 P2 ^# I! n$ R, n& H
( G9 p4 G  n. A6 Z5 V2 B5 M
81.6.3 验证算法文件是否可以正常使用
. |, v! ?+ |9 `. U# ?* \  w为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:
% w& C" g" W  x" U7 U8 V6 F7 g) j$ H  C8 w
3f2eefeef3db68623cb8969974c40a20.png

; R' F/ T1 G. \  F) C6 {& N( [: V7 U3 |
81.7 实验例程说明
0 l* K0 w2 @( `8 S5 A1 y* {4 Q  T本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。. b) N+ ?) p6 ~& G

$ u- h8 J9 E8 B2 d# J编译本章教程配套的例子,生成的算法文件位于此路径下:+ \* O- a% x4 i% y8 W# M
1 [/ h# v5 Z+ n3 P: s8 s
b7028271136c8053d7300063ed4aec3a.png

$ N6 p- g1 b" `& M! t- h. J0 }# r# P( j
81.8 总结4 H3 L4 e  m0 p# Z
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
* T5 n4 m! \0 F% L; B. H. z/ e& k- v; h+ b0 q3 Q1 v& o- G- N6 u2 \
1 \: |3 k+ _# Y: i

4 U1 V. b% ?5 N% O6 q4 w2 c5 Z
收藏 评论0 发布时间:2021-11-4 23:27

举报

0个回答

所属标签

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