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

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

[复制链接]
STMCU小助手 发布时间:2021-11-4 23:27
81.1 初学者重要提示
& O" ^' Z5 n1 [  QSPI Flash的相关知识点可以看第78章和79章。
' d  P- I' u2 k  I3 y  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。1 o! a9 Z5 V3 p* M
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
4 Z0 A9 i/ u# F! _& D4 X# |6 m* J  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。% j# S' j, y% j2 D# ~" ~4 l$ G
81.2 STM32CubeProg简介
$ q; f2 u4 l( G9 aSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。0 f, [2 m4 i) v
! I0 Y7 A+ _/ J; D3 {* I2 F' o( R
1bbabf500126f58ff1439b6bc36014be.png
$ \* z4 q. b/ [  b  Q- j

; I1 y7 [$ q4 a5 N! i81.3 STM32CubeProg下载算法基础知识! l! g/ e) i' `( Z( g
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
  Y: \, p' o! x  S
( N% B' v0 J, J( A% I. @3 P81.3.1 程序能够通过下载算法下载到芯片的核心思想/ y) t! g' Q& Y: }3 R' F4 O! c) a0 v" z
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。' _% V% c8 z' v6 l, h! B

7 T; b( U. w5 U9 H% S) _81.3.2 算法程序中擦除操作执行流程5 v, R+ ]" G# d" @& v; y+ d7 ~5 F" f
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
* y4 R/ m  f* r& f1 [2 t- }
8 ?$ a$ d1 k8 a; M6 A( R擦除操作大致流程:5 L; q6 ]) f: R0 n" x8 o% N
" a) X1 _. I4 p, q' U. A
161f92ebd9b1dbf9febba815fa091b8b.png
  r$ h8 A8 h' N9 M- T
- n% g- f9 r7 p
  加载算法到芯片RAM。! {0 R. p% b5 q' g! {: W
  执行初始化函数Init。
6 o5 _! Z7 c  Y+ N' {8 v  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。* g2 r% [, `! ^  \( Y. }& K' P
  执行Uinit函数。9 h1 l3 R9 ]1 V; K
  操作完毕。+ m* V" {& J; L* g( A9 Z, ^
81.3.3 算法程序中编程操作执行流程# b0 F" @! Q% m  J: [% v
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。7 V. {( L% R+ U1 q; e7 G% O1 \1 q

# C/ e1 d5 J% O, u编程操作大致流程:! M! c& E4 q  X: f+ _/ j$ @

# f% `$ E: o  Y3 H# H! Q  q6 v  f. g; z
d80dfd755411aa0d0565f1c56f10e66a.png
" j) R7 N& A' b9 G# c* L$ `0 }' F- \

9 E' X4 z9 S2 \6 B: e  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。! n% {9 y! S! q) N" q0 D
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
* e  u5 Z, ^* s7 r# J8 G, k  T  加载算法到RAM。
7 M5 D0 U; K& q0 H/ i5 Y  执行Init函数。
& Q7 o6 Z- ?/ V2 u: a' d3 \8 G  加载用户到RAM缓冲。
( o9 u4 e# o/ e# E/ w  执行Program Page页编程函数。
- o) i0 o1 P1 Z3 z' H' X  执行Uninit函数。
) ?2 u: ], ]+ T: m: I& C  操作完毕。
, C; F5 r+ [* }% M% n4 S81.3.4 算法程序中校验操作执行流程; k# t: {1 n3 f* L& g  g
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
0 Q7 s: [! n$ z- ~) V8 Z; W4 `/ u) O
校验操作大致流程:8 ?9 l; e/ M! o% Y, A% ^1 p
5 J& m5 ^8 t0 W7 |
f15191090ab169d48a203ea3617af9b4.png

5 \, t% i6 v# ]& `. D- H5 b  T* U0 ~% |1 i3 P
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。1 {: b% F; [( q  N- k' v
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:" w4 w' h5 @; B- x0 ]* k8 b
  加载算法到RAM。
- S5 x, U+ P$ f0 m3 u9 J  执行Init函数。* j- x5 Y  P) d4 x" {
  查看校验算法是否存在6 C$ U7 ^; J" ~' K' ?- S% o7 I
  如果有,加载应用程序到RAM并执行校验。9 }& Z! h2 B& J) x# X( z
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
' F* n- C3 m" N; y, m/ x9 W  执行Uninit函数。
& H& {* }5 P( f7 _5 ?0 P, I  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
) Z, u% x' w  ?7 H5 w; M  执行RecoverySupportStop,回复支持停止。4 k0 K; n) q$ \
  执行DebugCoreStop,调试内核停止。
0 ]8 V3 H8 h6 a  运行应用:
! `( s/ b4 b: |5 T2 A" T; z  执行失败, i0 i  Y& r6 Q) @) y
  执行成功,再执行硬件复位。
# j2 B! ?3 \% u' M" @+ |; |  操作完毕,停止调试端口。
6 w+ B. f6 c3 r+ C81.4 创建STM32CubeProg下载算法通用流程
- \4 a8 \+ W$ k+ G下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。, k7 V' Z( j; E" K
. ]+ Q. W# w2 V3 a
81.4.1 第1步,使用STM32CubeProg提供好的程序模板; B+ m3 @7 S+ ]* n/ \
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
2 @& c* l5 }$ C+ u
% Q1 X  b$ L: J% ?  I- ?( g
4fbfb0047e29fe23083224607059d97f.png
, J+ @0 X' j3 r2 s3 P  Q' z6 ^  `

3 ^3 \: f, t4 m3 Y+ K以M25P64为例(注,其余步骤就以这个为例子进行说明):
* ]+ k9 }  `0 E/ {: P
; y; R' T  K" S* _9 d
d1b1a340fdc655c1030330a7dad2abba.png

% a5 t1 @1 I9 J5 J' `
) s% J  d2 `) L- J81.4.2 第2步,修改工程名
( J' n2 h. ]" P9 ]% m  ^/ g; eSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
4 n4 }1 h$ Z9 \0 m6 {; i" v6 V/ e  l7 q# g+ Z. }/ m! ^% p' \% }+ s
81.4.3 第3步,修改使用的器件
4 ~9 ?$ \2 M- ?2 h- P( a0 o在MDK的Option选项里面设置使用的器件。5 G# Z3 Z' Q7 n

6 M$ W, z! a0 X' U2 I& ?4 v
092174ad976a8ce9c7fca533049e2dd0.png
# j! g; C+ g/ f  T/ w; X
- ?4 x* [: m2 ], S* r( v7 t. I9 K
81.4.4 第4步,修改输出算法文件的名字
7 C. l$ W% g" R) v" M这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
5 ~' M. b# l- V4 j2 O+ I) L- t( j
1 ?) i4 Q# ~$ }6 \
79c42251311dd3892fc7d6b8ddde68b8.png
' m4 P! Q& S  N3 o$ B1 Y6 J
: q6 n2 u# K/ m) J
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:- ~4 b' C# w. B1 P0 r- o4 Z1 V
e2fe61fa7ea99b25b178341b7350558f.png
8 n# Z; `' E/ e

