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

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

[复制链接]
STMCU小助手 发布时间:2021-11-4 23:27
81.1 初学者重要提示6 G. T/ F. b6 |4 d
  QSPI Flash的相关知识点可以看第78章和79章。0 b3 C5 u% M: \( X: ~  G
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
$ k+ d2 K% Q7 N: ?' d: L/ g+ u  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样" K$ h0 Q0 Q, x
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。+ @: F2 V" x5 S+ H  f; I
81.2 STM32CubeProg简介
* ]) s/ \, R! M) ?3 w6 ^STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
* Y% a: i0 G" ]6 k" r' R' s8 U
  ~: [4 p' }) {# S: }8 A+ z3 b$ ]
1bbabf500126f58ff1439b6bc36014be.png
" N9 o  }) ~: o4 Y; E" }' S6 U. l

+ h+ T* }! u7 h' s, L81.3 STM32CubeProg下载算法基础知识/ p! y/ u& j; a- P
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
8 _' F7 m# {- X+ X) M7 I9 U$ R+ C8 q. Q
81.3.1 程序能够通过下载算法下载到芯片的核心思想
6 L7 d* ?* f; }0 ?! X! O* O6 @1 F认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。2 W0 O9 g1 T5 [/ n  V) ?; N( d

, T8 \$ a4 O  U, g9 @81.3.2 算法程序中擦除操作执行流程
4 d! }, e4 n$ p1 m! @9 p% w9 x+ y注:下面是MDK的算法执行流程,STM32CubeProg是类似的。8 M' _' b. s& f5 a" M

: m6 o5 C, ^" b" y( y$ @擦除操作大致流程:
. z5 f/ I# M- |, L, e  r- s$ }
7 |# j  W: m% b4 K! \" q+ O- A
161f92ebd9b1dbf9febba815fa091b8b.png

1 A' G- A# q" c  D% x
, a% v- M7 N" U1 M8 q  加载算法到芯片RAM。, r3 Z# a, Y8 ]% G3 Z
  执行初始化函数Init。: Z5 y* Y+ A7 b1 B- k# U
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
# n2 k+ v" t; X  执行Uinit函数。
% Q! C6 i/ Q' t$ `2 ?" D2 k; V  操作完毕。
  D3 p" B( f2 k1 _+ o% i3 P81.3.3 算法程序中编程操作执行流程
9 U7 {  _5 B, g注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
) m( A% S6 M2 k3 t- Q" U- O* g* j# Y9 Q& W5 y, f& c
编程操作大致流程:
' k$ e( {6 X( D- k0 l9 B# i
9 @9 z# A: l4 H2 N
d80dfd755411aa0d0565f1c56f10e66a.png
$ n* ~2 V: z; m# Z! j: f5 O
1 [  u2 h# i' U
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。4 a8 {3 Q: m" X" b3 N0 Q
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:2 g% g& ?" G/ G) n' C( E
  加载算法到RAM。- y- Y1 J& t( Y
  执行Init函数。
: F8 u( R& q0 }) \/ a- j  加载用户到RAM缓冲。
/ M% c5 K& s! H- D  执行Program Page页编程函数。
# n6 m& G7 X" q% K1 d  执行Uninit函数。* i( l! ?' X3 y. n# U; U8 ?9 t
  操作完毕。
! p4 j, d! W9 Q4 O: R* k81.3.4 算法程序中校验操作执行流程
- q4 e; G8 f# k" j% O, D5 G2 [注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
; y3 m2 [+ H6 \
7 M% `5 O8 j0 z, O! T校验操作大致流程:3 ^& W- H, w- W
$ o$ c( v# m' I2 K2 _* x2 p
f15191090ab169d48a203ea3617af9b4.png

! L2 i  t0 L* [' K4 S
( P- w5 U) [- K. M, F  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。. I& W; V" q: R5 A8 L
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:5 t/ H1 Z. X( ~' H& x7 D5 s& `# Z: G+ B
  加载算法到RAM。
* x  ^& x- q' K4 P  执行Init函数。7 I( p) a0 V1 O. ^  v6 d0 i
  查看校验算法是否存在
6 R/ v; g; e/ P  如果有,加载应用程序到RAM并执行校验。
* V. Z6 M3 f: `2 r' d. c5 R- G  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
$ h! Z3 }- @  \& y: d  执行Uninit函数。
. x6 K$ i5 q( t  L  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。- g* L0 ?( [- e
  执行RecoverySupportStop,回复支持停止。% k* \* V- H$ A, }/ |9 b+ u
  执行DebugCoreStop,调试内核停止。
) e4 n/ O! M0 i" B8 R5 m  运行应用:
( z' q) X# A6 q0 ~$ a5 V  执行失败
( l. K0 k, U; S9 N! T9 I! U" a) k; {  执行成功,再执行硬件复位。& w0 I+ c( p2 \8 @2 {" o
  操作完毕,停止调试端口。
. X8 H% H9 e7 `! y% i6 Z$ L/ v81.4 创建STM32CubeProg下载算法通用流程( H2 G. ~# \' {' P: ?
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
( V) R0 q) P$ M" w' ?; f/ j, F2 N* C- c/ q& S: k2 l7 m/ J
81.4.1 第1步,使用STM32CubeProg提供好的程序模板4 `. H, S: Y# M, u
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader1 c8 p; c+ Z) w# e9 ?' u1 }+ [4 B
! j/ @; c6 J- a" ?$ n- C/ c: T* ^
4fbfb0047e29fe23083224607059d97f.png

5 W3 m* f. g8 b. o4 @2 s& @- L2 U' L: \$ b4 E
以M25P64为例(注,其余步骤就以这个为例子进行说明):) G( P* I. ]6 l9 e0 }
) M( l8 C9 D5 K8 y. n
d1b1a340fdc655c1030330a7dad2abba.png
8 }2 Z* w; J" [# a" w
9 V  ^) I( _' j
81.4.2 第2步,修改工程名& [3 ?( J& i& p- L1 v1 H; Y  k
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
/ e1 a% ^* b$ @8 d1 ?. \: ?1 f+ k. c- D$ T% n
81.4.3 第3步,修改使用的器件
: h7 `' S( z! B0 |* B在MDK的Option选项里面设置使用的器件。9 S2 k/ I# N7 {: f# `, d, E

8 M6 Z+ q+ [! h7 ~! Q0 U: q
092174ad976a8ce9c7fca533049e2dd0.png
3 K% t! N+ Y0 b9 F
) r- K8 O% x) W# m+ |8 Y
81.4.4 第4步,修改输出算法文件的名字( x! i$ @' `$ Z0 m
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
  [( ^2 {1 D0 m
" D* U1 }2 k, W1 x1 g4 o, O4 S
79c42251311dd3892fc7d6b8ddde68b8.png
; ^% k. N6 `  e1 M$ H9 q- D/ [- F
/ ?- r8 L" g% y3 u7 P6 Q
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
3 n% i3 i; ~2 l5 U+ S
e2fe61fa7ea99b25b178341b7350558f.png

0 Z# [5 x2 f' @. _
' h* [+ U; v6 _* l7 Z7 P/ @81.4.5 第5步,修改使用的库文件和头文件路径
/ d' M8 ~$ c. q# L" x0 {+ d根据大家使用的主控芯片,添加相应的库文件和头文件路径。' v9 F# q* ~5 \7 p: a
5 a9 i9 s; }! ^# l
81.4.6 第5步,修改编程算法文件Loader_Src.c. {- h1 k0 I( S' s& g
模板工程里面提供的是M25P64,部分代码如下:
+ X+ F5 F+ W4 s* \' o' J( b" {6 N$ K$ j0 P2 W# M4 m& S+ Q
  1. /**' p6 f8 B) U0 ?$ H# Y  A
  2.   * Description :& b5 O1 C- P5 U
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the" y/ W$ R' o3 T* b7 u, V7 l0 R. P1 x
  4.   * device and initilize the FSMC with the chosen configuration
    / L( x1 r: X- c0 [+ ~
  5.   * Inputs    :
    + R- Y# M0 F$ f; `& C$ l3 P! Q
  6.   *      None
    8 b( M% N! ]. v9 A
  7.   * outputs   :% P  `, u4 ~# A7 p5 l
  8.   *      R0             : "1"             : Operation succeeded4 z  \8 V  I+ o% X2 b9 n; Q
  9.   *               "0"             : Operation failure
    / U9 N, H% ]; |
  10.   * Note: Mandatory for all types of device
    2 o9 r# R2 E6 K6 h
  11.   */
    $ H0 o5 [# Y, }9 T' ^7 l: W# `; ^
  12. int Init (void)' O* T8 E; V" J% J# I) A% N1 v
  13. {  
    9 D! o: l+ @) b) i
  14.   SystemInit();' c0 \4 U6 R# s, ]  |* w
  15.   sFLASH_Init();
    / ]# }- ?* b1 X& @. V$ P- n- t
  16.   return 1;
    - r5 j6 O! m; `
  17. }: W( @, ?, d. b/ l1 m  r

  18. ) }) m$ D5 }( O  G& ^' G+ N

  19. * {3 G  w! A, d
  20. /**
    1 `3 V: ~& W/ r4 o: ^
  21.   * Description :
    4 a$ ]! \+ o3 h% {/ _) S, K: Y- R
  22.   * Read data from the device 9 g" G$ M" ]4 i- s  B
  23.   * Inputs    :- Y4 ]- p! ]0 l9 }
  24.   *      Address       : Write location' P5 _0 y" b; F5 C' w  y* e$ C& T
  25.   *      Size          : Length in bytes  
      B  T+ F7 j% r% t/ W
  26.   *      buffer        : Address where to get the data to write: s: m' k& b4 ^1 K+ B. a' _& l
  27.   * outputs   :
    / h( C5 u' L& f+ l" J" e( a2 m1 r
  28.   *      R0             : "1"             : Operation succeeded/ s$ `) y4 P! O* Y- i
  29.   *               "0"             : Operation failure
    8 f+ p9 F9 _5 A4 u# @# |# M1 `
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    % z0 Y4 C+ m  f, _. ~
  31.   */, H4 w/ V( I* K
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    2 x( d4 T, ^. \! J3 B
  33. {
    0 D) f7 A4 x9 O) D
  34.   sFLASH_ReadBuffer(buffer, Address, Size);& j4 O, \6 ^3 T4 d- Y( z
  35.   return 1;4 H' i& X% N1 e. Z: _4 S
  36. } $ i7 U5 O7 Y* N' v8 n

  37. 7 n: C- i7 |  V" g5 C
  38. ' e% h+ }( V( N  ~2 v2 ^
  39. /**8 j: p0 ?! u" w
  40.   * Description :3 o' ~1 M1 y1 Q" d% L; ]' O
  41.   * Write data from the device
      M/ `# |  e, r3 H9 {: R: u! p9 z
  42.   * Inputs    :- j* J' I+ a/ M$ R  e
  43.   *      Address       : Write location6 Y1 ~2 k# E  T3 i: h5 a; F
  44.   *      Size          : Length in bytes  
    0 n) J0 T# q$ C; ]
  45.   *      buffer        : Address where to get the data to write. ]6 V  Z& C( t1 Q0 Z9 E
  46.   * outputs   :& G! E. t) Y/ L! W9 S0 X- F
  47.   *      R0           : "1"             : Operation succeeded
    % p1 ~" X1 i5 o& j7 W& S" W
  48.   *                     "0"             : Operation failure0 e8 ?! J/ {! b' ~: o& V2 A
  49.   * Note: Mandatory for all types except SRAM and PSRAM    : D3 U5 k" r" d8 p: E; K
  50.   */, k4 q: C; K. K) \6 r/ V. r
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)$ E. M) j3 R7 _: G3 b+ S
  52. {
    0 C0 I/ }5 C5 x# |8 o7 z; j" Z: D
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    6 n: h8 D" m* a6 I/ b: I9 b
  54.   return 1;
    0 m- l5 q  P, g4 N/ i
  55. }
    - T; l; E3 C) U* Q' ]1 E* y

  56. % d* ~) d* P( x
  57. 7 W1 R( J* O) @
  58. /**
    8 C4 ^- }: k& g' y4 d! R8 {/ `
  59.   * Description :/ M- v* q! p: q0 l2 h
  60.   * Erase a full sector in the device; i! a2 r5 M; X/ _/ a0 X" B3 S
  61.   * Inputs    :7 V; h# K$ y. l
  62.   *     None
    ; Z: {  I' @# h% u9 Z  ?8 E+ Y
  63.   * outputs   :. J- P0 r; t2 W1 _. l) s' ?# F* |
  64.   *     R0             : "1" : Operation succeeded* H, w, o* {: g/ @! w5 y1 [( @
  65.   *              "0" : Operation failure
    7 V& \5 I7 h! ^( A; X
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    * Y9 x) }, k& ?- m9 k
  67.   */3 l) m+ c0 A! a# l- J
  68. int MassErase (void)9 z& I3 N  j' F- V9 B! b) U
  69. {  6 A/ r; q1 r& X) p
  70.   sFLASH_EraseBulk();8 ]9 \: e9 b3 n2 N; g
  71.   return 1;    1 x' s2 u0 {/ z
  72. }
