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

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

[复制链接]
STMCU小助手 发布时间:2021-11-4 23:27
81.1 初学者重要提示5 I5 L7 Y: K( {
  QSPI Flash的相关知识点可以看第78章和79章。6 {! u. J3 D* Y
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
. F1 v/ ]8 ]3 j2 l  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
! m7 y% C* ~1 Z/ Y; Q2 q, `  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。5 H. B  A5 J# l
81.2 STM32CubeProg简介8 N# ]; F. H8 [* s9 D* _$ q8 y& e
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。! i7 Z9 H- {5 `) T0 M
1 v# H: c5 z$ e" i( ^# n2 v
1bbabf500126f58ff1439b6bc36014be.png
# H! @2 I, T( F( w: T2 C- [% R6 M
) l  C: R. c) _* j" [" B
81.3 STM32CubeProg下载算法基础知识
% B$ y( ^9 t9 }2 P! k( F/ aSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
6 {! t( i1 d0 p4 d6 X9 ^3 s1 j) F9 d# a: W
81.3.1 程序能够通过下载算法下载到芯片的核心思想  T& m9 C/ ]) y3 d8 ]
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。
. N2 C" [; H4 O/ a* B6 w; g* F1 b3 R
3 Z5 Z5 g% E/ M$ t: d' f7 [81.3.2 算法程序中擦除操作执行流程- u; N9 Z! J9 M& a  d
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。' p+ {. H$ c9 O6 l: t

* t" d; H& r. h7 z擦除操作大致流程:
1 W/ g5 ^- q% E% K/ y+ A- h. c: \/ V$ ?
161f92ebd9b1dbf9febba815fa091b8b.png

! r: b5 ^  e& w, Y
; I, a- x- E6 @5 X  |5 S4 s# Z  加载算法到芯片RAM。
: U$ D& u) t" e2 l9 m: c& a- }  执行初始化函数Init。
7 x2 O0 Y$ c% R2 l9 n4 F/ y  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。) A& S* }7 x6 H6 A
  执行Uinit函数。! b. F: p% K# W' V
  操作完毕。/ M- h$ Y# q; o1 |" L5 o5 f
81.3.3 算法程序中编程操作执行流程
: [' {' x' M7 ^% V! c/ m注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。$ \5 a9 _% L7 B* G: k4 }4 z
5 `2 R! y$ C- `, f6 t$ g$ U
编程操作大致流程:% s4 L7 D) u  S

+ J% b, H8 @; G
d80dfd755411aa0d0565f1c56f10e66a.png
5 M- U  d( }$ @; W1 Q

. @0 |/ I) e; @4 J- r+ Y3 Q2 ]2 I  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。7 X) X! ]1 q, x; I" G
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:. Z' A7 Y: }' d9 \: ]1 Q: H1 {2 @% W
  加载算法到RAM。
5 O; ?: ?9 F, u& N9 ]9 p  执行Init函数。
( b4 n: U, V$ x  加载用户到RAM缓冲。
" z0 q0 W* |' X+ p, J4 G9 C4 K5 z3 {& f  执行Program Page页编程函数。2 Z3 e* k3 i+ G7 B
  执行Uninit函数。. ?+ t2 i4 V7 E* U) j
  操作完毕。
7 z/ K6 _8 x: w5 B& W! @( j81.3.4 算法程序中校验操作执行流程
" r: I# |/ k9 U$ M" A3 S+ F注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
& q* P+ F  t1 [- Q8 L) x( E, i7 ?4 u8 W% f
校验操作大致流程:
, N4 h5 r  s' p" o9 z1 _$ \& Q: K. @# ?. @
f15191090ab169d48a203ea3617af9b4.png

- X  s; Z% y1 G" s( @+ [* e
# q( x9 E2 z/ w  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。( [6 B  M0 `6 k& s# `
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:8 a) _1 Y7 f# R* n" f1 m* C3 C  j; }
  加载算法到RAM。
  Y$ s5 K7 \4 x( j* o  执行Init函数。: `0 @7 L' p; ?9 H
  查看校验算法是否存在
( p$ W( \. T* S( N) Q  如果有,加载应用程序到RAM并执行校验。
* L/ @- K/ q' M1 |$ F+ J  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
& `0 v! t# @5 Y! i# t, k" D  执行Uninit函数。- }9 M" v+ H5 ]. {
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
% ]1 I7 a% l+ i4 b6 L; k  执行RecoverySupportStop,回复支持停止。' H: v" G; P0 R
  执行DebugCoreStop,调试内核停止。
8 ]' {& z5 M: F6 x  运行应用:
4 s! r- ]7 }! X+ j  执行失败
2 q! }8 f+ P5 i0 m8 n- y  执行成功,再执行硬件复位。
2 A- O1 H0 S% o" C9 F  操作完毕,停止调试端口。3 h( I# t& k+ P
81.4 创建STM32CubeProg下载算法通用流程
  L4 b3 Y' [4 i( N下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