' m& Y% m, ]8 b* W81.4.5 第5步,修改使用的库文件和头文件路径2 y  W- L6 t! ^( R5 `
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
: J0 g1 E5 m% j7 `0 o1 \3 k$ g9 k- y& z) f4 ?/ u4 Z! v0 S1 v) [2 |7 Q) N
81.4.6 第5步,修改编程算法文件Loader_Src.c
5 z1 ?+ Q9 Q' `2 K/ v& E' e模板工程里面提供的是M25P64,部分代码如下:
& I+ U% T) Z; ~" H9 r5 g7 l$ t
& U  \. Q) x9 p5 e* [
  1. /**, i8 T. _3 q( G: u) ?: V5 P
  2.   * Description :5 b  Q. I4 r; h6 z$ z  W
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the" j! t& ?" `+ \
  4.   * device and initilize the FSMC with the chosen configuration * T0 R& \- V  L' m6 Z1 n+ B
  5.   * Inputs    :1 x" v& U" X  R% B, R  f
  6.   *      None
    ( w7 N; L) u  n" p
  7.   * outputs   :
    3 [1 v3 a! P* l. v3 o" G1 ]
  8.   *      R0             : "1"             : Operation succeeded. n& V6 H- I4 @* k) B
  9.   *               "0"             : Operation failure" D$ n: U2 M- ~; W
  10.   * Note: Mandatory for all types of device 4 f" `; w5 |$ E) z" S" d
  11.   */, s2 W( ~, B* g% A* g& X
  12. int Init (void)* P( [# f; V1 \( m) v" J
  13. {  4 F5 S+ L% ~2 f9 U5 N! Q
  14.   SystemInit();
    5 h6 I: x1 [1 V% V+ z
  15.   sFLASH_Init();/ }  _* Q; j+ n! \4 Q+ r1 {; O/ k9 ]
  16.   return 1;
    / @+ D3 I' P0 X- T& P3 N( R
  17. }. f6 N4 H! Z4 h3 w5 a, E
  18. ; \, ?6 x/ D/ k! v$ J" K. B0 Q
  19. 9 d9 D. j3 _& q* n4 c
  20. /**
    ! w& R$ ]  X& Q( u! W6 v) I* I; C
  21.   * Description :
    : M7 p5 I& @9 }4 T7 f; p8 E3 E
  22.   * Read data from the device 1 ?( Q; r3 c( R
  23.   * Inputs    :8 W: l4 d- @  }) g
  24.   *      Address       : Write location4 {7 D1 ?7 w1 P7 R
  25.   *      Size          : Length in bytes  
    + D8 S" r! `$ q( V; w
  26.   *      buffer        : Address where to get the data to write- J) Y! U' D0 D& k& {
  27.   * outputs   :1 v% x) H) w6 c; y
  28.   *      R0             : "1"             : Operation succeeded+ \( H/ w( y* V% ?' a
  29.   *               "0"             : Operation failure
      R3 P1 r+ D* r  _1 i, D2 I, f# o
  30.   * Note: Mandatory for all types except SRAM and PSRAM    - [% S2 r8 q9 }% Z4 @
  31.   */
    * M; g- R/ X1 w; F" z$ v
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)% P! b( l& y% L3 v7 I( O' v
  33. { + A5 ~% R# z  U6 w+ Y
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    $ h( S3 w4 q) D5 i! J; s
  35.   return 1;) M# _: t* J4 @
  36. }
    3 W, x) S7 r2 c2 x. Y

  37. 3 x" \4 S8 q" X) t4 @

  38. 3 ~) }. Q) U$ Z& @4 B
  39. /**$ [/ c5 _" f% c
  40.   * Description :
    3 e: H: s0 {; @
  41.   * Write data from the device 0 o7 P( u' @- h/ N7 H
  42.   * Inputs    :
    , ~$ h5 |% m( c6 v* s, o5 Z, |
  43.   *      Address       : Write location
    ' y, H5 n. }5 `+ |9 e* y6 f
  44.   *      Size          : Length in bytes  , ~1 B9 G% H# K  f# L4 L- |8 d
  45.   *      buffer        : Address where to get the data to write
    0 b& o' F9 N' O  ?; r; e0 F2 V
  46.   * outputs   :
    - L( X% J1 m# v8 C9 Z
  47.   *      R0           : "1"             : Operation succeeded
    ; N6 m/ e" E6 y" w
  48.   *                     "0"             : Operation failure
      {  O0 U( D2 d0 X3 l
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    $ ^! s; X6 _* M) A" c0 p
  50.   */
    * m  _' z2 J- b8 G) f" M0 U! y: f
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    ; L' }7 Z+ l; e8 B8 `1 ~* X! z0 }
  52. {
    0 k6 W: K3 ]2 c
  53.   sFLASH_WriteBuffer(buffer, Address, Size);7 g: F# P' `' h/ E" Y1 d1 V  l
  54.   return 1;
    % Q. w* B9 V( r3 E! Z* b! ^6 T
  55. } 3 g8 q! ^1 P& j, K

  56. # l6 e' D7 }% h5 y+ t2 m+ R5 I
  57. ) v4 y, {( H/ G! ^* D8 j) D
  58. /**
    ; D8 M& `' ~+ `; ]/ Y" h
  59.   * Description :
    : |0 \: P0 v$ Q5 C
  60.   * Erase a full sector in the device3 C; J3 h4 r1 R
  61.   * Inputs    :
    9 T& r, W: E7 [  @3 G, V* ^! y; Q; |
  62.   *     None
    ) e- o. H5 a  w* ]( U
  63.   * outputs   :+ q3 T& W$ w% R3 h/ Z3 u$ Q( s9 y  c
  64.   *     R0             : "1" : Operation succeeded
    6 X2 z6 \; i2 Y, p$ G
  65.   *              "0" : Operation failure
    % D( |% i- _7 |7 A8 C6 F
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH: w' D, D- r+ `5 a3 v
  67.   *// ^' A* ]$ s. I% R6 B, g& g0 I
  68. int MassErase (void)2 L1 t6 f2 ^: O& m; x# V
  69. {  
    : a6 h% r4 ^* R6 T0 F
  70.   sFLASH_EraseBulk();
    4 D% ]3 E3 m0 w9 ~$ g
  71.   return 1;   
    ! F, r/ c3 y: |4 z) u
  72. }
复制代码