复制代码
4 ]1 ?' E/ M& d( N
81.4.7 第6步,修改配置文件Dev_Inf.c
& }5 k' ]3 g- Z- ]" E5 v& f& z模板工程里面提供简单的配置说明:
, @3 `! @) T/ [- q4 i( Q3 |1 k3 h
, x$ J- \1 n9 B: }1 S+ Z
  1. #if defined (__ICCARM__)4 p3 U7 V/ y& b1 ?$ W! U5 V
  2. __root struct StorageInfo const StorageInfo  =  {% q1 y' U. t) J9 U! Y
  3. #else; s8 X: A) g* p" F- l* J
  4. struct StorageInfo const StorageInfo  =  {* |7 r$ u; H% j" t: _. k
  5. #endif- V% ^) Y/ |2 B2 _, ~8 Q( u4 q( @" A# r
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number* Z0 r. j0 r6 W( X6 [
  7.    SPI_FLASH,                       // Device Type
    ) b9 M2 E5 t. w" P, w) L
  8.    0x00000000,                     // Device Start Address
    ! v2 m2 N% g- N) t
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    1 X) X$ |) l5 |# j" T% W
  10.    0x00000100,                      // Programming Page Size 16Bytes
    ( x' L, _9 m% j$ i* }. u5 S
  11.    0xFF,                            // Initial Content of Erased Memory, ^( b( n: w! A+ V; A: i, C) a$ A
  12. // Specify Size and Address of Sectors (view example below)- S. ^! g" T5 Z" X  I9 @! B0 ]
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes   ~) u( o* \1 `: t6 e2 C
  14.    0x00000000, 0x00000000,
    2 q4 s9 Q- G$ \: ?2 ]
  15. };
复制代码

