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

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

[复制链接]
STMCU小助手 发布时间:2021-11-4 23:27
81.1 初学者重要提示# L! a1 ?) H# L% M- i5 ^& e2 L
  QSPI Flash的相关知识点可以看第78章和79章。1 P& H5 A6 w9 z* A+ M9 @* F
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
* E# r4 y# x/ s7 Y  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样- F1 i6 a" n* S
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。( I% b  w) L: e- |8 j
81.2 STM32CubeProg简介
2 E/ n# f* r! m; |STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
) ~# x7 F: i" h. B; p! k. N* {# x
6 _& v1 z0 h% z& y5 [( t8 M7 U. {
1bbabf500126f58ff1439b6bc36014be.png
# S9 i7 C  ^3 V7 {

, X# H9 f$ W, s. p1 H0 a6 v. K81.3 STM32CubeProg下载算法基础知识
! C( |7 X/ s, j' |3 p: fSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
* K7 B( Z/ J6 i- [+ A
; @4 r; b7 f$ @! W# o: W! V& _8 a81.3.1 程序能够通过下载算法下载到芯片的核心思想
' a, @1 Q8 }7 G认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。0 f$ D/ G: l" |/ i& {/ E$ M

3 }9 p' k0 @$ U/ [81.3.2 算法程序中擦除操作执行流程; |  P: E4 }: ~, t: S8 E; B
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。7 q0 n' N5 X. c: U0 G

4 i( I. H4 E; S! v9 E. L. |擦除操作大致流程:% J7 j# t* L0 E! m2 B

: W% x1 _$ N; }4 O/ X
161f92ebd9b1dbf9febba815fa091b8b.png

- ^6 O* g1 G. y1 L
( z4 e5 _* O4 b) Q/ k" U9 r4 ~  加载算法到芯片RAM。; [: L9 f% i4 g' m8 x8 E
  执行初始化函数Init。
7 `( u6 {" B) K8 K  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。4 [4 g3 M6 Z& \, B) P& c2 ]
  执行Uinit函数。
1 ^& n* f3 D2 S% \) u" O/ y! |  操作完毕。! p/ N4 F* X0 y! k2 h
81.3.3 算法程序中编程操作执行流程
# D, m$ l1 q* X  Q0 x! [; `( V注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
' U% C  }8 r) Y6 [! N8 p. G- D
8 w+ ~# i1 J, f4 g编程操作大致流程:6 K5 Y% t/ d* E( C; ~- _- g
4 i. `" g* r7 }* C! l$ }2 ~* a
d80dfd755411aa0d0565f1c56f10e66a.png

/ A" [+ Q! H$ w! t% M2 R# m2 H7 k! Q9 _
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
8 A; |6 O* N9 {  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:2 s; V* E( i9 y( v& L& y( I' d! Z
  加载算法到RAM。
+ g9 e$ H; [- w2 Q9 u  执行Init函数。
" T) f* @; Q8 \$ n  加载用户到RAM缓冲。- ^- |% d- ~9 j
  执行Program Page页编程函数。8 `7 _/ {1 F  t1 I$ r/ d
  执行Uninit函数。; b* ~5 c# X% m+ B# u9 h
  操作完毕。3 s7 c% g5 m: B9 Z. E! b* n1 H
81.3.4 算法程序中校验操作执行流程% ^1 m1 ?. X8 e5 D
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
/ {% e4 Z& Q0 E& N( D! [7 _: M% N. K1 M) k7 G/ k8 u1 i
校验操作大致流程:( e% S& h9 G1 V3 }
$ s, Y8 {$ `% {% s6 P4 L1 t0 A
f15191090ab169d48a203ea3617af9b4.png
1 z0 B6 @9 k8 s6 y) J
  Z! z! n; q' V0 X4 U8 J. g4 a/ ~
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
% r; p2 _  j3 B* A' s- C  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
/ {: v8 g) _# f; }! [% U- k7 F  加载算法到RAM。
0 y& Q( W/ r+ x1 `/ \! i  执行Init函数。, w- A, b% W. _, T& F' O2 b
  查看校验算法是否存在0 R7 f+ D4 i2 V8 o
  如果有,加载应用程序到RAM并执行校验。
/ w: n" d$ ?$ h4 W3 ]  @  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
  H9 L# c& R, f3 n' ~  执行Uninit函数。
4 i, h& ^% \. V- M/ ]8 V) C  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
# G' u/ T; @$ m  执行RecoverySupportStop,回复支持停止。
4 ]* F* X0 x; E2 u; K  执行DebugCoreStop,调试内核停止。
1 c8 `' E" }8 G; u4 r5 E: d  运行应用:8 o' ^4 O3 s3 T4 j; B
  执行失败: C8 g: o# L6 g" _
  执行成功,再执行硬件复位。% `9 X* d' ?, ^+ k+ R
  操作完毕,停止调试端口。
3 B# R9 E% J( m+ Y5 ?4 c81.4 创建STM32CubeProg下载算法通用流程, N( _: N& w  v& G+ x: s$ {  x
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。, y5 P) j$ |) a; h1 J/ O% O( c& {7 V

! n4 m+ X* l: f81.4.1 第1步,使用STM32CubeProg提供好的程序模板3 K2 c7 `3 R& U  Y+ G
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader; j/ M: A- X, R& K7 |
1 T' V0 h" d0 H  p3 u3 t# X2 |
4fbfb0047e29fe23083224607059d97f.png
! y7 v$ D* y/ S6 U- g$ s
  g& K* R- ~3 i$ a  n. E; S2 b
以M25P64为例(注,其余步骤就以这个为例子进行说明):  d  Y3 B. w2 n' E& w5 \

+ m) P  S5 p4 j4 m3 [, {( h( k) k0 b
d1b1a340fdc655c1030330a7dad2abba.png

1 a( P1 F; ]& w0 m+ H, Z. @# M$ |1 p7 V+ ^* d& c
81.4.2 第2步,修改工程名! h, G% W/ a/ u' O2 ?
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。7 `* I  K$ u8 ]) `: j, F

" f. e* s/ Z/ x7 S& _' o81.4.3 第3步,修改使用的器件( d( @6 Y2 N: n
在MDK的Option选项里面设置使用的器件。8 W! _  R5 W0 ?7 G+ y7 g& Y5 r

3 _  u. C5 D9 X) P/ O% [- A4 d
092174ad976a8ce9c7fca533049e2dd0.png
! n' i! p' p6 }/ m

/ H# ~) C0 \( _( r6 p81.4.4 第4步,修改输出算法文件的名字
1 H: C1 P" s$ D$ e3 g% \这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
+ u% H  s# O& H/ R/ m( X) k9 O* a& q7 X
79c42251311dd3892fc7d6b8ddde68b8.png

. ^8 ~5 U9 y* k- K" F" V; e8 G2 j
; x9 u& t' R- [8 P1 T) \, `注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:7 }6 D. D! U7 H6 Q
e2fe61fa7ea99b25b178341b7350558f.png

7 C! ^9 ^1 a3 j9 @8 A, ], j, U8 e+ y: w2 `/ h4 O6 Q
81.4.5 第5步,修改使用的库文件和头文件路径
5 T" Y0 ]; {) i; [. w& d- Y; G根据大家使用的主控芯片,添加相应的库文件和头文件路径。
  n1 v5 g, }+ Q
8 ~/ P3 G* ~! ?/ Y( S" U1 C81.4.6 第5步,修改编程算法文件Loader_Src.c
& ^" ~# N7 Y) q, K模板工程里面提供的是M25P64,部分代码如下:
0 ~0 s5 _% Q$ `; [- X
/ ]. Q! {1 N9 Z) K- K: m; R  N
  1. /**
    / \2 W* I% R+ Y8 v
  2.   * Description :6 G. t% w. k4 l
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    1 S4 ]: o- a# A7 z" u! H* b! {0 H
  4.   * device and initilize the FSMC with the chosen configuration
    % d, I3 v/ b6 j( l
  5.   * Inputs    :
    % B% B* U+ `4 ~/ y5 N
  6.   *      None
    * b1 ?0 c2 ~( ?4 T+ D$ u9 b
  7.   * outputs   :
    8 A$ b& j5 K* m! J! Q* I; S
  8.   *      R0             : "1"             : Operation succeeded
      B" g0 K& X' Y0 k' C. B6 J! }
  9.   *               "0"             : Operation failure4 u) L" X# a7 J  _+ Q- l6 t" I" U! I
  10.   * Note: Mandatory for all types of device ; C, z  J* B) u$ M$ Z8 o6 D
  11.   */
    / e8 c% Z5 ]9 R6 C
  12. int Init (void)
    ) ]3 c3 d3 h, Q9 \  X7 u8 ]  d
  13. {  
    $ i, H) i4 k8 D3 _3 e
  14.   SystemInit();
    0 m4 j* \) Z" m+ v6 h4 {
  15.   sFLASH_Init();, b0 ^, k3 ~4 O# ~# [* T- p
  16.   return 1;
    # i7 d; |) m2 F) V1 [9 Y  [
  17. }4 x) u* g& c/ h& I. y7 y7 V" }4 X

  18. , O2 g3 w+ G& S& J- ^, \! B0 H

  19. 1 R6 J! p5 }$ U8 u- f/ r! P
  20. /**
    ' F% u9 B7 k/ O( ^2 b. Y
  21.   * Description :
    ! N6 {6 _0 O7 H, x1 F0 m( ~3 K5 D
  22.   * Read data from the device
    ! m& ^0 l! ?. e8 Q5 r
  23.   * Inputs    :' m3 q1 F  l* A9 k
  24.   *      Address       : Write location1 Y9 u% H( {) {; O  {) s2 a
  25.   *      Size          : Length in bytes  
    : }5 y2 K; z7 x0 s# @8 @
  26.   *      buffer        : Address where to get the data to write7 ^6 _: W+ ?9 B5 ]: W
  27.   * outputs   :5 n. }0 M# ^2 M: L7 B
  28.   *      R0             : "1"             : Operation succeeded5 I- ^" q. F1 U; [& R( |
  29.   *               "0"             : Operation failure( B) e6 B, u( L0 t& L* l. J  \& W
  30.   * Note: Mandatory for all types except SRAM and PSRAM    7 q; m# r4 E9 j( u( U
  31.   *// n7 a; A: Z8 }, X
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)8 s% [- S# W, O8 A/ ~
  33. {
    / d* o) W! h5 N: P' j
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    1 T- Q2 \0 r) q& I7 q- Z
  35.   return 1;
    % `- j9 A9 B) F- r) U8 h
  36. } ' u+ B4 B+ ^% b, o" _7 _, A) D
  37. 2 b, B  o4 W" s0 d% o6 A

  38.   M6 H) |9 C0 F, o1 L7 e8 j
  39. /**
    ' {' V" l9 n$ J' `& d
  40.   * Description :
    2 q/ M: t3 a" T3 E
  41.   * Write data from the device 8 K) E4 V+ M% P2 i
  42.   * Inputs    :
    5 \% A, {0 V0 B# K6 _4 k- r# A' N
  43.   *      Address       : Write location
    , ]6 D+ c* \; y- w) }- Y' _
  44.   *      Size          : Length in bytes  " m) a& }) k8 E5 U1 y8 T
  45.   *      buffer        : Address where to get the data to write1 x" I8 j" g" x3 ~
  46.   * outputs   :
    . e, i9 W; O2 e( `' h
  47.   *      R0           : "1"             : Operation succeeded
    0 a+ A! B" Y8 T! z1 X
  48.   *                     "0"             : Operation failure5 c6 [3 n. @6 u3 O+ U: \; L; s4 |
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    " c6 K( G+ E- c+ g, N
  50.   */
    3 o8 r! l* {, U0 M
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    % m, U7 l0 a# g) v5 M
  52. {/ ~) B) ]7 a- v2 D+ `
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    2 s( F4 S) x! O3 M# ?1 G* X
  54.   return 1;
    . n5 |( B: F% W1 b8 E) C
  55. }
    % q; S/ t2 ]9 e3 S$ [

  56. % S9 ]" t$ [7 C* X3 G8 D- y, E5 U
  57. 5 n  l' q. J9 r4 v  m
  58. /**& C: q+ h5 E7 z' z' q
  59.   * Description :
    # Z, W- O5 j' l' V
  60.   * Erase a full sector in the device4 P$ ^. D/ h. |/ ]& B  C6 ?
  61.   * Inputs    :
    + E4 I6 a: m8 ~5 u
  62.   *     None- N8 n& W) {4 u- {3 Y: {9 A" }
  63.   * outputs   :
    , u0 E2 M( C4 {8 h
  64.   *     R0             : "1" : Operation succeeded
    ' {7 x3 ]: d" e6 d5 H  Z
  65.   *              "0" : Operation failure
    * ]) |8 o1 O1 t
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH6 w* [9 |* y7 F3 j3 d
  67.   */& L  j" Y8 Z+ A! G& E  z# M
  68. int MassErase (void)  b, Q: Y/ H5 y: f+ c1 Q% ]
  69. {  5 u6 u0 h1 C  H4 K
  70.   sFLASH_EraseBulk();
    . h, W! n* H' i# A: W0 y
  71.   return 1;   
    ) E6 f4 L* [2 f9 C( g% Y! F' g
  72. }
复制代码

# ?) M! v! n, M) g( M81.4.7 第6步,修改配置文件Dev_Inf.c' A3 j9 C% v% x. r# Q9 y
模板工程里面提供简单的配置说明:
9 \  u; a, Z3 A" ]) ?; t6 G' v
+ V# b) w* p2 N2 n: v
  1. #if defined (__ICCARM__)  t9 k2 W! j, O' a+ n
  2. __root struct StorageInfo const StorageInfo  =  {) J4 [$ b2 e" j3 X* M) D
  3. #else. p  P$ `( l; w
  4. struct StorageInfo const StorageInfo  =  {# a7 b* i; H5 y( @
  5. #endif. j. p) H: k: T* \- N; u6 |
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    ! C" e. g7 k/ F; w& z3 w" l0 ]" N
  7.    SPI_FLASH,                       // Device Type! U( g# H8 k. }0 I2 m# a0 ], j  V
  8.    0x00000000,                     // Device Start Address- W; F4 V8 p" d6 }4 Z2 d
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits): i6 G8 B  u/ g6 O  c5 |
  10.    0x00000100,                      // Programming Page Size 16Bytes% K; F5 m; h2 ^* @
  11.    0xFF,                            // Initial Content of Erased Memory! {, d" {+ \' O, C
  12. // Specify Size and Address of Sectors (view example below)
    * C8 I$ w5 H' Q2 ?8 u+ h
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes 6 C2 M3 ?! T- z4 W( Z; @
  14.    0x00000000, 0x00000000,1 f' b* D" R) B9 }* Y' Q
  15. };
复制代码
( O* g0 x: g+ n6 L
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
, A5 u. w) S6 ?) n: w7 |0 @" E: J3 @: Y: {
$ N$ H' Q2 B" P; }" l