/ B4 C0 W" ^% O$ P1 k+ X) ?3 M81.4.7 第6步,修改配置文件Dev_Inf.c
) I8 a3 K( z# T; @. B  k% I模板工程里面提供简单的配置说明:
- A5 H6 a& i% B+ g
, E, A. Q5 c: r3 t# U& C, N
  1. #if defined (__ICCARM__); n# M- @$ Y& l+ g
  2. __root struct StorageInfo const StorageInfo  =  {
    " g# y" Y+ l3 f( k% @
  3. #else
    5 v2 q5 c3 F5 t, Z% U' S& d
  4. struct StorageInfo const StorageInfo  =  {
    - A! u5 g  W# _+ e
  5. #endif1 ^. G$ l( g' w. Q  N
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number# S; W! Z' V0 Q' C) G. C
  7.    SPI_FLASH,                       // Device Type
    8 Q9 Y5 X% {6 D+ i+ e! ^- e4 M' `
  8.    0x00000000,                     // Device Start Address
    , a* {: Q* i/ U+ ^
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)) [4 `* b0 s% _- |0 i& l- M
  10.    0x00000100,                      // Programming Page Size 16Bytes
    ' j# y3 @3 T" V
  11.    0xFF,                            // Initial Content of Erased Memory
    ) ~5 }4 b1 i* O1 {" o% a" H" X& L. S/ l
  12. // Specify Size and Address of Sectors (view example below)
    4 G! t6 \, f8 O! [- a; q2 g
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes , B  ?& r2 P1 r
  14.    0x00000000, 0x00000000,
    $ r3 O4 V4 \/ ]5 i, ], N
  15. };
复制代码

$ `9 V- v- p! ^注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。5 m* V% x$ }! Q1 h
3 E5 O: U# N* B4 t7 Y* T2 \+ x
  l, @- X+ i8 r" V0 L4 R
: L) K  t$ s4 c8 F
81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
& l/ L  s, A: B2 e8 f6 qC和汇编的配置都勾选上:
$ C  l* Y8 _; j% a$ X$ N
* {: r1 X  V% q% ~7 X9 s
8f2ba38db4c1f685f131e7f1f4e98551.png
( Y6 t+ Y4 J7 A  r* c

8 Y8 i: A5 |. I( }- f, l汇编:
% a& w1 d  m) i! W6 b5 I# k$ v% ~+ @2 s; u6 J6 E. C5 ?" u- u7 _' @
0d264153b167afa2303144bdd378d501.png

& j0 U3 R2 ]2 G- @9 ^" r- r( G' x( X7 T) M1 f- h
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
6 E$ b- y' @5 l) Z* k+ G
* Z1 i+ ?$ Y5 m# Z7 ?& n(1)加载以响应运行事件。: {+ [2 Y" ~- n$ ?; Q
' b  n# K4 C- u6 \
(2)在不同情况下使用其他例程的不同组合加载到内存中。
  R- B, f/ R3 H9 H: ~0 X
, F% W  y" w/ ^0 I8 |6 @+ U(3)在执行期间映射到不同的地址。
. b: G7 n* \: `4 ~6 v3 ]: H( y  d. ?" P: L% k1 N
使用Read-Write position independence同理,表示的可读可写数据段。4 y6 o) A& r5 \' j6 {) q

& @7 q, l3 P- j$ I* t81.4.9 第8步,将程序可执行文件axf修改为stldr格式
5 T; S) S0 C; D9 ]6 z9 B通过下面的命令就可以将生成的axf可执行文件修改为stldr。! @" J: w/ L, `8 {2 l2 ^, H

8 l. w! o& a' ?0 t* j* U; J0 B2 l8 H
f50b1594f3741e77b4fbd5523564d3af.png

& }4 Z! b8 T" M( I' r# E4 m+ A& ^+ Z6 o/ _7 c* R
81.4.10   第9步,分散加载设置1 q3 ?8 x) ?1 c) i
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
: o1 l6 H5 m" U, H; S, q% X1 Z0 q( s  [2 G7 j6 y$ u  _
8a01cb7c9be38519d4d0510a218447e2.png
0 m; g$ h6 N9 o+ c; p" ~! D

+ i4 v0 G3 N; M- v" K$ F/ j7 q分散加载文件中的内容如下:# a9 e4 W) x# |: y- Z" c# w9 o

; s4 t" P0 B% _6 c5 x% H4 M
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    - x! S( ?% R! ?, Y$ p+ ~' }
  2. {
      \" B# ~" @) ], s' C" K
  3.   PrgCode +0           ; Code* I- Y' E+ q6 J
  4.   {
    $ p, x2 g, K: r
  5.     * (+RO)
    ) H- w/ q, u2 p7 X  \/ ~
  6.   }
    * m( q# j- k* [7 [
  7.   PrgData +0           ; Data1 `6 i6 j  n2 D: ^
  8.   {2 K+ K/ H% Z8 S- C" t
  9.     * (+RW,+ZI)
    . B/ H: W, F) l4 m. f) ^
  10.   }
    ( L* D. R$ p! c2 y% F
  11. }3 F5 H( u& T: [( ]# ?4 u* M

  12. 1 J6 v. X, ]4 K& ?1 @* @
  13. DEVICE_INFO +0               ; Device Info) X9 r) X  M' m& |! o: m  z
  14. {
    $ E+ {0 J, x2 s, f$ x& a/ i
  15.   DevInfo +0           ; Info structure! t! b2 N; R4 j8 O! k& ~
  16.   {, d5 ?. v& e* m! ?, n- K& {
  17.     dev_inf.o
    8 f- B5 |9 w% a
  18.   }5 J8 v3 e2 g$ H$ j3 \) A
  19. }
复制代码

8 j+ l* \9 T2 m, p这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。% M+ W; c: f: B0 z+ ~
& V: G- L$ l; z: O  Y
--diag_suppress L6305用于屏蔽L6503类型警告信息。
/ @+ t6 S7 a) J+ ?- N
7 R0 M, D* i3 X2 D* g1 J  q特别注意,设置了分散加载后,此处的配置就不再起作用了:
& O4 w0 N5 g$ E9 g! ~+ ?. `/ M0 P* N% Z8 Z3 B1 }8 g
13b022190bd5115ed249129fc3886a38.png

5 y4 @* w5 B! H* Z3 t# r+ u, h! B+ J0 n# L- k5 ~9 m3 I3 x
81.5 QSPI Flash的STM32CubeProg下载算法制作" x1 M& h4 P( m
下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。6 H7 z$ E- g% a

: H! G2 X- Z6 I9 H* p81.5.1 第1步,制作前重要提示+ [& T# E: l8 S
这两点非常重要:
7 B% Q0 ]/ J6 h  o& b9 \
$ Q1 D3 ]* o1 s" V6 r, Y" b/ ?$ b% f# _  程序里面不要开启任何中断,全部查询方式。' x0 V: i3 T8 \
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
8 p7 s$ K: W- ?; g8 U81.5.2 第2步,准备一个工程模板
2 V' H0 U9 ?/ S4 D- } 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
" C1 A2 N0 `) V8 R# m; W
3 c+ @- C8 z& m* ]
f46a8d2276d0ffb7beee251cdb0809a4.png

5 p* I  K; J% X& \) z# D
, Z8 s4 S' ]% O0 `81.5.3 第3步,修改HAL库
' I1 J3 B4 X% d# I4 I这一步比较重要,主要修改了以下三个文件:5 V: N" O+ A1 X( M' d3 G! Y. I7 o
& b% t/ L2 G: f% G* H5 w* A
1efb78256b9465df406e3e9e95ae30e5.png