: y5 r* N3 n9 a$ d注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
! t! [' [6 T9 ~( x& d. l3 d' r' H" @/ _
; B) w% M( d' o$ `
$ z0 J1 s, L3 c6 x
81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。3 K3 Z0 L' f/ ^! {# r" `2 j
C和汇编的配置都勾选上:
6 P" ~1 A3 b& w6 C3 O2 o0 M- o3 X+ L  N
8f2ba38db4c1f685f131e7f1f4e98551.png
( H- R$ w0 V8 N! W. e8 T9 h

1 n5 }) D( ]) j2 }汇编:; T* b/ L0 {' `: `- [4 @

9 u' D0 t( N% U, ~$ X
0d264153b167afa2303144bdd378d501.png
8 f, l; Z4 t( E# l& ^# G
: B" u0 b. p1 s3 |) S
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:6 n$ U/ T" Y, D6 V+ n; P$ \. o3 d

' d. q6 {4 z* E0 Q  K# n/ D(1)加载以响应运行事件。
9 [. f8 A2 F) K( F8 M- g8 h4 _& X7 B7 i$ ^1 v' N) W
(2)在不同情况下使用其他例程的不同组合加载到内存中。
: s, P' Y% W" l2 O8 X- s
# r1 X, y& K% j, c(3)在执行期间映射到不同的地址。
: {; z' `3 t8 h& r; }1 F
! ~. Q- R! b3 }& D+ h" w% X使用Read-Write position independence同理,表示的可读可写数据段。
$ y( B7 ^% r. M4 x
2 h: ^' s, N" ^3 ?: S. K) F7 F81.4.9 第8步,将程序可执行文件axf修改为stldr格式
/ K' [- _! Z* }/ C% a8 Z' [通过下面的命令就可以将生成的axf可执行文件修改为stldr。' C# g" j4 J/ e) p8 c/ X/ \

7 M4 C$ ?( `$ d) p; u& Z
f50b1594f3741e77b4fbd5523564d3af.png
! `( M. P, X; {

  X' j# H  ]- ?81.4.10   第9步,分散加载设置) C' `* r5 Q0 T  c  x
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。0 O: t4 U) o0 ~3 `' p7 f, I0 a5 E

1 W' `* \- X9 f7 c) G
8a01cb7c9be38519d4d0510a218447e2.png
$ H/ x9 _: ^" `: Z5 C5 @

% F( K5 A# ~* B. R0 [6 C- h分散加载文件中的内容如下:- X  Z: ]% }) b2 ?; Z

7 n+ ?; ]  E: E6 j4 a) b
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    9 L6 m# E1 v- {9 |  [; a
  2. {
    % @# q) e7 f) Z. O& t4 W& K; S
  3.   PrgCode +0           ; Code
    0 P2 C1 D$ |6 z: S: g0 h3 g
  4.   {/ j- w, Q5 I8 S, b
  5.     * (+RO)
    4 h& W5 ?; Q" O
  6.   }
    ' Y* d* d5 z# l
  7.   PrgData +0           ; Data- n: e% z7 [: b% F( ]
  8.   {
    ' [8 S" p0 y3 n9 J- M1 o* F
  9.     * (+RW,+ZI)2 Z1 S6 B9 g( k( z1 e
  10.   }, a9 B$ v- Z5 H6 Y
  11. }
    4 M$ Y2 Q3 ~1 Z
  12. + ?' |: b  V# J  z4 q7 s
  13. DEVICE_INFO +0               ; Device Info
    ' T  Q8 U$ H# V: y7 M$ Z' J
  14. {
    , t6 V5 r7 V" D! g/ _4 _1 L
  15.   DevInfo +0           ; Info structure* L* a& W$ r1 r1 F
  16.   {
    5 e. \' q% h5 t  x; d9 _! L
  17.     dev_inf.o
    4 q6 A9 }3 g1 h4 o" R- g
  18.   }" ~6 l5 [5 b6 j, ?9 Q6 f5 J) y& l. m
  19. }
复制代码
! w9 k# z+ j# X( [5 r
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。- N8 M3 L' {% Y6 H6 J
2 V  T+ H9 Q1 b0 U# X! j& x3 M
--diag_suppress L6305用于屏蔽L6503类型警告信息。* E4 M+ T1 n# X% F4 }3 \

! S! Q5 [2 f" d5 R0 K特别注意,设置了分散加载后,此处的配置就不再起作用了:6 @1 O8 J$ i) l6 E. M3 C4 M6 @

# J, ~. m) ?& `* ]
13b022190bd5115ed249129fc3886a38.png

  }$ ^3 I7 P+ q2 M
* t$ m! h6 c$ h. ?9 l& k: z- n0 m81.5 QSPI Flash的STM32CubeProg下载算法制作
+ Z$ H4 E  r5 r下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
# [$ {2 b0 @: \- t4 A: d& X; u
' Z# `) u. _. f. V  E) Z* ~81.5.1 第1步,制作前重要提示
! L$ j  b, m$ _5 S5 U% n这两点非常重要:5 j4 R4 C4 e  S- ]0 i! S
& F9 k" z5 R. Y1 m
  程序里面不要开启任何中断,全部查询方式。' B! O  O4 _8 C3 l
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
: {4 |/ }8 E. d" }" h. I81.5.2 第2步,准备一个工程模板. ^9 j9 Y2 F, G/ Z% a% v$ d
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。0 a' C0 x) B7 Q& p% r# f

9 ~" x% ?. `/ j9 _8 \4 z
f46a8d2276d0ffb7beee251cdb0809a4.png
# w5 F1 [0 D  w2 M9 _3 ]5 k

" B, A9 u7 x# \  `  b* A81.5.3 第3步,修改HAL库
" t: W; \) ?5 v5 P这一步比较重要,主要修改了以下三个文件:5 P1 a( v3 Z" S, g& v5 C; T6 K

7 ]: ~9 I+ D6 I0 |2 E5 W9 a
1efb78256b9465df406e3e9e95ae30e5.png
. k. W+ ~# k8 s% t+ W$ ^$ N, W

; |/ C* a5 b* `* {/ ]主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
' D9 K5 `' m5 ?5 c5 k& w
+ @* D& L7 ?6 {( V5 J81.5.4 第4步,时钟初始化# ]) K  u6 o6 K( z- P8 t& {. n
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:- ]% C! L/ K+ \! d& J3 P, I8 ?( d

- @+ K: j" J* B0 s5 W0 {4 C. l
  1. /*8 d8 R- k; ^+ D: _3 [( F- l1 d
  2. *********************************************************************************************************4 v' ]  v- R* m7 l, X- p1 X
  3. *    函 数 名: HAL_InitTick0 @) Q2 P" F$ [3 T9 ]# o
  4. *    功能说明: 重定向,不使用
    : s: J" C' e3 V$ E$ \8 _
  5. *    形    参: TickPriority
    2 @; Z8 F- E' s0 E! s
  6. *    返 回 值: 无
    . x+ R* v1 i, [; \" W
  7. *********************************************************************************************************
    1 p7 O4 J2 x& Y4 u* ~) S) q
  8. */) Y% ?' |+ l) y; K* a9 x. I
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)7 `* r, V8 z6 ?: d
  10. {
      _( q; l/ Y6 g: {( v
  11.     return HAL_OK;
    " f$ c9 u. e1 N- s
  12. }
复制代码

8 z: }* u  P( y  Q. K然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:3 a7 W8 m! ]3 V
: i, [: |' {- S
  1. #if !defined  (HSE_VALUE)
    ! f7 E- {* N) \) Q* a0 z
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */) U# y. h- e( }+ q4 R+ U2 S
  3. #endif /* HSE_VALUE */
复制代码

2 V) ?. S/ m+ s5 A8 S/ {最后修改PLL:: P0 a- q: m& H0 Y: r1 A

) [$ ?/ B& y" `( z
  1. /*9 [. t7 k4 S8 j' \+ c# h9 ^$ v
  2. *********************************************************************************************************
    . R, `3 w" L5 A7 `' b8 S, a8 {
  3. *    函 数 名: SystemClock_Config
    ; [$ u% I4 Y, u& \; ]  V' _
  4. *    功能说明: 初始化系统时钟
    & L5 C& d9 v$ D. H( Z! n& Y
  5. *                System Clock source            = PLL (HSE)
    & @/ S( I$ [6 W6 l
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)5 C+ G6 W3 d9 q$ _6 R
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    - _5 ~: A' ?1 V4 L
  8. *                AHB Prescaler                  = 28 j" V  L+ w; U& U+ G
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)8 \! T* I3 u; Q& O8 k! K: o- F
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    + M8 y! d. j" X/ b1 V4 X# O( Q  V9 [
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    & g6 U5 p* J4 t0 O
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)- y9 ~) _# y% w0 e0 [7 F, O
  13. *                HSE Frequency(Hz)              = 250000003 R* ], @) E- U4 ~/ X# t: A6 [, v, b
  14. *               PLL_M                          = 5
    * c; a5 Z, {- o' I: Q
  15. *                PLL_N                          = 160
    ' S6 m0 J) R- ?7 m1 n! y
  16. *                PLL_P                          = 2$ f  U  s$ ^. O) j" j- K+ m$ `7 \) N
  17. *                PLL_Q                          = 4
    ' R1 H. V( R1 U# Z! p
  18. *                PLL_R                          = 28 ?' o/ N1 t' R) Y* C- i
  19. *                VDD(V)                         = 3.37 ^% h! R' U2 E0 @
  20. *                Flash Latency(WS)              = 4, i$ F; b+ B9 v) _. [
  21. *    形    参: 无! k. C$ [2 l" @5 J7 u6 F9 P
  22. *    返 回 值: 1 表示失败,0 表示成功
    & D% r/ K- a3 ~; K( a
  23. *********************************************************************************************************$ J5 ~3 N, a3 l5 g. t$ b/ s8 Q
  24. */$ C1 p. J2 \* h' |! m* D" x
  25. int SystemClock_Config(void)
    5 ^1 t+ c0 s( u2 g
  26. {+ m5 F) }9 L3 Y/ D# A; j
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    9 a4 G" p1 O& M5 P! D1 S
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    8 g6 U- p4 I: }1 ~7 `: G
  29.     HAL_StatusTypeDef ret = HAL_OK;
    + U2 {$ G0 @: t0 [

  30. . F# A, P$ H8 g
  31.     /* 锁住SCU(Supply configuration update) */, L4 L1 B; t# w, U5 I  l5 g
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    3 \% q; f( `% i' `: Y
  33. 9 f6 a( L# _  F; R* O* s
  34.     /*
复制代码

2 b& C# `# v, [6 n" }$ M& {  X      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
0 G- s5 d# s4 o" S* h3 M         详情看参考手册的Table 12的表格。  S2 b) Z3 l9 G( p4 J! j) a
      2、这里选择使用VOS1,电压范围1.15V - 1.26V。
" B, j, [+ O7 D; n   
  1. */
    * @$ N$ j) ~2 O; d
  2.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);6 ^6 u1 N' ]5 t9 I1 L. M( l

  3. , g+ t( A8 R9 d
  4.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    & h5 @1 g2 B/ s! g

  5. 8 d! j0 D" x9 T; r5 o
  6.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    ! w% T7 w6 Y. k& W" S. M( y
  7.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    2 x4 t1 `5 H% C0 S" S
  8.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;! {, m& W' K# V
  9.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    3 {; u+ E: B* _/ ~, T0 }
  10.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    2 ?+ s$ s& v4 h2 i7 @! O" i' M5 Q
  11.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    ) @8 I  }9 z! k# a. ^/ h) f' ^5 G
  12.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    $ c) v$ K" q5 D4 Z/ Q9 P* `( n
  13. 2 p& s! \, Q: a, ~1 c
  14.     RCC_OscInitStruct.PLL.PLLM = 5;
    5 U0 G& O5 t7 h9 i
  15.     RCC_OscInitStruct.PLL.PLLN = 160;$ Z& Y" E/ p6 ^+ g% V
  16.     RCC_OscInitStruct.PLL.PLLP = 2;
      w% I+ I  `, Y' U2 N+ F7 w; A
  17.     RCC_OscInitStruct.PLL.PLLR = 2;# L  e! d, ^) W# Q% E$ P) l' C
  18.     RCC_OscInitStruct.PLL.PLLQ = 4;        0 z' [3 p/ m; o8 [" b

  19. 8 T( Q* O  [/ F2 [! \- B8 ~+ N
  20.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    $ D3 r4 a: e- F! ]3 V
  21.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    ) l9 p6 A/ T. l0 f) W0 i$ n
  22.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);  C- p. ^2 o+ A
  23.     if(ret != HAL_OK)
    ' ?) Y3 [+ L  q/ m5 d: V! ?! W
  24.     {
    * l0 n! u( W% ]& T9 z$ J& Y/ c7 V9 X
  25.         return 1;        
      h  c1 ]3 t% @6 z. A) i* d
  26.     }
    - X% q4 `9 Y; x7 M: q8 e6 c: [6 y4 d0 @

  27. . w6 j: T6 t: G, n
  28.     /*
    7 D# v( h( d4 s) }9 I1 Q
  29.        选择PLL的输出作为系统时钟' O" A) G8 z9 x; @
  30.        配置RCC_CLOCKTYPE_SYSCLK系统时钟; O. k$ n- C2 R6 h/ j, b9 E
  31.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    - f% g# l" \0 _: O5 n! u( ~
  32.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    % k, X/ w" m* E, ^+ S
  33.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    : i/ L. ]7 P, O
  34.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线1 j) x& M- ?) p- _% k
  35.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     + M: B  t3 ^# V0 D1 g7 M
  36.     */
    / l% F' j5 ]8 |( C* a6 a( M
  37.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    0 ~7 e9 Y2 e3 B% b, r8 r1 K6 t- J
  38.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    9 M" u" D0 Y* Z8 s) b7 F$ x4 s9 D+ J# D1 F
  39. 9 Q% ], e+ B2 s
  40.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    8 V, g7 f$ V( c4 v
  41.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    6 R! M- g# J% l8 t: {6 m
  42.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    3 q( C/ F3 y+ @+ }( }) T& |) s
  43.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    0 j1 D: b2 B  J/ _4 w# l
  44.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    ! W5 O9 W2 \- v
  45.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    ' x( m) q% {' x* p9 {$ |
  46.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    4 g4 f3 x- {: Y  f# U# D

  47. ! G+ z( J+ h: }8 S. N+ k0 b3 I! B3 R
  48.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    ' x5 t7 g3 Z8 P7 H2 a4 ^; o9 v5 ~% t+ n: ?
  49.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    . j" W, e4 p) H
  50.     if(ret != HAL_OK)
    " f7 p; h# N( F+ ~9 O6 s
  51.     {3 f. m) {0 P8 C( x
  52.         return 1;- H+ C1 E/ f0 z6 X* p
  53.     }
    , X. Y. k5 T, z7 S+ `* }% `# ]1 k
  54. ) Z. F0 O! B/ }+ ?8 \  U
  55.     /*9 F" K. q5 E$ T: G
  56.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    : t; F0 C1 J" Z" Y" |1 G3 L# [
  57.       (1)使能CSI clock3 v1 z2 V. k2 |# r3 }' R7 ^
  58.       (2)使能SYSCFG clock/ r1 d1 ]; u! m1 z
  59.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit06 h9 l9 V6 t& a( w! q3 J
  60.     */7 |) U( o0 j! j, Y/ G7 _
  61.     __HAL_RCC_CSI_ENABLE() ;
    2 \; V4 W& q, m" I! V

  62. 0 c8 b$ |0 t/ `0 @8 j, ^* w
  63.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    - i, m( c9 u( g( L1 N9 [4 B% o

  64. + _: W' L9 \( i
  65.     HAL_EnableCompensationCell();- p$ O$ |& R& F! I( x

  66. ' ?* v$ m! |+ T% [3 {0 q
  67.     __HAL_RCC_D2SRAM1_CLK_ENABLE();; u# n' l# g9 k, T
  68.     __HAL_RCC_D2SRAM2_CLK_ENABLE();: \+ o$ l  Y& k! O7 ~. r
  69.     __HAL_RCC_D2SRAM3_CLK_ENABLE();* z5 ~  L2 E" P, [% S
  70. $ F  ?8 l, G* H
  71.     return 0;' K8 C/ S6 X# F- K5 v! z
  72. }
复制代码
+ W% h. H* _; o
81.5.5 第5步,配置文件Dev_Inf.c的实现
* l! G9 Z/ M1 V' r  P  z4 N配置如下:/ P' g; I# v, q$ x3 F0 G
# ]0 b* S" l% Z# w) A
  1. #if defined (__ICCARM__)
      s" [2 L& v' s' K7 G, \/ f
  2. __root struct StorageInfo const StorageInfo  =  {
    $ a7 i1 i4 l, t2 a( |8 }7 n) f" A
  3. #else
    1 |8 Y* u9 r0 P$ q
  4. struct StorageInfo const StorageInfo =  {
    : ?" q( n5 p9 A5 E" v8 R" `
  5. #endif. ]7 ?* l% n, y& N
  6.     "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    9 q+ n0 f' F5 L  r3 T! s; h! @
  7.     NOR_FLASH,                      /* 设备类型 */$ {0 F1 J: Q; Z' ^7 c. g. M4 S
  8.     0x90000000,                     /* Flash起始地址 */
    3 x" U+ P# n! J  l3 O. g
  9.     32 * 1024 * 1024,               /* Flash大小,32MB */
    # {0 m& N3 e2 `/ |
  10.     4*1024,                         /* 编程页大小 */
    & ^( C$ q# s9 J! r4 K
  11.     0xFF,                           /* 擦除后的数值 */
    * r0 |8 K  j. a" q' T+ Y
  12.     512 , 64 * 1024,                /* 块个数和块大小 */
    / h9 I4 ]$ s. h6 B; b: C- u
  13.     0x00000000, 0x00000000,
    7 P9 s0 N7 |. ?  B6 L  p3 @
  14. };
复制代码

) A1 N$ N$ d8 @  q" a7 N注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:% {' }- E( W9 j2 t$ u& J* h

* R) T# \9 Q: s
bc495adf8d970e6e1fb6822b88b94e2f.png

5 T3 o- i4 R( |) P: b, R
0 [* ^  C( y0 ]  w; ~  f1 G81.5.6 第6步,编程文件Loader_Src.c的实现( g9 q- M' X: u8 p$ }
下面将变成文件中实现的几个函数为大家做个说明:
  d5 V$ L  w# l5 W0 J0 y4 U5 m: D: O+ p7 d+ |! n
  初始化函数Init
3 P4 e, B" g1 a
  1. /*. C# Q7 R% S; j% t! ?* T7 g! x
  2. ********************************************************************************************************** {9 U: o0 X# x1 y4 ^
  3. *    函 数 名: Init6 }$ V- k# f5 K4 X' m
  4. *    功能说明: Flash编程初始化. r7 ^; Q5 H) r+ Q9 }8 D
  5. *    形    参: 无$ X8 X9 [' x4 l* m: R  z
  6. *    返 回 值: 0 表示失败, 1表示成功
    & y1 p4 F, l- _7 n
  7. *********************************************************************************************************
    - F, ]9 e% Q: [$ F
  8. */
    / L$ e% x) n: u. a, I" Q  }/ [
  9. int Init(void)
    ! }* ~$ i- t8 ^  a7 |, T: _: y
  10. {   4 C3 Y7 ?  G: i8 l  Q
  11.     int result = 1;
    ' c( a1 X. |1 k& P: D# G: E$ I3 X3 E
  12. # ~  a* _! q" A: W) n+ f
  13.     /* 系统初始化 */
    7 g* c1 ]9 Z1 ?- Q- @* g8 j9 l
  14.     SystemInit();
    ! g, `. H8 L- t4 h1 Y  h

  15. ' C7 Q" l" W3 ~) X3 q& ]
  16.     /* 时钟初始化 */
    & p  M9 |$ Y# o. q8 D# \. g- O
  17.     result = SystemClock_Config();
    % s3 _5 H- y& o5 P  D7 I2 `# `
  18.     if (result == 1)! |7 G; o6 F3 Q. f* F
  19.     {
    * X$ C; @7 T8 x% P$ c
  20.         return 0;        + D4 V' Y/ Q4 `) a$ \
  21.     }
    3 {% E7 @2 a* f; I# [

  22. ( N' v& ~( G! a% G9 V) N& z
  23.     /* W25Q256初始化 */6 l( n# _) T/ V5 Y8 W! T
  24.     result = bsp_InitQSPI_W25Q256();
    " Q* j& [0 m& D4 E  g
  25.     if (result == 1)
    3 F7 ^% ~9 P3 B0 [" X. j$ X
  26.     {, N; \, w  x0 a" G1 }. s& d8 e
  27.         return 0;3 x9 D6 z. A; d8 x0 ~! v" A5 N
  28.     }
    5 v2 d  f  @; w; J& S2 ?
  29. 5 U9 R1 U$ Z2 @2 B  W3 M
  30.     /* 内存映射 */   
    9 M; @' l9 i# q3 d; E6 q
  31.     result = QSPI_MemoryMapped();
    . K. r8 R  Z1 v
  32.     if (result == 1)
    / j+ l% m) |, u( M9 f5 o1 i
  33.     {
    # {5 a  [9 |+ C
  34.         return 0;7 N7 R$ i: n+ I5 d: Z
  35.     }
    7 \8 b# g( D3 q: ]
  36.   Z/ v% P8 T% G6 l
  37.     return 1;
    % d) A- c* o+ T8 |! V+ r$ Y
  38. }
复制代码
. Q" \7 D/ M/ R5 R7 u
初始化完毕后将其设置为内存映射模式。- e: a) U) K0 U+ |0 q: q

3 t" \; Z& W& b  整个芯片擦除函数MassErase
- P- P8 V7 _! z. W* ~/ U. r整个芯片的擦除实现如下:4 x' u, V/ {* @  _# V

( p7 a4 L7 Y, c1 t
  1. /*
    / Q& G  b" t% u7 g! n9 I
  2. *********************************************************************************************************
    0 X1 j; c- g) w1 Y, z7 F$ w
  3. *    函 数 名: MassErase
    $ `  P6 L% U/ j
  4. *    功能说明: 整个芯片擦除
    0 c5 W7 ]/ @0 u1 N  {4 W
  5. *    形    参: 无
      o$ W! V' ~: r- G7 Q/ k
  6. *    返 回 值: 1 表示成功,0表示失败
    6 V7 |6 a- h& h, N" m/ @
  7. *********************************************************************************************************+ N/ X# f% a' b8 p6 r3 [8 F
  8. */  |: Q2 H5 J- K+ z- J# h: `( Q. q
  9. int MassErase(void)
    ( \4 f  z; n1 \8 ~* j
  10. {
    1 R6 c. D2 Y. o9 {( {  |
  11.     int result = 1;
    ' {. P0 g- A. }: \4 e5 {6 ?

  12. 1 N" S! L% y& n3 j/ l
  13.     /* W25Q256初始化 */* [) @5 C: p* N  E1 ^' `
  14.     result = bsp_InitQSPI_W25Q256();
    0 @0 o- W7 T9 p% q* k7 Y0 a& ?* W
  15.     if (result == 1)5 k8 x2 s; y/ H, Q3 |3 M- n' q' U, {
  16.     {9 P, U- @% _6 U. ?
  17.         return 0;
    ) Z: B* ?7 R2 l+ \" l- f- n
  18.     }0 |9 o) ]8 b8 w- [8 n

  19. 2 t8 c( g, T) _8 T- O. r
  20.     result = QSPI_EraseChip();
    : b% [' b2 j) e
  21.     if (result == 1)
    7 V' f! E% Y: C
  22.     {3 v. T* f! D6 U; _/ M: T$ ^1 {7 M
  23.         return 0;
    : D0 D( S1 `$ H4 K
  24.     }   
    & ~" h% W: r5 Z3 k3 n
  25. : v  r& Y$ V& s* |9 }; W! {
  26.     /* 内存映射 */    . b. _& B+ L' l3 A9 K
  27.     result = QSPI_MemoryMapped(); ) {0 ]! {3 ?+ ?* E: j* ]
  28.     if (result == 1)4 e/ k3 \( \: o- L
  29.     {7 r3 K$ {* q$ a* Y/ \
  30.         return 0;
    / h- N9 _3 U7 R/ _, M) ^
  31.     }
    , i( w: A9 I" D
  32. ; L+ O# @7 T7 I" U( @7 q* U7 m
  33.     return result;         
    ; {: m, Y6 i; \& Y7 C" T
  34. }
复制代码

2 e3 A* e! K  s$ ^  扇区擦除函数SectorErase
- L& u: [+ S6 ^( @; O$ X/ m- Z扇区擦除实现:4 z' x- C0 x; \  D9 l2 a

: M4 |9 X4 y1 q
  1. /*
    7 Y* i, `6 D% a. r
  2. *********************************************************************************************************' _+ R& P4 D5 Q- ?6 m6 C/ n9 y
  3. *    函 数 名: SectorErase& b* T4 K9 n- B. R
  4. *    功能说明: EraseStartAddress 擦除起始地址
    " u6 k' ~- m0 j
  5. *             EraseEndAddress   擦除结束地址
    6 O- H* }0 @' q0 M/ M1 i% r2 A
  6. *    形    参: adr 擦除地址
    : w  t$ m$ h6 Z% Y/ g$ y
  7. *    返 回 值: 1 表示成功,0表示失败
      F3 j5 U! v: m
  8. *********************************************************************************************************
    3 f& a6 ]" c! ~  O
  9. *// }1 f3 j2 c6 r# [
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)8 u# e' v8 ^" R2 Q% Y
  11. {6 a/ R5 \2 [: k* y1 \3 ]% [$ l. [9 }
  12.     uint32_t BlockAddr, result;
    ) u8 X. u: N/ G* ]
  13. 1 B# T/ X& n) b4 `5 z% t& s
  14.     EraseStartAddress -= QSPI_FLASH_MEM_ADDR;
    ; Z8 \) m6 L# V3 u4 b
  15.     EraseEndAddress -= QSPI_FLASH_MEM_ADDR;) }+ R, R2 [( H- L
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x10000; /* 64KB首地址 */# o$ k' }8 `+ A( k( C" v3 j

  17. ; y7 A* H5 Z2 c/ |# @- d
  18.     /* W25Q256初始化 *// h5 s. O- Y, N# P, {5 A8 N3 U3 g
  19.     result = bsp_InitQSPI_W25Q256();
    . `* m# F' d% ?8 H* f# d! z  x  K
  20.     if (result == 1)! c/ t* N" O9 A+ B$ P" b
  21.     {. r, P7 q' G- y4 C: l  u! d7 a
  22.         return 0;        " @6 \( H7 u; o% }
  23.     }( Q4 q7 k; O; X5 I2 R7 X
  24. ( ~) }0 ~2 o( x( }9 k2 }: a  c
  25.     while (EraseEndAddress >= EraseStartAddress)' |. }1 G! x$ n
  26.     {; o8 e0 F# p1 G1 _" v6 v1 C5 k
  27.         /* 防止超出256MB空间 */
    0 X0 u9 ]  G( l& Z1 O
  28.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;3 Q% E! V: W3 s, \+ r0 s5 u
  29. - g+ \$ _% T# w8 ~! f
  30.         result = QSPI_EraseSector(BlockAddr);    : r: ~% V" i( n3 a# y
  31.         if (result == 1): O+ S* [2 w$ Q2 Q0 `6 D8 V
  32.         {9 ?9 M6 S- x* p! w
  33.             QSPI_MemoryMapped(); 3 |, V8 N1 p' L4 {  {" b
  34.             return 0;        
    & y9 h4 q, N& C2 l# Q8 }/ M
  35.         }
    : Q$ l2 A2 o+ X3 k

  36. 3 V! i6 m5 M) n+ |3 a. v
  37.         EraseStartAddress += 0x10000;
    / U3 g7 P  ?2 z  d  Q3 `7 z
  38.     }. \  v3 @, h" \" U4 D
  39. $ V8 l5 I3 T1 B: m: z2 _' b: S; n
  40.     /* 内存映射 */    ) r% s3 n8 S5 Q" v
  41.     result = QSPI_MemoryMapped(); 3 U1 O4 b0 |+ X8 J" g6 I# U
  42.     if (result == 1). H5 f  I$ a/ e/ o) g* v. v1 g
  43.     {6 Q% S' t6 l: G/ k# L' o5 x- \. [
  44.         return 0;
    + J+ [4 y( l0 @8 j
  45.     }  l6 T' i  A( o" h

  46. % O9 G6 @! F1 P, x6 a( ^' P' a
  47.     return 1; " p8 e# J( l* v' Y* u
  48. }
复制代码
/ A$ M% O! A* @
这里要注意两点:( y/ n% ?2 a# u5 q. }% K5 F

9 T, c/ y  s& E8 R(1)     程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
- M9 H, H5 ^3 {9 ~$ J# s$ h6 }& n: i& S( b  Y$ d
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。" `1 \# R1 M2 P7 U+ N

: o7 t6 M8 y& m  页编程函数Write
- D+ Q8 }' H: D; o页编程函数实现如下:4 V& a5 `( q5 }$ a7 u
0 x. |( F8 _1 f7 w* W. L+ p2 ?
  1. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)1 e3 _9 i3 O, `
  2. { 3 J- T0 k7 n+ [9 I
  3.     int result = 0;
    * y& r/ C2 g4 |
  4.     uint32_t end_addr, current_size, current_addr;
    % v. P. R+ Y0 Z" U

  5.   }- n$ M# w: N1 }

  6. : _/ u: z8 G7 x
  7.     if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    ! [/ N: u6 p" y
  8.     {) A5 }3 j6 l9 S, [
  9.         return 0;& O* K+ }' ?, h1 t5 K& K5 L- a9 e
  10.     }
      @4 f- W, S) g: M% g( ]

  11. 7 Y! v9 H+ X: N! n8 J5 Y" f: y5 W2 ~
  12.     Address -= QSPI_FLASH_MEM_ADDR;1 S/ t# }3 h' d( W/ F0 S
  13. 9 x1 f/ e4 P. U
  14.     /* 计算写入地址和页末的字节大小 */
    6 {8 m- \. O4 W! n  r# s
  15.     current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);8 M$ c4 X3 M  y2 p* s  |( ^) M7 b" |
  16. ! U; L- x% j# `
  17.     /* 检测页剩余空间是否够用 */+ Y* N6 [9 j' K1 H7 k% T; _
  18.     if (current_size > Size)# Z: u$ K4 p9 G; V$ b9 o
  19.     {
    4 }( I2 ]: L" e, U
  20.         current_size = Size;
    5 ^1 x% F9 s" D/ c9 W8 I
  21.     }
    ' ^/ E! C, w# Y' @" u
  22. ; P+ d; X9 W( L2 R# W! E
  23.     /* W25Q256初始化 */
    ) n( b3 G. ]& }1 Z7 A
  24.     result = bsp_InitQSPI_W25Q256();1 |$ ^" K/ H1 \# `
  25.     if (result == 1)
    . b! x6 ~% D5 L; p+ w  B
  26.     {
    + `2 \3 O3 T( v2 f+ c( z4 j
  27.         return 0;
    2 _- ]! O9 v. ?
  28.     }6 q+ o; h$ a7 W) ~6 z% o* L) i

  29. 5 z% d0 S9 r2 ?3 d+ N8 g" D
  30.     current_addr = Address;
    8 g. Q* M/ n3 ]/ K3 v( x
  31.     end_addr = Address + Size;
    7 W. a7 {3 B6 N5 y7 e; s
  32. ) I5 F+ g. j$ v0 N5 y' W$ K
  33.     do{7 M) b8 B% E* N/ M0 x+ u
  34.         if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)
    2 R9 W& S0 Y2 V" s+ H9 Y7 p, n
  35.         {' [* R: l4 E8 d/ V# _* ~: A3 D% c
  36.             QSPI_MemoryMapped();  ' Y$ J% C/ [5 j
  37.             return 0;  
    4 G$ h. O/ T5 t. h; r" ?
  38.         }
    6 I8 A3 k" @- I0 L' c; ^3 B  P
  39. 5 `. c) R& K$ N/ b5 S6 u
  40.         /* 更新地址 */6 Q5 i% d2 N. V3 f0 h% p
  41.         current_addr += current_size;
    " q  }8 O! V5 q2 G& o/ N( s7 ]1 b# Q
  42.         buffer += current_size;( e+ m- [3 V6 C5 @6 p0 A7 V% ^
  43.         current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;& `7 \; I' S: H  s; q7 n8 M

  44. : g' _! v. y+ [; c( r
  45.     }while(current_addr < end_addr);
    0 u0 T; g' ]5 [, l% j, W2 x

  46. 1 C0 z0 w: V5 r+ @
  47.     /* 内存映射 */   
    % {  x% z+ v, J. k! i
  48.     result = QSPI_MemoryMapped();
    , M  f8 }1 a5 |! p
  49.     if (result == 1)
    3 y9 s$ s& C, d# Z
  50.     {
    ! @. s3 H3 T( h! F: Y
  51.         return 0;
    6 ]3 D" D6 f9 F0 K" P
  52.     }
    : r! p& M$ s% _
  53. ( D. a2 ]+ `2 i2 b. Z
  54.     return (1);  
    . K4 D- l* m1 f  u
  55. }
复制代码
  J$ |. U/ O8 q, I. m3 f: u
这里注意两点:
7 J4 Q$ g) ]) d  p
) [$ ?1 `; X: Z% D(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。
0 ?+ Q# N* S0 g1 h% Q2 q( m) r! ~% _9 P) K
(2)     程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。9 K% I  m2 m' u
* O, m# M  G- R( n5 v0 S, }
  读取和校验函数/ i( b+ c! N& Z" @0 D2 z. X
我们程序中未做读取和校验函数。+ s" ~) p3 y" j" I$ m4 l  g
+ \* w0 N- J# U" C, M$ [
(1)     如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
) B/ U% U9 P) W% I$ Z
9 Q8 w3 Y9 I$ K, r: ?5 C+ h(2)     如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。9 }  p$ l6 w! a% C
' I: q' D$ y1 p2 M/ Q: I
81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
- C$ Z+ E+ _) T: c4 L$ b4 g' A最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):6 C  Q* E6 H# [, j( R+ Y) a. Z, B

' J3 k. f, d& o
  1. /* ! Q6 A$ ]5 _- M1 d) m5 l+ S
  2.     STM32-V7开发板接线
    $ G! X: p- X2 V4 N$ k

  3. 3 f3 f. {9 E) P1 ?
  4.     PG6/QUADSPI_BK1_NCS     AF10
      J) u# R) X) b7 v/ q9 C7 }& Z
  5.     PF10/QUADSPI_CLK        AF96 }1 F: Z& A; t9 R. O9 v
  6.     PF8/QUADSPI_BK1_IO0     AF10! o0 Q5 ~/ ]8 X, ]/ o" u
  7.     PF9/QUADSPI_BK1_IO1     AF10
    0 q: R; ^8 R1 o8 u: J  W7 x
  8.     PF7/QUADSPI_BK1_IO2     AF9
    " [* C$ j+ E" ^2 s# X" b) N0 Y: z7 d
  9.     PF6/QUADSPI_BK1_IO3     AF9+ {$ ?# R# s( e+ I# M
  10. . {# \% Y3 T3 I) E4 E; J; @0 b
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB3 q6 D! P$ {) l% H0 L2 B
  12. ) [. t) D! z. o& X3 @
  13.     H7-TOOL开发板接线. \/ _. V; C4 W
  14. 7 U( j  }% L" \% g
  15.     PG6/QUADSPI_BK1_NCS     AF108 R8 r4 N1 ^! E7 S+ X/ s
  16.     PB2/QUADSPI_CLK         AF9
    / j& S3 o) o! j" {5 P
  17.     PD11/QUADSPI_BK1_IO0    AF10
    4 A( P- t8 r) E8 ]9 N: A
  18.     PD12/QUADSPI_BK1_IO1    AF106 _/ U* C, b4 W' L4 b8 E  `
  19.     PF7/QUADSPI_BK1_IO2     AF91 O. T) t1 X$ v: p
  20.     PD13/QUADSPI_BK1_IO3    AF9
    ' {# E3 v+ ~1 a5 b
  21. */0 @0 c2 q9 v# P! Z

  22.   M  p* L3 C1 k, c
  23. /* QSPI引脚和时钟相关配置宏定义 */8 ?& i3 U5 a+ @; q6 Q% ~
  24. #if 0. Q$ e2 X" P; e* a( p
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()$ o$ c" K8 I5 K; r9 V
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    & r4 `( p2 v1 ?# @, q. A5 o& p( h
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    + F5 r2 h% M$ e$ l0 c+ {
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
    ( n6 S1 ?6 A! f4 J8 C1 D9 @
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()  z. y& w: N  _! u. p3 ~
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    0 Y, U+ ~0 ]1 K5 I- a* D6 J
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    $ L4 w- J( k/ Q
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()9 P  e% t2 ]- e2 Q. }& f

  33. * s& e# G% ^2 R/ L4 Q
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    3 m% q7 ]( L' K2 _. i( X1 \# ]8 N) X
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()9 j" z9 W+ g+ s
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    . j/ }) [7 E8 f- y

  37. ' H" A% G: s. |& l$ O+ r; w
  38. #define QSPI_CS_PIN                     GPIO_PIN_6/ v/ x' z5 y+ K( [7 g4 M: z8 m
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    ' k, N, y# ?% q! y0 F% Q2 r3 h
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    3 k' Z' v2 |3 |/ d  n+ {
  41. 4 \$ L  {1 w0 H: x, C3 Y) Q
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2: _) W7 i! b$ O1 R4 h
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    , F- q" A* |; z  B, C8 ~2 c
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    ! j* }( T' z, d3 X! K+ I9 `

  45. 7 {# n# h1 p5 E; T8 F* f1 ?
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
    ; w5 \1 Y& b, g0 ]5 P+ y
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD0 S  R# b" ?. M
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI
    8 P( t: m; c; J' j3 _

  49. + J5 G& a2 C3 X7 g
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12/ L% ~& w; M6 @! w2 r7 L$ q2 ?. k
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD
    ) j2 G0 K( |0 r
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI
    0 V+ r# ]6 Y/ K% O9 p
  53. % |7 Q/ L! W# W/ V: |9 S
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7) ^( E( @+ F6 ^1 w' G
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    ' W2 M% [) M$ s: Q
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI9 l  t- Z' N/ D) t% k9 l

  57. , K- F; E# W# ?; d0 Q
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_131 j3 H2 G* X4 P, k
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD( o6 ]+ }  w. [0 m+ G1 O: S7 b% z5 u
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI2 C* Y# L8 Q  y7 ]& V3 [/ ]
  61. #else' j# d: X' m, t
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE(): D- j. a8 f8 v. b( v& C) h; B
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()8 R  p9 D2 R& C3 p
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    + j0 w1 l1 ?' m2 W9 p
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    9 j5 k* L3 e; o3 e9 ~3 w- p
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE(); N$ l) J! D9 s; z7 p6 s  O; e
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    - y+ Z/ j5 J  J2 [! M, ]% w% D3 {
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    7 r, t( J! G- I! c
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE(): R- I* X! v8 G

  70. & ~# [( M! Q& w8 ~- a+ i
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE(); s4 B  _6 e0 j! j7 U3 p. @) w
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    1 S3 D7 h3 ^, S& k' h$ w
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    1 j$ `( O7 Z8 o

  74. + q5 W3 ^4 p, V" G+ q, ~. R
  75. #define QSPI_CS_PIN                     GPIO_PIN_6
    + P- C  q/ v8 m$ `) l+ `* c  F! C
  76. #define QSPI_CS_GPIO_PORT               GPIOG% S9 c$ E4 {' y) m! ?- `  {
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    1 g# f+ {4 g5 S; }( K6 ]

  78. # T. B% _' n( w; c
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    0 t' N% Y+ w' V% h0 D
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
    ! Z: ^' F, I& n+ Y" T( i# U
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    8 k8 Y9 X: ]5 u8 d

  82. / c& Q! c& V& V4 t$ O4 B/ z
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_89 [$ P$ a1 r* j) K5 ~5 m
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF
    " ^( o, m1 n) J$ v; G/ M1 h
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI
    ! D/ g3 a3 ^4 N6 X+ @0 K
  86. 7 J5 b- t& Z; y1 w1 D5 P
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    0 S5 U) O- Y1 R2 W- _' M
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF
    1 W# W1 A- I% e/ J) n* C$ |
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI; a$ w5 b+ a& T9 z( }0 i% d

  90. ! Y1 G9 f* U8 W. K# u! w0 t
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7# e" r$ u( }! Q, ?% i
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF' g0 I( x/ B' G$ O+ D7 V
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI( U% q. W' F" ?; V, W! Y# I; j+ `6 U
  94.   x; v5 r# `5 B
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    # ?  w) P) h& i" }& h9 R/ A8 o
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF
    & r0 ^' Y2 |* k( c% Q
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    ) [$ Y* g# C$ _! B
  98. #endif
复制代码

% {8 }5 d( p+ e9 {7 _硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
  L$ |3 {% I/ G+ U6 c$ }% Q* h7 L/ j0 n3 s$ z1 G  U0 p5 ^& ]
主要是下面这几个:+ t" q8 _. [9 e+ a3 V6 W% k
4 D$ V' _( Q( s( f# k
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000
    $ a/ C. G" m4 h% o6 H
  2. + T9 f8 m. `1 r+ Q1 v2 H3 w
  3. /* W25Q256JV基本信息 */
    8 }/ K7 h' M7 V' Q$ P
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/# b; S7 v" l& H8 Q0 v, [5 d
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */* ~/ b+ e) ~' ^+ H5 {
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */& ^: R/ k. D, d
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    " n' t9 x- _1 d' Z- f1 n# H  s- X
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    " ?$ O! y5 H: i

  9. . [; c) |) u! R9 x8 k
  10. /* W25Q256JV相关命令 *// j% M8 S( k- {! n9 K+ @1 u
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */: I7 t# p, l8 b* d# I
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */  M3 P0 N  X* k# l
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */$ Z, x1 @7 i- ^4 V6 c! s* Y
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    $ J4 q% l; h+ ~- m& a
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */
    : H/ Y9 e) t- ?% j$ J6 y6 z/ p
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */
    / r! k/ g3 S7 F( D
  17. : X; Z" K% I" X! c
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */3 i  y$ ~& }7 T  Q' s

  19. % g. [+ W& L( ?$ `
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

+ \% X. g* }+ l- x" S7 M81.6 QSPI Flash的STM32CubeProg下载算法使用方法. F- z+ x& A4 z4 D
编译本章教程配套的例子,生成的算法文件位于此路径下:
/ y4 x$ d6 J8 e- B
8 w4 [: K. L/ n( v/ L0 B; ~
d45f9274d8762b095790b3e6778b0a93.png

( @( h4 y; Y$ v; u0 ~4 O5 }
5 ?3 F) u9 H) Y$ N. R( C81.6.1 下载算法存放位置& ~. V- X5 b9 a/ Q. }/ i
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
" L2 T$ }; ?- @; k. d3 \1 t4 q6 J, D# r8 G9 _) G" `% L6 X
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader6 ]+ S6 I# m: W8 I
0 u1 [# i$ ~5 B
60cce5a15415e27e9d5b75b05028d1c0.png
9 b- p1 j. V6 ~: _2 H
. }( z+ |/ F: C
81.6.2 STM32CubeProg下载配置
- d- V; y0 S, l/ B# ^: {8 F8 ]$ |我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。! _/ m( m7 C* X$ X: \/ z

7 S! C6 _3 c2 D) F9 C* N' x4 h; j& C
9cc179641e765ea43cb38d268bf734df.png
  Y  g" Y% ?; W: n* N% G4 P; I5 A0 P

3 [8 N, P/ @1 r$ H# w; o: v点击Connect后效果如下:
, K" |* P. o# A; v# `  v) G( I+ Y, J2 s0 J
b39ce8a738163430db2119bdd87e3fbb.png

3 O+ b7 l' W6 A
' u- k  H5 F( Y# ~/ T在这里选择我们制作的下载算法:- d+ {$ O$ t& e# @3 |1 j# D

; ]( s) Z3 Q8 l+ }. q) W
17f6e0f50a18ae818faaa1399daef12f.png

; ~- r" y4 p8 E- Z, F! ?6 S2 @8 y5 _% b$ f" N& Z% G
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming% v% X$ w& _: k2 c, p" U7 u, ?
) }2 `9 r4 X0 P
43a0d90faf1d392f5a74eaa7777e4178.png
: w' A2 [. D- J1 Y  Y% j4 ]

* k+ h. S& y1 T% S$ d下载完成后的效果如下:* `# E4 f6 s& Y: w- V
. _/ ?+ m; h: @
a1d6662baa9e7be00f7e67e02b5c9b79.png

6 g" a7 y6 U' ~7 u, c: O  n3 ~/ h$ y9 x8 [/ K! v9 U3 O
81.6.3 验证算法文件是否可以正常使用9 v* q8 o3 h8 |! Q+ ?. I
为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:: r/ ^9 N, ]; ]; c$ C
6 c9 m9 s7 U/ ]+ c
3f2eefeef3db68623cb8969974c40a20.png

( \5 u: E! `# n, \
1 V  E2 K2 q) p# Q- r# _81.7 实验例程说明0 M' ^! @# _  h5 `  V% w
本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。
* s6 R0 u" @/ m& `, O8 P' q  R; y. N9 I0 H
编译本章教程配套的例子,生成的算法文件位于此路径下:8 v; H, i, N( l7 s1 |
0 t: i, ?+ q( M/ J, [
b7028271136c8053d7300063ed4aec3a.png
/ P; b0 {# y% Y0 a
1 R9 P+ J* e: m
81.8 总结
4 l0 e, ?+ a8 ]6 o6 @' M4 O本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。+ T: v) N  }. G8 _: _/ P4 t% t
5 ~" S9 Z$ V8 K8 D( G
! W: S- l! V6 O( T" Q  m* r% z5 F
! U# Y! D0 K$ @. C; ~6 n& F( y
收藏 评论0 发布时间:2021-11-4 23:27

举报

0个回答

所属标签

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