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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 16:00
80.1 初学者重要提示, g& v/ K8 B( ~% M9 M/ i  y
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
0 L. h& P2 f9 A+ z1 c+ m80.2 MDK下载算法基础知识% U; J* C( h/ O
Flash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。  G' @0 |3 w' Z2 [9 z4 }& [
' z" N* M/ e4 \/ [3 j7 u
80.2.1 程序能够通过下载算法下载到芯片的核心思想
. ?8 x! D* c8 k6 q3 W5 O4 e认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。
) l2 @) A  N2 k: h! P+ B
+ D# @4 u& {' Z% a( U, Y80.2.2 算法程序中擦除操作执行流程7 [7 B' K! T6 o7 G% ]3 J; K% H1 w
擦除操作大致流程:
8 i2 b. x" d$ B0 C& z
. E  v4 Q7 k/ x$ U
d4b4af6b23f5f1d5ee3f3387ccb8fbdc.png
9 X  V0 b" r* m
& y+ O. S* i8 t$ `$ B9 W
  加载算法到芯片RAM。# L  ]& u- R& j+ i! ^
  执行初始化函数Init。" t5 ?8 J+ i* b, c
  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。, e* r  j4 v: o! F1 a) ^! D
  执行Uinit函数。: [: D. `* w( ^- q' R
  操作完毕。' `+ j9 Y9 {9 y8 v5 I) b
9 l6 j/ `# `  F3 I0 L0 P4 H
80.2.3 算法程序中编程操作执行流程
9 D5 O. p" C) X7 ~编程操作大致流程:6 d3 t! @- X8 z! p6 U, u
5 h/ Q* h2 [3 M
d3f601364bb033d42e275ad686c03726.png
; d& E! g* L7 e5 I

; S8 `  O8 i5 Y* C3 u  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。  d5 E6 B. M9 Q% F, G( F9 G
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
! s* w: Q4 E, O8 @; W; |  加载算法到RAM。" m6 ~6 k1 q1 ^+ g1 o
  执行Init函数。
3 M, ^3 \; B4 ~' M: j* ~  加载用户到RAM缓冲。/ R& c+ v7 }4 c( I3 X3 H5 E
  执行Program Page页编程函数。
+ w. L4 s; e; W( Q  执行Uninit函数。
: t( U/ S% r) i. I8 H  操作完毕。- L( @9 b, C" x
" F; R" M9 f. C8 {
80.2.4 算法程序中校验操作执行流程9 Z% u  t- e3 J/ L4 |; s
校验操作大致流程:
; a( b+ Z. ~3 E! G: f9 P% V' e
+ X/ |6 {; ], z- s
c7b2f373d946bc7d166ab1c1ba9b67e4.png
3 `0 E  D' L" `6 @- M

9 h$ O# G5 `; F# ]5 q  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。+ \9 Q& f6 x. e) L" x
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
# R- d  _0 {. w1 C* @0 d! V  加载算法到RAM。, b# b" k: J9 Q* D# Q6 _- y& X
  执行Init函数。
. m" S* Z* n# ~0 K+ Y3 l8 o5 z$ D  查看校验算法是否存在
7 ]. L6 M$ `  K, j1 P$ U' G% X8 k  如果有,加载应用程序到RAM并执行校验。( I: h3 q$ M. F4 a7 X* U  _
  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。
1 o% ?/ q: I/ b2 s  执行Uninit函数。
4 h4 \; C8 |$ f& k  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。( |9 P$ k4 D. p" k( m  \. N% j
  执行RecoverySupportStop,恢复支持停止。
- h- a. P# b& D# V* }  执行DebugCoreStop,调试内核停止。9 z( n  S4 E( y, i0 x0 b
  运行应用:- ?3 q- _5 f7 r, M% C( f6 B  }
  执行失败。% U5 t0 U( {, u
  执行成功,再执行硬件复位。$ X8 w+ @. ~  t" b
  操作完毕,停止调试端口。" p# Z: \+ l& h  I& G, Z3 `/ y
/ G( _9 Z- ?! c2 q/ V
80.3 创建MDK下载算法通用流程& g$ z# ^1 r' }5 O; G9 K
下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
; m" f; p1 u' p1 ^
3 @# E) g1 j" a/ A5 K8 ]1 N80.3.1 第1步,使用MDK提供好的程序模板
7 F8 h0 l, P; _5 P5 E位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。8 w; u5 W2 ^+ c# R/ K
" _; j# P3 u, g& S
效果如下:
/ r# N, ~+ [  ], {! d! F+ x8 k" V! t3 p) X( ~; E
8e08d948d34b38592e3699136725ade1.png
) E; C* E8 \2 k

$ v4 d  u5 i4 h1 N! ]: g* m0 U6 M& O80.3.2 第2步,修改工程名
+ A' y  v. q# L( @- ^9 X/ gMDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。9 Q5 ~0 f- R0 f
: P/ d* s) @$ b9 R
80.3.3 第3步,修改使用的器件2 ^% q! s' Q) }2 ~! K1 T1 k3 Q
在MDK的Option选项里面设置使用的器件。* i9 {6 K: T. n- b
, O( R3 ^" \- v9 w& [
347d6def457f6e101f1c2555f9156cda.png

) H1 T5 n1 A  s0 a' g$ T/ V1 v
80.3.4 第4步,修改输出算法文件的名字
7 E& g7 U; p9 k) {4 d" [这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。
4 s7 V( i+ \! U4 r5 Q4 C/ w
" z' ~0 \$ P' `0 u6 c% X5 f
07d754db49256bd0b73f4538a328f3cd.png
9 C- Q( D6 h, E8 Q
: V" @( h& D7 ^" ]& g8 X8 G$ @
注:MDK这里设置的名字与下面位置识别出来的算法名无关:
" P. z( }, C0 Z( y1 x( l2 |; v4 I( d9 ^0 U- e# c
a00b0ff99a0a691f19dfc2565fde811a.png

( S" I6 G# Y4 ^+ h+ I- Z: ~, u7 \6 ~4 {; r  c5 Y5 \6 c9 [
这个名字是在FlashDev.c里面定义的。' E2 Z# N$ k/ j' I3 Q9 I- \1 l
/ T* a+ T! {- v) a6 q1 n
80.3.5 第5步,修改编程算法文件FlashPrg.c+ d) ~/ ?! o& {$ }
模板工程里面仅提供了接口函数,内容需要用户自己填。
. p  L9 R8 l9 ]8 J; N7 C: H. C
$ F2 }. U$ {+ j, A3 u1 @
  1. /* - y4 I' ~. p1 \! O% q
  2.    Mandatory Flash Programming Functions (Called by FlashOS):5 f3 q6 u9 u3 a, T, G
  3.                 int Init        (unsigned long adr,   // Initialize Flash6 P& P, c  h( n3 o
  4.                                  unsigned long clk,) i( {1 r1 U' c0 U2 A' m
  5.                                  unsigned long fnc);. {/ P/ U9 Y/ n: k- ^' Y
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash
    ( Y5 l- g* x" e& |! i
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function( k' J4 r  }. [) z7 d) G. N% Z
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function2 V% R& e8 Z! t
  9.                                  unsigned long sz,: m( U. |9 `6 P# a% h" C- z
  10.                                  unsigned char *buf);
    / V* E3 g5 s& o8 I% A# L
  11. . v( n. |" B' M" u
  12.    Optional  Flash Programming Functions (Called by FlashOS):
    # O9 k& P7 U- `1 i: g
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check
      w. ^% H5 q& L
  14.                                  unsigned long sz,0 p/ d; `+ C+ I
  15.                                  unsigned char pat);
    # \& g( E; Q2 \6 f7 J7 A- ~
  16.                 int EraseChip   (void);               // Erase complete Device1 N8 P* O7 Q( N, ?( }
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function
    9 n2 ^8 _& S8 g: j
  18.                                  unsigned long sz,2 C# i1 r; x8 r3 v3 g: ]
  19.                                  unsigned char *buf);7 _. e+ a! ?4 Z. L7 L6 R* s& K7 r0 h1 X

  20. 3 S' [  F. n" g& B
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space
    ! o) s4 M# o9 W3 M7 s
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space# ?, H3 o' M. p% p% i
  23.        - if EraseChip is not provided than EraseSector for all sectors is called8 A4 |7 V) c: b, q, p
  24. */9 W! ^4 |# y  A' y6 O

  25. " t5 V. ]5 H, I1 L, D6 A
  26. /*
    6 @' o0 ]# g# I0 Y0 x  M
  27. *  Initialize Flash Programming Functions
    $ _3 c- y8 S9 j0 i* v5 n
  28. *    Parameter:      adr:  Device Base Address
    1 x' }# L* p+ n' g, Z4 s( d
  29. *                    clk:  Clock Frequency (Hz)
    : _1 g1 E) ?# C, \1 n5 M
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)1 U! I( q* g7 D: ^; c: h
  31. *    Return Value:   0 - OK,  1 - Failed
    . C# ?( N/ p: N+ o7 L
  32. */" Z* L0 Z/ U4 P/ O  r8 P- \  C; d8 j
  33. ' e# j. o9 Z3 E+ \7 Z8 Q$ L
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
    9 x$ X, z7 U1 b* W5 k9 _

  35. + @/ _! `' `9 G5 E1 Q% m
  36.   /* Add your Code */
    # }6 t/ N' y0 z  @3 p2 d
  37.   return (0);                                  // Finished without Errors) ?' S. y# Z' B7 g( _
  38. }
    7 f& }* e; E8 P1 N

  39. * _0 C& u, s5 Q/ y) N" i

  40. 7 ~* r3 \* }- U* e
  41. /*
    * T, z& J  k* o2 {1 u' b
  42. *  De-Initialize Flash Programming Functions  z" g9 B% d1 r1 C' x7 }9 t
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    3 }8 m( I  `& v' j+ x! O' ?2 H( y
  44. *    Return Value:   0 - OK,  1 - Failed. m( O5 t  m2 R5 Z1 q: `% u
  45. */! o/ s  }, J0 k
  46. , T  c6 K( m5 _, J& P: j
  47. int UnInit (unsigned long fnc) {
    9 Q2 m1 i7 Z/ e  }( r2 y; w% \; f

  48. / h0 j9 u6 T' c
  49.   /* Add your Code */
    + V' X) U4 Z. d" n( t3 I
  50.   return (0);                                  // Finished without Errors
    3 h$ z# k. z+ {$ M" D4 F# b
  51. }! }. }/ R# E6 @4 y+ w7 F5 x

  52. " \3 S0 W7 J- Z: x0 u: o0 a
  53. 5 T) m: o: o7 a3 Q, P5 {" P  s
  54. /*
      W0 j6 z  O$ O$ w" F% g
  55. *  Erase complete Flash Memory( y7 {* W) j# ^1 j* y
  56. *    Return Value:   0 - OK,  1 - Failed
    ) d% W$ M3 E( i8 l! k3 G
  57. */8 X* Y7 o- p! G/ u

  58. 8 |; i/ x3 r: z& l9 e; k5 K! M
  59. int EraseChip (void) {
    5 r  Z* k3 A, `, g  e) t
  60. 2 C, [# Q: i6 C/ P# x+ V: }
  61.   /* Add your Code */7 B# U; s! q2 V: p" T% Q
  62.   return (0);                                  // Finished without Errors/ F7 Z/ h3 @4 U. T  R& l
  63. }  |- F# r  w  @$ R7 V! A% |

  64. 9 L5 ~+ D$ d( S+ y4 |& b. K* c4 \4 z

  65. , f/ A0 M- V0 |9 j! x7 d- M
  66. /*
    2 O! D% J6 j& U0 J0 L5 Z
  67. *  Erase Sector in Flash Memory
    : F( W- A) k' O& }& t7 U
  68. *    Parameter:      adr:  Sector Address5 @8 ]; H# V) m- [
  69. *    Return Value:   0 - OK,  1 - Failed# Z* k# Z& X2 q" t: v+ T
  70. */
    * x, [3 z2 K, k

  71. # L4 a* {- M9 p( }# U/ U$ E: A
  72. int EraseSector (unsigned long adr) {
    $ D+ o; ^+ S+ G" [$ y1 U8 M
  73. 6 l% t7 X2 T3 f7 p
  74.   /* Add your Code */
    ( R/ s( w6 v9 C  e; b; A
  75.   return (0);                                  // Finished without Errors) l- t+ Z: s& i7 i
  76. }3 j# o* ^4 A& H8 y& @1 X8 k
  77. 1 @6 p7 U3 ~% `  b/ |* B

  78. 7 _+ A5 Q$ N% l8 J8 h; [0 o
  79. /*
    + R5 a; g" e/ t; k6 v1 L  ]) w$ J
  80. *  Program Page in Flash Memory6 g, k1 h* o6 K7 P) I( Z
  81. *    Parameter:      adr:  Page Start Address. i0 C8 \2 v/ o3 P
  82. *                    sz:   Page Size1 a! C5 |, a- b5 V2 b) S6 O$ r
  83. *                    buf:  Page Data4 A; n# N/ s. h: V- J6 C# w$ f( W
  84. *    Return Value:   0 - OK,  1 - Failed
    % m4 w& V+ _  ^. o+ b" t7 y+ g
  85. */
    ; P$ n! V! I1 z& t
  86. 3 ?1 y3 D$ I; S1 ~
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {7 _* D, N7 `. s; @
  88. $ x9 F( s1 o" H1 t
  89.   /* Add your Code */
    # r% o- x, a) h
  90.   return (0);                                  // Finished without Errors( W  i4 G. w, P) i. I- {% N( M
  91. }
复制代码
% G$ I" l7 }! m; m2 {
80.3.6 第6步,修改配置文件FlashDev.c) o9 D" e& j% f2 r& j$ t. X* f
模板工程里面提供简单的配置说明:
4 N9 l( F0 p. v0 o7 Y# G& |$ x! k- ]5 n3 }2 {, d
  1. struct FlashDevice const FlashDevice  =  {: D; P; e3 Z4 E' U# t
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!
    1 i% O0 a) R( O# p7 X8 }
  3.    "New Device 256kB Flash",   // Device Name
    : i2 N3 x6 q$ }+ |7 }
  4.    ONCHIP,                     // Device Type  k9 Q4 S& _2 q% S# a
  5.    0x00000000,                 // Device Start Address
    ' ]7 z  y# M) Z1 e0 a* S6 a: N2 \
  6.    0x00040000,                 // Device Size in Bytes (256kB)
    6 I9 E" L) l* p4 R4 }4 R; \2 r7 f
  7.    1024,                       // Programming Page Size
    & G4 q& {+ t+ S9 r) N
  8.    0,                          // Reserved, must be 0
    $ E* ^/ L3 }& q' b" P
  9.    0xFF,                       // Initial Content of Erased Memory. O" _# t7 F# t; @9 \
  10.    100,                        // Program Page Timeout 100 mSec- S& M* x# f4 J5 e. ]' r+ w3 |
  11.    3000,                       // Erase Sector Timeout 3000 mSec
    - k$ G) I3 s- t& F$ D6 y
  12. . D$ T4 I2 |7 q: n, }
  13. // Specify Size and Address of Sectors
    ! ?1 k6 s% `* T
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)
    2 Y! `, t5 w( O% ^
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors)
    , T% W3 F$ g5 k6 q% N' L
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)
    5 g# w/ Q# p  j7 X2 `! _, E! h' r# \
  17.    SECTOR_END
    ! ?4 u" {" E) }4 ?/ \, q
  18. };
