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

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

[复制链接]
STMCU小助手 发布时间:2021-11-4 23:27
81.1 初学者重要提示
9 f. @* k& `! `; N  QSPI Flash的相关知识点可以看第78章和79章。% _$ @! I! `8 [  Q. R. M
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
" M5 H. p: O8 R; J- d$ D; c  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
5 v3 s, n: `; @! Y# B& p- v# M  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
7 ~( K8 D( q$ G81.2 STM32CubeProg简介$ }% h  i0 o. @& S  I2 h
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。0 f/ o. e5 U# [$ P

2 ~+ n. Z" q, N6 D& M9 P! T" {. X
1bbabf500126f58ff1439b6bc36014be.png

9 \/ P3 |2 x7 V9 [, E3 S8 z1 s: e- ?1 S9 o; p
81.3 STM32CubeProg下载算法基础知识+ Q, X. `) a( ]) R3 G
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。  N  C4 f+ o& J1 ^4 B' w$ e6 @

) V: g% Q, f* K' K6 H5 l81.3.1 程序能够通过下载算法下载到芯片的核心思想9 f/ ?% ~8 i7 v. a2 _' u
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。9 ~2 C# t0 X% k. {

: H. T+ _3 [3 X& \81.3.2 算法程序中擦除操作执行流程
9 v3 [$ V, T/ w5 h注:下面是MDK的算法执行流程,STM32CubeProg是类似的。- b% k/ k* N4 b; O
. j7 G% t7 c- M2 ^3 k" u
擦除操作大致流程:9 D3 P% I) p! C

  i6 o) Q+ k  o+ s$ ]
161f92ebd9b1dbf9febba815fa091b8b.png
: F& g- d& j( ?8 ~0 L

& W( q0 i: N! X9 x+ `  加载算法到芯片RAM。; j" R% U4 T, y( y" l
  执行初始化函数Init。  r2 P1 u6 d3 M, o2 \' J# T
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
9 `% Y+ \; x1 v  执行Uinit函数。
+ y& A6 H/ V; g) f0 I; v( g0 E4 a  操作完毕。1 b5 y- u" ]9 {; N. R) C! x# H
81.3.3 算法程序中编程操作执行流程
! c% T3 n3 G, c0 h) f% O, B  V注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。& Z" n9 x2 l1 X% l# N% w" d

2 v, q5 t7 z+ L; G6 G编程操作大致流程:
# o2 \1 A" w' G; R8 V4 r3 g: p" |7 C2 J+ N- I- C% J5 g( S$ k5 l
d80dfd755411aa0d0565f1c56f10e66a.png
" L/ t( g" [4 i* j/ a! V% ]/ c- ^
! i" d( W3 Q- z, z/ v2 j
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
1 F7 C4 |2 O1 I3 a8 ?  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
9 J; D4 J9 ^4 W! I  加载算法到RAM。& t. [% A' A7 y0 [* U+ E
  执行Init函数。9 @( w7 J5 F& O3 j* x- |5 D) N
  加载用户到RAM缓冲。
* `+ p) _$ c2 l2 ~  执行Program Page页编程函数。! C8 d0 l( c! E& b. Z, a  P( e/ z# t2 [6 S
  执行Uninit函数。3 m3 _2 @* u: ^/ y* h' a
  操作完毕。
8 m3 C4 U1 a6 A/ c1 g! ]) Z81.3.4 算法程序中校验操作执行流程
( A. X& \0 r; M( q! ?注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
  `- C/ v7 s! k* v& j( s1 U4 s" Z% {6 E% W: d
校验操作大致流程:3 x' X8 ^6 m% B% i3 H1 ?& G6 P: _4 \
$ t0 f( R; r0 }
f15191090ab169d48a203ea3617af9b4.png
5 P5 I# {- V3 t& e

. t. i6 w$ b1 [  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。; w3 A8 U3 J: s- o8 ^* t  v9 d
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
/ ?+ \" t+ ^2 I) O, `  加载算法到RAM。
/ l. N3 G! L. s1 U( c+ c6 t  执行Init函数。
" M  ]: N" \, A, F5 U6 R+ [% l  查看校验算法是否存在
3 L( _8 _3 T3 p  如果有,加载应用程序到RAM并执行校验。
3 Z+ b$ j/ z4 Z1 ]7 y  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。. l$ F: Z4 W2 B$ h" \! ^$ U
  执行Uninit函数。
' w/ y4 t, R1 j6 Y0 a% B' m+ @& k  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
& C7 k! E% i3 \8 v. ~  执行RecoverySupportStop,回复支持停止。
$ X6 \! |1 ?* K' r/ [1 _( N3 ~  执行DebugCoreStop,调试内核停止。
& \  v% [5 z$ W1 P4 B  运行应用:
6 s! l) t# F( n) ]# U2 B  执行失败, N  J, v. C; z4 j  C. s
  执行成功,再执行硬件复位。9 b% [! [% ~+ }+ x% I: a9 w% [3 Z
  操作完毕,停止调试端口。
3 o  \& f. ]% P9 S0 S3 X6 G4 h81.4 创建STM32CubeProg下载算法通用流程- n- @; B- P) E' k9 x, K1 E# t) u# F
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。$ D5 q' @5 n, c& s& k8 n# }
5 Q1 ], z  B# s' W3 H  Q3 g8 r1 V. t
81.4.1 第1步,使用STM32CubeProg提供好的程序模板
5 ~+ u/ [2 A( M& @1 H) M8 R位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader5 G; q0 u4 |' s& F/ l, t

: K" G. g$ K) o* g" E/ x- F& c7 ^
4fbfb0047e29fe23083224607059d97f.png

+ D' Q* n0 [, ~) a" v9 }
/ X: _- G) l' G+ ?- Z1 y: D以M25P64为例(注,其余步骤就以这个为例子进行说明):% }6 M$ Y3 Z* }
5 g* {" Z  I2 A' E. k% L
d1b1a340fdc655c1030330a7dad2abba.png
9 o! j0 N9 C: {
5 L8 Z: M. K$ y9 s2 |9 g
81.4.2 第2步,修改工程名
1 h$ o0 C# M& N  O0 D+ LSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
0 Y) b8 n4 [! v5 ~) e
" J- T- D: }% x$ b2 L: s) M( I/ ?# _+ y81.4.3 第3步,修改使用的器件
. a: M: t# V; O, E6 i在MDK的Option选项里面设置使用的器件。; H  S) g0 h1 t7 _

' i- h! r, G0 G0 i
092174ad976a8ce9c7fca533049e2dd0.png

! C, n. q- a" b& w# P7 p. [. W; K5 ^& y
81.4.4 第4步,修改输出算法文件的名字
* ~; V6 h0 S/ q, z) q$ u1 N这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
1 ?9 I4 w5 K% s" h  N/ ~9 P8 C3 H' g* P: r( ~7 b; M( P3 M- ?
79c42251311dd3892fc7d6b8ddde68b8.png
2 T0 n8 }- [; v+ N7 E1 Y# `3 u
0 `+ o' X  L$ s. I
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
- L2 ^& ]2 e! f' x
e2fe61fa7ea99b25b178341b7350558f.png

/ Q9 G3 [  t/ ^9 o# j/ a* @; e. C7 g* K
81.4.5 第5步,修改使用的库文件和头文件路径
' i5 W2 X- q# d7 g根据大家使用的主控芯片,添加相应的库文件和头文件路径。
: q' |7 J& l3 D! ]. o7 Q. [; p% W) y' p) w% \  G9 c
81.4.6 第5步,修改编程算法文件Loader_Src.c/ l$ L) h1 S! u) ^# j
模板工程里面提供的是M25P64,部分代码如下:
2 I- _# F* H& l6 s8 u: E9 k* w# W# S3 G1 c3 k
  1. /**8 j& N6 N0 H; n7 _& L
  2.   * Description :
    3 O. Z9 ~) k% z4 ~; h' |
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the; E/ s0 O# M9 q0 @- T' X
  4.   * device and initilize the FSMC with the chosen configuration ; B  I4 i7 t  j! [
  5.   * Inputs    :% }4 Y2 f; P1 e
  6.   *      None0 H) n% y* G* Y6 B6 C
  7.   * outputs   :" L" @1 t% B1 z* n( P
  8.   *      R0             : "1"             : Operation succeeded- b. @: D; q* n' }! G" c
  9.   *               "0"             : Operation failure* u2 j' L+ L  o* w; c/ f
  10.   * Note: Mandatory for all types of device
    3 C' @/ t3 H3 c1 R( w
  11.   */
    ( f: U9 g% X; Z3 M" g' a$ i
  12. int Init (void)
    1 x' ~+ j( J+ X4 h! Y; P6 R
  13. {  8 X6 E7 e( ]# c# R7 S6 [
  14.   SystemInit();; o0 M- D. |! O. t- W0 K  c
  15.   sFLASH_Init();2 t. P: u1 U& Q, c
  16.   return 1;
    8 \8 l, K+ x3 w, S$ A0 @
  17. }
    " G; w1 H1 F% Y( H4 b

  18. + G* y2 O, m9 H
  19. / q) w/ c5 ~0 Z. Y% q+ Z
  20. /**  ^3 _* m+ {( Y2 E
  21.   * Description :% u7 l* ]9 E4 ?; _( [& k5 N
  22.   * Read data from the device
    % `1 @, S  C1 n7 E* B! |9 O
  23.   * Inputs    :. g: o& I6 c7 l) u; Z# Y; K  Q
  24.   *      Address       : Write location% k* q# @- b- v# J" D
  25.   *      Size          : Length in bytes  ' J; Z8 I% r) X
  26.   *      buffer        : Address where to get the data to write6 D3 D/ T/ s; I5 z1 h
  27.   * outputs   :
    1 p$ Z( B5 o5 U6 C* d
  28.   *      R0             : "1"             : Operation succeeded# Y# ?: z% ~3 O% k: y4 q5 Q# G* c& y
  29.   *               "0"             : Operation failure
    4 j' r0 F1 y( B5 p
  30.   * Note: Mandatory for all types except SRAM and PSRAM      b6 P6 k1 R0 c- |+ k
  31.   */
    ) I- B  a$ ]' m. ]4 Q3 I
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    2 P& {2 R* {% x1 r2 U& r1 G. Y& F
  33. {
    2 F, O% K' f1 C3 O* U  \
  34.   sFLASH_ReadBuffer(buffer, Address, Size);# E* D6 V: F# d& H( ^
  35.   return 1;
    $ s+ w# C$ O0 F$ Q9 @1 h; {
  36. }
      J* C' B* x1 `! J
  37. 6 J; w/ ^% M4 ~5 v/ h2 d

  38. - F& w6 `0 k# Y. g2 @" r+ D* n
  39. /**
    & V# W* ~/ {" G4 a+ I
  40.   * Description :3 C, E/ f; z# Q/ ?
  41.   * Write data from the device - w/ b0 }' \1 D
  42.   * Inputs    :
    / B) P+ `! K: D( m; Y
  43.   *      Address       : Write location) F3 w0 ^* t. I$ a$ h- b
  44.   *      Size          : Length in bytes  
    % y) h; W5 b5 L; D" D7 }( l
  45.   *      buffer        : Address where to get the data to write
    : `, N* V2 c) _: M# W- l) Q
  46.   * outputs   :5 f' \4 f' u# |1 y
  47.   *      R0           : "1"             : Operation succeeded6 d4 P$ W' c9 p( \; R7 G9 f0 N
  48.   *                     "0"             : Operation failure
    7 g+ [+ B0 c" D8 t
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    : u! p  @4 y- r; H( F4 E" |9 O
  50.   */
    ! b5 h  v- P9 I" w
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)' _1 L* ^% K6 ?, L+ W) g  n: m
  52. {; K8 e- A! \( l1 Q. j7 C7 z$ d9 [1 ^" o
  53.   sFLASH_WriteBuffer(buffer, Address, Size);1 ?; I& j( H' D7 w5 j3 r
  54.   return 1;
    9 P9 F0 b4 Y) O  H
  55. }
    # `+ e1 `1 ]9 I9 `( |

  56. 6 I2 M/ H  d  p9 N5 B( o
  57. # f$ r- R, I& j+ c( a& c8 p
  58. /**( w0 K) Y9 S4 N; f& f6 h
  59.   * Description :
    2 e9 B+ \, W3 J! N. H
  60.   * Erase a full sector in the device  A9 y2 C$ I4 D4 Z5 O9 K
  61.   * Inputs    :
    7 Y* L/ u7 s  q. h3 B* P# @6 `
  62.   *     None. d# J+ R8 E5 G9 n$ \0 j
  63.   * outputs   :
    * w- t/ ?$ Y8 s" g
  64.   *     R0             : "1" : Operation succeeded- R6 l- z  \4 J# F: D3 C  J
  65.   *              "0" : Operation failure0 h: z3 X7 F/ u! z% f, `
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    : x; \  G( c0 A0 y/ u! V5 R
  67.   */
    ! B+ e- f. e$ G. R
  68. int MassErase (void)9 M& A) w4 D/ Q8 C
  69. {  
      ^, B$ ?3 m4 N
  70.   sFLASH_EraseBulk();
    * ^, a, v6 V3 [8 Q
  71.   return 1;   
    * F% p2 p$ G7 ^
  72. }
复制代码
. O8 j$ M; a5 K, O4 H  V
81.4.7 第6步,修改配置文件Dev_Inf.c4 h3 M' s4 }! x7 Y4 c; u
模板工程里面提供简单的配置说明:- s: X# P( F2 a; K* t

+ \( j! Y7 e0 x2 X. d  M( f
  1. #if defined (__ICCARM__): G* i5 L, E3 ~0 x% F3 h6 T
  2. __root struct StorageInfo const StorageInfo  =  {+ R) f1 R$ n$ W5 _* J5 Z4 [
  3. #else
    3 T, q. j& C, B
  4. struct StorageInfo const StorageInfo  =  {! v! S% Q1 \7 f1 O
  5. #endif7 x. T: ]6 T' }& d7 R
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    $ ^& y. C( E  h5 X0 i
  7.    SPI_FLASH,                       // Device Type
    ! V, m: T! B2 L+ |. v/ Q/ p
  8.    0x00000000,                     // Device Start Address
    3 B8 Y) x2 t& x. Z+ c3 ~9 U
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)) l7 p) e7 ~2 {" v. u( ?- X
  10.    0x00000100,                      // Programming Page Size 16Bytes
    5 N  D9 e! d4 }4 \
  11.    0xFF,                            // Initial Content of Erased Memory3 r- h* O0 ~+ ^7 F5 s
  12. // Specify Size and Address of Sectors (view example below)
    4 v; K2 p, [, r
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes , _0 g( X  w. G# p- r# w
  14.    0x00000000, 0x00000000,( u% X& ]4 m+ E/ G& n- _
  15. };
复制代码
2 j5 e; `: m" G& }2 W
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
/ u- e4 r' g, C; Y8 w( b
7 U" R; q. f5 J% z7 G- [- I- a: R6 q# N  ?" P
; {' Z+ C$ q2 s6 O. m
81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。! P- f3 x1 F; {  n
C和汇编的配置都勾选上:9 Y& I7 X$ V& G  M. x4 ^6 ~" ^

5 w7 ^* [3 B0 y. c0 Q" S5 k
8f2ba38db4c1f685f131e7f1f4e98551.png

) v; M+ d6 k2 J% w4 I& u# J  h$ Q' o2 D: {0 ?
汇编:8 d1 ~$ j! A' [& T# M6 E& t- d
/ A5 J2 |/ d2 H0 V# l, H- }7 R
0d264153b167afa2303144bdd378d501.png
0 f) F5 o% X- S( i# ]
; r0 T( b, ?' q' P+ F  j3 U
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:. E* J( A0 R- j

. m+ }6 `, @+ l, u: _4 o. [4 c(1)加载以响应运行事件。
: h7 m' K7 ^% e9 T6 v+ R; c) P) N, F- X# J% a/ B
(2)在不同情况下使用其他例程的不同组合加载到内存中。
8 n5 J6 a& y, o, G# t9 I9 z; y# n/ {( f& X# }! o
(3)在执行期间映射到不同的地址。
0 v! y, h' O) n! S7 d$ D, m. P. O9 @, q: i/ {9 c$ V0 Q3 _
使用Read-Write position independence同理,表示的可读可写数据段。
7 l4 y! a, L& H0 P
' _  C' L! N& P) L4 E5 ]" ?81.4.9 第8步,将程序可执行文件axf修改为stldr格式0 @- Y0 ]; w+ Y/ u) J! V1 e. s  [' m
通过下面的命令就可以将生成的axf可执行文件修改为stldr。
* j+ @& y2 g& I- m7 G0 i1 i+ i0 C0 t' V" e8 d6 |1 j9 i1 I
f50b1594f3741e77b4fbd5523564d3af.png
& _" p7 }" m4 s$ Q
1 E$ }7 _- S# b: W  E
81.4.10   第9步,分散加载设置
5 i, `, G5 [1 `, H我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
& Y9 U6 g8 ?7 }2 j, a2 K+ Z- Z, N
) o! p# x5 x: g8 S- R7 h
8a01cb7c9be38519d4d0510a218447e2.png

5 q- I8 ?  @/ k* ^! c2 f4 I+ ~' r& A9 b
分散加载文件中的内容如下:
- ?0 R- C5 B2 u1 P5 `
& T; b4 h# d# m1 K
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions6 r( y. a+ a" Y# D( {
  2. {
    " x7 P) L  m" I  Y/ f8 M+ K) Z
  3.   PrgCode +0           ; Code" ~% E5 M% x1 P$ _( ]: {
  4.   {3 U/ I/ J) C" x; W, \2 D3 _
  5.     * (+RO)5 ~' \5 p* c. e- m) ]
  6.   }
    ! v2 G& S, g# r1 p9 ~  g+ A
  7.   PrgData +0           ; Data9 U2 Q' R7 @1 v8 h
  8.   {
    ( [$ q* i( c4 g5 f) ~8 c$ C
  9.     * (+RW,+ZI)
    * m" ?5 H  M8 F/ O7 M
  10.   }/ K7 y) o  ?$ H2 s9 R7 i! W& d
  11. }& q$ z8 A5 \* n* i7 k

  12. + e+ ]; O6 J0 X6 ]& R* r1 E% M
  13. DEVICE_INFO +0               ; Device Info/ H% Q. t, ?; G, X
  14. {7 L, V4 f1 L% j  b% o
  15.   DevInfo +0           ; Info structure' H1 x) @9 j) g6 x. ]
  16.   {3 V, }( t$ \" h0 G" J
  17.     dev_inf.o
    " m* D  b- w) U. g8 A6 z4 ^
  18.   }+ E* B4 ~5 l8 N5 }# J) Y
  19. }
复制代码
$ ^5 K' L6 Q. {% d$ n5 |- I9 S
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。3 m0 [8 l' l0 Y' c1 l+ D  P+ P3 C
" i5 E7 Z: ]9 |% z+ W6 C  u6 u7 \
--diag_suppress L6305用于屏蔽L6503类型警告信息。- E* A; a2 E( U: a

" H& M8 A! \+ r6 g0 k, _- ]  ?0 @% a特别注意,设置了分散加载后,此处的配置就不再起作用了:
1 J) m2 @5 L! V( n# y( {$ z! A1 U( R, p
13b022190bd5115ed249129fc3886a38.png

9 v9 B: K) W! X' w# n! v7 Y2 [, `* m. [1 W% ]* B# Q
81.5 QSPI Flash的STM32CubeProg下载算法制作  i8 h+ a( L, I: G, N
下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
* N% x% N0 _4 A% T6 W1 [- o0 A# F7 X, w8 `( o
81.5.1 第1步,制作前重要提示
2 R) B7 W  M: |( }1 J这两点非常重要:/ H5 m5 f. U3 \1 o, {1 `$ \/ V$ v0 N
) K/ ]& G6 }: ?( t
  程序里面不要开启任何中断,全部查询方式。
: o7 T; n5 p) m2 w0 ^' O* w6 W% _  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
8 x* q$ \& |6 B9 ?7 `: _81.5.2 第2步,准备一个工程模板
4 o4 |4 R' {( h8 V 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。1 ], c$ g" Q8 I! W
3 o, v/ M. |* z& R" ?# p
f46a8d2276d0ffb7beee251cdb0809a4.png

0 G0 u( h+ S1 [0 S2 c. {: m+ q. `+ i- l
81.5.3 第3步,修改HAL库4 n/ V) |' u+ ?$ B% ?7 V2 i
这一步比较重要,主要修改了以下三个文件:
& |0 _' ^' x  G# Z' d  ?
# j! |% C" c  A8 m+ @: B% l* ~% a
1efb78256b9465df406e3e9e95ae30e5.png
& f! w% M5 _1 P6 B9 a

, |+ k# F: s; K( _主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。  Q2 h' V" W1 f" t. U1 Q2 A
( m! i9 `" P+ C" H' i- X
81.5.4 第4步,时钟初始化
3 \6 X$ I, T: U! b4 o, K1 n; P我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
( y, K% B; U0 h0 \: `' {$ ~6 V2 ]) S9 ?3 V) ?
  1. /*1 _+ A5 L( {; F7 A! i5 O6 v
  2. *********************************************************************************************************
      E9 W5 l/ v- t; Q) M& I( w
  3. *    函 数 名: HAL_InitTick
    8 {( h7 C- X+ l4 c
  4. *    功能说明: 重定向,不使用
    8 c- y& o# [2 n, H% j- _& y
  5. *    形    参: TickPriority+ A" j* G# L8 {$ }
  6. *    返 回 值: 无
    " G+ H0 M6 d5 q8 j4 f1 r
  7. *********************************************************************************************************
    ; L% \2 p& U) R9 e0 U% W
  8. */
    9 d  ~  \; `2 n2 n% W8 n8 Y; J& o
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)! X8 ]. L4 }8 W: U' U% c
  10. {
    9 |" _9 v  U, K/ E  v
  11.     return HAL_OK;9 z+ Q  o% r5 ]! ?* s1 @% s2 O+ t
  12. }
复制代码
+ H- a; E$ S( k: d' N
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:( S5 u5 w# B4 F$ N, O; I' R2 r4 ]
( p. q1 B, F8 d  M( ]; v
  1. #if !defined  (HSE_VALUE)
    # `9 E: n2 g# f& i3 D$ `
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz *// ~" C! g: r% G+ T) j
  3. #endif /* HSE_VALUE */
复制代码
$ T9 m- M7 O( D+ c
最后修改PLL:
7 v- |4 _8 x6 k0 G9 L  |1 K2 p$ {, w5 H! Y( v8 Y
  1. /*
    - P  A& T) w. G9 R; C
  2. *********************************************************************************************************
    ( }: I: ^: e/ Y( I" c0 l# [
  3. *    函 数 名: SystemClock_Config
    ) H, D) ~  o  ^& f
  4. *    功能说明: 初始化系统时钟
    - q0 N7 A# k5 ~
  5. *                System Clock source            = PLL (HSE)
    7 n% n- e6 j, Q1 E( P. z$ P
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)$ s/ D$ `+ q, r9 g' f( ]
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)( t1 j% l: r0 r, u) m
  8. *                AHB Prescaler                  = 27 Z7 r0 M  l% f- o! a7 z7 T5 e0 K
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)' {: |4 o: B4 I/ G
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)! c6 I, ]* _. A/ w! `0 r( D
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz): v) f: S  m2 f9 ]% q/ ?8 p. e
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    ( l& J! l' t1 M% N) ^/ P
  13. *                HSE Frequency(Hz)              = 25000000- |" d( a( |, d5 `: Q$ |+ _
  14. *               PLL_M                          = 5! s6 H, E6 I9 G$ \2 Y
  15. *                PLL_N                          = 1608 F: \9 L. U2 q8 V% t
  16. *                PLL_P                          = 22 z0 N& Y9 u9 K: n  L7 ^
  17. *                PLL_Q                          = 4
    3 K/ k+ |! {3 D0 h5 `
  18. *                PLL_R                          = 2+ K. b/ b! K1 B& D8 U, v1 T
  19. *                VDD(V)                         = 3.3
    ( B  d9 e* ^) H7 @8 `
  20. *                Flash Latency(WS)              = 47 P! N0 ^2 W! W9 f  q
  21. *    形    参: 无6 D5 b7 Y2 I; q. S, s8 H" J
  22. *    返 回 值: 1 表示失败,0 表示成功
    . M9 [) |% U+ x* W& {
  23. *********************************************************************************************************
    , O( R  j7 [0 }: t" E4 P- j2 \6 Q9 ?
  24. */( u8 C. N6 M: ]5 n0 |
  25. int SystemClock_Config(void)
    9 c* [! }3 g# r8 f6 r
  26. {
    # y7 l" ~& G0 S* \6 M( V
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    $ _' l  m/ C6 V& P8 }
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    7 _7 a* F2 w& O; `
  29.     HAL_StatusTypeDef ret = HAL_OK;# E( `5 b0 d6 l4 U
  30. + ~' y" @$ S- u4 W/ i
  31.     /* 锁住SCU(Supply configuration update) */7 o' ?( `4 Y, _+ x' E# r
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    , ]1 {, z% \# u. D
  33.   y: [" h. e, F: O. ]6 q, ?
  34.     /*
复制代码

7 z& h- E7 y0 _( k      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
& Z! Z. W9 P5 e         详情看参考手册的Table 12的表格。
2 O, N# j0 a5 L# }. M      2、这里选择使用VOS1,电压范围1.15V - 1.26V。
: e' w- j0 v. b   
  1. */. e  P4 }" X$ I9 z
  2.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    4 n& h3 A  S( ^/ r' ^% w

  3. & g: {; J1 t7 x% R. x. ?
  4.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}( y7 [6 _5 }% y" t
  5. % ~1 B9 k7 A& k! X0 s
  6.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    . Q6 m6 K+ S; H
  7.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;, {5 U; h! {) T) S- [" F+ ?
  8.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    " U. q2 H, z: ]: K
  9.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;& P# g; F- z: \* c; D
  10.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    ; J/ J% k2 b# `
  11.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    + }7 D5 D+ g5 R" B; b+ r% W
  12.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    8 U( [" x: i1 w' V) j5 u4 G

  13. $ R8 ?; ]: d7 ?7 a& W
  14.     RCC_OscInitStruct.PLL.PLLM = 5;
    # f, B2 t% `# x: |: n. k# m3 d' l
  15.     RCC_OscInitStruct.PLL.PLLN = 160;
    5 ~4 ^" Y$ C  i
  16.     RCC_OscInitStruct.PLL.PLLP = 2;, `! t4 c: _$ H5 Q% Z
  17.     RCC_OscInitStruct.PLL.PLLR = 2;
    2 l+ l: Z/ o. \4 i
  18.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    9 o% f& J; R( J, X! i+ H2 y
  19. 2 l* F5 w% H* o# X
  20.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;' B% @, x) J0 a3 t0 ^& S# w
  21.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    0 O/ W3 j" ?* X, p1 d. s5 x2 `) q
  22.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    6 Q, ]( [+ _3 R$ `
  23.     if(ret != HAL_OK)& n7 b3 e) y+ U3 E
  24.     {
    - E* C, e" ]3 O- ^2 _+ m# s
  25.         return 1;        . ]8 w% r1 P1 w  x2 e( a
  26.     }+ \! @$ P2 n2 m% F

  27. 1 l' ~3 O* F6 F: D3 s8 H
  28.     /*
    & ^4 k6 S/ ^# `9 ~. D3 V
  29.        选择PLL的输出作为系统时钟  G0 b2 w  Y+ h' E9 {% s
  30.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    ( j  J/ M7 U2 h  c
  31.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线4 k0 w" [% x0 D# m
  32.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线' m$ \: Q1 h2 H0 k/ s
  33.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线2 N) l1 ?% ]5 T3 z6 @( h1 c" o# Q1 W
  34.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线- ?8 s4 b* B6 t0 k9 ]
  35.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     + \; v' R& y; O/ B, o
  36.     */
    : O+ a4 B1 h9 b; [) `$ }7 r
  37.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \. ?& z+ u" o  J* h4 C7 V
  38.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);7 u. t' N! M0 C7 z* |  \# k* W

  39.   e6 N4 c9 ?2 w1 I9 S4 R
  40.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;6 z4 U* J+ y  l1 |, A
  41.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    9 h3 B$ |  t$ ]% `; U" a
  42.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    ) k8 {/ x/ ~  ~. e
  43.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  / N7 _, u0 A  }! p/ q
  44.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
      s& J  c) [5 r
  45.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    8 \: f5 v$ C4 ?8 p
  46.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; , i% L0 ]% B( z+ ?( s9 Y1 c

  47. 5 r9 s: n6 t* n6 G, Q6 C$ g# |! h
  48.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    9 B' Z8 ]0 B. |6 Z- c7 |
  49.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);# s$ G4 ^1 R3 P" v$ `
  50.     if(ret != HAL_OK)
    4 o% b- p* [/ u* l( x
  51.     {
    : ]" f# ~& `" F* Z% G4 s/ L2 a' U
  52.         return 1;! H, `. u4 w) ]7 b$ Q) ~9 d4 Q* ~) ]
  53.     }/ B5 L/ f2 }( m1 d7 z5 |  R5 B: N

  54.   `" Q$ `: o" O# e$ M+ K* d$ N
  55.     /*
    $ \% q0 i3 B, z6 r+ |+ [; Z: N5 _. n
  56.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    1 _* W: N! G) T. j+ J
  57.       (1)使能CSI clock$ S" b" U* K1 {3 U5 H. u% }5 j
  58.       (2)使能SYSCFG clock
    9 F/ l9 |5 u3 j  y6 T' H& {
  59.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    . v. M6 D; y5 b1 n3 r) ~
  60.     */4 |- d( L  g- X/ z; i2 A4 @3 J. }
  61.     __HAL_RCC_CSI_ENABLE() ;
      ?. Q2 s3 L7 t, d% C" S$ P# ?' ?

  62. ' w. p0 E: l" `: _, ]; ?+ j* C1 Z. H
  63.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;# ^" b" ?2 [9 b5 H1 H
  64. ; e, _* O  _- a/ ?8 Y
  65.     HAL_EnableCompensationCell();
    ( y. D5 r. Y& M6 W

  66. : @  t+ A: i) W5 d# O/ v+ S% j
  67.     __HAL_RCC_D2SRAM1_CLK_ENABLE();- ]1 m9 A+ J: v# ~2 \+ T
  68.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    8 J) T2 Q7 A8 w% M3 h( i8 A+ u
  69.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    - ^. S! Y+ ^) I: d+ u, A  z* C
  70. ' u2 G* r4 W" _' z' T! d: U$ t
  71.     return 0;: _: y% x$ o9 S# Y. f
  72. }
复制代码

' y6 S0 N7 }( s- o; F! _81.5.5 第5步,配置文件Dev_Inf.c的实现
! c0 ]: Q; B* h4 k- n3 a1 n. L$ c配置如下:& p5 U0 O- {& }! t2 x& G% p( Q7 ~+ @

) m# l* l& y# L6 \4 A
  1. #if defined (__ICCARM__)
    ' i9 J- _, [: E6 K3 H/ }: g# K1 w
  2. __root struct StorageInfo const StorageInfo  =  {
    6 C8 u9 B! A; C5 ?* \( Q
  3. #else# x; Z1 T* w. a+ X: H6 j: S
  4. struct StorageInfo const StorageInfo =  {
    + r6 J/ {( w0 f1 z
  5. #endif
    ( j; z+ S2 U, [' E
  6.     "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    0 k7 z2 ^1 i# u  ^8 c5 h
  7.     NOR_FLASH,                      /* 设备类型 */( G! @! T% b# u5 ?' H
  8.     0x90000000,                     /* Flash起始地址 */
    5 I3 U0 M, e" m2 q4 B
  9.     32 * 1024 * 1024,               /* Flash大小,32MB */
    & r, H: j5 P9 W9 ?$ ^
  10.     4*1024,                         /* 编程页大小 */* S/ }; S( B2 K' q; Z
  11.     0xFF,                           /* 擦除后的数值 */
    & M- P7 c7 J4 E* D
  12.     512 , 64 * 1024,                /* 块个数和块大小 */7 s/ }( W1 A* N
  13.     0x00000000, 0x00000000,
    6 @' y- X9 I" e" }
  14. };
复制代码
, H; M' ?" G1 }6 X3 E! a- [
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:- v* s: h: d0 ?+ ^$ S
, x5 [3 m8 d% x6 i5 A) G
bc495adf8d970e6e1fb6822b88b94e2f.png
( V) L  g1 Q' {9 k5 b& X

" Y4 c2 \; ?. b& v7 X81.5.6 第6步,编程文件Loader_Src.c的实现
! l: q) K5 I7 o2 U" o7 g; Z下面将变成文件中实现的几个函数为大家做个说明:1 o% ?: X- Y) D

1 }% C; y' O  v( h- M7 U  初始化函数Init7 w3 @% C- s3 h- m
  1. /*
    8 C) A! t3 B4 f8 ]- m% i
  2. ********************************************************************************************************** a% R9 B9 l5 [; q$ Z
  3. *    函 数 名: Init
    . [' V7 y9 L6 W
  4. *    功能说明: Flash编程初始化" J5 h  \+ {- p: e
  5. *    形    参: 无
    ; O  x3 _8 k0 F5 L/ x( y% ^
  6. *    返 回 值: 0 表示失败, 1表示成功
    4 ?* |6 n' S7 B$ t: {
  7. *********************************************************************************************************0 q7 X  A( A9 a, K  J
  8. */& _3 e" g' ]/ P" a9 a
  9. int Init(void)/ m  Q( u; w, x9 `
  10. {   2 ]; u, T% G. o, X! @1 o9 \
  11.     int result = 1;9 @% Y  G4 X9 v5 s1 D8 Y+ W1 t

  12. 1 F' J8 d8 W: y! p- S0 e% W4 a
  13.     /* 系统初始化 */& ?2 ?! M  t2 G5 P8 j) v" o
  14.     SystemInit(); ; O2 _, R2 I" ^% l
  15. 3 [6 C! F. P- X3 ]( C; X& A
  16.     /* 时钟初始化 */! J# x7 t# x# x! b7 Q/ _
  17.     result = SystemClock_Config();
    ' ]5 p6 c- q& W" E5 I/ M! i
  18.     if (result == 1)
    5 x" @- G8 Y! W
  19.     {
    ( w: Y8 C7 Q4 p' V! \
  20.         return 0;        
    5 e. H2 j( n0 J6 ?- F
  21.     }
    7 a, N6 _# R0 {% w  f' e
  22. 5 ^  r+ h9 p' i/ u2 I# b% D
  23.     /* W25Q256初始化 */3 ?" z% [. P) U9 J. G
  24.     result = bsp_InitQSPI_W25Q256();
    " v% y1 {, B+ Z. e  u
  25.     if (result == 1)% [9 }4 O, i+ y
  26.     {! C+ w5 S; r& A
  27.         return 0;; K7 B: o8 M5 p5 a, U
  28.     }
    9 {' T3 c) t3 J& N, O- ~

  29. 0 ~6 p1 e) K" U
  30.     /* 内存映射 */   
    + z- B  ]7 H: v2 W; {' \
  31.     result = QSPI_MemoryMapped(); 1 V4 ?/ h# M; M
  32.     if (result == 1)
    8 `' B& u" w% q" w6 i8 J
  33.     {1 O  [+ y# H2 s
  34.         return 0;+ x" B+ H; i# V3 b
  35.     }1 ]- B  l+ w% r$ p. {1 D' T  \4 I
  36. + A/ ~. ]) l" x$ t
  37.     return 1;
    + A& A# {/ w: i7 z" y) Q0 E
  38. }
复制代码
! _0 [0 v% V( i
初始化完毕后将其设置为内存映射模式。
. t7 [1 L" T/ w- t* H% ?$ S
; Z8 D% d% @$ S: z  整个芯片擦除函数MassErase8 J  y& ^3 X) x- ?, t' e" ~% M
整个芯片的擦除实现如下:
, r5 K* a! \& \; q1 W7 z$ I/ U8 w  g; z" t: S" z8 B, y
  1. /*
    0 f3 g" p2 N. S( S) {* I
  2. *********************************************************************************************************
    / t. Z. @( ?' [' x9 H0 f. q
  3. *    函 数 名: MassErase0 p$ b) }3 a' j7 ^, p" E+ w
  4. *    功能说明: 整个芯片擦除' W3 ~$ T8 n) l, ~" e
  5. *    形    参: 无
    % R0 u+ b( \- n4 W+ a+ x  x8 k
  6. *    返 回 值: 1 表示成功,0表示失败; R) s- u  W" b! P( X
  7. *********************************************************************************************************
    2 ], w' h# b1 Z, J; n! }
  8. */3 s- `6 L4 i( z% Z% |  X$ X
  9. int MassErase(void)
    . s. R+ T0 d0 c$ |
  10. {
    . n/ n" s, `& E4 A
  11.     int result = 1;5 N( ^! j- O8 ~% }; r1 O- M9 W6 x; y7 l4 c
  12. + C1 }) U3 _+ x( v! Q# N
  13.     /* W25Q256初始化 */
    2 N6 ]; I/ o+ a. Y' Q
  14.     result = bsp_InitQSPI_W25Q256();# P3 F& |4 Y# ^* p5 K$ m8 l6 z
  15.     if (result == 1)
    % y! c2 ?, G9 Y6 |. T
  16.     {7 k' B% E- |4 ~# R' }
  17.         return 0;
    ) D7 o- T3 o: J$ p: {
  18.     }$ O: g. `) \2 |* o1 p( `5 Z

  19. 9 L% R2 l/ r) M
  20.     result = QSPI_EraseChip(); ; @& O* O6 z+ K7 W' }% _
  21.     if (result == 1)
    3 l- i) S5 W+ P$ g
  22.     {: s6 o6 \1 P  l. o, g
  23.         return 0;7 w  ]' s" o* J2 F; ]
  24.     }    7 f: ]  }) l5 ?9 d7 `0 Y: w; P

  25. 5 S& c: s: L* ?. M4 t7 i
  26.     /* 内存映射 */   
    0 t+ H  \) w6 `) W% n. a  d1 [
  27.     result = QSPI_MemoryMapped();
    5 i3 C- @( I4 f
  28.     if (result == 1)6 x# M) n! ^: w+ C: j5 o
  29.     {& r2 Z$ S. r# G5 c" i8 M  H! |
  30.         return 0;* v) c7 u7 K9 [# _
  31.     }0 x; G% @; w+ m+ X+ K. c- t- L

  32. - D+ R8 E& Q- i: T
  33.     return result;          ! {, B( b! z& ^2 _
  34. }
复制代码
2 m0 e( f$ Z2 s5 Y1 H$ Y! C1 p' _
  扇区擦除函数SectorErase
5 [1 I8 W' j$ e扇区擦除实现:4 \( P% z3 N8 I7 }8 |; a

5 F+ {4 S; N" I, F8 x. F1 k7 Y& U
  1. /*4 ~1 C/ d, B' k2 P1 H
  2. *********************************************************************************************************- u9 B1 V; u3 t: W
  3. *    函 数 名: SectorErase. n6 M. ]3 s" O% s: V/ L
  4. *    功能说明: EraseStartAddress 擦除起始地址
    ' X( K* D1 s, l' k& v
  5. *             EraseEndAddress   擦除结束地址
    % y1 S' J; X/ l6 J
  6. *    形    参: adr 擦除地址' [/ L% Y7 q) ~
  7. *    返 回 值: 1 表示成功,0表示失败
    % K; E! [1 m: c
  8. *********************************************************************************************************
    7 o$ F4 t, f. `2 P" ]
  9. */! h- N4 V& G) f+ t
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
      t8 o* T0 u0 W, X2 t) P
  11. {7 O# U& }' n: B% d1 Z
  12.     uint32_t BlockAddr, result;
    / w/ J* C. z5 C2 d
  13. 2 ^' M0 r: j! l
  14.     EraseStartAddress -= QSPI_FLASH_MEM_ADDR;2 P% X" H. C2 `4 X
  15.     EraseEndAddress -= QSPI_FLASH_MEM_ADDR;
    : l/ o" y5 C' ]
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x10000; /* 64KB首地址 */
    9 T0 Y( w. d, s; p

  17. . x. {8 D$ f8 C9 L9 w8 X
  18.     /* W25Q256初始化 */- q7 b7 E; Z  I" p
  19.     result = bsp_InitQSPI_W25Q256();% s9 H7 h$ ~- \! s
  20.     if (result == 1)
    6 b' U. W8 T+ a' ?- d8 \
  21.     {- _- F5 x5 Z  t
  22.         return 0;        3 n% O3 m% a3 ^5 }/ C9 y3 _
  23.     }3 \" R: z8 ~: x% C2 r

  24. 2 q: C) p/ f- u7 T3 h. ~+ k$ B
  25.     while (EraseEndAddress >= EraseStartAddress); Z, L0 f5 R, C$ \
  26.     {
    ! Z" {# u( L6 c/ \' w) |
  27.         /* 防止超出256MB空间 */3 r4 A1 l# x6 r6 t
  28.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;, f; J7 U# n- ]6 x2 n) Z

  29. 7 i# g& q2 x" @
  30.         result = QSPI_EraseSector(BlockAddr);   
    % w" N6 {) l  m0 U7 ?
  31.         if (result == 1)4 Y. R) c; @$ T; D' _$ N4 r
  32.         {* `0 O5 R/ q0 n% W# C
  33.             QSPI_MemoryMapped();
    4 [$ c7 Z: x% B* N0 L/ b
  34.             return 0;        : K$ U% r. N3 L, T2 s
  35.         }3 z0 q& {. K) P
  36. ! t. U! p3 M- ^" A4 J( Z
  37.         EraseStartAddress += 0x10000;
    ( n( \8 C# Q( [4 R2 f2 s' ]. A: A
  38.     }& M" F# Q9 ?6 A+ k

  39. 7 U4 H5 X3 \7 ]9 P* z
  40.     /* 内存映射 */   
    9 N6 d1 \: i' p1 r' ^
  41.     result = QSPI_MemoryMapped();
    ; `" V& i" y  ^! X7 q- r) V( q
  42.     if (result == 1)
    & t- [+ Q0 Q5 ^2 ~9 F6 i
  43.     {; O) ]2 y, {4 r- `: w( L
  44.         return 0;) A; Z  Q: l; o6 J5 f
  45.     }2 S, {7 [& J/ Z

  46. * x, K7 Z* N3 M; [
  47.     return 1;
    % a! f+ c* A6 B/ b
  48. }
复制代码

7 `2 v; c  _0 O/ S* A, s这里要注意两点:
; b0 ?4 y, l% _8 S5 u" r6 ~0 O' D0 z' I
(1)     程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
$ m: n( ?& _2 w9 P; ~: w3 I! {( O3 I" e! [6 j: d
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。
3 J% m' p/ I- E9 H% r& f3 h
' t' K& M; \1 n. f1 t  页编程函数Write
  I! X5 ?) p  ~7 p9 O' y页编程函数实现如下:2 F5 T% Y1 y/ z8 u: E

" D; Z3 N6 Z8 q, T
  1. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)7 z$ ^, ^. U* A2 r0 ^6 B
  2. {
    ! `, ?3 {- k8 c: N" g' w
  3.     int result = 0;
    % [  B) r+ p. f
  4.     uint32_t end_addr, current_size, current_addr;
    8 {5 u8 R9 t3 E7 p
  5. 1 g, f  J1 w1 P7 i5 Q- i% l( Q
  6. + r0 {& D. m5 d4 g
  7.     if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    + {9 E7 _* s) ?
  8.     {! G" e! }, J( Z
  9.         return 0;& g( x( {0 u3 U' C7 B* G9 C5 w
  10.     }
    ( s7 ]9 K8 `. b! b# `) p# u7 u2 d- x
  11. & o" c5 Z; d5 i, r/ G, Z  ?
  12.     Address -= QSPI_FLASH_MEM_ADDR;
    . m+ ]7 h. X, w  H% ]

  13. & o& O! ~+ i' P
  14.     /* 计算写入地址和页末的字节大小 */
    . ?( J/ N7 B% U7 `' j7 j
  15.     current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);
    , u- D9 t% A- K8 B
  16. . x% I, T& M0 R6 S1 w$ p& _1 h
  17.     /* 检测页剩余空间是否够用 */
    9 i! D- D/ D" ~- n1 l8 {
  18.     if (current_size > Size)
    * C0 [' x1 S2 a3 Y
  19.     {
    " d7 J8 s' w. x. r2 l8 ?
  20.         current_size = Size;# ~1 S; a$ C! T; J. {  t
  21.     }
    3 @3 O' b3 X- J1 @8 H

  22. 3 V1 \+ o  P& S
  23.     /* W25Q256初始化 */
    3 R5 ?  |- G/ C: \, Y, c! M4 b
  24.     result = bsp_InitQSPI_W25Q256();
    + y# s6 x+ N+ D
  25.     if (result == 1)9 e9 D  ^" N0 R/ O& t) I) g
  26.     {
    , }" v; h+ }4 R+ e' D3 E
  27.         return 0;
    # @2 B! L' V2 K! H
  28.     }
    4 f# w. {8 Q' A) R9 s$ N, T

  29. 9 p# e& n7 R. w! Y+ M) K
  30.     current_addr = Address;! f: j/ K3 @% x: U/ f8 `- [
  31.     end_addr = Address + Size;
    / w1 T. w9 N4 T, k$ _% @' K( q

  32. + S# h3 L6 T8 k: O, F
  33.     do{
    * T0 O) B" ]' a! g: A
  34.         if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)$ C  H9 v2 I2 ]1 h5 }
  35.         {6 P5 K9 w4 X) F" `. `5 b& W
  36.             QSPI_MemoryMapped();  & H1 p; ^+ Y# c( I! e1 y" P
  37.             return 0;  
    , ~7 N; e* S# [0 Z7 {
  38.         }
    ( D  p8 D1 v% i( q0 N& f
  39. 9 i- q; ~& l" s- k9 z
  40.         /* 更新地址 */
    9 b. O5 d9 p) g  g
  41.         current_addr += current_size;
    . o! z9 h1 E! a- i' f. @
  42.         buffer += current_size;
    $ j4 A/ }) ]( Q3 a
  43.         current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;4 G8 N2 E( T% O8 ?
  44. ) T/ c2 x4 y* C( _6 a
  45.     }while(current_addr < end_addr);
    9 H" X- Q  e5 J1 M, i6 }

  46. 6 k% |# S/ v% ^* }, z
  47.     /* 内存映射 */   
    - e- Z, X9 r* ~  {. J1 x% W; U. Y- V
  48.     result = QSPI_MemoryMapped(); 8 P4 z# z7 C; u3 n
  49.     if (result == 1)
    : Q( B3 t& R7 r# O# i
  50.     {
    " r5 H6 b& w% \: V
  51.         return 0;- Y9 o, B! @3 F' j
  52.     }/ x8 w3 y/ b+ F! n) V; Z4 G
  53. & @5 e6 `3 {. {
  54.     return (1);  ) [) A( k% P! F
  55. }
复制代码

2 {  f2 L1 S  N/ Q  d这里注意两点:
1 V1 x/ N; `. K+ E$ Y" I# M5 l: \- D  q, L& O3 D8 j. i' J. G. F
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。0 `, C6 X$ _! O! c

; q4 n( a9 m/ @2 R; S(2)     程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
/ D5 {: j7 d$ }7 F- ~$ u5 P+ t6 {# F6 ^' P
  读取和校验函数
: M& U" M0 O3 ~. i' P2 V+ @我们程序中未做读取和校验函数。
0 ]. J& |' b/ S$ a% t6 }/ n& _; r0 m" ^1 s1 B$ B
(1)     如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
* K- U: U- E: j) M/ w/ S* k& R1 E; ^5 N& O0 S8 u9 d
(2)     如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
0 I- i9 ^% m! ?9 d0 D* W/ ~
  h: {( `" Y6 j8 N) H8 T' @81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)- Y9 M/ H9 o  b6 }
最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
0 c7 Y% ]+ A/ H) r' [
7 i3 U& }) S" `7 i, e
  1. /*
    7 K9 K; W! w9 w9 l. P  f( ^
  2.     STM32-V7开发板接线4 N' T+ j) ~& ]( i, Y. {' d
  3. 2 N! [9 Z) h" v) N8 |. p  K! v
  4.     PG6/QUADSPI_BK1_NCS     AF10& _* T( h- x4 V6 \% W0 Q8 |
  5.     PF10/QUADSPI_CLK        AF9; X! H6 v. `$ c$ Q
  6.     PF8/QUADSPI_BK1_IO0     AF10
    : i4 }1 }4 e' r; P% R" P. ]3 n
  7.     PF9/QUADSPI_BK1_IO1     AF10
    $ m% d8 z( Y- L% Z& |
  8.     PF7/QUADSPI_BK1_IO2     AF9- G; o! N+ j! f3 b) L
  9.     PF6/QUADSPI_BK1_IO3     AF9, l2 k& s' C( H
  10. ) a! n6 @0 l/ y& P
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB6 r5 A+ G( R6 z# `8 w
  12. ( I% q8 g1 h: ]6 q5 _! g4 J
  13.     H7-TOOL开发板接线
    : I: j! D7 G8 n4 B. N
  14. / d  b& B  w( c7 x6 i3 K
  15.     PG6/QUADSPI_BK1_NCS     AF10
    . X) p7 H2 N  g: g
  16.     PB2/QUADSPI_CLK         AF96 t0 v! v1 J/ |, }3 A: J
  17.     PD11/QUADSPI_BK1_IO0    AF105 {1 U# e* [! o2 h
  18.     PD12/QUADSPI_BK1_IO1    AF10
    , Q6 A; @1 K( u- _0 }
  19.     PF7/QUADSPI_BK1_IO2     AF9
    0 v4 T3 T6 t% w9 C6 ?
  20.     PD13/QUADSPI_BK1_IO3    AF9
    6 W7 G2 A8 m+ E
  21. */
    2 ^/ h- _' _8 V( a& ~
  22. " V: H  X; v- h2 j
  23. /* QSPI引脚和时钟相关配置宏定义 */
    : `9 C  Z* l2 m% V& N2 {
  24. #if 0
      H$ [1 V4 u9 c7 N& c4 U
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()+ G, d9 U, F8 F; d% R
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
      u& j4 s; j1 F, s: T
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    ) o& N, F2 z. V3 t2 C0 n" Q! c$ P
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
    2 G8 e7 y" Y2 L9 t. m
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()0 a2 n1 m$ R2 \2 r- j# }# Y: e
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    - G; ?$ M" `  Y. A) B1 }: s! {
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    0 c* D8 |: F/ V6 Q; y# e
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()  {* f' z. G" \

  33. + U# ~1 [1 B/ ]% }" `0 r: r
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    / G8 [6 l# r* c6 X7 U6 _; O6 x
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    , h. D0 X- t0 [3 v0 O
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    0 p9 f0 G% S# d+ p( ^; i
  37. ( m' m" \4 ~6 j2 p; P+ g
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    4 N, L3 ~; A2 B7 V
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    9 S5 L: q8 l; g, ]$ w( e# D' O  i; W
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI6 A/ R5 S8 c' ^- C$ A0 M6 w
  41. " l9 g) B9 }: i3 l3 Y/ `- V4 ?+ c
  42. #define QSPI_CLK_PIN                    GPIO_PIN_22 i# J8 ?. Y4 m1 }7 ~& `
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    . l5 P( G) z0 y; f
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    & K" A$ j3 W6 o
  45.   d. n0 m1 W! T
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11' Y0 u  r9 \5 |1 x) K" `
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    8 E- M; f$ L; K5 s# A; D. H
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI" d' r6 t% |- w0 v; l9 W
  49. . ]: m( w# Z& [# O5 \$ |
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12
    . |+ M6 p  g3 z; r( n
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD& T9 H! P) b% [4 p  _
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI, ]! L* R4 d4 A4 P( F
  53. : z/ a- T# C) f- o* m! i7 m: r3 b
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7' u9 ]2 S5 S9 x: J
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF8 u! `) ~# _: Z5 _) g7 r! r% ?. O0 I
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI: X( f$ W6 p' W
  57. 2 A# g6 R6 A8 u( z2 j2 P
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_131 P2 O5 r. Y. X6 H' c7 \
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD
    / O% ~$ K# W3 c4 G
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI- ?9 P$ N9 J2 x3 d
  61. #else
    , o1 [% Q4 Z) ~5 k
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE(). X( i0 O$ ~! B' v" {2 Y, Q# }+ K6 K
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    " n0 Z( |6 g' H9 v
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    3 E2 l9 L, h5 Y; F& ?9 ~
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()( |& k/ r! l3 B: j+ {* F3 b
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()' M! f* z6 F# u( I5 d: f/ V
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    3 A, p+ Z% t" G! e+ u: j
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()2 [6 p( K6 b& r. L+ @7 W, `# `
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()# f6 x3 `7 D5 \1 o: {' E& {' Q

  70. 0 c  P% G: L: D. Q) G  T, C
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    % K& N/ X8 a! {
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()8 {8 c0 k& a  P2 d) y% O% W3 e8 P
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    3 f4 b# `- a' c; M4 k- }& W# s

  74. 9 \4 S+ w/ @5 W
  75. #define QSPI_CS_PIN                     GPIO_PIN_6
    4 q" o8 E* A. S; D
  76. #define QSPI_CS_GPIO_PORT               GPIOG
    ; r, D. u9 i; M# ~7 j/ h" |
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    ' H9 y6 b: @$ K: v* ^: e) x. _

  78. - o5 O7 @/ f. D  _
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    & E: D1 R5 L9 |" D# U
  80. #define QSPI_CLK_GPIO_PORT              GPIOF2 ]! }/ K' f5 y. k6 _
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI3 p$ x" F' q: h

  82. 1 j" N7 \; H9 r0 a; _
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_85 E' e( I2 p; w3 T' m
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF
    3 B( ]! C& }+ {! g+ \
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI% r/ }; F, t6 I* [. R  X
  86. 4 r. {. u1 O/ d$ F2 L& I
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9& D( [6 t9 e$ L8 t2 Q* l9 F0 G+ F
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF$ t% ?; D1 v& |! x
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI' O* M& o2 |2 o* e7 W+ f5 S

  90. 9 P8 g6 }* F* l1 W
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    8 Z2 C5 g; Q  [2 F7 H) \' ]
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF. o8 G! F9 w: l2 j: D' W3 A5 v
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI$ w3 l; ^6 }1 p; C% `4 o/ }

  94. 8 a, W# [5 z2 {* \- R  M% @
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_63 D/ l* h5 x3 P$ }3 u
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF
    , ^: r% y0 f1 }+ _, j
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI& X: a. g# q* w( k6 p
  98. #endif
复制代码

/ A/ t1 U" `# [9 _" B( E9 t硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
- o. S, F/ B/ P6 w
/ Z* \% P( S" o7 B* f3 Q主要是下面这几个:# Y5 @" m8 o. R( T6 q# d& F
: z: Z3 e+ o, }2 u3 z
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000" d$ q  g/ _  C& g! t5 @
  2. 8 V$ l6 U7 N. N" V, F. L0 x
  3. /* W25Q256JV基本信息 */
    * M/ M$ [: O8 a1 k
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/: A7 W% c( f* G
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */
    & c# K0 [: x$ g9 c6 R7 R9 U
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */
    ) G% p. `2 R4 w# |0 F* ^1 K) s
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    3 R6 L  _; X  W4 u' E( Y
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/7 B9 {$ \" F' x3 a. k  h$ E

  9. 4 Q- t* t- a1 k/ I
  10. /* W25Q256JV相关命令 */
    - B2 p+ Y# N& ?
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */1 t% V. P& f* K# z( W& l2 S
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */
    3 ~# ^/ g4 ]/ S2 H2 U( P
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */
    : }$ G+ f3 d* s6 N& F  K( S
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */6 @5 P" \5 ]$ @) H& M: `
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */4 l- W8 _: l& Z7 |# G) J
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */
    8 e+ I. p" v0 G
  17. & x$ q' x8 N1 X- N7 H
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */. C' _9 Y3 x3 e

  19. ! s; O- b$ m, r5 e3 s3 d2 r0 S
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码
3 N6 ]- s9 ?% _0 {+ j8 _' m% q
81.6 QSPI Flash的STM32CubeProg下载算法使用方法
1 A5 q7 ?1 b: e0 c/ i编译本章教程配套的例子,生成的算法文件位于此路径下:( C& \5 C; a; O

' ~3 A5 X2 _$ E) m
d45f9274d8762b095790b3e6778b0a93.png

: y# u# _! y' S* J
* c' ~5 N  D/ U& W- R1 V81.6.1 下载算法存放位置7 i; ]' u9 D7 j( `" y
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:* n7 G5 V4 ?, Q/ D7 D& M

! x4 b- v- f7 K% v\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
5 K' p; A+ b# \2 X) ?0 s' i  q4 m# d
60cce5a15415e27e9d5b75b05028d1c0.png

" Q2 A1 e8 H' F
. m4 m* U6 `) W" W( k81.6.2 STM32CubeProg下载配置# r- q4 c' Y9 C( e
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
2 P5 A6 f# Z7 p6 _: @0 x# |6 w9 Q. q; o3 }" x3 w- }) I
9cc179641e765ea43cb38d268bf734df.png

: R& \0 f4 |9 F+ R0 M
5 Z- _" _& R* p1 W6 H& y* X点击Connect后效果如下:
' h# i1 A2 \* ]0 M( i6 I  X4 s! N1 M, e' I9 P
b39ce8a738163430db2119bdd87e3fbb.png

$ \* {9 `: y5 V; @
, Y+ t1 g/ ]  i3 s8 w在这里选择我们制作的下载算法:/ O! \/ p5 W7 {! W; ^8 W9 C5 P
- c! X- R, Q. g. _
17f6e0f50a18ae818faaa1399daef12f.png
# w* _. |0 [" ~; z

8 N9 C  X7 t) K3 a8 z* q+ i3 y任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming! N4 M) T. m& T

- \% }+ k3 E4 `1 Z. |
43a0d90faf1d392f5a74eaa7777e4178.png
3 ^6 O& U. m/ j# a* R& S
6 K, J+ M/ h5 f5 [+ I& X# U
下载完成后的效果如下:( e% @& M6 p$ q6 s$ W

7 p# u0 N1 L+ M/ T1 c4 @0 g2 X
a1d6662baa9e7be00f7e67e02b5c9b79.png

1 f. C7 t, s/ p$ [: Q3 o
( B+ J$ a5 H. u2 a81.6.3 验证算法文件是否可以正常使用
# ]/ i8 d; p% h- j1 D0 @0 T8 X为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:. @; t# Q" B; s- h6 u
! C' \. a5 o9 o2 a1 C
3f2eefeef3db68623cb8969974c40a20.png

+ O$ F. u* w" o' M. H& F0 D) n$ p# k' z. Z
81.7 实验例程说明
! I/ @1 D& {  q9 R, K3 W+ E本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。
* D- S2 F) e2 O1 ?+ ^! y. U
# k: V7 `9 C" X编译本章教程配套的例子,生成的算法文件位于此路径下:
. m  X0 N( q6 c: T4 h: s5 W5 j5 x
, D4 p1 |" ^* a5 R
b7028271136c8053d7300063ed4aec3a.png
: J" s! A3 C1 U& r8 j+ c# t& ]) f$ O
. s2 {$ X. V4 o  B: v
81.8 总结2 o9 h+ w' n4 Z$ ?9 n
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。. o  |; @: w$ b) C- y
& v1 `8 o9 E3 U

9 `" h2 P0 b+ u5 A) i0 n( B4 S4 [3 m( T
收藏 评论0 发布时间:2021-11-4 23:27

举报

0个回答

所属标签

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