2 D. a8 j. D2 k7 _' g9 E; G
/ V' u' o) D& E& q  ~3 J主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
3 l& o/ B. t/ r1 _- q5 l
; |! n* i/ o: b; {! g' x& ~/ S81.5.4 第4步,时钟初始化5 t5 C5 b& u7 X+ `
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
6 [; ~8 u' m: V' N+ ^
' V" u0 E% `) h5 ~7 m
  1. /*
    2 f* ^5 T! d: {. [# J- W
  2. *********************************************************************************************************0 H9 O- W6 @  V  q
  3. *    函 数 名: HAL_InitTick, j' w0 j9 f; P
  4. *    功能说明: 重定向,不使用
    7 i- M0 \' r! C" d, F
  5. *    形    参: TickPriority
    1 R- _6 j4 x' A* l+ D
  6. *    返 回 值: 无
    ' Z, J# j9 N" A4 W1 v0 {" E: ?
  7. *********************************************************************************************************
    / k6 c2 y( e/ q: d/ K8 Y
  8. */
    8 l* E" P5 k+ D) [; H
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    & e/ D/ C: l* z
  10. {
    6 n4 X) P2 j9 K
  11.     return HAL_OK;
    & S4 f+ B. T! [( f1 W0 N
  12. }
复制代码
. N8 i4 o# e2 O7 N) w0 R
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
1 X* F$ b2 m8 j" n4 J9 s
) W/ E3 z4 x5 m$ ?( F$ q
  1. #if !defined  (HSE_VALUE) : W! H( H8 v& ~/ i$ e3 A
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    . i( {9 ^4 y2 G+ T
  3. #endif /* HSE_VALUE */
复制代码
) n; p$ A' Y: k5 j: ^
最后修改PLL:
( L& M  ]' r" D- K7 a; _5 M2 j5 l4 L7 i4 u' q
  1. /*5 ?  l# D1 G7 r5 `/ [7 F7 [
  2. *********************************************************************************************************2 O+ x' d$ _$ I
  3. *    函 数 名: SystemClock_Config
    * n, N" i$ o$ }. R% U; e
  4. *    功能说明: 初始化系统时钟
    3 j2 Z2 \0 v. ]2 r9 G/ s+ g
  5. *                System Clock source            = PLL (HSE)* v; l0 t; I. s/ w3 P3 ?- n
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    : [+ B. j. R" j6 V4 e/ }3 v% `
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    : F# {0 |" T8 v) U. q; }/ D2 p
  8. *                AHB Prescaler                  = 2/ Y6 k3 a7 z) |2 Z+ i
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    0 x1 z5 p3 X/ N: v6 ]( |, |: [& U2 O& j
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)* s/ x5 o6 U6 R5 _
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    1 @4 T4 _" z  y3 N# B+ G4 p
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    - p! Y) L: L- ^. ]! a" u  t; Y0 ^
  13. *                HSE Frequency(Hz)              = 25000000. ^9 S1 i" O$ n6 h1 d
  14. *               PLL_M                          = 51 H' B: t+ ^5 Z
  15. *                PLL_N                          = 160$ M: x7 `+ H2 Y' M: R' s
  16. *                PLL_P                          = 2
    9 R; c! J- a; c) o* D  G
  17. *                PLL_Q                          = 4+ |7 V$ S6 @# u. P. s4 q: G
  18. *                PLL_R                          = 2$ Q3 ~. L5 p( v  v
  19. *                VDD(V)                         = 3.3
    " q0 M% S! ^% g/ x4 S9 R4 A
  20. *                Flash Latency(WS)              = 4+ f  }+ w# x: H9 Y. \- g
  21. *    形    参: 无2 x- q7 m! x* O8 n9 E# R
  22. *    返 回 值: 1 表示失败,0 表示成功
    1 P& J, F1 |3 f: e, L
  23. *********************************************************************************************************' J! ]4 K6 r9 P/ d' |9 y- W/ O
  24. */
    * `' w7 o* R, y$ ~- F7 ~
  25. int SystemClock_Config(void)
    + J% m- I$ b3 w9 C  u- S
  26. {
    0 x- F- i) a1 u% O3 G3 s" R# L
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};9 m: e+ w! H5 f! k- T' Q
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};+ F9 e! q$ R; _: \
  29.     HAL_StatusTypeDef ret = HAL_OK;
    % }  M9 u0 Q" I+ W3 ]4 c

  30.   {  I" Z; d/ ^; k, ?! F; E
  31.     /* 锁住SCU(Supply configuration update) */
    5 r( H/ h- o5 j' j. f6 c$ E
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);6 s: x. H9 f7 V3 p: |

  33. : |. n* }2 e5 ]: a% P4 @
  34.     /*
复制代码

+ ]3 a' J  G! G0 r' P4 ?9 N/ N      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,5 R2 p$ N1 {% ?8 e% n# H: \9 T0 s
         详情看参考手册的Table 12的表格。
) v/ H( k8 ^7 N# u; u. R3 Z      2、这里选择使用VOS1,电压范围1.15V - 1.26V。
) r. @3 S& Y+ G1 L4 m1 F1 l   
  1. */" |, J+ ?5 z3 j$ @; {
  2.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    0 N$ u4 G* \0 ?6 ]' z

  3. / }) o) ~2 q1 b# _
  4.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    . @) Z6 `; B9 h6 Y; \

  5. ; B: M; m- O! k# r
  6.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    5 _0 Q/ I$ z4 E0 [
  7.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    : j& K! ?' d/ f$ W
  8.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    : U* i+ T3 b4 V* g
  9.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    ) N  Y1 |+ C8 h( |2 E& n# s
  10.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;; V' q/ ~) o! y' a. }# ]
  11.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    / V# ~1 @# p* J
  12.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;" v7 Z$ [/ N3 |9 k  ?+ k
  13. 7 P& z$ s4 i* d5 a! x$ i+ Y
  14.     RCC_OscInitStruct.PLL.PLLM = 5;5 _" E  {- y& C- F6 H- o
  15.     RCC_OscInitStruct.PLL.PLLN = 160;+ \+ x( V3 l& P. ?# _) P
  16.     RCC_OscInitStruct.PLL.PLLP = 2;& C! A3 j: E- u8 K( m% c* `
  17.     RCC_OscInitStruct.PLL.PLLR = 2;; p- W7 T5 A, W+ y7 u
  18.     RCC_OscInitStruct.PLL.PLLQ = 4;        0 t  l9 K4 q) n6 v' s6 |
  19. 5 o! K: Z3 F1 L7 J% y, g, o
  20.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;: W. ?3 {* v! ?4 M- y* M0 O
  21.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    % u! I5 \( j* t' n/ y' G
  22.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);; O& G  d$ L- L( f
  23.     if(ret != HAL_OK)! M4 \% \- z7 Y! g+ a3 T
  24.     {7 I9 M$ w1 }9 F$ ]8 o9 K
  25.         return 1;        
    7 V2 F& T: z9 N; t
  26.     }3 L- @+ X* D' D5 o

  27. 1 |, c! @7 q+ J; {
  28.     /* " E8 L  U0 A" z5 B: ]
  29.        选择PLL的输出作为系统时钟
    % [8 S* {  t6 [! k, h8 I" J0 {6 o
  30.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    ! Y9 P' B/ b% \( Y$ Q
  31.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线( N% ?. C% m8 O' U
  32.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线, F# A7 g9 J, E4 g7 D7 q( G
  33.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    9 J9 i+ O% i( H: B" Y0 ~$ ~+ s( X& C
  34.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    ! }* B4 e9 G, N4 `' k4 J2 v6 l8 ^
  35.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     & O/ Z) o  a6 [) F# G# H4 u2 t
  36.     */% G& o* i  E5 ^4 q
  37.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \4 Z) p3 S- u! H
  38.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    2 t9 ?# X) \% B* s0 ~/ F
  39. ; t4 N: N+ H" r. q" j- C8 P
  40.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    8 C  t3 h8 G7 g7 r7 j0 ?* N
  41.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;5 b, ^3 \, \+ b$ o; ]4 M2 F, a0 Q
  42.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;- y' s3 d- O2 y) }
  43.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  3 m+ F; D7 P9 N* z6 U5 q$ ]
  44.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    ) V, e2 @9 c6 V+ m" b4 m& h
  45.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; ( [4 _2 n5 n- _7 l: @3 }
  46.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; : ]) v8 l1 G3 M! [

  47. ! U- k" g) k4 n) {! ?
  48.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    % f- z! {# q$ W7 S2 j& b! d
  49.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    4 Z. f3 w% ?/ ^6 }2 e; n
  50.     if(ret != HAL_OK)
    6 Z- `9 t0 e, K
  51.     {5 x1 c! m& ]# u6 h7 V
  52.         return 1;/ @+ w' R. S" X$ f
  53.     }3 r- u- e3 }/ ?- k8 [+ c1 V
  54. ) R/ k; }+ P0 [! c- _$ \
  55.     /*: ?. t( \- E1 }3 u; |
  56.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 . z* h: R: L; b1 v: \8 Q1 y; [, O' Q
  57.       (1)使能CSI clock, L+ t5 J2 `. k- B8 G8 X5 _6 R
  58.       (2)使能SYSCFG clock
    2 ~' F; S& Z1 I0 Y$ ~5 w* C
  59.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0" G! T0 v6 f6 m  k+ d! q1 o
  60.     */
    / r( [/ K6 v9 b$ {
  61.     __HAL_RCC_CSI_ENABLE() ;
    : U% H% T: Y. F: ?7 V& B. g2 {8 V/ U
  62. $ R8 A- Q% _" C8 F- T7 x% T3 f, r  @
  63.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
      ?- T6 m7 e. F( L$ Q$ @
  64. 2 u& H1 o* f( z
  65.     HAL_EnableCompensationCell();/ x9 J7 y$ t2 E) l

  66.   Q! Z5 l) d3 p* d, @/ ^0 s9 P
  67.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    9 X* p4 l; G& A
  68.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    ( k# P1 y3 y, E  s  l& \
  69.     __HAL_RCC_D2SRAM3_CLK_ENABLE();$ l2 ?/ x* ~4 ~$ S9 H9 |

  70. ! \/ r9 n* B) {  M" D8 P- e5 O
  71.     return 0;$ ^2 s, a4 [- U, b) {8 o
  72. }
复制代码
9 Q7 E# R+ t& b/ H) d9 A2 L# |' G
81.5.5 第5步,配置文件Dev_Inf.c的实现8 Q0 r4 N4 P! i! x
配置如下:
: q! D$ y1 z4 n) M
  Z% \" T( A# Z
  1. #if defined (__ICCARM__)
    6 h9 C  ^# l  M+ R
  2. __root struct StorageInfo const StorageInfo  =  {9 W/ _+ s- f% l
  3. #else
    , |& ?) d. ?- a$ g4 I( `3 o3 E
  4. struct StorageInfo const StorageInfo =  {
    & W, _6 Y6 p3 M+ t' d: O
  5. #endif- f5 I. i2 f- w
  6.     "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */2 v# B% m, \( {" `9 ?
  7.     NOR_FLASH,                      /* 设备类型 */# Y6 }2 |. `! K/ \7 B5 d" M
  8.     0x90000000,                     /* Flash起始地址 */; w9 [& e& e& S8 \% ~
  9.     32 * 1024 * 1024,               /* Flash大小,32MB */* ^% G1 x# M) u; f3 V
  10.     4*1024,                         /* 编程页大小 */! w& d- N+ E2 T9 s& R% K
  11.     0xFF,                           /* 擦除后的数值 */, u* g$ A% B# L0 n
  12.     512 , 64 * 1024,                /* 块个数和块大小 */) ~! h! s2 b0 A$ j% W/ ^' x3 k; N
  13.     0x00000000, 0x00000000,9 k6 O* Q) v1 j6 f; J
  14. };
复制代码

5 r4 y+ b6 ~8 B1 T) M3 Z注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:
1 s+ v3 U* {1 w! Y0 H3 p6 D# Y8 K7 k/ A+ I! ?
bc495adf8d970e6e1fb6822b88b94e2f.png

0 o9 Z1 \" o- ^/ F
' T8 Y0 G* @* [; p/ K* T5 W- q81.5.6 第6步,编程文件Loader_Src.c的实现
' M4 h) x7 E/ c: v下面将变成文件中实现的几个函数为大家做个说明:
- \/ A5 X. \' ^3 [8 m9 }/ j( }
  j! I3 a' i; i9 }; w  初始化函数Init
6 w6 U; L! v/ [, I
  1. /*7 H: M3 z# G1 x1 G* Z$ ~
  2. *********************************************************************************************************
    # G$ n) M! `7 t  O6 |
  3. *    函 数 名: Init/ C5 n3 Z/ ~7 x
  4. *    功能说明: Flash编程初始化
    7 m% P4 y! B2 S: z$ t/ ]2 J+ p% X8 C
  5. *    形    参: 无
    5 Y2 E9 `& l9 [8 u8 ~/ W
  6. *    返 回 值: 0 表示失败, 1表示成功
    4 f- o5 q5 i4 R! x/ A0 l( B% Q
  7. *********************************************************************************************************
    " f3 [: u2 S! s1 v9 R, C
  8. */
    ) c$ W4 U3 Z6 v3 C
  9. int Init(void)" _7 V- p0 ?6 a# @' P: c
  10. {   . q% N; n% d4 R6 Y7 X7 J! q0 A
  11.     int result = 1;
    9 L( `! W  J; B4 B: W" x& M* T; ]

  12. : N! Z0 y) k) |
  13.     /* 系统初始化 */: j" F5 V" k* O& ~
  14.     SystemInit();
    2 l" D* ]9 L0 p* L

  15. . I, }. e: @; u( h4 y6 @' ~9 G0 K
  16.     /* 时钟初始化 */
    : X  n: `" Q* ?0 M6 s
  17.     result = SystemClock_Config();+ T& N2 j  H6 y+ M) W& S% @" ^
  18.     if (result == 1): m8 ?, S- A' ^& F
  19.     {, D1 C- e1 S7 D% P
  20.         return 0;        + S  [9 [2 E9 D! Y7 ?) L
  21.     }4 l7 a& ]+ r7 G' _2 {+ \

  22. $ z5 f: W6 ^  v6 t
  23.     /* W25Q256初始化 */
    4 N9 A# Q- r) W7 G/ J, k
  24.     result = bsp_InitQSPI_W25Q256();, N1 h- P' Z" R* H+ f* ~7 f' W" S* z
  25.     if (result == 1)
    5 p; e3 B1 {8 X# ]" ^) |
  26.     {, c% o' I) k# B1 h% t7 k0 M
  27.         return 0;
    7 d. |  X4 M# Y0 p1 x
  28.     }
    ) ?/ ~/ i8 E1 ~# m& Z) \
  29. ( D' @, b' j' E4 J+ p1 \/ Y9 G
  30.     /* 内存映射 */    & ~# B* e7 u* l' w1 C5 Z
  31.     result = QSPI_MemoryMapped(); 5 Y2 \! m$ S: g9 N& k) E
  32.     if (result == 1)( U' [. I8 g/ \8 h% D' M2 Z
  33.     {" ~, ?7 C, X) K( p) [+ v3 L
  34.         return 0;, R% s2 P5 x# X( h) D% U7 Z3 J) k
  35.     }7 c. m9 Q3 F4 U9 d; Y8 E

  36. . s3 ]* ~- G9 I
  37.     return 1;
    " @5 a. q1 y) R' B- W
  38. }
复制代码
  Z; Q: x3 ~; L: X4 R
初始化完毕后将其设置为内存映射模式。# f. ^8 z6 d0 P. D% L  [
7 Y4 {/ p# H  D8 w) I
  整个芯片擦除函数MassErase
* s2 b7 K) C  K9 H3 L$ I整个芯片的擦除实现如下:5 n, H2 m) \) u% A0 y, R
! c+ X* o  w1 D. L* v: h, e
  1. /*
    0 D/ H+ Y; L% ?1 v% |* U
  2. *********************************************************************************************************
    6 g. ^3 S4 R7 [( _  h/ I' p
  3. *    函 数 名: MassErase1 o- A6 D# t% P4 W3 H
  4. *    功能说明: 整个芯片擦除
    % R6 j6 t7 x7 g
  5. *    形    参: 无
    ! Z1 u6 E4 P* Y
  6. *    返 回 值: 1 表示成功,0表示失败7 P- k) B; s9 @2 ?4 a9 |$ R
  7. *********************************************************************************************************
    " h2 b# v+ {( n0 M( V- U! D! A
  8. */
    ' S& }+ U; `: M7 ~! t; m
  9. int MassErase(void)
    ' f; W  t' d& p& Y* Z5 D! u; ~% W
  10. {
    . a8 d; b: x/ b$ C2 z
  11.     int result = 1;
    # f" O6 U" b9 x6 s% |# }

  12. " h6 k* y  u' C, ]6 o
  13.     /* W25Q256初始化 */9 }2 T9 B% Y6 X( z% @0 I9 e' e1 R0 N
  14.     result = bsp_InitQSPI_W25Q256();
    * M6 H1 C9 q. h5 p4 C. B
  15.     if (result == 1)2 N' ~+ R: `" A  B
  16.     {
    5 g# Z) B2 m  R+ b" I$ A
  17.         return 0;
    . m2 |; E& I# R, J* o0 R/ q3 g
  18.     }
    + K& a2 z- ?* a" m
  19. 9 E6 |* j0 K! R& [2 c
  20.     result = QSPI_EraseChip();
    $ ]( V1 t: `2 ~! Y- M
  21.     if (result == 1)
    - r* V3 u  O) ]# j+ q$ m& G( ]
  22.     {8 O' o6 s" p$ }+ G0 e* T# ]- K
  23.         return 0;
    " N# [7 M8 p3 z% S2 U5 \
  24.     }    + F. o, o0 Y( @0 q0 p
  25. $ d6 Y0 H# r% d( P& a; h
  26.     /* 内存映射 */   
    # Z& N) Y, S9 C" ~' `/ z4 N2 o
  27.     result = QSPI_MemoryMapped(); ( ?- A" O% \- x7 @
  28.     if (result == 1)" v7 Y8 ^5 n3 A
  29.     {8 W) z/ C9 R1 J! Q/ q0 d8 z  ?
  30.         return 0;$ i* I4 ]) c3 B
  31.     }: T0 L7 j1 _. ?+ {

  32. 5 p& Y2 U3 |. y' K! E& i) D
  33.     return result;          + g1 X4 x' {8 v/ F  @, _
  34. }
复制代码

, Z. j' n0 Q. g+ a/ b% A% H' \  扇区擦除函数SectorErase% P" m, U; R% [$ X: n* m
扇区擦除实现:7 h+ m. F6 y& t4 R

' v/ N& [3 @% G
  1. /*
    / `5 y& n3 K0 M5 I$ y3 x$ ]) c
  2. *********************************************************************************************************, Y5 |: B- Y! ~( N
  3. *    函 数 名: SectorErase
    9 @, G# S; W: v* i
  4. *    功能说明: EraseStartAddress 擦除起始地址
    5 P6 Y. X" `* B$ S: O- x* T
  5. *             EraseEndAddress   擦除结束地址
    5 q7 k# q& D/ K- E1 n3 Y& W
  6. *    形    参: adr 擦除地址
    ) t# q# R! p7 D8 s4 D
  7. *    返 回 值: 1 表示成功,0表示失败3 U9 N+ ~# o  {, }
  8. *********************************************************************************************************5 i, i' G3 |! D5 q. o
  9. */
    - d( o6 a' i( t
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    7 f' g3 j6 T$ s; G' D
  11. {; |) T1 P! S3 J3 W# y. L
  12.     uint32_t BlockAddr, result;# \; P; Z6 }6 J0 }( @' C" G
  13. 0 m+ X+ N0 f0 G! E' Z
  14.     EraseStartAddress -= QSPI_FLASH_MEM_ADDR;0 M- r# X# v* t! K/ f: i
  15.     EraseEndAddress -= QSPI_FLASH_MEM_ADDR;6 v+ X& ^* y2 e
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x10000; /* 64KB首地址 */1 B/ T" E% i8 {8 I2 S$ K
  17. ( g1 O3 ?2 n- F' @% f7 n
  18.     /* W25Q256初始化 */
    $ H7 `, G7 h2 g6 I6 `6 f
  19.     result = bsp_InitQSPI_W25Q256();
    + l8 [! P! m" T1 X+ W/ P$ h
  20.     if (result == 1)
    9 n% f9 |: A# H
  21.     {
    3 b/ x/ Q2 O# J
  22.         return 0;        + _7 ^  B8 {0 y$ n6 y. _! b
  23.     }
    - ]3 v* s5 P6 H0 R
  24. " G3 d* h4 [% b6 N' n2 h
  25.     while (EraseEndAddress >= EraseStartAddress)0 G" B6 t: K7 a1 k. f  J* \$ V
  26.     {
    ' k" `) g5 d1 ?- ~) N  f( [6 I
  27.         /* 防止超出256MB空间 */1 j1 H% Y1 r! k$ }. q
  28.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    5 H2 Y2 [# Y: M

  29. 6 l. C, T+ B5 J4 z  E4 {* M
  30.         result = QSPI_EraseSector(BlockAddr);   
    & }% x' f7 A% z6 G
  31.         if (result == 1)
    8 [+ k4 a% M# p" ]' _, z$ V
  32.         {
    $ a8 }1 v9 h3 l) k  F; u/ y7 y
  33.             QSPI_MemoryMapped(); # H2 V! P2 K* O* s, R) i$ a  H! Y# ~; i0 A
  34.             return 0;        / G3 }& _1 b0 [7 ^) g8 Q3 f( @
  35.         }/ l7 ?* m' T8 R/ V: [3 _

  36. 7 K- g! @0 F5 S5 t) B( A) b
  37.         EraseStartAddress += 0x10000;
    2 }' i5 {% E4 d$ s2 R7 t0 Z
  38.     }
    $ I0 \) h, j7 q  R3 u* _: M

  39. 8 E7 j0 R" V4 O% J- _: \
  40.     /* 内存映射 */   
    " |; K, W4 H' o7 a9 L0 I
  41.     result = QSPI_MemoryMapped();
    ; F4 J" B1 W! V( a. I
  42.     if (result == 1)
    ) Q) F* U! [4 V
  43.     {
    ) K/ B0 x) h0 m7 R
  44.         return 0;
    : v% Y: t) t9 f5 R$ P& O
  45.     }
    ( v7 v" r& E* Y+ Y+ `
  46.   Q0 g0 G) a1 V
  47.     return 1; 8 c/ X& ^: p- K- n1 D6 l; @
  48. }