复制代码

% b" s' I9 U4 {5 M: V注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。( i+ \! f% p& l( X% j
4 Y3 b* z' G. Q+ `7 l6 V% x6 w: y! x
80.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关0 j+ ]: t% k9 a' b+ ?5 `$ z
C和汇编的配置都勾选上:
7 c4 j* r( y. \1 A, {' b( W( S6 X" d4 N  l& ?5 U# n0 B1 J
1d27a7314fc6c472937ad43852722b6b.png

4 v0 n; n. @4 M& G* i
0 s% Z3 z6 [# f6 x" w汇编:6 L/ c' c- f( r
" F0 d7 w% S6 z
bbea604970c4ef63c77f10e553e28ea6.png
% i8 z9 T% Q: E3 G: f5 q4 y
% P. U; P: w+ P7 d! c
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:( V( _- d; m# s2 ^
" m& |& Z0 J1 t9 w1 ]8 C/ q7 r
(1)加载以响应运行事件。: T! y" r5 m" Z6 @$ u- z" Y( w

  w8 O; e* b8 I/ u(2)在不同情况下使用其他例程的不同组合加载到内存中。
, W% }/ P2 h" F9 S# a
4 L2 t* y4 C% \! ~( K(3)在执行期间映射到不同的地址。7 Q% _2 {5 p; _0 Y4 r

. v1 \; Z+ X6 r使用Read-Write position independence同理,表示的可读可写数据段。
$ h2 u2 t  W& f) U; u$ v" B
: q8 A( d. h0 m- F4 @! H( I3 Q2 @- c80.3.8 第8步,将程序可执行文件axf修改为flm格式

1 @+ e2 ]3 C7 k( `+ e' [5 y9 _" J通过下面的命令就可以将生成的axf可执行文件修改为flm。* X9 `& a3 A3 G7 s) e& m

4 e9 D: l& C" q1 ]% k* R/ i
6b504444979e9fa3a3477c6284abca8b.png
+ t# m/ r% m8 W( J
- X8 ^$ a" v* ]
80.3.9 第9步,分散加载设置
' X0 c, f! ?. y我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。
3 k: w9 o4 i* @* ?, T. `, N& _4 p
e8e79854b204b618bbda32af92f28bf2.png

1 g6 r, w* V, e& i( t: T
* R5 j, \% w* Y3 |# D0 \分散加载文件中的内容如下:: C3 |. _9 U6 {  Y

' g# L% K% t( o) \- a
  1. ; Linker Control File (scatter-loading)& ?" P! j: o. D; D
  2. ;" @# I( s8 L/ W# A" f. S
  3. $ e' O8 S; ^; T; {
  4. PRG 0 PI               ; Programming Functions
    ( X, _8 U5 R' W
  5. {& i! d! F, R; }0 Q2 C
  6.   PrgCode +0           ; Code
    ! W* c* I8 _: g9 k  X
  7.   {
    * r  w4 r5 B" l, }% J; m- a
  8.     * (+RO), e& Z  X3 Q- T1 S. H  N
  9.   }
    9 c1 j% \6 G! N
  10.   PrgData +0           ; Data# _7 m  u0 u# z% x! b- e
  11.   {7 t+ d" v4 x* a  L; I
  12.     * (+RW,+ZI)
    ( n/ A0 d% P1 S. J. S
  13.   }7 ~3 N, j+ B- S# \) m* D- g0 F' K
  14. }
    - b" E7 B) H9 N0 b* Z) S5 s, f

  15. 0 C4 y. j* l& ^  ~
  16. DSCR +0                ; Device Description& W! \. a4 C$ I% a! ]
  17. {3 k6 ^/ j4 N4 T0 r/ w* I1 D% U8 i6 u
  18.   DevDscr +0
    ) \* y3 M4 z/ A* l& ?( q/ w' k
  19.   {$ g% Y. u. M5 i8 I2 r1 e
  20.     FlashDev.o
    8 f6 a% I9 {4 h( ~7 t) Y
  21.   }! w: H/ J, j+ @3 G: \
  22. }
复制代码

) B) r, Q# H( t: n4 X--diag_suppress L6305用于屏蔽L6503类型警告信息。8 M0 C# r% \  Z3 Z
" h* r0 i. n$ T% w9 O
特别注意,设置了分散加载后,此处的配置就不再起作用了:
/ ?9 z+ E! d7 U* D4 m
  i2 o* i5 v5 T& |9 t
b249eb40e5562b04ac3eb05ec0219c1d.png
6 k+ f. p! Q: N2 j. i# K" @

7 h4 B, g6 C/ P80.4 QSPI Flash的MDK下载算法制作/ A" Z  D/ z& A; u/ W) ]2 {0 S
下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。1 k2 x7 d! [! W0 p9 R: C
4 b* t) _8 T, L9 v4 N2 e
80.4.1 第1步,制作前重要提示  n2 z# g5 x) t+ ]% b' p
这两点非常重要:( y6 W! p, X( Y0 B0 ?

% u: |; e! o- @  程序里面不要开启任何中断,全部查询方式。7 N7 y# a$ n* ^
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。& v1 N3 R9 A/ ~
, U4 H. j- I& v
80.4.2 第2步,准备一个工程模板
; ~, E/ _$ n- P8 C$ s( D6 {2 M; x 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
5 p- R; b4 X; _4 p" X3 l
! n) T. l$ v( p7 i& p/ [+ E
a91aaf84a25d730b77273624d2b502f6.png
: j0 C8 @* k2 E/ ?$ Q, K* q
. R6 }0 O. J  d) q/ t: ]
80.4.3 第3步,修改HAL库
- k- b2 ~  i  U. K- }. u% t, P1 J这一步比较重要,主要修改了以下三个文件:
/ C2 r4 C, A7 _" l; w1 A% n4 a
" B1 d  S6 y6 t* z1 F
4471afac6bbc201866d0d9a8ea7d876b.png
, ?) e; w7 {5 T4 u: E1 z

1 _, }1 e* U! `主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
# n4 i1 n& T8 e7 Q% |& w( Y7 ~6 \
1 B  |) V# z( \2 A2 x80.4.4 第4步,时钟初始化6 q, S( K; s' i6 e! g  U
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:: ]1 Y3 T! Q# b3 m' d
: R+ `  R& W4 [; z' J* {; v
  1. /*
    ' ~8 t9 N% K/ j0 }0 g
  2. *********************************************************************************************************& Q, T% I) l  M7 V+ X+ ]
  3. *    函 数 名: HAL_InitTick: g, y7 ]7 h, Z0 T& @7 a! g& X
  4. *    功能说明: 重定向,不使用3 i/ K4 v1 f4 T; H8 D2 ^
  5. *    形    参: TickPriority' v6 l; K; N$ P' ^9 a
  6. *    返 回 值: 无7 q+ X+ B5 r2 R1 N2 N* X7 Z0 R
  7. *********************************************************************************************************
    ; _! m- T; }" m1 k6 g! f! a# ]2 G
  8. */
    ; H5 X% d+ |( N0 ?. G
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    + e" I! S+ l! _! f0 Z' p$ G$ `8 J% {
  10. {% z- M, `! b  K$ e  k
  11.     return HAL_OK;
    & o7 [4 ]: W9 x5 `: }! i
  12. }
复制代码
. C8 Z  z3 `3 H- s; J3 A
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:3 u9 f* g4 H& Z' |
+ \9 M! D; j; b& a5 r3 h) ~* Q
  1. #if !defined  (HSE_VALUE) ' J: I, Y7 F* T2 B* e9 d+ p( `
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    " f* v0 `2 P% |
  3. #endif /* HSE_VALUE */
复制代码
2 w& U& \2 |& Q" N
最后修改PLL:6 g1 I! X$ L7 t. H( F) o
( W$ e) g; ~( [/ ^
  1. /*  O" [3 w* x$ a
  2. *********************************************************************************************************. W, f& m& _$ {3 t2 Q& [
  3. *    函 数 名: SystemClock_Config/ B. h: C# d7 U! B/ _" f
  4. *    功能说明: 初始化系统时钟
    * c2 V( g6 m8 X1 c6 O
  5. *                System Clock source            = PLL (HSE)6 a# X" o% b' ?! w3 \  g
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    9 [2 k: S) `) h* ?! |" _
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)6 B$ g7 F) R- x) c
  8. *                AHB Prescaler                  = 2
    8 l/ M, b6 L! `3 c6 l; Q
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    5 z0 ?2 D' A- g( A0 g9 ?/ J! A
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)# t. n' j/ k7 {- u8 A. X
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    & a# c. M+ J0 d2 J
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
      W( y" p. ]2 P& y
  13. *                HSE Frequency(Hz)              = 25000000
    % k) b  E3 f: v( {
  14. *               PLL_M                          = 5; [, v, l6 S3 C4 q  G+ [! b) P* [6 ?* ?5 @
  15. *                PLL_N                          = 1604 t+ C8 O( _  b/ G' S# M
  16. *                PLL_P                          = 2
    9 u0 e) Y0 ^( O: g; i' `
  17. *                PLL_Q                          = 4
    $ \* b/ d. l( I
  18. *                PLL_R                          = 2& f4 |, G" [% |0 f3 {& p$ t4 W
  19. *                VDD(V)                         = 3.3( s1 h1 q0 `8 Y  a. L  j; U' i* c1 [
  20. *                Flash Latency(WS)              = 4
    # b, y# j0 K/ U' I5 ~' [8 B
  21. *    形    参: 无* g+ }9 x3 i: {! L
  22. *    返 回 值: 1 表示失败,0 表示成功7 B7 M" T7 n8 U( R& c3 [3 m
  23. *********************************************************************************************************3 C% j) j* |/ a$ V3 W/ V8 R
  24. */( S; r# a; ~! X5 g7 ~6 m6 \' n
  25. int SystemClock_Config(void)9 Y2 \* D( j& o& ^# _: O
  26. {
    , W  j# t8 S  j4 C, l
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    3 j7 o' v  \; O+ H& d0 t: o6 O% a( y* C
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    * j% Z! m8 I( M  n, `* `, ]/ ^( \- e
  29.     HAL_StatusTypeDef ret = HAL_OK;
    ) \" \5 i% D5 {* S
  30. / @+ [% m- g% l, ~
  31.     /* 锁住SCU(Supply configuration update) */7 o' C% ^- I4 m1 {2 C
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    8 w2 ]# j* Y1 B

  33. 2 V, U9 `8 Z6 N
  34.     /*
    % _3 h7 ~4 g( h6 f! y
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    & m& F. a0 s$ v, ?4 L6 d& P
  36.          详情看参考手册的Table 12的表格。7 v7 y. v8 H- C) P
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    % |6 i& O6 A6 V
  38.     */5 F4 X4 B1 W) n' b8 ~9 V
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    . Q1 k# a& r* o. M& l* l, k% a

  40. 0 E& _+ H( {6 @$ ^* P: \/ Y
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}% J* e, |1 O) w7 S( c* J2 ~

  42. 5 C! _! ]5 S9 G' U
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */# v6 O; Q. M- ~/ S( C% Y. A+ l
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    " U: \/ ~8 ]5 L9 x8 q, e( v$ v
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;9 M6 i( i2 S" }! P, P! `% }- c5 Y
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;# t# v7 L. I: Z/ b
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;6 @8 f' O  l% ?; i3 Q5 ?+ @
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    , n/ M* V- w: D" p
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    + g9 o: ~& v4 Z
  50.   V2 T/ Z! Z8 P) z# y8 C
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    5 @! K4 ^6 l8 h4 @# u' t
  52.     RCC_OscInitStruct.PLL.PLLN = 160;2 @' s2 k9 x( d- S# q
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    : d. D) f" D* w9 X& O
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    + j, i/ h" p2 q$ ]: O4 V
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    5 A2 Q* w- c2 M4 e
  56. " k" Z/ K+ p% H7 n& X9 x* L  B& R
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    8 r! M! M3 c' G! A# {5 b% V0 K4 |
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    / P! I4 U! i5 `% Q
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    4 U5 C+ J) h1 F- S9 H
  60.     if(ret != HAL_OK)
    ) S; K1 G2 P- F, k
  61.     {4 ?: B+ ^' d$ X$ q) ^5 R. }$ D6 n
  62.         return 1;        
    + Q! i* g1 ?) o$ }: _4 v
  63.     }* k! c8 z$ J4 w% f$ D# j8 E6 c9 i

  64. ! p5 {6 i+ A# x3 S- D
  65.     /*
    " g; l! I. Z/ \) Z
  66.        选择PLL的输出作为系统时钟
    3 q' I4 Y4 R( |6 |  V) F" q
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟4 H3 U4 E# L2 H* Z; w# N
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线+ D+ R8 ?% X! G! ]& \
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    $ M9 |/ Y& y' C$ N0 k
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线3 R# b- Z  f6 o  @1 F- w
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线  }+ a( ~% u% \3 [
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     ' s- r5 ]7 w3 ^1 d
  73.     */* G& g6 W( T; Y# y* d. n( Q
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    ( H& C7 Q, F6 P( N2 b( }7 A
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);( A" u) a  s8 v7 M( q

  76. ; t7 e+ X" Y1 P/ `
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    # q0 j0 [* E  B# @( h' l3 D
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    * u* d3 _: |- s
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;2 E1 v1 g% ?: i+ S9 v4 l2 f, J
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    # \; w' _9 ]' U% B4 U
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    + ]% ~$ y6 Q+ _4 v
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    . ^0 }/ m  ~$ H  a4 s
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
      h( h+ f9 b% f
  84.   z8 M. R7 }' e
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    4 Z4 i2 [" Z  U) H: o
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    , W& o1 E1 I/ \' ]- K' W- f
  87.     if(ret != HAL_OK)
    ) U6 c. d- V7 \. F6 |3 M
  88.     {
    2 q$ l& H  j9 ^, i4 Y! e
  89.         return 1;4 ]3 [; z  U3 U" O
  90.     }) T; {) Q+ x! w( B8 c2 W
  91.   M, ?0 @9 x4 B2 O' C8 ^
  92.     /*
    5 j. r: t! h# b8 a/ U' a
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    % n  c$ E5 i3 F
  94.       (1)使能CSI clock
    6 G- ]7 F/ f2 P: C( r  I7 _. `# X  O
  95.       (2)使能SYSCFG clock' y+ Z( c& m& Y
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    ) \0 ^$ V" ^2 H* g3 Z  n( F
  97.     */8 c7 [6 c) `, q0 W$ v
  98.     __HAL_RCC_CSI_ENABLE() ;4 o9 R  p1 q% O8 P; m: z

  99. 3 f6 l7 a) w/ i" ~. ^4 ?
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;0 [& c/ A9 f2 ^, {$ u

  101. ' L8 Z0 `" D* O; Y  ~, I9 s3 N
  102.     HAL_EnableCompensationCell();
    1 b- m. \- U, ]" k" m  U
  103. 5 k( k4 ^/ I+ Q7 L* A* L
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    : i# O; ~! B( s8 i; h6 V6 `/ F, h: X
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    . t& t' }$ E! Q' o, d
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();/ ?  G! }! e4 G

  107. 9 u- K4 f+ O$ E+ a6 x3 A
  108.     return 0;4 P/ w% P9 S6 v9 t0 H$ q5 ?
  109. }
复制代码
& C  c- Z, ~0 Y  O
80.4.5 第5步,配置文件FlashDev.c的实现% a' K( V( z5 z, d/ W
配置如下:
, Z; ?$ j0 t$ c: P8 T/ h8 _+ K- t2 T4 e/ m& |' e/ t% ]; K
  1. struct FlashDevice const FlashDevice  =  {
    2 Z  P3 b0 m: `* _9 z) J7 S' }
  2.     FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */2 p7 P2 I7 q& I
  3.     "ARMFLY_STM32H7x_QSPI_W25Q256",   /* 算法名,添加算法到MDK安装目录会显示此名字 */
    5 f* m7 Q' n1 V# ~# j0 {2 |
  4.     EXTSPI,                           /* 设备类型 */6 W, L$ e3 G. [8 W8 ?4 q0 g. p
  5.     0x90000000,                       /* Flash起始地址 */. p* g) g- F% x/ H* V& N0 b5 ]
  6.     32 * 1024 * 1024,                 /* Flash大小,32MB */
    9 a* c0 S9 H2 v4 x
  7.     4 * 1024,                         /* 编程页大小 */
    * o2 U. R9 Z& U9 p' Q- _
  8.     0,                                /* 保留,必须为0 */
    5 q" v/ K" H* r1 A
  9.     0xFF,                             /* 擦除后的数值 */; j: s: V0 C  C2 {
  10.     1000,                             /* 页编程等待时间 */' Q8 k: @, c/ H1 C8 y; ~3 G- e: f
  11.     6000,                             /* 扇区擦除等待时间 */  f% f8 t: h* X8 U; w
  12.     64 * 1024, 0x000000,              /* 扇区大小,扇区地址 */
    / ~9 ~) V8 y  f2 y
  13.     SECTOR_END    8 d0 N# {$ C4 W0 A1 N
  14. };
复制代码

8 l4 h" \7 T, \" z注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:" }! r% S& \( \( j7 ~% \
+ L/ [* P7 Y5 l' ]) l8 U
ffed7a581be4c5da388ee29fb67249b4.png

* k& V2 m5 ~0 h# q6 ^# J4 W0 w1 Z# D
80.4.6 第6步,编程文件FlashPrg.c的实现
4 i) e0 A6 T. ?( o8 T下面将文件中实现的几个函数为大家做个说明:
7 Y: s6 ~7 J9 k; Q: w) @8 P0 W" w* \3 z0 C. J
  初始化函数Init
# u5 t" F1 L8 X& B. \4 T
  1. /*# d& {3 Z3 f& W! l" r2 P6 @% W
  2. *********************************************************************************************************
    ( o7 h, x: M6 y* Q4 W& @/ u
  3. *    函 数 名: Init
    : ]3 N% a8 o' t  [. ]
  4. *    功能说明: Flash编程初始化' }! e! p& F6 L' Z3 L
  5. *    形    参: adr Flash基地址,芯片首地址。! K+ h/ s/ |7 y, K% e
  6. *             clk 时钟频率. h& K% S- g6 k( L" k6 b
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    6 N% q0 ^# O$ a* M' R3 t
  8. *    返 回 值: 0 表示成功, 1表示失败
      E# I9 O( o& Q
  9. *********************************************************************************************************/ l1 e+ |: i5 {* }
  10. */
    5 _! j! K0 q' i1 y* R( K
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) 3 v5 |7 M* u* j' r
  12. {* R) K- {: u  v+ _/ T* [4 Y5 i
  13.     int result = 0;
    2 |2 X7 S" W* D% S

  14.   k% x1 k) f8 d, w9 R% K
  15.     /* 系统初始化 */
    + {% n4 `: s4 O* Y$ `
  16.     SystemInit(); ) U+ U- _3 v4 z
  17. ! t7 m! D5 A& e  t- y! F
  18.     /* 时钟初始化 */" u4 v9 a4 R, b
  19.     result = SystemClock_Config();
    ) C% J8 U) }$ q# T
  20.     if (result  != 0); j$ w" Z( ~; ^
  21.     {
    . q) i* W- r3 c, R! p
  22.         return 1;        * u9 l" d% e5 z  J4 x
  23.     }$ [- x7 ]1 E& m! f% ]
  24. 4 r& B: W8 O# m: ]8 }. d
  25.     /* W25Q256初始化 */9 K4 T1 i. b3 W  G! @
  26.     result = bsp_InitQSPI_W25Q256();$ w) M  _+ \/ d5 P9 j
  27.     if (result != 0)
    ( d; `7 G' O* z* n, ~$ ?* x
  28.     {* ?3 g. J4 k6 P
  29.         return 1;
    - p; v- w% q* P- B' y
  30.     }0 H) w4 X( k. o- k* y  B1 y
  31. 3 z& r9 v, C/ d1 i
  32.     /* 内存映射 */    ( K) z! Y3 f# u3 y
  33.     result = QSPI_MemoryMapped();
    ) S' E% B6 S  [8 R0 A
  34.     if (result != 0)" B! J9 |  s8 M5 s' F
  35.     {9 n2 `9 e0 j. I
  36.         return 1;
    . i( x& U4 d  [' f
  37.     }; _8 W8 w( i, ~5 B6 c# i# w( r! Z, ~
  38.   J9 j# f: N) Q* I" h0 i4 w; U9 ]
  39.     return 0;
    & y" z. U. W4 I( m) ~7 {7 F
  40. }
复制代码
# J- f" r& {$ H+ Z& c7 `4 W
初始化完毕后将其设置为内存映射模式。
, {  w! p5 R. L% A9 q3 S" N* |) f, ~  J; v  P# i% B
  复位初始化函数Uinit/ n% _% t9 |& [1 _# a( e9 F% B4 Y
擦除,编程和校验函数后都会调用此函数。
9 f: S+ k0 \: W3 J2 W: d: J
1 ]) O# r' Y% O& m2 V- Q. o8 B
  1. /*
    ( O5 p; l2 K8 E- g% V
  2. *********************************************************************************************************
    $ f8 _+ T! U! O. A5 U5 E0 `
  3. *    函 数 名: UnInit3 {3 ~" y& Y; f/ I, |& X6 T4 J
  4. *    功能说明: 复位初始化" e* T' X* U; ?- Q  C1 O
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify* v0 V: F3 ~1 k  ?+ n+ c
  6. *    返 回 值: 0 表示成功, 1表示失败
    ! \7 ^/ w6 m5 x8 [: y! c
  7. *********************************************************************************************************  }$ c+ D/ X: ~# z; Q$ a# o
  8. */% }5 B5 {8 }& _. N1 q2 k8 \
  9. int UnInit (unsigned long fnc)
    , f# i, C2 E' M, o+ \8 M9 H
  10. { 5 z8 {* q6 {" U& x) j3 `
  11.     int result = 0;4 i# g# M7 Z: A# K

  12. ( i4 c0 J# O1 X' l0 I% n% s2 Q
  13.     /* W25Q256初始化 *// b4 Y5 I/ Z. F! p
  14.     result = bsp_InitQSPI_W25Q256();
    , J+ ^) v; ]8 \& M
  15.     if (result != 0)
    4 A6 q3 t( u/ p
  16.     {" X+ J5 b9 w" L- g5 W' `
  17.         return 1;+ v8 b" N' P* K: F" a
  18.     }
    1 p3 |3 e  y5 e: i6 J* X9 b. E
  19. * N. t3 T2 v% ~5 a2 ~8 h( n: ~
  20.     /* 内存映射 */   
      p# n. A1 `% a$ @5 R$ q4 B2 m- h
  21.     result = QSPI_MemoryMapped();
    9 N: O- V3 r0 @* J
  22.     if (result != 0)
    9 R2 h. V. Z  t3 ~
  23.     {( O! H# K3 [& E7 E1 }" G
  24.         return 1;+ ~5 ^; o% W6 b0 p, g2 T
  25.     }
    % l0 H' ?( ~- a4 W! @$ `
  26. 6 ]) x' L$ ?- N8 w
  27.     return (0);
    - x, n, N! ]" x5 ~4 d4 n
  28. }
复制代码

7 {  p1 C5 U4 v复位初始化这里,直接将其设置为内存映射模式。
3 l7 l# k) Y; b$ {  M9 ~2 C; g, i, N: l6 N2 U1 I' p: @8 u
  整个芯片擦除函数EraseChip4 O1 A2 G8 Q" M- {! q2 r
如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:
9 g- q: d- f" n) n6 m  E
4 O  ~4 y( K( u
f623e662bf09233ea594f820f4c7952a.png

" N) y1 g7 Y4 k8 Q5 i4 I3 g$ @4 {' C% Z3 ~( o) Y
实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间,比如32MB QSPI Flash整个芯片擦除需要300秒左右。3 N4 Y% H" B2 F9 F3 C4 b2 }+ \

$ P( d4 ^# z* s4 ^  ~另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。$ S, I+ d  Y: h, Z+ W
4 o/ Y$ B6 V- ~' ]
  1. /*2 `/ B" c$ N# W0 t1 w! o+ D
  2. *********************************************************************************************************& G7 J2 _: N; i4 v1 ?. V
  3. *    函 数 名: UnInit
    2 N( H2 U" F, A6 ?
  4. *    功能说明: 复位初始化7 G3 I" j8 ?5 ]2 R) R) m
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify7 }5 C) [( C, K# U5 w' Q
  6. *    返 回 值: 0 表示成功, 1表示失败
    1 b3 c* P) |; P. ]
  7. *********************************************************************************************************
    6 Z- S" Q5 G3 x& ?5 e/ r9 [
  8. */
    / E2 o" r+ C, }5 n
  9. int UnInit (unsigned long fnc) % c) W4 h1 t* i  d' t. G6 T
  10. {
    1 Q  Y- q1 A7 ~- I: v+ h3 ?: j
  11.     int result = 0;
    - B( Q; y5 E, I  y

  12. . J  |4 G6 Z- T  U1 w  u& q
  13.     /* W25Q256初始化 */4 `2 o. P: A; N# k4 K7 Z: |
  14.     result = bsp_InitQSPI_W25Q256();
    7 q  P5 M0 s1 g# B& T9 l' F+ T2 ?! M
  15.     if (result != 0)8 a- g/ k1 ^4 J. m8 L2 n
  16.     {& s8 L( v$ J- u1 f$ g( F2 n$ v
  17.         return 1;
    ( K' X- Y3 C- {
  18.     }* _2 L; H0 n% }5 n6 _- `0 A
  19. " }5 L. b& n+ R4 F3 u
  20.     /* 内存映射 */   
    - X0 p5 l7 y% W- Z5 f3 s
  21.     result = QSPI_MemoryMapped(); : M) r4 Z! i) v  Q0 R3 k
  22.     if (result != 0)
    1 V& B- ]8 _/ e/ a2 N/ N2 x
  23.     {% A2 ^$ o. a  p+ |2 @' T
  24.         return 1;
    ) `/ m2 ]% x' S3 W$ U2 z' G4 {; F
  25.     }
    & U6 Y0 X( H. ^6 X' m
  26. : u, r+ C5 V8 I8 e" Y, ~
  27.     return (0);
    ( ~& w- L/ `: l. p
  28. }
复制代码
' H% r- s) S7 K: u- ^' R
  扇区擦除函数EraseSector" x0 ^. \" f# P( Z$ ?; t* q5 w) f
如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:+ Q' Y. S4 N! _# G& T/ V% m

& |0 O8 O  k/ Y5 z2 ]$ n8 N0 a, w
733d5af26119f64bff0261515013d3a6.png

# R3 X; W9 o! E4 d: t' T/ y6 a( H$ c, d0 N; q
  1. /*8 t. W- T6 B" W/ d
  2. *********************************************************************************************************9 W$ W, \! S9 O
  3. *    函 数 名: EraseSector
    4 A) t/ Y7 E+ U/ C# E, U7 \
  4. *    功能说明: 扇区擦除* Q: @0 |2 j- ^2 }
  5. *    形    参: adr 擦除地址
    & |, ^+ C6 ^* ?
  6. *    返 回 值: 无
    , i4 ~8 W+ P9 ^, J! e6 W
  7. *********************************************************************************************************
    + u: N+ f# @! B* c
  8. */
    * P9 l. l: i/ r  s
  9. int EraseSector (unsigned long adr) 3 O( w$ T, o' h  N
  10. {   
    $ j5 a: X" k; ?' l/ J8 x
  11.     int result = 0;
    5 c; s* J4 X* K+ J
  12. 0 x+ U* z1 `; y/ B$ v! W
  13.     /* 地址要在操作的芯片范围内 */
    0 W0 u, B% a* l
  14.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    : ~7 N- y4 ~  ~$ ^1 b
  15.     {0 j' `3 B. Y2 ?, g
  16.         return 1;( K- T9 ^- e' Y6 n/ o( X0 _4 c+ y; k
  17.     }
    . X2 j/ A7 p. |

  18. 0 X. ~8 d: ]7 I" o+ C0 D
  19.     adr -= QSPI_FLASH_MEM_ADDR;
    4 z( w) E# t; M+ R( G9 Y8 B' C

  20. # L- u3 H% C5 F! [
  21.     /* W25Q256初始化 */9 M; A9 I- N9 Q" w. o+ ?+ }
  22.     result = bsp_InitQSPI_W25Q256();7 c/ H- w- |* Q; \0 c* g
  23.     if (result != 0)$ Y; L4 R' `/ l/ R
  24.     {
    " w8 O9 N. G3 I* |7 p
  25.         return 1;
    - Y: `- l: ~* |! v; W1 N
  26.     }
    & K4 V1 H" s$ f" x( \1 y
  27. 8 |$ R* ~. @% e. B5 T4 I
  28.     /* 扇区擦除 */) P; d$ \# }2 \; I4 n* q
  29.     result = QSPI_EraseSector(adr);  
    ! v; z, ^6 e- n) S. _
  30.     if (result != 0)
    6 d$ v# N" Y9 |8 O8 \8 f$ h
  31.     {, ?: Z$ t) X& U: T/ n" U
  32.         return 1;
    " \% j: n5 O. \, B% J
  33.     }    ' s1 [4 p0 S! [7 c
  34. ! N5 Q9 d; z( }. f! L! h; l, W, U
  35.     /* 内存映射 */    : b' Z9 O% i* O# u% L, M
  36.     result = QSPI_MemoryMapped();
    & X2 q# e  }  N' U1 \: }/ v
  37.     if (result != 0)
    0 c% A5 n  {+ B% l7 i# L  L
  38.     {, D& A7 _) O" [5 y: f7 [4 Y
  39.         return 1;2 S( v( ]2 e2 Y0 I1 {1 e
  40.     }
    4 e+ x$ \$ {3 Z4 t6 A8 R
  41. 3 t- U0 s6 X, e0 L5 u
  42.     return 0;   
    : s! ?4 g5 `; Q! A; B. ]
  43. }
复制代码

( W1 ^) W! q" d4 s+ i这里要注意两点:
' _9 w2 J# H  d+ ^2 b! r) x' U
* z/ E! l& c  K6 m' y6 S. y* q(1)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。- ^( k  Y/ J  S7 I
5 \' Q: R) j$ u2 P3 Y
(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。" ?, k  d) ~* i+ y+ D2 J

1 J' Z+ M, m/ T! j  页编程函数ProgramPage

5 C( R( ^9 B  y% w/ a页编程函数实现如下:! o  E) }) l* ^. E* W, }  F1 K

* _! q- l  M. u) a, ?% k  r
  1. /*
    / w+ T" K9 o4 Q4 L2 ~
  2. *********************************************************************************************************
    1 Q( Q$ Y8 }. m0 m: G3 {  X
  3. *    函 数 名: ProgramPage* I3 Q: r, Z( z5 g! t
  4. *    功能说明: 页编程9 D& ~! r& Z$ j( {$ H' X8 D7 g
  5. *    形    参: adr 页起始地址, e$ `* }: i; o  q8 g
  6. *             sz  页大小
    , M! j! v3 z2 W/ j- x6 A0 a5 _
  7. *             buf 要写入的数据地址5 [, m1 S8 g: y/ I2 B& W9 L
  8. *    返 回 值: 无3 ^# K2 i6 p1 W: {) z& `$ U2 L9 i
  9. *********************************************************************************************************
    & d/ B% G0 F' V4 M3 t
  10. */
    4 h6 d3 d+ A& I5 g
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) 9 e: h  L5 ?# R! p& U
  12. {
    % M( d, d8 k. i8 b$ b& H
  13.     int size;
    5 {" ]' f. t2 }* b2 `
  14.     int result = 0;. i6 e% T/ v& E( }2 D* ]9 B
  15. + ]& w2 d& w4 e) K) ~% L
  16.     /* 地址要在操作的芯片范围内 */   
    ; G% A5 Y* @9 I0 y( h) }
  17.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)7 S3 T. S( ]' p) C6 G
  18.     {
    , c+ ^* L4 u/ q6 V% e7 e8 Y2 _( Y
  19.         return 1;
    1 ~" r" B, o3 R7 M0 |
  20.     }6 t4 y. V6 W  y! w6 X' \
  21. 9 w' M( i# |  p/ x8 a; R
  22.     /* W25Q256初始化 */4 S6 e* {, w. A" f
  23.     result = bsp_InitQSPI_W25Q256();4 O" N6 ~. L2 o0 ^+ N' G+ l
  24.     if (result != 0)
    1 H& k9 q' H; S% L: q1 w
  25.     {( X! ~/ P( o. h4 f: z; t
  26.         return 1;
    7 [6 a; K& b, b" T' I, [0 i( M
  27.     }7 [3 {/ G) I1 U$ A5 ]3 c8 s& ^

  28. ; R9 K* x" p( c0 B9 s& V0 E
  29.     adr -= QSPI_FLASH_MEM_ADDR;. n* E" t# b( g$ L* H: H
  30.     size =  sz;
    + |9 Y, @7 @* Z

  31. & [  S7 _& u" E% w3 q
  32.     /* 页编程 */
    8 }4 }5 u4 O5 v+ D7 B
  33.     while(size > 0)
    ! u0 k4 J- D6 p: `; U. T
  34.     {+ ~7 v9 O. n# U
  35.         if (QSPI_WriteBuffer(buf, adr, 256) == 1)% b8 n2 z4 x  [0 ~1 H: n: x) X. ~
  36.         {
    6 C, X3 B- Q: s! z; Q
  37.             QSPI_MemoryMapped();
    & w: K- b) s- U4 |! ^  O
  38.   S' n/ i6 `" @$ r$ x/ o5 D
  39.             return 1;   ! {/ s+ a7 e3 _0 c3 _" J
  40.         }( p7 o' p& f4 r/ c
  41.         size -= 256;6 J9 Z5 o: A# |0 ?2 w; Z0 q6 Y
  42.         adr += 256;* W* O+ x3 u; D0 l5 O! ^% h
  43.         buf += 256;
    : N2 u+ @7 F) V2 k& F0 I3 o+ T
  44.     }
    % _. @- |( H* {* V0 C

  45. * A* y$ i$ s3 E# E4 J' D
  46.     /* 内存映射 */    ) v1 v# l7 v; M+ L! @' l
  47.     result = QSPI_MemoryMapped();
    : B: d# ~5 s* I! b7 n: E
  48.     if (result != 0), o& z- x* u, ~4 u7 ~
  49.     {
    ! `8 |2 L/ s& b3 ?
  50.         return 1;% ~' `. V8 |/ E3 E, x, E
  51.     }
    / |' I; H1 v( n! E  h

  52. 7 L% b) S+ e% k
  53.     return (0);                     
    3 z! ?/ y& D) B& ]1 s/ j0 N6 E
  54. }
复制代码
! t& `4 @+ |- l9 }
这里注意两点:2 i+ C% K3 y" u, p! r6 T
3 b6 ]5 c4 |( X4 M1 u
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。7 P$ A- E- }/ S  v8 O% h

' s0 j" z  F* b1 y(2)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。) ^. @0 \2 a2 R2 B
/ e0 s1 t3 ^% F* H: C: o. h
  读取和校验函数
$ i1 ~  ^4 P7 w! R. U- v: ~我们程序中未做读取和校验函数。
. ?  q6 @' t! V9 R" O! s% w9 \
. M6 W: h3 b) D(1)     如果程序中未做读取函数,那么MDK会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。1 M8 }8 m$ R- A8 \6 v7 t
* J& O% u% G" ^9 k3 e
(2)     如果程序中未做校验函数,那么MDK会读取数据做CRC校验。
: s6 J# W/ B: ?" w. \. ]$ A$ Z* H" o4 j' i! g3 @. h
80.4.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
7 x; l/ x6 b: B# ?1 b, r/ C
最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):( Q# X& P* n, Q/ Q$ Z  A0 H
" d4 F6 f4 N7 K% q% R4 Y
  1. /*
    7 \, f# [/ I4 I7 U/ z" F: n
  2.     STM32-V7开发板接线
    / ?# H6 D" h* y1 `  d) R1 i- G) N9 p
  3. 3 p8 V# B& _' s2 o* o
  4.     PG6/QUADSPI_BK1_NCS     AF10
    2 u3 l+ F/ [# M- Y7 B
  5.     PF10/QUADSPI_CLK        AF9# a! J7 g0 W8 i7 a
  6.     PF8/QUADSPI_BK1_IO0     AF10
    ; L; u* `/ F) z! ^- y: u
  7.     PF9/QUADSPI_BK1_IO1     AF100 T4 \5 C, f+ p4 s& Q% q
  8.     PF7/QUADSPI_BK1_IO2     AF9' F% K$ n4 x1 @  [+ g( W
  9.     PF6/QUADSPI_BK1_IO3     AF9
    1 b3 }$ h. \" R1 r, w- ^4 M
  10. # |6 y3 x( l: N4 q
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB  ?4 V* ?& W7 k* C" V
  12. ( \1 s8 O" V3 `2 w5 u( ~
  13.     H7-TOOL开发板接线- `% l. ]& x9 P  @+ s
  14. $ {" K/ X% Y5 N
  15.     PG6/QUADSPI_BK1_NCS     AF10; b; }9 @1 D* Z! m' N# q, c8 D
  16.     PB2/QUADSPI_CLK         AF9
    $ T+ ~2 o! I* i
  17.     PD11/QUADSPI_BK1_IO0    AF10
    / D5 w' r. Q# D2 ]9 J4 e, Q
  18.     PD12/QUADSPI_BK1_IO1    AF10
    7 B: \5 d* p% e3 g7 P/ I
  19.     PF7/QUADSPI_BK1_IO2     AF9
    # W- v: p" u  g+ d
  20.     PD13/QUADSPI_BK1_IO3    AF9( s& }' O0 K3 u( m3 Y) i$ S& U
  21. */  J7 w  i# p; Q7 e& \9 F# s: T

  22. & J& l, s" q8 j" K
  23. /* QSPI引脚和时钟相关配置宏定义 */) ?9 Z3 o) T" W* [  }; S. p
  24. #if 03 g! d7 O9 K. t1 p. S: ]' @, M( ]
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    , d( c% I% `) V. V3 Z: k
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()% K* [, J6 {& a8 w; x
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    & J# r% H% `6 C2 l
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
    ! v- k* d  a( r8 R% w5 ]9 C& z2 Y; ]
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    ; {! I- ^) |' w# e" t
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()7 e, \4 |. [' N& j7 X
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    7 p& f( [5 l7 ~7 C. w+ o
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    + \2 Y# w2 ?$ a* G( X
  33. 9 A) s4 Y. y1 N  f/ `, E/ v
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()+ R; O) ?) c5 H4 v3 b* D
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    8 R6 \4 y' @* l/ O
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    2 `7 X4 B6 `8 T0 r& O9 t( A
  37. , z2 {; B* o3 l7 K9 a1 I
  38. #define QSPI_CS_PIN                     GPIO_PIN_6( X' ~! [$ x1 n$ i$ o& i" K4 v0 z
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    ! `% Q4 D% g' \4 j  \; x1 Y+ a/ j& A& b
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    4 B% G8 V) V6 X3 _3 l+ R2 p

  41. * |( ^1 f, ?8 W0 A: a2 P' d9 B
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2( D' J0 ~6 Z! y  N
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    2 ]9 ]* h4 _0 S' _: q6 C- \) g% n
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI  b, B) S9 X" ~* a. E
  45. 5 v- L" y! {& n) h
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_117 Z( R, n' ~+ n# x, T2 J" q7 R8 Q
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD' J8 C8 [7 D$ j6 Q
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI+ x& E3 O% C, e& ~& i0 J

  49. - J4 Q& X" s9 r- f$ l! m" N
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12
    3 m% Q" f# x# g$ }- S! h6 d) X
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD8 v( h- e# ]+ r! l
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI
    ) q3 f" U/ q/ g. k, @/ L  D
  53. * ]6 K$ p; m7 N, M1 h6 Z
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7# P3 o% u2 W1 d
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF4 @# ?8 h! k9 `; v( s
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI/ }* a0 T' E; m5 I/ @
  57. $ k  W2 x) Q8 S0 {( \5 Y/ W5 ^' A6 ~+ I
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13" F0 i4 z. d+ k2 e0 g- Z3 b- Z
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD8 \2 @9 |) m( e7 t
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI, f9 T2 x8 ^+ K6 b% i' m$ b# \
  61. #else! W" g  H  l9 R
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    6 t3 D2 x2 J& G0 y$ N
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    8 H6 s8 o+ h2 b( g: W
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    # S7 p! H" S* I+ m5 j
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    4 n# `- E1 p0 B; k  S
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE(): @/ H6 v0 r4 b3 Z! k4 W5 b, M: |
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()& F- R- n0 Y$ v! _) M
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    ) _1 {5 ^% J- B
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()7 C6 {- o' _! h% K. a

  70. 3 ~: X! e5 H) C3 [" b: H% S5 y
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE(), z; T& T- F' L, R  k
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()  m$ T- F; c8 B% \2 U& a
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    - _; d; _9 u4 l8 k7 ~

  74. - n5 B6 z' b6 P) c0 ^0 B# T- U$ C
  75. #define QSPI_CS_PIN                     GPIO_PIN_6: X) W- M' i( K* O2 a2 J
  76. #define QSPI_CS_GPIO_PORT               GPIOG
    5 {7 d, |6 \5 z5 q
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI' p! R; G6 ~) y" X9 R" F6 m
  78. 3 u9 K; X1 @# s+ @0 O- j
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10' }# y4 P8 v* G" H
  80. #define QSPI_CLK_GPIO_PORT              GPIOF* p* V4 w& \' B" s7 v. E
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    ) u& R* l4 C& @
  82. - H" i. s! a4 A; g, \# ]/ ]
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8" j4 U7 r, r$ K: I
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF5 Z2 l( F. W; ~  k
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI. m/ ~  v6 w2 d( `7 y

  86. 5 _, P9 Z8 R/ q8 b
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    8 C. M! |. {( O: ~
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF9 M( Z6 Y; H, l! |
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI
    , h2 T$ t' C9 m: O' o
  90. # A+ w! U2 D0 f( J( Z/ g. u7 T
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7- o6 [% d5 @: M) v- X6 @* M
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF0 H2 f. W' z& _% @! n
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    9 z  h1 F) H& C* N* ]( q

  94. + [5 C: x1 `  ]9 }
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_66 h! p8 ~2 P0 E  Y4 X
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF- ^. ~% ~+ n+ q: r9 s9 h% X
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI; U( Z* N# i! o4 Y; h
  98. #endif
复制代码
* a/ h1 a2 O0 `2 I1 _+ F
硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
* S8 H% X3 Y; q$ c9 d9 [% \+ i
- h/ C  T" o) W  b主要是下面这几个:) _  w  z, P' d
5 _: _. l( h6 K9 e
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000
    ; ^% T4 C9 ~8 O
  2. # ^- g0 u  H8 z  f
  3. /* W25Q256JV基本信息 */% N- @) _8 u) J( l+ ~+ F
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/
    * [, c, D6 @4 k4 t4 d+ `
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */  g! k& v) q% B- c
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */
    , o' a, ^& n$ {& Y7 @- c
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    5 \& L" g+ v. Y" v
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/" T) z! F$ R& o  L. _  |4 p
  9. ; f- X' ?" A/ R9 b( {7 w
  10. /* W25Q256JV相关命令 */
    9 O0 R) W: g" P' v* ]4 z
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */& z% ?4 f( q: l' N7 h
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */& S5 G' R$ I0 _7 R* n
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */% \, x' U; J6 M& z
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    : E" @+ g+ _. X. y5 v
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */
    ! F9 u' r" Q7 W$ z- {* Y+ m
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */
    5 H/ [0 k& B1 L) _& E/ e. Y7 \
  17. 3 m8 i( F) y7 x9 N/ T& ^
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    1 D' |( k' S! U$ `) _

  19. ' i7 ?+ O7 o( ]/ x
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

+ @# A6 R; U" E6 `+ K8 R3 I! `4 x80.5 QSPI Flash的MDK下载算法使用方法7 h1 b. a# o' C! i6 L4 [0 m$ ^
编译本章教程配套的例子,生成的算法文件位于此路径下:
9 H9 B- |/ J# o0 R" m- s$ p6 U+ x2 `% H* ^: N- k
ed1f9465c60f92522e906639b76b29f3.png
: ?6 F3 w) @) @: K9 R

# D5 \, x6 g9 r0 S& w  v8 ?80.5.1 下载算法存放位置
/ Z+ r" H" @: R生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:
9 n) {4 F8 X- b5 ?* e. I+ E' A# ]4 s
/ F" }$ x: z$ o( F+ u  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。
2 l( T8 J1 D$ ^' I& A6 y  第2种:MDK的安装目录 \ARM\Flash里面。
+ _+ B! b5 s" g4 S6 X9 ]/ I9 p7 W1 M0 E+ |7 q
9d5d65cb90fb28ad36464cb1b8368759.png

' G2 @$ a+ {' V6 D3 b3 v- [$ H' ]; @6 X, M( A. `! `
80.5.2 下载配置
) A8 ?( L: F. o3 V$ ~+ e; a- f注意这里一定要够大,否则会提示算法文件无法加载:
, r: S  ?( j% n0 M; `. i8 `) e5 ?. W$ H6 ]* v
8eefa57c2d7f128449000c2fb4e788d9.png
2 P- o; V- J* }9 H4 `9 |2 Y

1 o0 B( K( r9 n; ^1 ]2 m( k4 A5 J我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。
" f* t* N9 x  t. l  E- I8 s* `0 h' Y  [$ b
如果要下载程序到QSPI Flash里面,需要做如下配置:$ H( ~/ `% h4 q; a* T
% q, c  w# L# H" k
caa01b15ccf9a9700690d2ad1bdedecd.png
# l5 R, Y' C9 ~% o8 n

1 `; n# Q$ Z1 r$ R8 o80.5.3 调试配置
! n: e) q% @- e8 n! U* M& t
注意这里一定要够大,否则会提示算法文件无法加载:2 q# i- o* z$ o8 ^2 p& x) p8 }
2 f; P: e* T3 @$ s4 V
f23244d646498fc30aa5f1301a327430.png
, X2 d1 e* ^6 u

3 _9 ^/ }5 u" w. d我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。
7 D5 P" ]5 ]! J9 l- F1 f6 S
1 o- Z. U  M4 V7 r如果要做调试下载,需要做如下配置:
4 F) A# R( h$ D, D5 M
3 c" _3 u5 ?4 a' N; N) q+ I* W
84ee4945e5faabd776fedd6f8f7d96d2.png