* m6 m- B6 y, M: @6 C) d2 b81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。+ r. j, ~3 W$ J; b& K  J2 n
C和汇编的配置都勾选上:
& v( q  J4 N6 p! }% d/ L/ a, O$ ^. j" A
8f2ba38db4c1f685f131e7f1f4e98551.png
; ~4 f' H" w( q: \# b& C" X0 w0 ~7 p
) H+ F) m1 R: k4 q6 b  b$ n- i
汇编:, E! `! z$ O0 P" M7 N, O

: ]/ b) O+ w( H- b  I
0d264153b167afa2303144bdd378d501.png

6 H. P& l% ?: `  H; o( ~4 G- x6 ]5 c- m, @% V* z
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:4 ~( H5 J8 R9 b

5 Q  S4 |4 [8 b( ^(1)加载以响应运行事件。
* t1 U/ w7 M( x, W
# S8 O" f/ G5 c8 o: h(2)在不同情况下使用其他例程的不同组合加载到内存中。* g0 V* h) S, K6 \5 H

2 D9 T" d& p% i' x(3)在执行期间映射到不同的地址。
& t, V+ ?4 Q" t- b( V5 V3 c7 N# m7 g6 g# {
使用Read-Write position independence同理,表示的可读可写数据段。
1 [$ M" }* v' T2 R1 O) [- r
' ~. B$ w4 }( u- E5 x81.4.9 第8步,将程序可执行文件axf修改为stldr格式
6 p) _5 _9 e3 s+ N通过下面的命令就可以将生成的axf可执行文件修改为stldr。$ I5 }6 c6 Q$ t* b, ^8 q1 v

6 e( V- l0 O% i: {- K- m
f50b1594f3741e77b4fbd5523564d3af.png

/ Y# s# G4 ?/ r3 P
: w1 o8 h- p1 L2 \+ B* R; z81.4.10   第9步,分散加载设置
9 O  i: P3 l1 M( d我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。$ r/ ?' z4 `( I/ b8 |$ s6 Z2 P& N, X
; Z' e; Q$ z; [- `! Q; C- ]
8a01cb7c9be38519d4d0510a218447e2.png

. L# T' e( F& t
2 ?- [# @# A1 n+ p# T! O8 c分散加载文件中的内容如下:
- x& `0 h0 t# C; E  {8 t7 w2 W
9 D- H9 M8 L% }6 p/ N
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions( W- v" V, X& m9 Q5 I( O8 T
  2. {( s! v. \0 g1 h5 \& Q
  3.   PrgCode +0           ; Code: g3 s% @0 n5 C0 E* n7 f4 J4 z. v
  4.   {
    3 U+ p4 p! {5 M+ H, \# b% A& }
  5.     * (+RO)
    ; H' C5 A0 e8 {  D' ?2 I
  6.   }2 v& @) u" w/ J
  7.   PrgData +0           ; Data
    & ^0 q4 _+ a0 `
  8.   {# c; E/ o0 L2 h
  9.     * (+RW,+ZI)6 Y8 h9 {8 \3 g3 _2 Z% N
  10.   }
    ! `% B! |% F/ o# N: R
  11. }, V4 v" o7 b/ S* d) n, ~9 U2 B
  12. 9 E% m7 L$ L! L1 q% f  Y
  13. DEVICE_INFO +0               ; Device Info
    3 ~" M4 M/ r4 e9 \2 g& b& ]9 f
  14. {$ D4 G; B4 R4 b* @1 B3 K
  15.   DevInfo +0           ; Info structure; I$ l" k4 a; Z
  16.   {
    ' y+ ^9 B0 Z$ S5 l  T
  17.     dev_inf.o& ~: g+ X/ h8 Q" q& P& ~1 _' N! a
  18.   }$ Q& q) z9 `2 a: x8 W( k
  19. }
复制代码

3 M. T9 F5 m7 @$ ~这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。7 ^' H# A, Z9 }0 D$ N
: A' R/ l* P' _. ]+ e
--diag_suppress L6305用于屏蔽L6503类型警告信息。# U/ q$ Q2 g1 Y6 Y/ T  A
6 ~$ w5 H* A; `
特别注意,设置了分散加载后,此处的配置就不再起作用了:
6 n9 y1 k; |/ K- N  [( }' e% y
, I7 p# _2 V6 O& R$ J
13b022190bd5115ed249129fc3886a38.png

# s" k/ S5 B0 Y3 X& E& E2 _6 i' N) [6 |
* u7 `& B7 Z7 W1 e+ @81.5 QSPI Flash的STM32CubeProg下载算法制作
3 J7 e5 M1 e: ?下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
& S6 G  ^; L) T0 z- d8 G' I9 {4 d8 D# g! K% m' w
81.5.1 第1步,制作前重要提示, V$ @, c4 k1 K  K6 {1 q& A5 l6 i
这两点非常重要:4 q+ t- D1 c6 v! n# q
: [. L% @) j7 q4 O
  程序里面不要开启任何中断,全部查询方式。" _9 Y+ D* E! A  G
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
5 e* D, @+ |: N. w1 M8 J81.5.2 第2步,准备一个工程模板! R, v: U1 T$ r, Q1 N
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。0 b! h/ O# P* ]. L+ X; l$ D
* y+ p2 U2 y! |1 K
f46a8d2276d0ffb7beee251cdb0809a4.png
( y7 }& v4 H* b  m8 A
, U. G" F" `, j2 |
81.5.3 第3步,修改HAL库
5 Q, J' q2 U" m& A* }这一步比较重要,主要修改了以下三个文件:
. c6 n/ m1 V3 Z
3 i3 @; h. I5 _  t9 s! i
1efb78256b9465df406e3e9e95ae30e5.png
1 @6 M6 _# {1 B) W8 a7 V$ d7 p

) S& R- W- G" V8 L+ R主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。% Y, M& J* D! g5 m- Z/ S1 e

' l" ^! N" z3 `  M) W81.5.4 第4步,时钟初始化
% y& ^% g1 c( u. k$ F' u2 l我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
) E, ?6 P, j. G3 t0 S2 X0 V; c) {' f5 s: S, o
  1. /*# S! [/ ?* }' b9 o+ D3 b
  2. *********************************************************************************************************
    # g/ N: D+ x" V6 x% L4 g* w
  3. *    函 数 名: HAL_InitTick
    3 {' t( n3 a$ ^$ y2 A
  4. *    功能说明: 重定向,不使用4 @5 ~' S% r( f2 s6 R8 E2 C
  5. *    形    参: TickPriority
    + q0 T- Z" v" n1 C% ]
  6. *    返 回 值: 无
    - x9 x1 \( {8 m8 T
  7. *********************************************************************************************************
    * W. X  w3 f! x
  8. */- e; K6 {$ U3 c# G6 ]/ _5 \; r* L, v
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    ' s$ z# n9 l0 D1 l0 n
  10. {
    $ V' b# }% m- v# p4 d  r6 y$ g
  11.     return HAL_OK;
    3 V# g/ Y, v# V6 Z; X' l
  12. }
复制代码

5 |! s5 H5 B! O; U+ b然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:9 \# J, t2 Y5 a0 ^$ h3 L
& j- m: b4 d4 a8 h4 e
  1. #if !defined  (HSE_VALUE)
      u- W: A# ]$ T4 b2 [
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */# j( @+ A* E1 @0 \6 a; S
  3. #endif /* HSE_VALUE */
复制代码
. k: x6 B) H' h0 Q$ e
最后修改PLL:
; \1 O) K  m/ ?6 r& s; {" i1 @# H2 @7 E1 ?5 f
  1. /*
    % P1 z$ V! W  K
  2. *********************************************************************************************************
    ) c  ]0 \4 o, c0 |2 e: K
  3. *    函 数 名: SystemClock_Config% p' H* H' [( S  z, J  E& C
  4. *    功能说明: 初始化系统时钟1 O; v+ ^. E1 k; y
  5. *                System Clock source            = PLL (HSE)
    ' M& ]% V0 a; Y! w
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    * S2 S5 M1 y& S5 l) m
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)8 ]/ [# p" G& Z" Q
  8. *                AHB Prescaler                  = 2
    3 ~) f! h# I9 O7 f3 I
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    $ O7 ^0 }4 z! g
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    4 v- X- Q1 N  u" T% |
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)8 m8 U& r0 m5 b% c$ W6 u. t7 H3 g" O
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz); A* I% K* J9 g3 A% ~: d
  13. *                HSE Frequency(Hz)              = 25000000
    5 I  P6 ?: h9 o5 n1 H7 ~5 z
  14. *               PLL_M                          = 5
    . Q6 _5 ?9 R" e% d" Y# B
  15. *                PLL_N                          = 160  a9 w9 d- V+ \8 x6 E
  16. *                PLL_P                          = 28 C' m. Z% n! \$ Q7 i) P
  17. *                PLL_Q                          = 4# d9 W( R2 j$ M) p! v( V% N$ b! m/ x
  18. *                PLL_R                          = 2
    5 V  g, E& M2 q: H* R
  19. *                VDD(V)                         = 3.37 X  S3 y# j, q4 K& e! a3 K$ X: N5 ?
  20. *                Flash Latency(WS)              = 46 B8 [8 l& s  F5 T. D- O! O
  21. *    形    参: 无# g: W4 E' a1 n! a
  22. *    返 回 值: 1 表示失败,0 表示成功5 x& G# S( t, l8 k% B4 u( ]- n' }0 T
  23. *********************************************************************************************************' }8 d0 J- L. }, ?9 C0 }
  24. */
    2 L. ?! S* c# Q: i! _6 H
  25. int SystemClock_Config(void)
    ) `  O& D2 ^. {0 y# p; z, ^
  26. {9 T# b( c7 c$ C1 M: q
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};; r" N  N+ H! h! p
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    % O9 w+ l8 T2 k/ q0 `
  29.     HAL_StatusTypeDef ret = HAL_OK;
    0 Z6 {  O# a2 \+ Z- ?. i
  30. ! A+ C, S; y( v# W; I
  31.     /* 锁住SCU(Supply configuration update) */0 k1 N+ E% ?# z  q3 P
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);) {% H% b# X7 B2 L' b! a4 X

  33. , [) H+ u. w) D6 Y& k! t
  34.     /*
复制代码
- |( P  S0 R  L3 _9 o3 n
      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,# v3 b) ?2 ^* @8 d1 V
         详情看参考手册的Table 12的表格。
* x6 U5 p% R8 M, S" @( h" }      2、这里选择使用VOS1,电压范围1.15V - 1.26V。
8 a/ ?; X3 J6 P. e& c- h* @/ }' h   
  1. */
    % [4 {6 C" {0 J5 `
  2.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    1 W6 L6 e+ n+ S3 n9 X& ]) `8 ]$ n
  3. / X* h4 q1 l3 i- R; J; Z0 B
  4.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}: T0 K2 ]2 o( Z2 W. \& v0 G
  5. , ?# V/ @+ L% }6 I( S
  6.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    ; q8 R2 L3 q' `% D0 G4 u
  7.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    7 S+ M- n3 n0 w. a- w" x- k
  8.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;0 f, h1 k/ |1 o
  9.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    3 N9 @3 Z- t$ b& V$ q
  10.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;+ h, L+ b- [) B6 T; M6 H0 ?0 W" |
  11.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    + n. g% w* n# J8 O
  12.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    8 P* \7 ?! B# t2 n( P

  13. - Z5 {; ~$ @  |! T3 o
  14.     RCC_OscInitStruct.PLL.PLLM = 5;
    " c. Z& n* c5 {9 d; W/ e$ Z
  15.     RCC_OscInitStruct.PLL.PLLN = 160;
    4 t" }4 p/ W! o% F+ I
  16.     RCC_OscInitStruct.PLL.PLLP = 2;
    ! _( C9 ]# p' J0 a7 ~4 B0 y
  17.     RCC_OscInitStruct.PLL.PLLR = 2;
    7 w, W# e/ u- H) M2 }0 ~2 i
  18.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    5 P& R  {6 q( v
  19. 3 G, L/ F+ l4 C. g7 p% J3 z9 S
  20.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    - ^9 Y, C# k* r/ e( U: ^8 N
  21.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    % z9 A3 J9 F. X6 c
  22.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);- h2 z# W1 U; e" E; h
  23.     if(ret != HAL_OK)
      o1 T+ B8 D1 e$ P( N" D
  24.     {
    # m/ X, ^3 W3 f& {$ t. q: m( _
  25.         return 1;        
    , n* w4 [0 e' O
  26.     }
    + b- z0 q9 ^! ]+ z

  27. / @8 Q  e( z  w
  28.     /* ) d5 T/ {# B8 Y  ~
  29.        选择PLL的输出作为系统时钟9 E( q6 z1 k0 i3 D. J
  30.        配置RCC_CLOCKTYPE_SYSCLK系统时钟! j9 g- t9 ~! N) M& Y3 q- S+ P
  31.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线: Q* Y" q! t( `' R/ j- O
  32.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    ! p1 r# z- M! N% w: Z1 f
  33.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    , ~& f4 g" _% x7 U* t
  34.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线% M" I1 V5 C' u/ U! v% f. Y
  35.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    ( `8 k! ]2 H0 M
  36.     */' ?# J' }3 B0 M. Z
  37.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \" U! C8 t% u9 s+ X& i; s! D
  38.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    / p5 e8 h* i$ f4 C2 C$ I
  39. + B5 w- ~% [! u  y
  40.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;3 _: z0 b4 k9 w4 m
  41.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    / s, R# K' ?* t- K" x. w
  42.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;6 S* }2 O3 H5 g% ]0 w
  43.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  : r+ e1 E" ]9 Q6 k1 l' s
  44.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    8 f% g6 F2 w' Y, b8 S* O8 ^
  45.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; % h" ?8 L0 E1 e, y7 w8 m4 l
  46.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; & e6 f5 O$ W2 E6 q; o% g
  47. ; a" ?7 t9 B6 d4 k3 Q
  48.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    3 K, F; c( v8 r0 K9 T9 F$ G
  49.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);+ _2 T7 ~. m  M2 M! R
  50.     if(ret != HAL_OK)+ s0 v# g8 [8 j+ \, e; a2 b6 b' n0 q
  51.     {
    : Q: x/ b, I2 R# W- f" u- f: j' f5 Q  {5 f
  52.         return 1;6 r& [9 k4 @7 S7 n+ ?
  53.     }3 H3 A' U; R1 u/ \+ T/ g

  54. - G0 _8 X+ L3 _. y" V
  55.     /*  k. K) V' G$ \: ?& `: X, Y
  56.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 ! U+ ]( `$ f- \. ^% A# D
  57.       (1)使能CSI clock, M/ `- z0 L7 P( X- C
  58.       (2)使能SYSCFG clock
    4 J# V# D/ J  g- D
  59.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    ! I9 x8 B( G! r' Z% U
  60.     */# _5 o- D8 z$ n/ v, K8 ~# j  s
  61.     __HAL_RCC_CSI_ENABLE() ;, {5 ]# ]$ r+ A. e/ ^7 S

  62. ; G' ?. n( M$ J0 `" t3 m5 H
  63.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;6 z% ?, `, P# F/ ?% m

  64.   x* d  x& U9 g
  65.     HAL_EnableCompensationCell();) c, \& t9 [" X2 V0 z; z

  66. * e7 u' C0 t% f* `' k
  67.     __HAL_RCC_D2SRAM1_CLK_ENABLE();2 g+ o6 H2 A3 T: t  c+ _- R9 H
  68.     __HAL_RCC_D2SRAM2_CLK_ENABLE();; g. V! V9 ^) E- m! x/ |8 v
  69.     __HAL_RCC_D2SRAM3_CLK_ENABLE();  R  x6 H3 k  I- z% d! V# m- V4 y( R
  70. 9 n' Z4 R" a2 r" K4 q- _/ J
  71.     return 0;5 k6 Q% ]! @% z$ k, L' b( b
  72. }
复制代码
. _0 O, ]7 c" s9 T
81.5.5 第5步,配置文件Dev_Inf.c的实现
* s% E* k6 U% [2 C% K$ ^$ X2 g配置如下:  B; [4 X7 y# U) \5 |# i

' ~4 H( K& _. y
  1. #if defined (__ICCARM__)( u3 Y$ F2 c2 J. `  m3 X
  2. __root struct StorageInfo const StorageInfo  =  {, z& Q& p! {( k/ ^
  3. #else, ?7 C8 U- G3 y0 Y, K/ h
  4. struct StorageInfo const StorageInfo =  {* S, a  G+ t& _2 w/ j  o; U- x: z& V
  5. #endif
    9 N- L+ q# I3 v' B  A+ A) d
  6.     "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */$ o& f( L* e! u
  7.     NOR_FLASH,                      /* 设备类型 */$ N& R* v! e4 s( p; |
  8.     0x90000000,                     /* Flash起始地址 */
    8 J3 m# y! r/ N, ?; E/ G5 B' w
  9.     32 * 1024 * 1024,               /* Flash大小,32MB */1 }, ]5 h, G7 G# F. @: h
  10.     4*1024,                         /* 编程页大小 */* B2 X2 Q, b: \- {4 r9 z& q' M0 m
  11.     0xFF,                           /* 擦除后的数值 */
    ) m6 B5 M6 \4 |% O& w0 v
  12.     512 , 64 * 1024,                /* 块个数和块大小 */2 N4 |5 w% L- s6 a
  13.     0x00000000, 0x00000000,
    & X4 O) _: D7 r% c
  14. };
复制代码

. x8 ]& J! i0 @$ ^8 d注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:+ i3 ~4 K$ s7 o, w
- Q) K/ \/ ^8 y) M. R% H) V
bc495adf8d970e6e1fb6822b88b94e2f.png

0 |/ q! Z* ?0 _5 b4 \: V1 W9 }; V* K9 ?5 |$ o4 S4 J
81.5.6 第6步,编程文件Loader_Src.c的实现) H) |0 E9 [$ G* J/ Q
下面将变成文件中实现的几个函数为大家做个说明:
2 N& X2 a$ W/ ^' x5 x2 y. S1 n+ L& z3 Y( @& N. ?5 `0 y
  初始化函数Init
( f  g/ ?4 B7 o1 C6 C' h
  1. /*" R  x+ S/ z# L7 `* [
  2. *********************************************************************************************************9 T9 l( d  a! }* a" _: I; [4 ]
  3. *    函 数 名: Init
    4 ~, e0 x. E9 Y4 u
  4. *    功能说明: Flash编程初始化# C' g: }- s* s. n
  5. *    形    参: 无
    * ?% V5 {. c( Y% i
  6. *    返 回 值: 0 表示失败, 1表示成功) e' I3 d8 K' s
  7. *********************************************************************************************************
    2 Y9 s" ~) t$ Q! Q$ G0 H0 Y
  8. */
    ( E7 P% @: O  W) c. P
  9. int Init(void)
    - M6 Z2 S2 m7 F- e
  10. {   & D; J4 `0 k0 F4 ?0 D7 o
  11.     int result = 1;! T2 y  Q# v, _5 @' @# z, V- f

  12. ( G5 A' z, A* Y, r6 m
  13.     /* 系统初始化 *// E- d+ \9 W+ g: z
  14.     SystemInit(); $ R3 n6 E) ?4 c( |* X# N  G
  15. 0 W1 R' e2 v5 p4 g
  16.     /* 时钟初始化 */
    . q# }# ?+ Z+ V2 p9 I6 ?, Q" ~
  17.     result = SystemClock_Config();
      g; e3 V% p9 O) p- F- \" Y
  18.     if (result == 1)+ c9 l. Z: s  M, a, W- r
  19.     {: s! e* i& `' m4 o7 i3 T
  20.         return 0;        + b8 d0 X- }- K! H- D
  21.     }5 V* ^" n, e- M+ s$ `1 H0 b; y& [
  22. # F8 b# r2 w# S" u  H
  23.     /* W25Q256初始化 */
      h* Q3 h. o) l( l6 z
  24.     result = bsp_InitQSPI_W25Q256();1 I$ ?* r1 g( z  f
  25.     if (result == 1)
    3 s5 t+ a: Y/ E* R- l
  26.     {8 M" |+ S; e, q% \
  27.         return 0;: ]2 X) y, f2 Y/ c$ j/ V3 g
  28.     }/ _4 B2 R- M1 x& o+ y
  29. 0 G2 r# ?# f6 k. |
  30.     /* 内存映射 */    : z" j, T5 X; n$ L6 S9 w
  31.     result = QSPI_MemoryMapped();
    8 m+ {# O6 ^. W& R& i# f
  32.     if (result == 1)
    5 E' H# L8 h1 c+ ]
  33.     {/ e4 v. h4 B) X, P5 i" W6 T! |- M
  34.         return 0;- C# T2 ^, {# C
  35.     }8 L1 n* q. X/ ]( f* m: c2 O0 N6 L
  36. 1 H  {) Q9 @" p7 z
  37.     return 1;2 O$ P# M4 l. r8 F- s. s2 X
  38. }
复制代码
! i% d& \! ], X$ s$ @4 D- O
初始化完毕后将其设置为内存映射模式。
# @+ A8 d) B& [4 O* q( v7 f" k
; j1 n, l7 j+ N  整个芯片擦除函数MassErase# t. j8 a8 e# m9 M
整个芯片的擦除实现如下:( E/ k4 C( m# k$ x# Q
/ t6 C8 p; j2 {. J: q0 e
  1. /*
    % L4 B. {* d7 T. a3 e
  2. *********************************************************************************************************
    $ z7 d) H$ j# N3 b: F# U
  3. *    函 数 名: MassErase
    9 ]% r) j9 x* d9 m% a7 I4 [% ]- x, P
  4. *    功能说明: 整个芯片擦除
    7 i0 a& d! ^* k) n, o" I  x6 U  Y
  5. *    形    参: 无: Y, G9 h/ v8 k: p
  6. *    返 回 值: 1 表示成功,0表示失败
    ( W' Z$ @" d  o8 L% \
  7. *********************************************************************************************************  G+ o- t! |0 S0 ?! t" W9 H
  8. */
    # |4 X/ g: `( I. B& {9 [
  9. int MassErase(void): Z8 S2 y1 X" x
  10. {
    2 _- V) P  s; |9 K. `+ n
  11.     int result = 1;/ V/ G. k1 v* f5 ~. M1 O/ J+ e
  12. + L; E* S( L7 s1 O# {
  13.     /* W25Q256初始化 */& f" L/ X" ~  A! b9 `
  14.     result = bsp_InitQSPI_W25Q256();
    3 F7 O6 B: T" p8 G  r2 z
  15.     if (result == 1)# U$ j+ p) ^  p
  16.     {# O$ R7 p. n( K+ y+ ^
  17.         return 0;
    8 q" N, z6 B% D8 f# h* q5 u
  18.     }
    , L9 L7 s0 t6 S) P

  19. 0 t  j9 s1 g) I8 D. o3 B7 m$ C2 H" O
  20.     result = QSPI_EraseChip();
    6 l8 a9 M" K2 ?4 f$ O+ ]$ ?
  21.     if (result == 1)
    3 ^- I1 A  i; @# H, @) p
  22.     {
    : u6 s% S3 @7 s! _
  23.         return 0;5 b7 C- ^) l. j! e
  24.     }   
    5 B6 p4 F) M1 {- r8 _$ o7 P
  25. 7 Q( X1 P% c( Q; W& P2 f, a, V. W
  26.     /* 内存映射 */   
    3 U+ ?) x* ]! D+ D# D0 g
  27.     result = QSPI_MemoryMapped();
    ! u( L8 \, t) z+ K$ `& W
  28.     if (result == 1)
    7 ^: J" r, s! [( d- @2 m9 y
  29.     {
    - m3 `" F7 _1 x6 K' ?
  30.         return 0;  y/ W2 |& G9 T9 {: E" O% c, L
  31.     }
    + u8 }* R" |& v/ B& K

  32. 3 @7 p; a7 [, o8 w& c9 p. f& \4 Y
  33.     return result;          / H/ Z8 ^) h0 O+ y+ O6 {0 K
  34. }