复制代码

- M4 e0 A# y! Z2 j$ D; V这里要注意两点:& i9 H5 e- {# J8 L! k2 m" c! ]
, K' z2 _7 ~1 ]
(1)     程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
3 _' A% C  T$ y- `; n- [$ k2 E1 a8 a, w
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。
* \+ J3 v( z/ v! b  k
9 V+ F% _3 B3 D  页编程函数Write
) O% F, b4 ~( s+ c! M5 e: G页编程函数实现如下:) u. a& @% ~; J2 |' Z  M2 p- R

' j2 U9 j3 P( C  i
  1. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)0 h/ P% A/ D+ @" @5 [) `% r
  2. { 7 h! I( ]% L/ n& s
  3.     int result = 0;
    3 T* H: z6 P5 L
  4.     uint32_t end_addr, current_size, current_addr;/ r2 B. J6 f5 A! x

  5. # H* r' K% x- B1 a% U# N7 \

  6. 7 C1 j# t- }3 p4 x; S
  7.     if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    8 k0 [) n1 y2 G/ v1 R  v* b' \
  8.     {, y9 L) B, a$ s( x1 E7 r2 W1 ]/ H
  9.         return 0;( B4 m3 p1 I0 q2 V
  10.     }
    6 `+ m4 @% p2 p7 G- Y& T" U

  11. " i" I/ ?: m- o' P1 c" S
  12.     Address -= QSPI_FLASH_MEM_ADDR;
    % W) u! i5 j9 w% z

  13. 1 G/ K" Z5 Y  q6 ?
  14.     /* 计算写入地址和页末的字节大小 */
    : z1 B  e) M3 b/ T* D2 U
  15.     current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);
    1 d% M7 t3 x& E1 i) Z
  16. 9 ?: W. c( m5 B6 h, l9 |# Z
  17.     /* 检测页剩余空间是否够用 */% b$ o4 o$ `* Q; p8 r
  18.     if (current_size > Size)0 D1 z* q* q  {$ g8 |1 T! W
  19.     {
    5 i; F9 Q0 g' ]2 |3 Q
  20.         current_size = Size;, P+ s5 z; l; f+ i' V
  21.     }
    8 B8 \. E0 B3 d; A
  22. ) c9 |/ G8 a  {
  23.     /* W25Q256初始化 */3 |$ R/ R& B7 P4 D, v" J
  24.     result = bsp_InitQSPI_W25Q256();
    ! w8 ]( K0 O5 z/ B  K6 E! E
  25.     if (result == 1)
    , i; r/ L, c6 k
  26.     {1 g' `1 B; r. z# [9 {
  27.         return 0;, e3 S3 V% O4 m5 i5 J. Q+ M
  28.     }
    ' c# p5 N  [3 T! Z$ o

  29. - k, o4 O. D, |! `, e
  30.     current_addr = Address;9 e3 y: O/ w+ o1 L$ i" [$ P4 g
  31.     end_addr = Address + Size;
    $ {' m5 d% W1 F/ @1 z) m+ J
  32. 0 X" X( B; a: }3 w% @6 [
  33.     do{( o/ [1 i- r* b3 ?6 J; }8 s2 ~
  34.         if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)
    . j1 g$ H7 P0 ]' Y
  35.         {
    4 a& j: w; }2 V$ w: m+ b" K8 `
  36.             QSPI_MemoryMapped();  5 N' Q. X8 W" N! G& F. e* s* q
  37.             return 0;  1 D) P& t$ g! N6 E( _9 Y9 G+ g
  38.         }
    8 l/ L7 _- k$ V* }5 B% Z" h

  39. . Z3 o9 Q7 o, G$ Q' t
  40.         /* 更新地址 */
    % S& S$ S2 ]  b0 x% _
  41.         current_addr += current_size;6 h2 l' ]- T% {6 r8 ], d" y
  42.         buffer += current_size;8 Y3 J% ?2 v$ f' J0 L" m
  43.         current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;
    7 q" X- w% v7 S! w% Y: {
  44. ) ^) q; D4 t: h/ u
  45.     }while(current_addr < end_addr);
    4 _" k+ S* U+ D- D+ @

  46. # z" }- C9 `$ Z) u3 ~( g; O
  47.     /* 内存映射 */    ' p9 X" a7 B( w6 M, U
  48.     result = QSPI_MemoryMapped(); # r5 a- d) j  S
  49.     if (result == 1)  \4 G! p' S) D; M9 d8 {, w
  50.     {
    ' T) C/ M% a* b) A# u. E
  51.         return 0;
    6 z& |& y) m4 C! W; Q$ f, E1 I
  52.     }5 v  D; `8 {# l( e- e, M0 m8 H& a

  53. ) S  |2 N6 @8 O/ A2 L
  54.     return (1);  9 ?) V* |& i8 r. X) o3 }
  55. }
复制代码
$ L& }9 c' [0 O3 [
这里注意两点:
2 u! b. k' M& A1 d$ c. @
  y& r( P7 D# }9 }' E(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。  a& Y* j# W* U7 H
1 A; m( E! ]/ d7 K
(2)     程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。. \; k; o5 F: @2 D0 `: d/ o# w