1 h9 a' G  ]* W0 x; U4 {/ [& H
+ N5 B& k% P* O; ~1 f: G* }80.5.4 验证算法文件是否可以正常使用) m2 N, r$ _" \$ `) E' C
为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。: b" a# f1 S! R3 s

3 x& e! `4 s! j/ k80.6 实验例程说明

& Z6 G3 D# f# Z9 n# u# \1 q本章配套例子:V7-060_QSPI Flash的MDK下载算法制作。
& G" P5 r4 J* T! e  Q
  h, b; M1 w% [/ u! y; O3 ~编译后,算法文件会存到此路径下:; A" d' q2 l& h( @& ^0 b
0 O! E/ f4 C8 v  Y8 L0 U

& y( I8 z0 S# L
) W% f4 [, \+ |/ H) ]4 n8 ]80.7 总结
7 V% a# r1 Y8 C: g- b7 J# \( S- r, P& c本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
  c8 H- T( k; t; `4 D5 S# F! N5 q+ r1 ~/ J' {+ {1 u

& K" y# C0 f+ P: `5 r
, D2 m: \& l3 s7 V1 Y' ]: i2 v$ a/ K" e1 D# `( X

  }8 [) a& l5 h' @& g; y3 g
b69a199f1b4a12d880e64c7ba134070c.png
赞 收藏 评论0 发布时间:2021-12-19 16:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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