复制代码

' }5 @0 T) ?) P& R$ W( A  扇区擦除函数SectorErase
) e4 t  o" H5 m, ]8 E扇区擦除实现:
3 r; j* ^+ `, o4 o  }$ o
4 y6 S( X$ _2 j6 v8 G) U% W
  1. /*7 x( L( o4 W, l- R) m
  2. *********************************************************************************************************
    $ n% H' }" K: a8 ?2 B8 l, ]) Y! T
  3. *    函 数 名: SectorErase7 }: g1 Z4 z5 A; d
  4. *    功能说明: EraseStartAddress 擦除起始地址
    & M( k) K, H: p) t( i
  5. *             EraseEndAddress   擦除结束地址
    ( M- X$ Q8 [& z) h& l% t* k
  6. *    形    参: adr 擦除地址* P$ S. S6 F5 U
  7. *    返 回 值: 1 表示成功,0表示失败
      {" H# h3 q) t6 o. m3 w
  8. *********************************************************************************************************) e9 a6 [# v: i+ g
  9. */) t& Q: \5 O% }
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)' ?/ i& }$ p, q/ a9 N: y
  11. {
    4 }% c$ R! m- N- ]- K% U
  12.     uint32_t BlockAddr, result;
    4 j- }, P+ [9 n' \- k; O
  13. 1 Q% I0 X$ k% f# h: x2 F
  14.     EraseStartAddress -= QSPI_FLASH_MEM_ADDR;
    / [  V" U1 L* ]# P* Y# B8 `$ @
  15.     EraseEndAddress -= QSPI_FLASH_MEM_ADDR;
    9 x# r5 o1 U& I1 T
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x10000; /* 64KB首地址 */4 G: n; p* S( }# F8 H( b
  17. & H( k% `* I6 L( X
  18.     /* W25Q256初始化 */
    * v& `6 u: w) Q5 d+ \( [0 h8 _
  19.     result = bsp_InitQSPI_W25Q256();8 |9 W; c$ m+ c- h( D- a
  20.     if (result == 1)# C+ e( ~7 k9 e
  21.     {' z- {- v5 ?/ u7 y$ o& D) z' ^
  22.         return 0;        : {0 X" R% u+ h+ l
  23.     }
      |; D; x. T" A: s0 {
  24. - @3 J* g6 c' Y8 F" |' g
  25.     while (EraseEndAddress >= EraseStartAddress)
    2 g; e' W+ @3 Y6 c3 F7 E: u
  26.     {6 z1 j* R9 ?: i
  27.         /* 防止超出256MB空间 */  F8 ~  S# U: u7 S: _1 Q) @- [* N
  28.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;. r7 i3 T6 o% \; ]6 R

  29. & h* w. o% o+ J
  30.         result = QSPI_EraseSector(BlockAddr);   
    & o0 |3 w; T' @5 Z4 ?- `! f
  31.         if (result == 1)" h7 h. l! u- {9 {2 |6 t" @4 V9 r
  32.         {
    + @& m; I+ u/ v- e
  33.             QSPI_MemoryMapped(); / @9 `2 F4 Q" ?
  34.             return 0;        ' x) H8 {+ Z1 V  F9 `
  35.         }
    ' i/ r6 w. c7 T7 Q

  36. / d! W: z4 Y8 s- f6 a
  37.         EraseStartAddress += 0x10000;
    ) B1 j9 H6 ?6 |, \2 |' B$ v  I
  38.     }! P  Q$ J  ]* T( l6 F0 I

  39. ! A: k/ V0 ^8 h6 Q% c( ^
  40.     /* 内存映射 */    # i' ^2 H5 p: K  ]
  41.     result = QSPI_MemoryMapped();
    ; i3 d" h" l3 e  @
  42.     if (result == 1)/ i5 M* d# l- U' Q& v
  43.     {
      N5 K" d8 b! r+ n1 r7 k8 A/ T
  44.         return 0;
      Z2 c* j' X6 W) B2 t, @' [  q
  45.     }
    # d0 i+ e/ d' R( v1 M
  46. 0 P" [1 Q, S/ d8 h' e: R3 L; M
  47.     return 1;
    + v5 g" L' s" s4 Y6 w2 w
  48. }
复制代码

- H: m$ Z" C: Q9 N- f) O; T) W这里要注意两点:
' }3 C2 [- M. S( ^2 {5 y
5 H( D* H/ n. _8 L(1)     程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。. p$ H# J+ V* j" s( U# n8 f, o

5 A# C; H  Y/ T2 E9 x5 f' R+ \: k, D(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。  v1 L& @, @1 Y

2 M' V! F% @3 l; N4 a  c; Z  页编程函数Write
$ o1 @) W5 q- D+ `% J$ g& L  _/ U+ V页编程函数实现如下:
% ~- e$ \$ V7 \- F- ~6 i
% i( Q! @9 O) e$ C3 l
  1. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    3 u/ f& P4 O8 m$ w" F9 R3 F4 d( B$ [
  2. { 6 F4 Q% c# @/ a( ^
  3.     int result = 0;2 ]. d" t/ i3 Q6 p
  4.     uint32_t end_addr, current_size, current_addr;
    1 v6 s- D4 |7 u) s
  5. 5 }$ y* G/ J4 Q

  6. % w* X  E7 |& i# ?
  7.     if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    , _. {+ i7 p6 f  B
  8.     {8 w0 B# |3 ?& w% \* k, m9 }
  9.         return 0;# _: Y  q( ?! U; ^, F
  10.     }
    7 W5 g: I/ X  m/ W, l& S

  11. , U6 J: J8 g9 s
  12.     Address -= QSPI_FLASH_MEM_ADDR;
    $ o  ~* g9 ~# I. V

  13. ; W' h4 J7 u0 U
  14.     /* 计算写入地址和页末的字节大小 */& g/ R/ s- M5 `) J; @/ B8 [" I+ R
  15.     current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);
    9 o2 D% i) E1 r' j# @+ \
  16. 5 g& Y5 [! A, ^1 \- a
  17.     /* 检测页剩余空间是否够用 */
    ! Q8 A; {9 J) E5 b% J
  18.     if (current_size > Size)
    $ [" b) c, s% R! g9 _
  19.     {
    ) }9 G% _" @* h. ]: z  g
  20.         current_size = Size;* O  N7 J# H) ?
  21.     }
    . [7 J4 j6 n, f+ x
  22. . w. a# [& ~; i
  23.     /* W25Q256初始化 */; O  |$ J) E- g7 H! @
  24.     result = bsp_InitQSPI_W25Q256();
    6 x# j8 A% g" N* v
  25.     if (result == 1)
    $ |5 D& \, K* t
  26.     {( E+ b% ^2 x( w- G' I8 d
  27.         return 0;
    - _$ V5 _; i' g: \. B
  28.     }6 }# g( {! ?. P8 _5 ]

  29. & \4 C8 r4 F0 @! L
  30.     current_addr = Address;
    . e4 o1 v) V$ a- E
  31.     end_addr = Address + Size;5 B1 m" P$ e/ f) x0 v$ u- y

  32. 8 z& T( ]! [5 f3 f
  33.     do{& @6 U. q( W; Q1 C& d9 d
  34.         if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)
    ; O4 q2 q/ F- O* H+ I
  35.         {7 R! {) t0 V/ m" ]
  36.             QSPI_MemoryMapped();  
    6 R6 N; F) O( g# V3 L( K
  37.             return 0;  
    7 l1 S$ n8 H2 S' _, T1 ^
  38.         }+ h, w4 d4 O' k* ?& `0 J

  39. & o9 s  r1 p" ~
  40.         /* 更新地址 */
    % C. s* m" v7 f
  41.         current_addr += current_size;  A6 \+ d" p7 u* R, ?8 v; Q9 P
  42.         buffer += current_size;
    * x( E  t7 @5 Z6 G6 C
  43.         current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;
      r  T* n$ `' w* Q
  44. " w9 x  p2 X( A- }( u) i3 j
  45.     }while(current_addr < end_addr);! \+ W; e. z, N$ L; w2 x3 P$ c& R

  46. , Q$ o" W* V% d7 j, b# r
  47.     /* 内存映射 */   
    / m# m, k2 ^" Z1 C1 z4 H0 w
  48.     result = QSPI_MemoryMapped(); . f. ]7 ~- ^2 f4 |: V
  49.     if (result == 1)
    - u. F) A7 k9 `$ X1 {/ o
  50.     {8 r* S) V. z" x7 J4 P" }& x
  51.         return 0;
    " B3 U% {3 T  I: y
  52.     }9 q! |  w- ~# f

  53. , I) l2 i- ?- v% ^
  54.     return (1);  
    # {+ t* t; j+ c
  55. }