( L+ P, w  N. Y2 p  读取和校验函数
4 @" Q! [3 d% b: U6 K我们程序中未做读取和校验函数。4 O) R  D, L; C1 e) p) z" |

: m* e8 v3 ?3 H, m(1)     如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。- `# h9 E1 r% t2 |* e

5 E- }+ M% I" Q(2)     如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。+ c; c3 @; Q$ h' a* u
9 G7 c& M; s5 A- x
81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)# w# n; q5 L: |$ Z- ~
最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
. ^2 Z0 K! }+ e! X0 O7 l6 ~3 n: h
4 o3 k  i; S. b% h- R* ?
  1. /*
    1 y* m, v/ \! Z* r, P2 P0 w" D
  2.     STM32-V7开发板接线0 f4 r: s7 l; V; Q# E

  3.   E5 F- ?) G6 N3 l- E
  4.     PG6/QUADSPI_BK1_NCS     AF10
    7 Z6 m& w: Y1 ^1 {8 I
  5.     PF10/QUADSPI_CLK        AF9
    " W+ M$ g/ R; ^
  6.     PF8/QUADSPI_BK1_IO0     AF107 X6 w8 i* E" J! z* K
  7.     PF9/QUADSPI_BK1_IO1     AF10; B: {0 Q8 t2 E
  8.     PF7/QUADSPI_BK1_IO2     AF9
    & J+ Q" ]  Y4 `% S1 [4 B2 F
  9.     PF6/QUADSPI_BK1_IO3     AF9' m' N4 I+ z! }% P! |

  10. ; o7 m, f& H7 Y6 F
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB/ c0 e8 d: b( h1 l$ O' b) [
  12. ' F/ ?1 j0 ]# K9 m4 w+ T
  13.     H7-TOOL开发板接线
    1 {" P. y( U3 F

  14. * |% v; i% x6 c* D! A: |4 i" T
  15.     PG6/QUADSPI_BK1_NCS     AF10! I& J: b( F" M
  16.     PB2/QUADSPI_CLK         AF9- Q- v2 p0 k& T. ]" g; f4 `
  17.     PD11/QUADSPI_BK1_IO0    AF10
    ; x, B8 o( e* P" K! e6 N0 [
  18.     PD12/QUADSPI_BK1_IO1    AF100 B/ Y# ]! Y6 V" \2 {' ]
  19.     PF7/QUADSPI_BK1_IO2     AF9% X) f% A5 X/ r3 |: t/ U
  20.     PD13/QUADSPI_BK1_IO3    AF9
    7 F) W) v& a- Y( b* [& `0 _
  21. */% g; E- p( w- T1 @
  22. * q0 F" J" B! _
  23. /* QSPI引脚和时钟相关配置宏定义 */
    " q* `( T! e" T  Z
  24. #if 05 P) v3 E1 \6 S1 }7 Q3 V! |& N
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    5 _5 |0 M+ e% j, s% Y! o7 _4 X
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()* m. V& b8 {4 P) k& x
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()2 p* C, ?, v* q0 x
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()) c# P( q0 X7 Q3 z6 p  r/ K( h
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    ( H% D- u* H# h0 E+ e) o
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    $ ]4 M( H) i0 |. F; ]( K5 X8 y0 t
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    1 D& ]; y) Z" x8 ]
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()$ P1 L0 @  q! y+ b- X
  33. & Q0 p7 J- ~5 o6 e& @3 Z  f
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    $ h. J% f4 c- U% O' i& ~1 K- B
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()& Z+ {( h( w% w% j/ ^; A  N. |
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()" H$ e: l& \' }+ c" G& f3 {
  37.   ~3 U, ~' {2 ^5 V, n0 I- c
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    & \0 m" M: c" `/ E$ N
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    & i. W& a6 @: G/ l: K
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI8 g. ]( Z+ V* e) L. o9 X

  41. ) c- S1 R% n2 w
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2
    " E6 q! Z7 |% h; }7 I8 j
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    ' j& V" s3 h+ l/ \
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    + r) W2 f( U' H
  45. 7 G; n# g8 r& u5 d- u
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
    0 N% W, \! P! V! D5 m: D
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    # Y" i4 j) E% _" p
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI5 Z( I/ T6 C$ G: w# P2 N! t$ e7 I0 [6 `

  49. 4 W. e) r( A# F! c# r  [) ^
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12* i/ [* W; d! h9 K
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD9 A/ _6 L5 a$ Q: t+ ~! g! X2 x4 n
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI( D4 r- F7 r: x: q3 c3 E* B

  53. 5 _7 Z/ m6 Y3 I, |8 u* I# \3 r% L8 S
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    * D. ]" u7 _8 ]# r7 w
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
      s' w3 N8 H& ^6 s
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI( S0 A# x5 T/ m; P2 e. W1 c
  57. 4 ~" G9 {$ X$ [# Q) M
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13( H0 S. n& A" Y0 Z+ U! g* E% I
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD
    , {- }. \5 j8 {
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    6 T! I, Z! j' i2 f% g6 }2 U
  61. #else: ?: t. H4 i' U3 z2 Z" g
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()- N$ i$ b& k4 G+ P; H" u9 M
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()+ @% s5 g# S# f" \7 n' `. W
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    1 J" f+ X' g, f4 v$ f# ~% s
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    ; ~7 x( w8 e: E( F
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    $ W0 ?9 U) f$ C8 ?
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    1 }7 m% q/ l2 z) e- V$ m
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()+ j& K" W; u1 V4 r4 V7 B. s
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()) M" v7 a4 J5 f

  70. : @7 Y$ x8 i0 Q, S3 }+ J, g
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
      o- ]& D& ^" B4 L) v
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()" D# q' R9 V$ V" B6 v
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()' Z# u/ K" _0 H: N$ G0 b1 p* J
  74. # I0 {: y, t4 z" e% M/ P+ Y# c
  75. #define QSPI_CS_PIN                     GPIO_PIN_61 h- G6 C% d- M
  76. #define QSPI_CS_GPIO_PORT               GPIOG5 b2 v! a2 v* o
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI# U) @  v- Y% H6 Q; l

  78. 5 H0 v# I  w$ f6 v! @9 S
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    ( V* W7 V0 H: ~; `, H: Y3 O
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
    5 v9 U$ Z$ L. `$ u5 _
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI. x* i0 Z8 R$ `$ X
  82. $ ~! q$ E: i, Z0 }" V  C
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8( ]4 Y  K6 _$ E% ?* U
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF" P) ]: `. h* E* t* e# N
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI" M% b7 ~4 [4 x" P6 D  D6 K" r
  86. & u* {+ g' ?* [3 t3 z4 O
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    : U; I  A" a! _- _9 Q9 Q
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF
    1 R" I, U! q5 ]! ?' o- @' s1 y
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI
    : S+ [% `% D0 K% ^. Z8 a
  90. " f$ W* u' V' M7 ]. c/ H
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    6 W+ M/ O6 Y( g
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    . p- V0 @! J5 B8 J1 M* }8 Y" i
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    ) m; a( I1 F9 ~0 z: r
  94. 6 i2 f) h7 F$ k. X& J% u
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    + [( O" p5 @  ]* A
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF; q) _' ]7 b/ N: K9 E5 g  B
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI* k: ?# }1 n. I" H
  98. #endif
复制代码

5 a2 C% a# C* a硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:- z) n# L6 N4 u7 T, K- N: h

0 a* r  C& j1 O1 k2 n主要是下面这几个:1 D+ N9 _' k& q* y9 G) t( ]0 b

& g4 ~1 |5 |. M* G; p$ L
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000
    " ?( U+ y2 H2 y0 B. U0 j0 y) @
  2. 2 n6 X: G' d' j7 f6 }' A
  3. /* W25Q256JV基本信息 */
    * P: V0 R- ?7 x1 n  w3 P1 o6 E
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/$ s! H5 |0 ?4 a* Y# `. Y8 E
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */
    9 Y$ G8 D& ~2 P# e& C1 ~) b5 Z' N% Q) p
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */) x  i% e7 o7 c* o" E5 _# V% B, A1 _' T
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */6 I0 T9 U  K) U4 j
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    - ]: A- c! o. X0 Z$ v
  9. * ~) t$ p! P, J, \
  10. /* W25Q256JV相关命令 */
    3 C+ R% K# [3 P% v+ y& M
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */
    $ F7 q! J4 j. U, p2 {
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */6 H9 M. T4 s7 X" K7 t& K$ ~
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */
    0 S" {6 z3 r0 t. ^' p# m  H
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    / s; _, N2 M% x" Y+ v/ M) Z
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */1 }) C3 U5 ]' T0 s  N! d
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */2 x7 }/ w: Z( r8 |4 `! d. S
  17.   ~0 \+ ]( P. w, u0 I$ A
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    ! r" y& X( S% n3 s2 J+ t! `
  19. * Y4 k/ ~4 ?  v
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

2 m1 F! ^! t5 K- x81.6 QSPI Flash的STM32CubeProg下载算法使用方法) x+ i* [; D8 l, b" z, _
编译本章教程配套的例子,生成的算法文件位于此路径下:  g2 w3 |) x) o5 y
* K+ ~- s7 ?8 U( g% y0 N) j
d45f9274d8762b095790b3e6778b0a93.png