3 `; z: G! j6 s6 K
  g, P( J" n  c1 U; ^& f4 T81.4.1 第1步,使用STM32CubeProg提供好的程序模板' {2 a5 B" S! y/ N3 c+ \
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader  l! n. P) n( v& H9 Y% A
; g( I# q5 k# E6 }% k
4fbfb0047e29fe23083224607059d97f.png
6 _- n  M6 {$ r6 J# b, }9 q
* S" a' m: ~8 q
以M25P64为例(注,其余步骤就以这个为例子进行说明):. u* {/ ]& i6 A; L9 O; W
! z+ h- B, e# Q0 R# k+ t
d1b1a340fdc655c1030330a7dad2abba.png

. t: [/ ?. a5 f+ {5 ^2 [* P6 J9 ?! g  a% ?
81.4.2 第2步,修改工程名! I* r6 y/ Q* B1 f" }! r
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
7 A- f) b' y2 a% I+ I! S
  Y! I/ U" g; \7 f* H- w+ u81.4.3 第3步,修改使用的器件
7 D5 `- I6 w8 s在MDK的Option选项里面设置使用的器件。
9 Z7 R5 X$ _; d- ?5 Y+ `( Y( b* ?$ x/ Y
092174ad976a8ce9c7fca533049e2dd0.png
% U8 X" b5 W/ y% E

! P# c* o2 a* \7 L3 h2 e# J& I9 O/ [81.4.4 第4步,修改输出算法文件的名字
1 N" ?3 }  J/ U4 }这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。% F% w* h. C( F
3 M% F$ D5 o3 M$ D0 Y0 V  f
79c42251311dd3892fc7d6b8ddde68b8.png
2 Q. P4 E5 h7 \" ]

0 z& s, C! j! g4 X/ m' m2 L注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
6 }3 E/ T% H# g% G8 M* ?
e2fe61fa7ea99b25b178341b7350558f.png
5 d5 l1 @3 z- a  c9 B" Q, z$ G
( a$ Q7 n7 ~  a% R0 j
81.4.5 第5步,修改使用的库文件和头文件路径& _. R/ M( x8 v8 j0 l0 O& }, {
根据大家使用的主控芯片,添加相应的库文件和头文件路径。5 Z: s2 G$ V  h+ w7 P+ H# b) Q
8 c! G6 }; M( b9 T; D+ X
81.4.6 第5步,修改编程算法文件Loader_Src.c/ h1 `/ c5 c0 F
模板工程里面提供的是M25P64,部分代码如下:$ {9 `8 W' B- y" q. c" T3 n8 O

7 `% T& J9 h4 k/ P; V# c
  1. /**# ?  ^- ~1 S: W0 m' @. P
  2.   * Description :9 j9 G7 i" I# q( }
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the* K6 V0 H8 X* \; g$ A( `
  4.   * device and initilize the FSMC with the chosen configuration - W. d( \% f9 d# C% i  h
  5.   * Inputs    :+ A1 U4 @1 I0 f: L' ?! C
  6.   *      None
    9 _  i5 T+ w, L$ u
  7.   * outputs   :
    2 h, v' S3 P1 z& D( H
  8.   *      R0             : "1"             : Operation succeeded+ J' a0 D0 l5 f5 z
  9.   *               "0"             : Operation failure
    % u$ _1 w- }2 t9 V5 }6 S6 M  A
  10.   * Note: Mandatory for all types of device : B# y+ e, y# }. }+ P
  11.   */
      I" r. J  n* y' K
  12. int Init (void)8 f! M+ _: W+ }. q1 f
  13. {  # z. ?7 z3 ]: o" Z0 G
  14.   SystemInit();6 J9 L  `5 q% T& v7 R
  15.   sFLASH_Init();) i3 d, ?) |2 i
  16.   return 1;
    * {0 g$ A0 R3 Y8 H3 p) B
  17. }
    / ^- o* ^$ T6 R: i& Z2 [' Z
  18. 9 T9 Y8 A7 t6 I- E; w4 P8 H
  19. ! p% b7 N9 {4 y
  20. /*** D% R1 G% R. v7 k4 M* E9 D
  21.   * Description :: U, G$ y2 o) ~) j& a& Y  p4 ^, D
  22.   * Read data from the device
    $ u6 n4 ]" K9 M
  23.   * Inputs    :
    + b1 E# ]& Y$ Y9 ?) e
  24.   *      Address       : Write location
    . o1 T  z1 a7 ~
  25.   *      Size          : Length in bytes  8 ]6 y4 I% }6 a; I6 l3 q
  26.   *      buffer        : Address where to get the data to write
    3 x& _! b- a; }' t
  27.   * outputs   :- W- ~* _+ S( D! @! u" f$ ]
  28.   *      R0             : "1"             : Operation succeeded6 o0 C8 [7 K- a7 J7 y3 O
  29.   *               "0"             : Operation failure
      K* k6 O4 u& ^4 u2 @
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    8 p& L7 Z. p$ X6 `9 X+ q2 k
  31.   *// d4 T5 N1 \9 ?' |8 ^0 v: Y3 F
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)8 s/ w4 Z" f/ S' H
  33. {
    # r: H$ @# @) @% W
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    4 h9 S0 r" Z  R. U  q
  35.   return 1;$ \7 S/ U% D9 x+ p* }9 M8 e0 ~
  36. } & R5 ?! s7 f" T: ]- j9 h
  37. 3 V7 T- z% |3 F3 V6 ~) D

  38. # o  `. X+ U5 A8 @
  39. /**( ~- l2 \" _4 N& s& ?4 C
  40.   * Description :1 r$ B! I1 K$ t1 e* l; Q: K) u
  41.   * Write data from the device . z0 G+ G4 J4 j- ^% J5 U
  42.   * Inputs    :
    2 G- H- d% A0 h0 B
  43.   *      Address       : Write location
    : ^+ s& ]3 w! [0 t' b, s& `
  44.   *      Size          : Length in bytes  
    4 b2 i. g: u# k( _3 [0 _7 O
  45.   *      buffer        : Address where to get the data to write0 s3 q0 C6 u. F% _5 `
  46.   * outputs   :
    9 x# V/ ~) l  v8 d+ n
  47.   *      R0           : "1"             : Operation succeeded( g, B( q2 o) |6 T  Q; o- H
  48.   *                     "0"             : Operation failure
    3 S( ~, |  r6 C% ?/ W
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    0 k- N, b: P. N
  50.   */
    # \4 _- u- v5 H' n
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    - G& G9 p( r$ s# m3 y
  52. {; U8 A. v3 ]  G% _- q. U: i
  53.   sFLASH_WriteBuffer(buffer, Address, Size);2 f* Y, _3 U! V' H
  54.   return 1;, t9 @+ h9 O3 t  ]5 ?
  55. }
    7 }+ I" j/ S# `/ S& o8 |) ?. E

  56. 6 D7 k" f" _! `0 I$ X2 \& w

  57. 5 U( B# z9 }; R2 P$ z1 V7 d: `
  58. /**' `' u! D! ~/ V1 ~3 n# f0 E& K% N
  59.   * Description :' B5 P" r7 o1 q" S! [! p; X3 y3 v8 W! G
  60.   * Erase a full sector in the device/ P. ^1 Z  ?- f5 r
  61.   * Inputs    :- u0 k( o% X2 J$ N- h% z9 ?
  62.   *     None
    - C8 g+ O1 Y4 ^$ G' W5 S) d
  63.   * outputs   :- [0 s  {# |7 M+ {5 J! D
  64.   *     R0             : "1" : Operation succeeded2 E* A# d- c( z# t, }' f
  65.   *              "0" : Operation failure2 T% H  B& }9 s6 T
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH( x7 E% {. T1 G/ H5 e9 K! X% d
  67.   */7 Z1 r8 f. h1 o; v9 S) E
  68. int MassErase (void)
    * s5 s/ [7 f$ Q. k# k
  69. {  ; G) I$ G$ J8 L9 p" L( D) b
  70.   sFLASH_EraseBulk();
    $ f0 U+ g0 P5 `; E4 b. \- q
  71.   return 1;   
    & ^2 M5 A$ A% Q3 o( G2 H2 V
  72. }
复制代码

; y1 E, E5 f* N81.4.7 第6步,修改配置文件Dev_Inf.c5 o* O3 b  v+ ]
模板工程里面提供简单的配置说明:% ~7 B2 C+ k) H9 d
' b9 i; r5 ?5 J0 C0 f* S* m
  1. #if defined (__ICCARM__)
    ' |8 J$ |, }; U& w
  2. __root struct StorageInfo const StorageInfo  =  {7 m2 y( m% A; |& r& ~. N
  3. #else% j4 ^7 s5 r: e" t4 O8 q3 g( B5 ~
  4. struct StorageInfo const StorageInfo  =  {
    * X9 u4 X: |7 B% N
  5. #endif1 j, Y- R( a4 y) Y8 z  E. w
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number) k+ b  f6 C% W, n; k$ j
  7.    SPI_FLASH,                       // Device Type
    3 v. N( V" i% m& Y9 F
  8.    0x00000000,                     // Device Start Address
    6 Q; C) k7 s) f9 Q' ]
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    9 ?  b* K: ?3 O, b5 d" k
  10.    0x00000100,                      // Programming Page Size 16Bytes# o) e/ w& u# j& f  F6 o
  11.    0xFF,                            // Initial Content of Erased Memory
    ! x* `, A+ f, p) [# y8 r
  12. // Specify Size and Address of Sectors (view example below)* t9 \; R; k" S5 J7 k5 @1 G1 c7 r
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes ' l  j3 q& o& z1 ]& V
  14.    0x00000000, 0x00000000,+ }3 C5 f0 `' A' _4 U0 w
  15. };
复制代码
4 }$ ^+ H/ i, Z! r3 g5 V
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
6 `: T# j& E4 U$ H1 ?, u
$ f# j% g! I+ U/ T5 D2 I, x, E% P# M+ y6 n' t3 R

0 y8 Q7 w; G, O, ]81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
' b8 W. i7 _2 F$ qC和汇编的配置都勾选上:% {. d, _0 n% s- A. R- C

% N# D# m# w" |6 B% ?9 C7 I
8f2ba38db4c1f685f131e7f1f4e98551.png
" h9 ^9 O0 j) J2 ?/ L
* q# Q) \9 R7 S4 `
汇编:
' ]; e! F+ X2 P6 u' Y; v9 v! o/ g1 ~' n, k' L; V# U
0d264153b167afa2303144bdd378d501.png
2 ^# {. o8 u/ C, D) |" G; X+ _