复制代码

% l9 z: [4 G6 P# P+ T) I这里注意两点:7 a6 m8 Z9 x: B- ], {3 c

' ?; f2 w+ N2 w(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。
$ ~$ i( @& k' J& n- j( `% F4 O& e% K/ G# n8 C/ t  T
(2)     程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。9 K$ A* P+ ]. P# `. G9 s7 S  w
- j+ A. U% Z1 e1 q! B  }
  读取和校验函数
# _, u; S$ u, _. e4 u我们程序中未做读取和校验函数。1 ~5 b2 e8 ]' h, r6 T
& ~; s2 E2 v3 g% n" O1 m
(1)     如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
  U& W" O; S4 O( l
% V7 q, c( r/ x1 i! [0 v! p' J9 K" t(2)     如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
6 ]* {$ L  R5 [- x. c6 j: }
/ u' d- k, O# V- G/ H! F2 y1 F81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)6 B, ^1 @& l( q3 X) J
最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
& K( K( \: d( z/ R
7 k& x9 y" \2 k7 j
  1. /*
    1 |% U2 c3 N1 x) p$ D+ J9 J
  2.     STM32-V7开发板接线
    1 `5 L7 f1 J( S' d2 n

  3. 0 }) }8 h# f- e9 p1 z! @
  4.     PG6/QUADSPI_BK1_NCS     AF101 P2 h! s6 a' Z, P8 f4 T* q/ o
  5.     PF10/QUADSPI_CLK        AF9; W% w3 s- P9 m2 l: t
  6.     PF8/QUADSPI_BK1_IO0     AF10
    # U# V1 Z$ O( z) r4 z: ^. }  B! Q
  7.     PF9/QUADSPI_BK1_IO1     AF10
    7 E: |( [" O$ u2 ]5 }8 t& Y
  8.     PF7/QUADSPI_BK1_IO2     AF9
    & b+ l, T& S. _/ N" a
  9.     PF6/QUADSPI_BK1_IO3     AF94 u9 e- J/ h. h) J) m( B/ c2 K- ?

  10. 7 Q& n& b7 G4 L/ {5 x+ W; F% R
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB: p- t% F/ i( N- d' L1 r2 a

  12. - m7 Y1 q0 B2 K+ G# q$ l* e: v7 V
  13.     H7-TOOL开发板接线. J, P; @$ U" w% \/ w

  14. 8 Q$ |( A7 T0 x" X
  15.     PG6/QUADSPI_BK1_NCS     AF10* k& u9 k' g$ h
  16.     PB2/QUADSPI_CLK         AF99 p1 B( w6 T' X5 y0 l) r: M
  17.     PD11/QUADSPI_BK1_IO0    AF10
    - ?1 |' a; ]* B& t
  18.     PD12/QUADSPI_BK1_IO1    AF10
    ) T" P( y! J0 N. i$ v* U3 z
  19.     PF7/QUADSPI_BK1_IO2     AF9
    2 Z" k: I! j; E0 q( i- a* y
  20.     PD13/QUADSPI_BK1_IO3    AF9
    " P& H0 g3 C, ]9 x) v4 T( j# ?
  21. */+ o+ X) A' L5 e

  22. # [% n8 E* u4 h; b1 z" m$ k
  23. /* QSPI引脚和时钟相关配置宏定义 */
    3 Y; P8 t4 J2 f/ F' h8 D5 O
  24. #if 0
    % ~. J8 l' \* ?, W
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()8 m+ Q$ d# d; W( o4 J
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()" l  H* Y3 d2 u2 K! R
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()" @7 |" T7 f3 W" Q$ \' Q: A, k. @7 @
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
    ( v1 Q6 [: r2 F& M8 K
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    9 R5 Q! |. n3 o4 i; Z  U( R+ f
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    5 {' f) W% w$ m- r6 _
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()* N; V2 }$ f  u$ M; L
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()& Y5 J( B5 h& {! i6 r( L, D

  33. $ ?/ _* [3 Y. c$ {4 S% ~" d
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()  D0 K$ v6 ^$ p# I2 N2 ^4 i
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    6 k8 c3 }# h$ _  ]  I0 q
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    4 R: T2 d! Z( d8 w- g

  37. / w+ q! {( L' E- y  y* d3 a# C
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    7 p3 _8 @6 n- Z4 g" T4 ]9 B- N& O
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    ! D9 a% K/ ~4 b# k3 t; p; M  z5 }
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    8 t% T5 g, @) }2 j9 O
  41. # ~5 N& ^/ n6 R, p4 g5 [( r( X
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2
    $ [7 E; ]! b; B% d1 I- E: h
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    ( o  Q$ f2 q0 ?
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI* @- l& Q" E# b6 D1 u

  45. 0 u/ \  G9 c9 a% k9 Q3 n
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11$ ?" Y4 P2 j* M* v$ Y: J4 T
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    ) m! g* R! }, H* H" S5 f
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI% v& H7 N6 V  g4 P; B8 x

  49. 6 Q! `. g. h. D! T
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_125 j6 J) K' `# N8 F( l3 s
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD1 U- e9 a. S2 R* |% ?" u7 A$ a2 @
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI0 z  z& ^5 P' h- ]4 K: G
  53. : }: G. U! G9 H5 c: E; {
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    6 x* |% Q0 l0 Z5 A& S& e2 d
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF8 d5 @$ |7 X* z( v( R& o9 [
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    * s1 f0 `3 s$ Y+ V
  57. 5 c, [+ q2 M/ m8 Z1 `
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13
    : m; A/ K. H' j. d2 H
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD
    9 c4 s6 f! ~1 ]1 Y
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    + z& x% n' O! @' _
  61. #else
    $ a% K" \3 n3 s9 e3 Y3 [
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()% f+ ^6 Y3 w; M  t; x% L5 ]
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    1 r' K/ V2 X. Y& Q- n8 T# ~& c
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    2 z) W1 `! g' N( ^
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    7 U0 K: v* r( y9 S
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()" }2 h$ b: h4 J! }: \
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()* B9 m' V$ k, V" I2 u
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()% G) P. \' w: Z
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    ( c. Z* g# O9 {
  70. 5 @0 q" |0 o3 ?+ w7 `! B
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    1 p% V# ]( t/ M+ u/ w
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    6 z% q% q. u9 j7 c1 W: p% U
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    : ^6 \" N  {& B% M7 g6 V. _
  74. ( W# Z( ~; I' d  e2 }
  75. #define QSPI_CS_PIN                     GPIO_PIN_60 \  h2 d$ v5 r' h
  76. #define QSPI_CS_GPIO_PORT               GPIOG
    9 v  S* ^! H8 I0 c9 i
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI2 }0 b( d9 z% r6 H& i: x; U6 r! c2 y
  78. ' g+ s* [2 a) A. O+ A
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    ( c0 j2 p* X& c( N5 L' u. p7 n9 x
  80. #define QSPI_CLK_GPIO_PORT              GPIOF' v- |: x1 O& E( J
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI' n0 F3 o2 z' D% N) m

  82. $ F$ g" R" V9 w- J/ w! n( U5 }
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8
    + t8 O/ A! w' m6 ^. i
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF
    1 M: _+ U) S. N2 `( R, F5 e
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI  k/ o2 b% n2 j4 u* \/ O, V, g
  86. 3 a1 W+ ?; ?3 \9 e$ c
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    ( |" ?, U9 L( [/ V; a
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF3 [' G; v0 D3 @& V+ t# T
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI* E& A) L) z+ K# O) s+ {" \: A
  90. 5 Q+ ~. G* x- [2 M0 h' @# b
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    # K, U4 U# Z& N. z% W
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF* N/ ]( q% c7 f3 t
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI! m, U9 V4 X4 }% D5 Z
  94. , S+ z- p$ S. ?% x9 Q
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    - V; r) e; D! j# P/ [
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF
    % ^9 G% \( t0 y) V
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI" y( L7 h5 \, `# o
  98. #endif
复制代码
; u; {$ g5 F4 V$ c7 r1 w+ d) ~
硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
1 C1 O3 o' R0 v: A
( z4 z6 b1 T8 x7 s" ?2 s, a4 I主要是下面这几个:6 e; Y2 M1 S% v( X# I. I, @
: T+ X, e& `8 }9 S1 J1 _
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000/ w( S' K  I# b! d; @  ?$ c

  2. / ~) n7 {. p; v' n3 ~$ a
  3. /* W25Q256JV基本信息 */
    ) Y( r" Q4 g. ^3 g$ l- M
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/+ j+ d* k, s9 J: f& n
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */( V2 L5 O0 u, A% v4 k
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */& A1 U$ i0 s+ s7 D! ^8 d0 o
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    9 k, A# R8 t& T+ J
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    * i9 M" d/ b5 [1 H  N: m# E

  9. " d- d4 S) t: W! C9 U
  10. /* W25Q256JV相关命令 */. m- u+ c3 ~* n6 [/ z
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */
    - t3 C8 ~, j1 H4 K% d3 S2 ]
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */
    2 }! D2 P& ], [2 K6 E
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */: ?2 D' X' h. A$ V* H  J
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    2 T: r# L0 Y  A
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */+ [% [; B) b# M7 m
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */2 F5 W. |* F: y  Q- B6 W
  17. 6 ]- i/ m; _# e
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    " n2 L+ d! h; H7 b. y

  19. , B8 u2 X+ c( N4 n8 G
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

+ v$ ~4 i+ ^5 r: t81.6 QSPI Flash的STM32CubeProg下载算法使用方法
' `& a$ n8 }0 D- k  b) ~( N$ {编译本章教程配套的例子,生成的算法文件位于此路径下:
- D- s2 S# R8 G0 I* n0 y4 L
, x" G5 E' Z' G. g% R" d: Y+ Y5 ?
d45f9274d8762b095790b3e6778b0a93.png
: `1 C9 k# i( ?: ^/ ~! g+ o; S
7 A; ~+ \( u$ S. o
81.6.1 下载算法存放位置
9 E1 f/ J6 z3 ]; n- o3 P生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:2 r/ y' _1 s; d& C0 o8 j
+ _5 J% a* o" \% w
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
/ w9 y9 X* K3 l7 P" I% ^& _
0 T' z2 K8 B' A2 z
60cce5a15415e27e9d5b75b05028d1c0.png

1 D% X4 C' U, R) g5 B' G4 g0 l3 u
$ S. K: v8 }3 c0 V) w81.6.2 STM32CubeProg下载配置1 m0 w1 v% M/ F1 m
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。4 ^+ s- L2 d0 O. M3 X7 t7 f* u

. l# R1 r, E) d* L. G( }
9cc179641e765ea43cb38d268bf734df.png

; a3 k2 e9 \: B' W) ~( y" [8 X# X2 d
6 X0 z- G2 `7 F% x点击Connect后效果如下:
$ ~% l% R3 y) x( _" I8 h- W, n/ ]
b39ce8a738163430db2119bdd87e3fbb.png
! E# J4 U5 x; @$ W: Z' o! i' m6 K
; C( D5 D) b2 v! j: O* A  r3 h
在这里选择我们制作的下载算法:
6 I( B8 P5 P+ ?+ u1 W8 `& O  ^& ?* C/ U3 a5 k/ X3 }* R
17f6e0f50a18ae818faaa1399daef12f.png
# {, S5 _8 m8 h4 |  g5 a
( k- L$ o7 t1 ^. x2 k5 A( X# F) M
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
0 z, V. c8 ~/ F- o+ Y2 K8 g4 _' b/ i( G
43a0d90faf1d392f5a74eaa7777e4178.png
; d$ }6 P, r2 H3 O

* x$ K% H" v+ P/ p5 j& k* Y下载完成后的效果如下:
: m* J7 ~6 T' S7 C9 Z6 h/ v3 N' p' ]% q0 _4 H' z0 A8 j; {7 S
a1d6662baa9e7be00f7e67e02b5c9b79.png

' H" |  ?! h; J# x7 g' q9 }1 ?3 X; U) y9 M4 K- z# U
81.6.3 验证算法文件是否可以正常使用% A. C: G. \# o4 W7 o
为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:, e% W; L. D6 Z7 J$ L

4 z0 X' x: X0 N/ \, @! e% W& R
3f2eefeef3db68623cb8969974c40a20.png
5 P; N$ E# L9 P% @$ u; I; `

9 X- _. D- n/ G( K81.7 实验例程说明- f3 t5 G2 v4 F5 n: z3 X" P
本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。
( V) j* B  z4 ~* U. U( c2 |, ~3 V6 W0 Z, V+ |5 H) T
编译本章教程配套的例子,生成的算法文件位于此路径下:
& w4 J5 p0 M$ T/ F3 S3 `# x% Q3 b8 [$ ]4 [
b7028271136c8053d7300063ed4aec3a.png
! M% I4 e& u7 _. G  y: V
+ _( w( [6 z% v& ^! L6 n: E
81.8 总结' I' J: C+ r6 g0 ~( ]8 ]9 p
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。6 j7 j1 A* E  n( j3 q* l8 L
; B+ }' b7 k  l4 c& T

+ ?" V4 D- [5 r; M( U/ Z) L. P9 V4 U5 ]4 M9 O9 |' z
收藏 评论0 发布时间:2021-11-4 23:27

举报

0个回答

所属标签

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