0 k/ t% \- `/ k; @
( M* |4 h# ]2 z# K8 t% R) p81.6.1 下载算法存放位置
, c! ?- T- Y: F1 x! s: ~生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
; S4 Q) a; d7 ~: j0 R- @) s5 l+ v: z6 c& W1 W
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader8 K2 ]: j% E. s6 x

- H; L& [" O1 I2 \: F2 z9 j
60cce5a15415e27e9d5b75b05028d1c0.png

+ g! @$ a6 ~4 o: _4 J$ }* M# O& E; r- F
81.6.2 STM32CubeProg下载配置# N1 S5 |6 N, |; j) r7 s6 q
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
' M7 k% F  J2 L& K! M. u5 q1 T5 K. V5 _9 j1 {
9cc179641e765ea43cb38d268bf734df.png

9 K% h% F* N3 t6 R" {# z0 Q
, u! s% A3 L9 R: B3 K点击Connect后效果如下:; J2 Z4 h3 X4 K) I! ~

/ r5 s1 v" T+ X9 E+ ^
b39ce8a738163430db2119bdd87e3fbb.png

' v/ b+ c$ f% H& }
% y' X. S( V  M( G7 Q2 n在这里选择我们制作的下载算法:
5 S6 F- `- F! H. D) A$ t/ c7 K  D
17f6e0f50a18ae818faaa1399daef12f.png
% M$ [+ r" b7 y4 k
2 J% f( o6 |# \9 m5 C" q
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming: _, C. @# M. N- y# p" _
1 J4 \8 G7 o& [5 g6 b, h
43a0d90faf1d392f5a74eaa7777e4178.png
) v% G3 d" O$ B1 R) K( C# O- s: U
! f! O$ o6 G9 z
下载完成后的效果如下:, g* Y& {" F8 ?& z
% ^+ z0 V6 F2 V% L) j' @$ I
a1d6662baa9e7be00f7e67e02b5c9b79.png
+ m4 Z/ p, C2 [  L) t0 T! W- |

' q2 W1 I7 U* Q% U* V+ R% Z2 L81.6.3 验证算法文件是否可以正常使用3 r) F) W0 m  `
为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:
  e* B' f* i$ C) @7 V6 F8 F3 ?: x
0 k. F! g. s1 F! h' {' Z" a* e
3f2eefeef3db68623cb8969974c40a20.png

2 E4 e6 x2 Z: `& f7 \% w, Q" a4 H( V; m
81.7 实验例程说明
6 O% l& t5 V' R# p9 [本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。/ a* x2 m# F$ ?) _3 l
- H9 X% o0 S6 b3 d( c
编译本章教程配套的例子,生成的算法文件位于此路径下:7 v8 u! a: y  Y& A, \* Y& }+ h

7 y6 T; I$ ?8 ~* K" o+ r
b7028271136c8053d7300063ed4aec3a.png

+ b' M) [8 ~0 H* ~) b  X. L
( X  w8 K/ C/ ?8 m0 y1 n. X2 L81.8 总结
! }/ O# Z1 x, i本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。7 e& @, e5 |* t) {! H' Q

$ V! [' D( T/ A0 \' K( y
5 I* N& e! F, Q2 K1 e' K/ I. n% y. o, g
收藏 评论0 发布时间:2021-11-4 23:27

举报

0个回答

所属标签

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