6 Y  R6 Q( a5 |; H5 F如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
6 x0 D" B# d2 Z2 E9 b0 L* i
4 r. w) C5 ?2 L' C. g(1)加载以响应运行事件。- H& z" X: d3 b8 X, T5 B) {' q

* d; z5 L! O: c6 o! V9 w(2)在不同情况下使用其他例程的不同组合加载到内存中。! r/ K0 I& J% U1 W5 j8 C
3 I& o4 B: m% H/ M* p$ t" E
(3)在执行期间映射到不同的地址。
( Y; Z' r) ^8 Y1 M2 g7 \. x% l( f
! [/ K  L! b4 O+ s6 u& ], G8 l使用Read-Write position independence同理,表示的可读可写数据段。
. e0 a9 R$ I4 m; @/ S0 r7 W3 p" p
81.4.9 第8步,将程序可执行文件axf修改为stldr格式: }$ E, p+ l8 k7 K  C7 p
通过下面的命令就可以将生成的axf可执行文件修改为stldr。: d1 [8 q0 {4 ?0 ]' c

: Q# [! [3 ]% l" \8 S5 s- X4 ^
f50b1594f3741e77b4fbd5523564d3af.png
/ [; n- P- H  q( o4 d9 W

( L  Z. [% q: I+ G# }, W. i2 e, K1 O81.4.10   第9步,分散加载设置
$ t$ w3 s9 K  x) T" F" s3 ]: _我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
0 ]) @' t# K. K
8 }, H4 D5 M$ v, Y$ K5 k: k
8a01cb7c9be38519d4d0510a218447e2.png
  O5 J3 X- P& t1 d; |  T

8 L. n9 `6 b4 S, K; \& ^% H2 I6 R分散加载文件中的内容如下:
! Q3 a$ b9 B1 _- W9 }9 N
2 ]1 |8 R0 Y# `7 R3 f! g( Y. E2 g
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    1 c! j7 \5 o9 h/ z/ c( Y
  2. {' g4 s+ Q5 C. o- F1 Z+ B
  3.   PrgCode +0           ; Code  @/ Z* P5 Y. Q) S0 ]. q
  4.   {
    ) I- F2 @% K0 @$ c
  5.     * (+RO)  ~0 k5 b5 E* \& _' @1 S
  6.   }* M3 }8 y+ A# H; W+ H) [( m& u4 V2 n8 e
  7.   PrgData +0           ; Data
    $ ]9 V# V8 @5 T1 ?3 _' r
  8.   {
    : x" p- U  L+ E& C
  9.     * (+RW,+ZI)
    * |3 O4 G1 p- N8 H
  10.   }% T& I  K/ R* U; D6 o
  11. }
    6 B* N8 `* a( R  u

  12. ; Q0 J4 o2 v/ W% r2 |7 P* p+ Q+ a
  13. DEVICE_INFO +0               ; Device Info
    + S$ h0 h2 D! Z5 b
  14. {
    - b$ i$ }7 s8 ~/ e
  15.   DevInfo +0           ; Info structure9 G: k8 F6 o$ z
  16.   {. E$ g: h8 F1 T6 g- u- g
  17.     dev_inf.o
    ) N* b/ e. w9 _- P
  18.   }5 ?$ S" U1 a. S
  19. }
复制代码
2 r5 K- g, A' s6 q7 c
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
" |  w7 g4 p4 Z# q/ T$ _
) ^& U2 c: C- H+ m8 P( E- W--diag_suppress L6305用于屏蔽L6503类型警告信息。
& |4 i, v7 v4 D4 u- B# p2 Y1 `! C
. D- q$ F" l% N* v特别注意,设置了分散加载后,此处的配置就不再起作用了:9 Z' |$ y5 Z" p8 X6 v# A: \6 i

: W/ I) i( |8 ^, ]4 w) s: x
13b022190bd5115ed249129fc3886a38.png
0 G  K( l0 l6 ]* F; }" I! X
% A! t0 _8 B( e7 R) }: y& n
81.5 QSPI Flash的STM32CubeProg下载算法制作
: l% ^: A) O! ~9 l; Q" E: }2 j5 f; {下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。. I. M( d$ i7 C. N
2 R2 J  L1 L! E4 N4 c3 Y& m8 e
81.5.1 第1步,制作前重要提示
# S9 c6 H9 f  U( D, Q! f" s% K这两点非常重要:
5 N8 k) i# x, ?( A3 }
* B) a3 C" n# V0 e1 X  程序里面不要开启任何中断,全部查询方式。
3 Y6 k, v, O: e$ b  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
2 K- M6 z. P' e' \, K' z81.5.2 第2步,准备一个工程模板
1 B8 d( O& T1 V4 J  v4 P; {/ a 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。5 y/ ?" [0 L3 C9 w: M* K

+ C$ e  q6 N: i( l; O% }2 W
f46a8d2276d0ffb7beee251cdb0809a4.png
# \1 v! s& A0 ]2 E) M+ x& J

. S" I9 n+ a" ~3 W; J4 u/ M& O1 E7 a81.5.3 第3步,修改HAL库7 v* [% Y' ?  t
这一步比较重要,主要修改了以下三个文件:* B( [8 o/ j; i( B6 Q& M
  t/ h! c& z9 u9 ~3 \- ^3 Z% k, t
1efb78256b9465df406e3e9e95ae30e5.png
4 W7 Y5 C3 B7 V( `
2 _8 L5 p$ R% N' c: H7 p' D7 w0 O
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。* Y9 x8 D8 S' ~( D

& F2 h$ x$ r5 U. C. o* W5 C  i81.5.4 第4步,时钟初始化
3 E4 z- B# ]5 ]/ y  m4 U% f9 n我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
4 x: L$ Y9 @% n9 x( \- [! T  S& p" a9 k& I" T
  1. /*
    : |, G8 a- u* `3 ?. e; X
  2. *********************************************************************************************************
    ; Z( C& w; f; X* O- @- n3 m- u
  3. *    函 数 名: HAL_InitTick
    2 t  {1 ]' l! w* ]" i: Y
  4. *    功能说明: 重定向,不使用9 z/ J5 j4 ]' X4 n: q
  5. *    形    参: TickPriority
    5 g2 r# n! X! P. o
  6. *    返 回 值: 无
    ' l* x5 ^9 A( h& _$ m# w
  7. *********************************************************************************************************) @5 Y' {; X( D& j
  8. */
    ( l/ U  V4 n  \1 k( _
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority): F: l# w: D4 g3 x+ }3 P: n
  10. {
    , D- |6 a( Q/ q" P: l" {8 m5 O( o
  11.     return HAL_OK;
      C, N  w1 m. [+ i# E
  12. }
复制代码

) q* z: J- |5 }! C" [; z/ v然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:& o' ]/ {9 x1 M6 w
  Y8 [2 _3 h- o; r
  1. #if !defined  (HSE_VALUE)
    # s2 u. e8 U( U/ k/ a/ F
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    " f# f, K: R2 e* @
  3. #endif /* HSE_VALUE */
复制代码

5 Y3 P: J0 c8 o5 N: u最后修改PLL:
6 L$ d- Z0 |: c0 |9 p8 ^; n) M. q1 e" }
  1. /*
    + `( V$ P8 x3 N8 {, Z
  2. *********************************************************************************************************0 Z6 Z' T: ]5 B1 J
  3. *    函 数 名: SystemClock_Config
    , Q7 |1 s3 y5 h! `+ f
  4. *    功能说明: 初始化系统时钟
    * l# Y5 v" _5 ~6 n6 `0 X
  5. *                System Clock source            = PLL (HSE), z: d( Z6 z+ g( J0 Z+ ~4 J
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)+ L8 ?% n/ D4 }. ?! A' _5 y
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    2 P( G! P/ y, h* M
  8. *                AHB Prescaler                  = 2
    : ]* o5 P- ]1 I; b9 C& _
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)5 C# h8 Y4 w& N7 c2 D: u( `; Z; j! o
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    + h$ ?& ~  b$ \
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)) k  c* y- U# w5 N5 [
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    # w  X' n& Z1 R/ q& n% y
  13. *                HSE Frequency(Hz)              = 25000000( M1 J5 ?0 n# Q" i6 o: r7 _/ \
  14. *               PLL_M                          = 5$ G, v- q& _* R1 K* v
  15. *                PLL_N                          = 1604 g. I, V+ ?) J/ E7 f/ O/ ]9 z
  16. *                PLL_P                          = 24 f) [0 N- [% ~8 ^1 B! M, C
  17. *                PLL_Q                          = 4
    0 s; A- k  i) S/ ^& e/ ~, g
  18. *                PLL_R                          = 2
    5 I& K7 `9 E' j* `# K& c$ L
  19. *                VDD(V)                         = 3.3
    . m: l3 ]/ ^8 g0 i8 p1 @% ?
  20. *                Flash Latency(WS)              = 4: E$ L9 B/ b3 t3 b) ]. w7 i
  21. *    形    参: 无
    3 p3 ~! d* x/ }, m6 r+ G7 b
  22. *    返 回 值: 1 表示失败,0 表示成功7 ]6 `4 X  o- ?* a9 n
  23. *********************************************************************************************************
    ! w* [# L7 D$ z) q: e1 P+ n
  24. */
    $ ]8 d8 r7 @- `2 l( _0 s
  25. int SystemClock_Config(void)
    ; m1 G: i7 x& K) C3 l, N  Q1 {
  26. {, V3 ]+ U$ O+ t5 Z
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    8 Z0 |! J9 S- p1 ~
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    6 Q; Y% j3 U1 R* p' k: m7 m
  29.     HAL_StatusTypeDef ret = HAL_OK;
    9 r  _5 o0 R& V2 x- T' F

  30. : V5 |% n* J' ]; c3 i* X
  31.     /* 锁住SCU(Supply configuration update) */' M6 p6 g7 B3 Q' K. U
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);# }$ C% v  F$ a  b. @; E+ x2 |
  33. ' x: J; C! {% t; G. E
  34.     /*
复制代码

! J) F6 I! }) [4 ?* R" R. e: l      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,3 |- t3 A" p, Y3 A
         详情看参考手册的Table 12的表格。
5 V) s, v* a9 q& d: }9 W      2、这里选择使用VOS1,电压范围1.15V - 1.26V。7 F. d* M7 ~! m4 e/ |1 w0 {) Q
   
  1. */8 Y' s  Z$ u& H; S: i9 L8 Z$ m5 t" m
  2.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);# A  u1 s9 {* d

  3. # _8 D3 o: X5 I0 t3 a
  4.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}' y1 ^+ W) B' t3 `% Q
  5. 7 S& @5 R: `% J' v% y& @4 f
  6.     /* 使能HSE,并选择HSE作为PLL时钟源 */: y# |- P* R8 ]9 g; p% W6 P- K
  7.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    : \) d5 m: j3 B# G, d$ y8 ^
  8.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;5 p5 D1 x& c$ j9 Z) ^3 W
  9.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    ! u$ D, @" L2 W9 \
  10.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;5 Y+ a( w- K3 C. x$ A# i
  11.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;! J7 F3 C# |# {2 e
  12.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;4 c$ e' |4 ?$ B

  13. 4 B8 S5 g  Q9 t3 |8 `
  14.     RCC_OscInitStruct.PLL.PLLM = 5;! g% S' L  o1 \. P) ]6 B  y
  15.     RCC_OscInitStruct.PLL.PLLN = 160;
    % B3 K% {, l( V0 @& V8 f
  16.     RCC_OscInitStruct.PLL.PLLP = 2;
    # A1 M3 P! ~$ c7 g! U, `. H. O
  17.     RCC_OscInitStruct.PLL.PLLR = 2;, i* `* s2 y6 p% J# L# Q" d
  18.     RCC_OscInitStruct.PLL.PLLQ = 4;        6 N5 I. C, [; q

  19. , ]: w7 B2 @6 c0 G6 }
  20.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    9 _6 f9 ^, z% w
  21.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    " O: b( O7 X! U/ r0 K  t# c( p* T0 @
  22.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);( X! W5 [+ L4 z0 V1 T
  23.     if(ret != HAL_OK)
    / d+ e3 @- x  v- h  _8 v: [; y
  24.     {
    - V# X. N  ?% u
  25.         return 1;        
    . d  [8 a( M& e. e" x% Y, T
  26.     }8 _( O/ B6 B0 d2 Q7 S; ^
  27. 3 d! q: r2 E2 V1 ^, y
  28.     /* 0 p9 U6 ^- U0 P1 M# p. @
  29.        选择PLL的输出作为系统时钟
    + m8 a" c2 s0 ~$ V5 Y5 e- B
  30.        配置RCC_CLOCKTYPE_SYSCLK系统时钟! o- }& {. m, t0 @6 F
  31.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    / v3 k3 u+ z: T- j
  32.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    8 {+ L1 ^# K, k6 i3 S/ R- }
  33.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线' Y# K% v- @2 q0 h2 k
  34.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    / J2 R( v" t! I- q  r
  35.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     ! F, @; O' _2 X' f0 ]7 f
  36.     */
    + f* Z3 r+ K6 F4 \1 l8 h' I
  37.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    7 P. I* ]( q2 q; r4 f
  38.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    " M6 _6 L+ H/ z3 l
  39. - o0 a& K. N8 |5 \( m) `
  40.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    " M& E4 ]; E7 [! {$ _  q) L
  41.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;! x& r5 p* e; c) \* o
  42.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;8 Z- F3 u& O! o2 r$ |) h. E% L
  43.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  * r6 v3 G) l( ?5 T; L& T% m5 ]( {9 X
  44.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; ; s- R, b! K, v  ?' f" n
  45.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 1 U- H& W* y9 p3 W( x5 x
  46.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; + L! G6 W& Q% u

  47.   ^5 o3 y( P% ~6 r' A
  48.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */1 U- Z5 S6 U  K% c0 \
  49.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    : r1 b/ c& D+ V- k4 d, F. o  M
  50.     if(ret != HAL_OK)
    2 @6 \. Z/ W' c2 I. |. A! X$ F
  51.     {% t( G% b2 }, X; |% D
  52.         return 1;
      \+ y* `. y5 ^: y5 Q0 x( X
  53.     }- g3 a8 ]4 J* v8 |- A  N3 u+ c6 X
  54. % o4 T" |! p: C! X, P
  55.     /*
    & \4 a) F" \% `) H- x9 I  Y% j
  56.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 5 @' t: j* z! F2 v
  57.       (1)使能CSI clock
    2 w( b( e6 y, @
  58.       (2)使能SYSCFG clock
    + Y% p4 ^: c6 _$ S4 `/ S
  59.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    : b/ Y' L6 b$ G. m2 @: I; o6 K2 P
  60.     */
    ! i( r+ T9 n+ m1 l9 |
  61.     __HAL_RCC_CSI_ENABLE() ;
    0 f, v2 B6 R8 G! f$ k' `
  62. % _0 B9 q; c9 q4 Y" ?
  63.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    : s2 S, T, ?" l. m
  64. 3 @) F8 P. V* d2 F/ `9 G% C- y
  65.     HAL_EnableCompensationCell();+ ]) E! V/ n' h# c

  66. 5 b; @) m/ {) Z: L# J
  67.     __HAL_RCC_D2SRAM1_CLK_ENABLE();, x' N- A; _8 i8 g! d6 Q
  68.     __HAL_RCC_D2SRAM2_CLK_ENABLE();( }" w8 F3 o( W9 n+ U
  69.     __HAL_RCC_D2SRAM3_CLK_ENABLE();. q* m# e" @; \

  70. ; }; J: r( `  ?4 U
  71.     return 0;
    8 r5 m0 o/ J' A
  72. }
复制代码

  K: z3 F2 Y( X9 e( ]9 k81.5.5 第5步,配置文件Dev_Inf.c的实现
7 @) @6 B; G' t) f: z2 }4 E$ n配置如下:
& R1 ~% Y% W. c  E' ?2 B! E6 Y
" d$ b0 q9 ]1 T- J/ j
  1. #if defined (__ICCARM__); f+ m5 D: s3 `' v. f
  2. __root struct StorageInfo const StorageInfo  =  {
    / F! J# t+ C: T1 V
  3. #else/ U7 U# O  I$ B$ [
  4. struct StorageInfo const StorageInfo =  {$ A! a# g( s% p
  5. #endif8 F3 g/ O1 e6 }1 k# ?- P* U
  6.     "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */- I. n; }% C7 D* K" x/ U$ n: d( M
  7.     NOR_FLASH,                      /* 设备类型 */
    ' I5 p6 D6 u( y4 C: d$ r8 `
  8.     0x90000000,                     /* Flash起始地址 */
      M2 v  n* k- L4 n1 Y9 _
  9.     32 * 1024 * 1024,               /* Flash大小,32MB */
    $ @0 A% M8 M. L5 p' g
  10.     4*1024,                         /* 编程页大小 */
    / t$ g4 [! H& P; C- O& p/ C
  11.     0xFF,                           /* 擦除后的数值 */
    0 ^- G( ~5 Y/ [4 r( D3 u" b0 I! }
  12.     512 , 64 * 1024,                /* 块个数和块大小 */
    / V4 T' G" |5 R! o% A
  13.     0x00000000, 0x00000000,1 L5 s5 j  n3 [. F  R$ V
  14. };
复制代码

! k9 e# J. {5 h' ~注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:! x: S/ r) w* ^! H
6 u8 B8 r& t9 B  }
bc495adf8d970e6e1fb6822b88b94e2f.png
" G# G, ~, t% `6 l
' o9 I$ D7 ?# ], B2 Y' j
81.5.6 第6步,编程文件Loader_Src.c的实现- d+ k, G1 V, s- ]$ C
下面将变成文件中实现的几个函数为大家做个说明:2 v) S& Z1 e& ~2 N. ?0 g

! ]2 t4 B, [6 c( x  S% ?  初始化函数Init
/ o8 m2 D7 g2 |/ ^
  1. /*% q- Z7 o* l, Z
  2. *********************************************************************************************************3 y0 P) K' P) c2 ?$ a
  3. *    函 数 名: Init
    ) x, q: Z1 E. p' s- u' R
  4. *    功能说明: Flash编程初始化( i% l; m6 n3 W8 ~( A
  5. *    形    参: 无
    ( ?& k' Y- V+ E9 F* f
  6. *    返 回 值: 0 表示失败, 1表示成功
    3 b- P4 R7 _; {# H# N
  7. *********************************************************************************************************) y" R( O8 k7 A, ?
  8. */3 G7 Y* Q3 i( Y9 v! b. A* |  \
  9. int Init(void)2 S  {( ?, a# O
  10. {   
    4 f5 d3 a" _1 W* C1 J" |
  11.     int result = 1;, N" ?5 V' g# Z8 |8 U

  12. 6 W) u* v$ u8 M5 ^0 n) C
  13.     /* 系统初始化 */& m& ~) f  J  h7 C8 K* R$ ^1 o# e
  14.     SystemInit();
      D; i. _; U% m! L% }
  15. 2 ^9 @2 _3 e( o! U
  16.     /* 时钟初始化 */
    1 ~% t$ Q5 Q) \; T( {5 f& L" a
  17.     result = SystemClock_Config();
    # L1 {! A5 l5 P8 h1 L+ W
  18.     if (result == 1)* z+ n" Y& T; q( o$ L! @9 M
  19.     {
    1 T, Z% z$ E0 P: {# c
  20.         return 0;        
    / `, ?! }% x$ W, H; G8 v7 N
  21.     }
    # l( g- Y$ _/ \

  22. " |8 a( W5 r2 x
  23.     /* W25Q256初始化 */
    4 e5 U9 T4 y+ g$ J# M2 ^
  24.     result = bsp_InitQSPI_W25Q256();
    9 Y$ G: K1 P* T: E' o( q
  25.     if (result == 1)
    ' _! V5 g, D" w7 j6 I
  26.     {$ e; C3 a' O1 U' L
  27.         return 0;
    % d2 @* {: ^2 ~* a- ~) W# o
  28.     }
    4 h) u& e  Z" M1 Q2 U; Q
  29. ; J: r5 o5 p4 a) Y! q7 t
  30.     /* 内存映射 */   
    ; q. d& }  P1 ~# D7 B0 P
  31.     result = QSPI_MemoryMapped();
    0 Z4 S) F' a& K/ D+ s
  32.     if (result == 1); i0 S3 C8 K" f( B1 B) e
  33.     {) D0 P7 P9 T+ r# I& Z
  34.         return 0;
    1 J' k( u5 c" q$ T! D# o
  35.     }, W4 w! c, Y+ m. [, [, _
  36. % O8 q+ ?! z% L- z
  37.     return 1;
    2 G( v& z& S1 r0 T( k- G! s' n
  38. }
复制代码
$ R$ D/ R$ Y2 w9 d; m
初始化完毕后将其设置为内存映射模式。
# o* Z. H  ^( I$ N, H* M
2 v3 a7 a5 a  S: g3 @7 m3 Q2 V! m  整个芯片擦除函数MassErase6 g+ K, k3 b9 E. n/ J9 G( e
整个芯片的擦除实现如下:. w, M* M+ L& a. w3 y

" p' f! L/ f! q4 Q
  1. /*
    + H6 M/ d$ K, y0 Y, g+ j* \
  2. *********************************************************************************************************5 j0 G' q: c! s' |# ^
  3. *    函 数 名: MassErase
    6 b1 v7 W+ q- }
  4. *    功能说明: 整个芯片擦除
    & A! K4 q9 a- Z3 q8 q% w
  5. *    形    参: 无
    1 S1 x* [) S: U# r& R
  6. *    返 回 值: 1 表示成功,0表示失败
    # h! X0 W* ?$ k: Y6 }4 t
  7. *********************************************************************************************************+ E6 h5 L/ S5 ^' _- n
  8. */
    $ Q5 S- @& C# v1 w+ g0 o8 q" h. W
  9. int MassErase(void)( R3 o% L2 u/ p$ n6 U
  10. {# l1 s, Z. v- m; V' p% \1 f
  11.     int result = 1;! ~; E. i6 Y7 A  U- D
  12. - `5 \( ]% V% U* C2 `7 K
  13.     /* W25Q256初始化 */
    5 ?! E  Z1 F0 I5 r' N
  14.     result = bsp_InitQSPI_W25Q256();
    + l. G( r& H$ t9 [& Z  s/ N
  15.     if (result == 1), A& k  U& B3 a5 Z, w. t
  16.     {
    + k- \' ~' m% C1 q
  17.         return 0;
    ! s4 s) u: L! K3 W, h& Q
  18.     }) I( W  p- @3 v/ T( m- M; P1 N

  19. 5 U2 z2 d$ K$ M: m- W. M1 Q9 x
  20.     result = QSPI_EraseChip(); / y1 N  S- ]5 n! q$ V
  21.     if (result == 1)6 S1 w- j+ Y+ r7 {) s' [
  22.     {
    + s# F% u8 e4 J+ W6 \  z4 G5 Q
  23.         return 0;
    8 s6 A) r" E7 k  }7 @. D
  24.     }   
    " ~2 ]$ l! o7 x: h1 `1 s2 W+ |

  25. 1 x0 V0 h5 `4 R9 e( V1 \
  26.     /* 内存映射 */    & Z1 V. W# i* Q# H, X' N" i
  27.     result = QSPI_MemoryMapped(); / ?4 l: V9 L+ b3 p3 [
  28.     if (result == 1)
    4 D  k2 V' w6 z1 m9 s
  29.     {9 O4 S+ p- ?8 ^
  30.         return 0;/ m6 D' ~* }5 y# Q6 V9 P! n8 T
  31.     }
    % f* q/ ?! m9 l% o' V; q

  32. ' [$ a: R4 H: y2 K0 i
  33.     return result;          ( d/ M( s: ~3 j1 H: U6 c
  34. }
复制代码

2 |) p- d7 E& C2 B- S  扇区擦除函数SectorErase! V" f# X, S; x
扇区擦除实现:
; n) d/ ^* j* W  {4 Z
6 a. z5 D2 i' Q
  1. /*9 c2 d; }# H# j" j, p, n$ a; @
  2. *********************************************************************************************************8 I4 d6 a& U$ i* p
  3. *    函 数 名: SectorErase
    - B1 }* y( b' x' H$ v& q! L
  4. *    功能说明: EraseStartAddress 擦除起始地址/ h7 D$ x4 c% l! H" k
  5. *             EraseEndAddress   擦除结束地址$ ~- s: @3 P# B' ~
  6. *    形    参: adr 擦除地址. [; @) z, U3 c: P
  7. *    返 回 值: 1 表示成功,0表示失败: W2 o. D! @  c; A
  8. *********************************************************************************************************1 b% i, x* w) E; q5 n5 [( L
  9. */
    ! e" S2 }$ U: J
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)$ `" c* F, \2 ~9 i3 o/ H: K0 }/ T
  11. {
    + ?4 m- K; D6 z' y* n% [2 N. V' w
  12.     uint32_t BlockAddr, result;* N( ]: m' t" ?5 \- v0 v  o
  13. ; E1 B- }# n& u: X2 b  [8 W
  14.     EraseStartAddress -= QSPI_FLASH_MEM_ADDR;2 |1 E0 {1 W! S
  15.     EraseEndAddress -= QSPI_FLASH_MEM_ADDR;8 V1 E% S0 k3 Z  ^$ n; e
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x10000; /* 64KB首地址 */1 ?4 F! s: J, C/ D( f: \

  17. ( ~+ n3 H+ M0 y2 N- Q' L, e
  18.     /* W25Q256初始化 */& ], ~- Q/ Z; w8 l/ I; X" w9 I
  19.     result = bsp_InitQSPI_W25Q256();7 C% T8 T* f) J0 Q9 U1 y
  20.     if (result == 1)" g1 X" e- u2 y
  21.     {1 ?5 g& n/ t  V& M0 y
  22.         return 0;        % c9 g6 q" B) Z3 {3 ]3 W
  23.     }
    $ [( X; b! l+ F

  24. & C: g" {6 g+ Q4 }% k3 g3 `) ]
  25.     while (EraseEndAddress >= EraseStartAddress)
    9 q7 l& A; q5 U! [- d( O4 u4 D- b. |
  26.     {6 G8 z; g% p! {9 j
  27.         /* 防止超出256MB空间 */: J! q- \3 U( |4 l1 a' z
  28.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;, n8 N+ p7 n9 X
  29. * W+ u7 b8 Y+ J' ~! I" U9 O
  30.         result = QSPI_EraseSector(BlockAddr);   
    4 P' v- H7 {  d
  31.         if (result == 1)
    , o  D: M! J6 _2 R
  32.         {
    * ~6 T& |1 z9 s: C
  33.             QSPI_MemoryMapped(); * z4 O+ P5 n# P' c2 J
  34.             return 0;        
      Q+ |, q5 w+ x, T: I9 e  I  K
  35.         }
    , }8 d9 \/ j& [$ k& H& V

  36. 7 D! ?5 R+ z; T9 D3 r$ W6 Y
  37.         EraseStartAddress += 0x10000;
    ; _, Q8 A0 R2 H. s; G7 P  F
  38.     }
    3 [4 A( g" m7 \2 S& q, P
  39. ! m; p# P* d+ e0 v! H
  40.     /* 内存映射 */   
    1 ?  ^; {$ U7 `, S
  41.     result = QSPI_MemoryMapped(); , p0 _6 u, D" j2 K3 D- K
  42.     if (result == 1)
    6 y# W0 ], J$ j, D7 H' o8 V$ K) Y- P
  43.     {% P' R9 x4 T9 x; g) g' L2 X
  44.         return 0;  K7 a8 T! G. ?# k- g) i
  45.     }
    5 f. g1 N! ~7 x+ i# `, M

  46. ( V; ]' N* }1 A. U$ k; S  H
  47.     return 1;
    0 p9 U3 a7 @# I7 E! E% E. |
  48. }
复制代码
. i. \* d( t4 |2 h7 w$ u4 A5 I
这里要注意两点:
4 Q; i4 z* |3 v  h2 y- Q( t- D; M) P* H* f- v
(1)     程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。4 W! _! F. A' Q* _4 G; K

& N6 O0 t5 `. @) H8 j  k' Q(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。
) ?6 s8 b, \6 o7 i9 P' ?8 `4 s( j
  页编程函数Write
, U! o/ N+ R: ^9 ?5 ~/ }5 F9 X页编程函数实现如下:! z" ]3 O6 S0 A9 P( W( H( `
* T, C5 x8 T8 F- A- e: ~/ M
  1. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    & S: S+ M" u# o0 ]
  2. { 5 q5 D- ]7 l4 A# S0 u3 b
  3.     int result = 0;
    7 r4 _$ u# n, E
  4.     uint32_t end_addr, current_size, current_addr;1 j  l& I0 [$ i1 y  {

  5. % x& o6 a  E: O: z; c* V
  6. ( S4 ^, a& J$ F, b9 Z- ~. U
  7.     if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)) ]# C& c0 c' [6 o1 P
  8.     {9 ?( N+ d, S1 _
  9.         return 0;1 O4 _: m# e; Y, ^) z! _: |4 D
  10.     }
      K4 \0 l* `/ ?6 Y+ k# w7 z
  11. 6 W+ \3 s& B& Y  |" U  t9 b( {( E
  12.     Address -= QSPI_FLASH_MEM_ADDR;0 s+ ]# k1 S& I0 I6 V
  13. 9 B- \) u0 a, `7 x' G/ t
  14.     /* 计算写入地址和页末的字节大小 */
    6 n* n& Q' {2 S! Q% L
  15.     current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);8 B" b5 T5 n# K  Q. y/ @9 R

  16. 8 ~  j$ k4 z8 V6 S$ `
  17.     /* 检测页剩余空间是否够用 */
    * n9 S8 K) o8 R: a( e1 D8 O9 T
  18.     if (current_size > Size)# J" B8 b+ e- f+ \% Z9 s/ D
  19.     {: _+ Z7 h% N) F5 H. X
  20.         current_size = Size;
    " p' G- |! d5 i
  21.     }; I: Q# p6 H4 R& h' N* ~
  22. 5 O  i( h) s! X6 m" m
  23.     /* W25Q256初始化 */
    4 q* U7 L7 R5 j& D5 z( y+ L& R
  24.     result = bsp_InitQSPI_W25Q256();
    + x* L) M; G1 ~7 P- @
  25.     if (result == 1)- R. s+ r6 m7 n
  26.     {
    % Q( d1 L( N; P" u0 z
  27.         return 0;: J6 ^# p3 j4 H  I" w: L
  28.     }
    ; W. N+ |% D" f

  29. . L$ ]/ U+ @3 k% }: X) }) A
  30.     current_addr = Address;
    ! T8 X- O* j* [
  31.     end_addr = Address + Size;) _- j- s( C/ C# ]7 y3 |0 m

  32. & p/ b! J4 ?' ^- J
  33.     do{
    2 z! W9 O3 Z3 s7 Q. z, P/ }
  34.         if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)8 z4 x1 A* S! C8 b- q3 R
  35.         {
    . n9 L; N0 w7 s4 Y9 C4 q2 l6 |
  36.             QSPI_MemoryMapped();  + B% _: W+ Y# v) [* F7 c
  37.             return 0;  
    . z/ M) a7 D$ u: ]/ `
  38.         }" E& v  n8 [" ]; T4 @
  39.   d! X5 ^2 s8 ?( K0 p0 u* B
  40.         /* 更新地址 */1 i: o% U6 ~9 @/ S; F* O; t+ E
  41.         current_addr += current_size;
    / z# S9 e0 k) Q3 a% e% h
  42.         buffer += current_size;" r  _5 V1 ~$ ~! F! O
  43.         current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;
    9 s9 Q0 m' O' g. H3 L
  44. 8 S, N. l7 ?# U4 k8 j  R" v1 F
  45.     }while(current_addr < end_addr);
    + X- i/ W5 K' o$ a0 k' n) ~
  46. & Y7 r3 Y* D: C  \# e
  47.     /* 内存映射 */    * e4 k2 H+ ?) ~$ x& Q
  48.     result = QSPI_MemoryMapped();
    9 `4 \  _/ D6 ?, L' ~
  49.     if (result == 1)
    : E4 I1 D3 Q, c3 h, d+ v
  50.     {7 o/ V1 k) R) U3 X0 ~( X( ?
  51.         return 0;
    0 p- ~8 H0 G; P+ D
  52.     }/ r, n3 o8 a: @6 g  M- B
  53. & t7 S8 Q9 J& [& H+ p
  54.     return (1);  2 r: p% s1 C6 k2 Y7 f* O$ l. W
  55. }
复制代码

1 X* f$ e5 _: M这里注意两点:
6 B. L, r! x9 j5 {) Z3 p" X
; ^5 ]0 p' o  p! l; @. u(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。" N' `5 u( u  ^+ ]

% ]$ w& s+ a' ?(2)     程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。  V$ c* `, h. ^7 v, _& n
8 J, M, b: U1 O, y  s2 f- k
  读取和校验函数
! a' d, i6 [! }$ ~$ D我们程序中未做读取和校验函数。
  C4 m8 p: ]5 M3 f6 y: M% Q& m
& q8 A# u1 h8 n8 B+ W: ?. p(1)     如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。# Q/ n) ]/ e; L3 n  G4 I8 g
/ T6 X& ]$ `5 {
(2)     如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
# P8 n' W+ O/ J3 ^8 v, P8 q  g$ P& Z( O5 @) C' `/ r5 n8 P% D, D* }& B
81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
) P/ E# Z$ v) H8 T1 v# }最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
: v: l- n+ t) L& ~: b% g; Q4 t. K5 t& z9 C4 f
  1. /*
    7 g7 I! v8 y8 c* t4 S( J6 ]
  2.     STM32-V7开发板接线
    & t) M5 Z: c* m" y( C# V9 {/ K
  3. 6 _( X8 W7 D% X) A
  4.     PG6/QUADSPI_BK1_NCS     AF10
    - Y9 n- p7 C8 k9 O
  5.     PF10/QUADSPI_CLK        AF93 m+ ~, A- b9 f" N) N! ?$ Q
  6.     PF8/QUADSPI_BK1_IO0     AF10
    4 C, \$ ~- S) u% p, j+ K  z3 ]
  7.     PF9/QUADSPI_BK1_IO1     AF10; u$ z  K( B2 y" j. U& B' i1 k3 J7 M
  8.     PF7/QUADSPI_BK1_IO2     AF97 X% a! y8 {! S1 ]# Q/ }
  9.     PF6/QUADSPI_BK1_IO3     AF9
    7 b2 z4 N, e8 w4 q/ p* `5 I
  10. $ J- G" c6 s& {9 n9 @  Y
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
    9 ?1 l' G4 z/ Y6 [& g+ ^

  12. - [* k( a" y& t1 L/ b
  13.     H7-TOOL开发板接线4 |! W% ]0 p% t% E! O, U

  14. . o; V* f( i4 b% s& o0 t# ?" c" G, \
  15.     PG6/QUADSPI_BK1_NCS     AF10
      o, }5 D. H$ ]
  16.     PB2/QUADSPI_CLK         AF94 F+ G  R# _7 m/ |3 k. F
  17.     PD11/QUADSPI_BK1_IO0    AF10( W+ x. J/ c5 u7 {4 o
  18.     PD12/QUADSPI_BK1_IO1    AF10
    8 X. B7 m$ i4 k% V# ^) s; ~
  19.     PF7/QUADSPI_BK1_IO2     AF9* d. z  |7 `! h0 L* [) F" }9 L  p
  20.     PD13/QUADSPI_BK1_IO3    AF9
    , p; j# }' i" M. P% w" |
  21. */* e) Q7 @, d: V$ V3 X
  22. % q7 g8 T" R5 h- W0 e
  23. /* QSPI引脚和时钟相关配置宏定义 */
    . N; ]& l% G; @' J0 |: y
  24. #if 0$ ~9 `8 h, ~0 Y2 f) ]  G6 b- e
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()& g% S0 t0 u" L+ G+ ^4 ]6 H% T
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()  U) @/ \; v/ Q3 c' _& ~
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()5 C% W% N/ }/ g
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()6 f- w/ G) n4 B( C. q3 l
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()  X" Y/ W9 E0 Y( O' F
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()3 N: `1 _2 E5 j7 @( X8 T
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE(): B; c/ v  a1 j& l
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    & I1 g, f' i. q" \) ?
  33. , ~4 C4 V- I  }, u0 }
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    + L& b* Q% j0 w8 a+ m. ^
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()9 Y; A3 d2 P+ e
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    3 \9 a! A, w5 r2 ]% I  v! R' a- [/ z

  37. . m5 v/ n8 S$ E: w: w4 l/ k0 w1 D) X
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    7 x. [0 a. E6 o
  39. #define QSPI_CS_GPIO_PORT               GPIOG: u5 x6 i. E, Z; O' b
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI0 [+ g6 R3 W1 a+ Z( \& ]
  41.   I( F* P  K2 J/ C
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2
    ! b- Z( U, G, h2 V
  43. #define QSPI_CLK_GPIO_PORT              GPIOB2 y! g8 n( g$ a. U; G$ \
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI5 S0 t  h; D3 V  X& J
  45. ( M4 r7 C! g% w- Y
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
    ) P1 f, \2 z5 L& O
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    ) B* O4 ?- k) J4 n* R% P
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI
    : O4 [6 s: g- C" z7 w5 f6 d* e. {
  49. ' R, G6 W( B+ Q& y6 O
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_126 |2 P4 K( ?! O! n2 u; R2 c
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD0 t$ v* S3 e+ M+ B% ^
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI1 O, R! P7 c; {: v: n, J) ^8 D

  53. $ K# B2 o8 r4 i
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    0 K, q9 v- F" E; V% Z
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF2 m  G5 j, F3 |, t$ n+ D" v: a
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI) C# x3 ^# W5 Q0 N0 A9 m4 \

  57. ! E; C* _" C7 _
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13
    4 e* c  D# m- |' q
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD
    % k2 j( k9 D; @, E
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI5 w9 M0 t: e- s
  61. #else/ N# e( @' Z7 [
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()$ v: m5 `! s' W  v, T
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()2 J. M/ c% l. J3 F$ a
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    5 Q, T; k# O  o; W3 _2 K9 d
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    : J+ P3 K4 D9 l5 E
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    4 b4 L4 [$ \6 w6 X+ W
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    8 v8 y% K! K: \/ }9 @/ G& h$ n& u
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    4 E& k5 l9 Y- y
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    + V- h9 W5 E# u& ?6 G

  70. % [; ]- T: _6 z( r( l
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()3 L3 i# \! a* o0 S
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()4 v3 x- o) v7 p. m& {; t2 _
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    / g5 v, ^- v7 M4 _8 d4 M5 X

  74. 8 b, i" T( m! X4 i8 W
  75. #define QSPI_CS_PIN                     GPIO_PIN_6
    % v, s! U+ j- {
  76. #define QSPI_CS_GPIO_PORT               GPIOG! \. g& t- m1 S! Z4 R
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    9 S+ A6 X4 N3 S0 Q+ I9 `
  78. , f. R! X3 P7 e3 }5 N; x) T! K
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10! H9 y+ Q! i+ I8 m2 W  n) i
  80. #define QSPI_CLK_GPIO_PORT              GPIOF7 h; b& F" J, [" G: V
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI) O+ k7 }1 Q, A/ H1 k
  82. : z: m4 {" L- A
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8- G. K# H3 ]+ q  C) ]
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF
    ( l- A) P8 I2 r$ Z$ d5 {
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI+ r: P& E$ x9 Y2 z, J' Q3 z' m# T
  86. ' t# w" F7 z! I( O* ~
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9# |- s, e! F4 c2 @8 S4 U
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF$ m: [; L" t& u1 `% u; h
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI" E& ?, L2 x* B; g" Z5 y' Z8 O: ~

  90. / R( O+ T$ J! p. t- W
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7. T0 V4 R6 X( ~6 b; C
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF3 F  y* G9 R$ Z3 R
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI$ L5 a' ~% p$ f$ U  _2 p- M* w6 H
  94. . }9 m0 \+ b9 c- J( P7 o! i
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6' w4 s( m3 k- O- R3 ~, e
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF
    7 N9 v/ n" x' a% f$ B/ b: F
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    1 r- a  k. T4 N0 k
  98. #endif
复制代码

, w5 k( l0 D' I" |5 i" Q4 K7 U硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:! k+ Q6 ?  l8 T3 P: z& k
+ f' p- G1 L+ a5 ]: e( N) `
主要是下面这几个:8 ~/ x! s$ `  m0 b6 Z
4 K0 }6 @  q3 T% O' s- K! c
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000
    2 d  t/ k$ [1 l) R3 o

  2. # F9 S9 n3 L  u, M
  3. /* W25Q256JV基本信息 */
    2 D+ L1 w% |' l) u* W6 g( `  F
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*// K  d0 s9 t0 x9 _
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */" v4 c8 Y! y" C
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */
    & V" c, k1 D2 @+ x- O
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    7 x3 j- u3 l! X9 m" p, Q; m3 @
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    & p4 O: |" `* J, v# r) T

  9. 3 l  y( Y1 j( G" q# |
  10. /* W25Q256JV相关命令 */  U  R  _0 c' \8 R
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */
    / a( H6 L7 n$ J: f0 K
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */" \1 c  a. e* y/ u! F% S) }
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */
    % S  ~5 D% w6 c& b1 b
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    5 k4 U1 m+ {- Q1 s; W& Q1 M
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */  M3 Q5 R  `7 X2 A7 W
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */# `1 M, d7 ^# m& I: Y
  17. 8 Y! q$ ?: m# O! H* F. Z8 J
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */2 Y4 m6 D& N: J4 V+ J8 _
  19. - M: |- {$ p5 E2 Y, f$ s
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

- [: w) |0 B3 b8 r: ^$ A81.6 QSPI Flash的STM32CubeProg下载算法使用方法! B3 c4 c9 K9 J& y7 k
编译本章教程配套的例子,生成的算法文件位于此路径下:
  o# J& z8 U1 b, Z, m1 B- Y# W  I/ T( T  |+ P, j
d45f9274d8762b095790b3e6778b0a93.png

. u4 G- i/ X3 A& y* |+ X! f$ s8 Z# e! c. m2 ~
81.6.1 下载算法存放位置5 \- r$ \" D8 i2 K
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
# D% I$ Y4 a" P! ?. N6 b6 o. e/ c  n4 P6 z
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader) K" @5 l5 |( C* d; b2 e0 D" x8 T7 h
; o3 I# M3 ?* h( N7 J2 b: s; B6 i
60cce5a15415e27e9d5b75b05028d1c0.png

, @9 E, U7 J1 W
( k4 M+ ?/ }& h& B* j& B' B. R  h81.6.2 STM32CubeProg下载配置1 k% F$ S% G( R' q. \+ [' w) G' D' W
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
% }# y, i& x# z2 O7 _; J: O* I8 u- `' L4 r/ s. ]; }. L. ^0 C! d
9cc179641e765ea43cb38d268bf734df.png

$ e8 ]2 F  d* W5 L
: M) V/ ^/ V' q0 A点击Connect后效果如下:8 B& K. r! d$ M# i5 j

( U2 L5 [: a0 }8 {
b39ce8a738163430db2119bdd87e3fbb.png

; O6 a! y( i  C3 \3 F7 G% A; R- e& u8 y9 v0 K
在这里选择我们制作的下载算法:
" N! @7 N+ o0 t, U( ]' q' v% Q5 P# t9 P, n! {6 Y, x
17f6e0f50a18ae818faaa1399daef12f.png

& L  A1 P9 _8 x2 J0 E0 i$ K4 x' P" Z  V; {, i
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming; h. h4 u# I$ p

* e1 Z5 a$ _+ C# G) j
43a0d90faf1d392f5a74eaa7777e4178.png

& B, b2 B6 E8 X2 N& e
) Z6 P/ W$ B: X下载完成后的效果如下:) G% e2 m/ f* J9 f# b

8 V- [+ O/ z- h
a1d6662baa9e7be00f7e67e02b5c9b79.png
# ]6 l+ c6 J8 H7 w

: {; A9 S/ A! u) X- }: y81.6.3 验证算法文件是否可以正常使用
5 V- C. o' n! n: }" i为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:  P% S1 x; \% N: Q$ H

4 {7 |- A2 ?- S0 L* t
3f2eefeef3db68623cb8969974c40a20.png

& z8 g& a/ o  j, n0 R" s" R( H' t4 B7 `/ R1 m( K8 [: b, N8 \: e
81.7 实验例程说明5 T  y1 n  v7 M# z  l7 L' y
本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。
! @- ]" J( d1 |/ q" q/ w6 |5 O* h4 n- z# K
编译本章教程配套的例子,生成的算法文件位于此路径下:( d. l- j, i2 j  x1 ~

5 G3 ~. r+ ]7 v& |" i3 G: ^7 S5 r6 Z
b7028271136c8053d7300063ed4aec3a.png

/ o5 E' ?# v4 q3 Q2 c$ z# Q: }. r; n3 n/ T, u% Q
81.8 总结5 T, Z& V- u3 Z/ U, I' P4 x
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。% n& Y3 }, R* X6 j! m
# J0 E! U$ s! ?8 |
2 A' a# y% v; V. a$ S) V- ^

4 M# r% l# G: i# ?' S, K" {
赞 收藏 评论0 发布时间:2021-11-4 23:27

举报

0个回答

所属标签

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