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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 16:00
80.1 初学者重要提示
4 v' [5 p; X3 O4 [  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
; n  B- D+ _& h2 M: n80.2 MDK下载算法基础知识
$ _4 a& }6 F- @2 N' @Flash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。$ ~9 o. O/ ^( `

0 s$ O" K7 g* x$ B" I( @! H80.2.1 程序能够通过下载算法下载到芯片的核心思想
8 {7 a7 O1 ^. c* f# B认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。% c0 J3 ]& z: E% {" e3 ~

/ t  H' ^" F& X( h9 O2 a80.2.2 算法程序中擦除操作执行流程
1 z9 @) E' G: Q6 N0 x" w擦除操作大致流程:9 N" W' o; V1 A0 C; W

! U! j+ E5 m% c. F- q
d4b4af6b23f5f1d5ee3f3387ccb8fbdc.png
7 }7 m) k" P" V* h/ F* V

$ z# X* C+ [/ w4 Z/ o  加载算法到芯片RAM。
; n" n6 ~* }5 ~( x- D+ R' r* g  执行初始化函数Init。; T5 J# c4 _" F$ ~0 e
  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。$ _( c2 M7 `. H4 \2 R
  执行Uinit函数。
1 R( T" r; e1 K+ T6 |% J* U  操作完毕。
# N, ^. U: o* V7 e& S( Y2 C" g. w' w9 ?2 I7 x
80.2.3 算法程序中编程操作执行流程- f, Z1 M7 R+ }. v/ G6 t: J
编程操作大致流程:
% g- y( z: F9 H8 a- T: q' ?7 ~
8 v3 M4 g7 i$ o" E% f. \
d3f601364bb033d42e275ad686c03726.png

/ |& |! [" w% O+ C4 P) D. p0 z9 E! y
  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。5 p! W4 i0 F9 g$ z, k5 U* E+ T
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:5 p' R8 l2 a+ l/ \* u8 Y- q
  加载算法到RAM。
: j) S% Z" t% T! I+ ^  执行Init函数。
( f2 ]( }- a: Z2 z) y6 P/ E  m/ n4 o  加载用户到RAM缓冲。' z7 K" B0 G0 |
  执行Program Page页编程函数。$ w$ l7 b3 R7 ~) k
  执行Uninit函数。; S" f6 A' T$ |. E
  操作完毕。. @! Z; z, A* q  d6 a

9 r8 T! s4 b3 L80.2.4 算法程序中校验操作执行流程
' E* I7 g8 x! l3 r/ T9 W: N校验操作大致流程:8 g3 R; H! q6 Z6 ~, l
9 F8 ?. ~4 ^! w1 m) |
c7b2f373d946bc7d166ab1c1ba9b67e4.png
3 P. ]4 A/ x$ V2 G/ B) e
- y& Q! f7 ]# ]
  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。
9 z  r/ _1 N" K) S  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
% b' c/ V) {3 K" W3 @# t# a5 {  加载算法到RAM。. I& G' ]: ^* C1 {) z7 D+ P
  执行Init函数。/ A6 h! ?$ h. b2 }/ F7 G( f" {
  查看校验算法是否存在; X6 y5 v4 K6 [8 C" D
  如果有,加载应用程序到RAM并执行校验。
+ L4 t) f7 X7 ~& W& H- Q5 i! s  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。, l6 v6 r3 K0 c9 B
  执行Uninit函数。
' V' N7 @& b$ z- i4 _: h  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
$ ?& Z* }- C; B3 e3 g6 J  执行RecoverySupportStop,恢复支持停止。
( i( `( C/ L2 r; p, `  执行DebugCoreStop,调试内核停止。
. i. }6 M. E! i6 G7 G" U8 o; I  运行应用:+ p; W" Z9 X8 _  i4 J( Y
  执行失败。2 r' [; O, g, B  \7 [
  执行成功,再执行硬件复位。5 i+ M) G; u4 N* {
  操作完毕,停止调试端口。6 p3 o) a! z. E. s
1 K+ C) q( z7 x+ r/ a
80.3 创建MDK下载算法通用流程. x% N0 ?+ f7 t8 Q% D
下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
+ B* a$ m6 \2 C3 X" b% W5 _  X1 `* x3 |1 I1 u4 {6 I, o/ H$ Q7 E
80.3.1 第1步,使用MDK提供好的程序模板
2 f7 j" {5 X1 k) \  I5 h+ H位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。; w% d5 ]4 @5 C0 x  x* R

; }& M2 u/ R6 R1 a" k( a4 U2 W. b效果如下:  V  _# Z% o' r. [+ z
7 C; n4 L9 l. }8 m
8e08d948d34b38592e3699136725ade1.png
0 {3 ]( Y1 R0 P7 V6 L9 `

& \* B/ B3 P( R7 V; w80.3.2 第2步,修改工程名
' e( D! G3 S: [& q. \MDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。
2 s/ J' ^5 w5 q  o8 S, c6 d* K
2 z! G. p; e  ~! d# w) {2 f80.3.3 第3步,修改使用的器件
0 x1 p) w, i1 t$ i在MDK的Option选项里面设置使用的器件。: E) |) V- }# r; [4 [+ i4 W
9 [/ J0 C' x  X! E! ?
347d6def457f6e101f1c2555f9156cda.png

  P' R. D+ F( z9 c" v' z+ q9 p
/ C' r4 P" l+ y: c3 |8 S1 q80.3.4 第4步,修改输出算法文件的名字7 e" P, e  H( I1 b. e
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。
" n, Q; s9 D" P0 ~( J* {
6 s) \, j, ^. a1 d# S* F
07d754db49256bd0b73f4538a328f3cd.png

+ `: h6 o6 m6 g+ y4 @; D2 `1 a. y- Q* C; |4 A- J  P
注:MDK这里设置的名字与下面位置识别出来的算法名无关:0 N4 Z# \' t, F% ?
. K/ e; }, e1 B
a00b0ff99a0a691f19dfc2565fde811a.png
7 O7 P- r) o) U- J% q0 ~
. O; \# x( \( R$ n& ]. d
这个名字是在FlashDev.c里面定义的。9 T' j2 h5 F' l

" k3 f" W# g$ ~1 c) k80.3.5 第5步,修改编程算法文件FlashPrg.c
' B$ w/ o/ Z% D8 U( o* D模板工程里面仅提供了接口函数,内容需要用户自己填。$ z/ c1 n% m; E

! X( D4 p9 q1 v/ y3 o, S; W
  1. /*
    + a, a* N" |0 y2 Y7 V( M
  2.    Mandatory Flash Programming Functions (Called by FlashOS):6 ]0 ?. Y( L) p% F& |! \1 X  M
  3.                 int Init        (unsigned long adr,   // Initialize Flash' h$ a1 w4 X# D+ ?( q- D
  4.                                  unsigned long clk,6 V* l9 p5 M. J8 z( a/ X
  5.                                  unsigned long fnc);- i1 j, Q/ G+ I  B' J4 i8 F+ a
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash% B, a; |! i7 x3 S8 X& k$ ~
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function' ]$ D' [8 D4 Q$ r5 x& V! G
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function! K& i% A4 b! X8 H
  9.                                  unsigned long sz,
    # N/ y8 v6 Q7 x) P% \
  10.                                  unsigned char *buf);, A& G; P- L4 a, U
  11. 3 D4 b' [3 F1 ]* J3 E
  12.    Optional  Flash Programming Functions (Called by FlashOS):0 y1 \7 k/ b2 {) {8 U) Y* [
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check
    4 w: \/ C" T  i: }3 l
  14.                                  unsigned long sz,& d1 T1 V) k* c! M
  15.                                  unsigned char pat);
    ) H1 ~9 J- i+ s  K
  16.                 int EraseChip   (void);               // Erase complete Device. m' X1 _2 |0 T: N! B
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function
      _* W( Q9 r" W  y  n: S9 d& e
  18.                                  unsigned long sz,# y$ {% Z3 X7 P! T+ X0 T5 ^9 o+ k: k
  19.                                  unsigned char *buf);
    # _! @3 x2 I& V  j' r' P
  20. 5 j4 I! ^4 I; j
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space+ r0 [& M" m, u
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space
    ) r3 N, m( L! y5 w" {
  23.        - if EraseChip is not provided than EraseSector for all sectors is called
    + ]3 M( W% j$ a: i+ j
  24. */
    6 u2 X" e: o% l7 H! u
  25. 6 k- K! f( [& H  l  z
  26. /*
    & @- C* H- F0 {+ Z7 P7 n% R+ C/ \
  27. *  Initialize Flash Programming Functions$ {# O: z% t* U: K/ X" Y. L
  28. *    Parameter:      adr:  Device Base Address9 Y0 f' i/ D3 P5 {8 p  g. h* f
  29. *                    clk:  Clock Frequency (Hz)
    ; w3 a! a; Z0 M5 d; D
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    & W9 {: J4 Y! d' z2 K% Y. I
  31. *    Return Value:   0 - OK,  1 - Failed! N* X& P9 R5 q
  32. */( [8 [9 b, c0 T2 l5 ]

  33. 0 d4 m( _$ ~4 }) V, F, o
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
    , ~/ A+ D* W6 f

  35. * o' P! i1 N8 H% ?- q* E( W
  36.   /* Add your Code */
    ! D- M0 ?# _! I: I# s
  37.   return (0);                                  // Finished without Errors
    ; ?# r* _; @: U5 Q$ U1 W; `
  38. }
    ( M7 @6 c. W9 \4 n# ]) z
  39. . h$ h+ l5 ~8 X8 {5 V! u. Q

  40. 1 }4 K, @! v4 ~- E7 r9 m+ M
  41. /*$ o; Q" K2 z5 t1 O) S0 P8 u
  42. *  De-Initialize Flash Programming Functions! m0 `3 `" Z8 l/ Y( J! K7 J' e
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)1 e- c! E$ k$ q7 @4 M, x& C
  44. *    Return Value:   0 - OK,  1 - Failed
    2 J2 u0 Z* ~+ q2 j/ l( l. ^- C
  45. */; ~# S) I+ h2 s; d# s

  46.   i/ v! j, R& ]( z! O
  47. int UnInit (unsigned long fnc) {# \& y" J5 y; Q, U. `! T

  48. 3 G' |$ {. K$ |8 c+ j( e
  49.   /* Add your Code */1 x( _6 P7 Y! d
  50.   return (0);                                  // Finished without Errors
    # K2 \, l2 \, R
  51. }
    " W1 Q* L) |. @* |! G6 G
  52. 8 g7 x2 o2 B# h! q- J/ n- d

  53. , a/ s6 w6 K& s& x5 x& S2 @
  54. /*. a; ~2 t4 Y" d. h
  55. *  Erase complete Flash Memory9 B4 `6 n- r; |) n  F
  56. *    Return Value:   0 - OK,  1 - Failed2 [1 J# i* V1 E. ^
  57. */" h) {7 P5 B' S1 c7 z

  58. : C( ]! D* J' l" C# H9 D/ z( `
  59. int EraseChip (void) {
    & R0 C1 C/ C% N6 X7 {
  60. ! v, a3 ]' l% T) ?. d
  61.   /* Add your Code */
    - ?! g# W0 ~0 P; H
  62.   return (0);                                  // Finished without Errors0 n4 G' W% l/ ~7 }7 S( I
  63. }( R, X9 A& Z0 Y" j$ i
  64. 2 z$ A7 h( F$ b

  65. * `0 |2 [" r2 |" ]/ }0 y; u
  66. /*) f$ d5 d2 O/ K
  67. *  Erase Sector in Flash Memory
    4 a' o7 G( Q) t0 d' Q4 N( t- @/ J
  68. *    Parameter:      adr:  Sector Address, v: s) T. n2 M$ z" Z
  69. *    Return Value:   0 - OK,  1 - Failed1 r& s2 p! m  B* B
  70. */6 g) L4 i; r1 g4 Q  H

  71. 0 c$ s' D" I* T- T. Z6 l
  72. int EraseSector (unsigned long adr) {
    9 o& z2 X5 _# ?( c$ \" ~; ^: |
  73. 9 T! Q5 |2 q! H
  74.   /* Add your Code */
    ; J, v" E% u$ ?& n* \
  75.   return (0);                                  // Finished without Errors+ ]2 G! t- f7 @6 {
  76. }2 ]6 H, q$ M2 a/ n; ~/ S! y( g
  77. $ S, n: M5 F) K; S2 V

  78. + C, e) G! ~9 `# m3 k
  79. /*; F: B# B  V, r' L
  80. *  Program Page in Flash Memory
    1 W; t8 ^: K% x5 q1 v2 E  C" A
  81. *    Parameter:      adr:  Page Start Address. u  R6 d4 \+ t% Z* ?3 G9 t
  82. *                    sz:   Page Size" z2 |1 y/ j# t; v! K# S
  83. *                    buf:  Page Data, E! S% w8 r5 F5 r8 c
  84. *    Return Value:   0 - OK,  1 - Failed
    " a4 a, b5 m& f8 m4 g: N
  85. */
    # k: }" c7 n- W  l9 p5 _# [
  86. ) r. J. Z) f  d! Y. i
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {( b. w" u* v; d+ [* ~
  88. $ t: h+ a3 K0 T5 c- I8 y# t
  89.   /* Add your Code */
    6 a3 ]- ~! Q3 Y1 W
  90.   return (0);                                  // Finished without Errors
    1 f1 q9 e% i2 s/ f9 |& F# s
  91. }
复制代码

9 i/ J: h3 U' f, `+ v% m( E80.3.6 第6步,修改配置文件FlashDev.c
) ~  o8 ^5 ]" c9 m6 G. Q* d3 l模板工程里面提供简单的配置说明:
# L8 d4 v2 h) J- W9 Q
, i$ n& F3 l: z
  1. struct FlashDevice const FlashDevice  =  {8 t. K0 y; q0 E4 d( ]# ~3 G  A
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!; s- q, f  C4 v7 M) m( O" r: T
  3.    "New Device 256kB Flash",   // Device Name 5 T* n) x) C0 D# \9 l
  4.    ONCHIP,                     // Device Type
    " D4 n- E) b: S) X& n5 C4 n
  5.    0x00000000,                 // Device Start Address
    0 l: D' h4 k9 u# _5 N( x
  6.    0x00040000,                 // Device Size in Bytes (256kB)5 C6 L# N- Q" i% m# e1 v
  7.    1024,                       // Programming Page Size7 R( U( s! L/ X- z6 v* A
  8.    0,                          // Reserved, must be 0
    8 a; t$ O9 P) g: j- G: _* u
  9.    0xFF,                       // Initial Content of Erased Memory
    8 B( F6 A$ ?+ }4 B* C
  10.    100,                        // Program Page Timeout 100 mSec
    8 R9 J' w2 C2 X. `. B  n
  11.    3000,                       // Erase Sector Timeout 3000 mSec
    # d. K, h6 M# J

  12. 8 {4 r0 z+ a. z0 G( A4 H/ Z
  13. // Specify Size and Address of Sectors
    + z) D$ I1 c& x6 p9 r8 F: z
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors); N5 K0 c2 M" h& Q& j4 |" u8 k# g
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors) . W/ Z7 J7 c& R  M, Y) J7 p
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)1 a# N" T. d, V7 W
  17.    SECTOR_END
    + T8 Z. {% Z5 q# W. D( p$ F" ]
  18. };
复制代码

4 ~+ W1 k' X4 H  c& }- T; m4 F, q/ L注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。) H8 H+ V5 s+ w( r, A, ~; o
  x& h% H. B" Z/ u
80.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关
" v' v' C8 d$ o7 ]) C4 k5 |C和汇编的配置都勾选上:5 a5 u  k5 i* R% s) Z. }3 h
. D2 G1 U% y6 T
1d27a7314fc6c472937ad43852722b6b.png
1 c5 `4 n% a+ U  A0 I1 m' O  W

, `* j/ v- Q9 C+ F汇编:! a! f1 D: P% \# f1 z8 J3 f: [
4 h9 T7 F$ ?, y2 V* [. H; b, m
bbea604970c4ef63c77f10e553e28ea6.png

' E& d7 I* a( y, d- Z5 v4 |2 Q2 s( _6 _  `
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:/ F, j% u. e# k; \0 E4 T
8 W' g& O1 S& _$ f3 }" A, \! s' X+ O
(1)加载以响应运行事件。- A" w3 W" A2 h( s! X

: c9 Y* ~) V; x8 f0 n(2)在不同情况下使用其他例程的不同组合加载到内存中。! k% q2 t' o. n' O7 y
# h8 x$ Q# y0 A6 T( f
(3)在执行期间映射到不同的地址。8 ~8 z% [5 U* B0 Q' e! n; ?: q

# g# X5 D" _. A9 S5 ~  ?0 a使用Read-Write position independence同理,表示的可读可写数据段。2 L; Z7 f6 g: D: d5 w0 p3 G7 u

1 ?& r* r, ]4 w* _) F: Z& R/ J; J( l; V80.3.8 第8步,将程序可执行文件axf修改为flm格式

# x/ v+ G) Q2 w3 o9 w, d通过下面的命令就可以将生成的axf可执行文件修改为flm。
/ D! f9 i( e9 p  p; ~( K5 E
, q$ x. L& n, Z
6b504444979e9fa3a3477c6284abca8b.png

" T; z, ~) @6 [+ `0 K$ m) J3 i
$ t. }6 x) J  z7 B% P80.3.9 第9步,分散加载设置/ [6 d0 w5 E% m6 l6 R
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。" t* u! G' y& G/ n: B
. u" m6 q' }+ }- W! H8 P" s/ D
e8e79854b204b618bbda32af92f28bf2.png

3 `( \) E7 E; B) I7 W- j0 R! Y0 L  N
分散加载文件中的内容如下:* q; `6 J" c. L5 w) D  F/ ^" d  i

7 E# ?# b+ h* k4 T7 m3 `
  1. ; Linker Control File (scatter-loading)
    - E& S: K. z; ^( T  d
  2. ;' }" v. Q3 f! B# T

  3. ' ^4 P  K1 r0 o9 U) A
  4. PRG 0 PI               ; Programming Functions
    & y3 H( H( b, d0 e' d% ^: p
  5. {% i- {) M1 l; z0 i
  6.   PrgCode +0           ; Code
    9 g5 T  I* C" D
  7.   {
    * ^$ Q" A& b1 D( v
  8.     * (+RO)* [7 s% ^& v" o  b  L9 V
  9.   }
    " n5 f, ^! Q6 ?: C# D
  10.   PrgData +0           ; Data
    1 y) A$ E$ n( E* K: R: ?
  11.   {$ z% Z/ f% \; m: @' E
  12.     * (+RW,+ZI)& n. s0 K, }# V7 ^
  13.   }
    6 C3 _! A; d) p! R- N9 P9 J/ @
  14. }
    ; V$ B3 U- Z* F$ M0 K
  15. * o! e* ~# u  S  ^  s( i/ h
  16. DSCR +0                ; Device Description
    * P  @+ F4 w1 B3 I0 q
  17. {% C" `: I: R0 j0 i: y6 W5 b
  18.   DevDscr +05 y4 c" x3 J! `2 T  j# K0 X
  19.   {1 S5 U+ Y7 ?6 t; u& n/ ]7 U
  20.     FlashDev.o
    ( ~! w# R8 b4 P) K  T& E1 l" P5 p5 X, L
  21.   }
    3 K) y$ _, O; N  X/ K- q- T
  22. }
复制代码

1 [: W7 _4 o0 R$ [--diag_suppress L6305用于屏蔽L6503类型警告信息。
+ |% w, R3 D' _! |0 p* n8 _6 }) o' |+ n
特别注意,设置了分散加载后,此处的配置就不再起作用了:
. q% V# J% A/ k9 x% @: c" ~
  s2 c% |& }2 E6 c& |& [+ [
b249eb40e5562b04ac3eb05ec0219c1d.png
8 X- R* f, C5 y' x8 H, `  b& b
, j9 `1 N6 U4 l. x
80.4 QSPI Flash的MDK下载算法制作
# C2 Z3 i+ Z) K& X4 X) ^) L& }下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。0 g. y; x- X# G8 c7 `" M) s# q

; J$ K4 o9 P6 U& s/ _80.4.1 第1步,制作前重要提示
9 G9 g5 a+ o! f8 X这两点非常重要:6 n1 ~& n% M- W

- ^# I/ a( m: o0 ~4 ^  程序里面不要开启任何中断,全部查询方式。
7 ~; [; ]$ \) S7 r5 T( M  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。5 v+ w1 q2 `% r

% |/ U& v" U2 Z' p* d8 m$ j/ W80.4.2 第2步,准备一个工程模板
- B7 ]5 t$ R( W( J7 ]" U% ^ 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
, o5 r6 \) e/ q* V" Q1 g# A, V# L1 P+ s& x8 Z, Q
a91aaf84a25d730b77273624d2b502f6.png

3 e% x* }" r( c  @
, O9 e' C# H4 ^3 U80.4.3 第3步,修改HAL库
5 g% b* Y8 ~% x$ @$ z. |7 n这一步比较重要,主要修改了以下三个文件:
4 p' e: f; o* h- ]
# |5 l) h$ }& ^" [) V
4471afac6bbc201866d0d9a8ea7d876b.png
7 A9 H+ G7 z2 [; P9 ]1 s0 C
! i5 |7 }4 L; d1 Z) F
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
' Y2 Q* Z3 K& q- l0 Y- C5 ]
/ n* ?& Z) w3 I% }. P% k7 n80.4.4 第4步,时钟初始化
/ Z4 B* V" B9 V; t5 I+ D我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:  }4 S6 @3 q8 Y. r

5 f: Q, u' {! j3 v2 l
  1. /*
    . l! c! f% l) L$ ?" j
  2. *********************************************************************************************************
    / C( m% W1 I" o$ t  V$ D$ H
  3. *    函 数 名: HAL_InitTick
    4 w/ L8 X, v) H: P4 R
  4. *    功能说明: 重定向,不使用4 ?! j- ?& k1 ?+ q" K9 J
  5. *    形    参: TickPriority4 |1 b$ G3 m, s. N' U& u  |
  6. *    返 回 值: 无
    $ [7 W9 A7 X# n6 ^4 u% \. {3 i
  7. *********************************************************************************************************. `9 D1 L% H# w
  8. */
    0 u7 K8 V2 u) a3 N1 r2 \( W& }
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)5 J# @! r6 ?& G' G
  10. {) W3 F! |$ V# }4 P3 b
  11.     return HAL_OK;
    $ ^' Z7 E9 G# g7 }! F/ Y
  12. }
复制代码
2 Y# x) v% c. d
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:/ @9 C; Q; N5 G3 h2 A1 t# j

6 z8 S5 z& D! W/ ^
  1. #if !defined  (HSE_VALUE)
    ( u: P1 k) J# D+ |
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */( M4 B/ j: Z* ?& z; l7 w
  3. #endif /* HSE_VALUE */
复制代码
& A2 r+ e2 O) u) K) V- H$ k+ }# b1 S
最后修改PLL:
! F1 J$ U% T) _) W( u1 x4 K8 i& G3 z9 N, H/ ~$ i
  1. /*' B/ V/ u: T7 ~3 C7 n
  2. *********************************************************************************************************
    ( [! y4 [$ S$ F1 E7 F! V) W
  3. *    函 数 名: SystemClock_Config* h' c$ B% O* J. l/ C- p
  4. *    功能说明: 初始化系统时钟/ S; z' \, u$ L  D
  5. *                System Clock source            = PLL (HSE)0 [; G, a, }2 L' b1 y0 O. Q% x
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)8 z& _. }" |* G4 u! z4 m
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)' a' K' d* b7 k
  8. *                AHB Prescaler                  = 2
    - \: ?! Z' u% b/ S
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)  J" D# G# u" f! H
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)7 G9 t/ l; r! c7 R- B: P' N% e
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz): Z/ ~% ?4 q( F8 p5 [+ q* L9 ?
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
      U% t+ ]0 u" @$ `% g
  13. *                HSE Frequency(Hz)              = 25000000
    8 n! M5 ^4 q2 A6 `$ P
  14. *               PLL_M                          = 5
    , N9 @7 H8 s# K' I& F6 [' X0 U
  15. *                PLL_N                          = 160: i3 h9 f8 o& k" b
  16. *                PLL_P                          = 2
    1 H; z" P& E) q+ h
  17. *                PLL_Q                          = 4. D$ m+ E7 W: J# [
  18. *                PLL_R                          = 23 Z; o; G  S0 ?7 T, O
  19. *                VDD(V)                         = 3.3) d& J: q! u" \7 i, ^" @( l! @
  20. *                Flash Latency(WS)              = 4" P+ W4 b* U) R' v) P
  21. *    形    参: 无! y) u0 U7 y8 r8 K
  22. *    返 回 值: 1 表示失败,0 表示成功9 o6 ]/ S' Z/ x% N5 B7 I  z
  23. *********************************************************************************************************
    : e4 H' w2 b: a5 n  v
  24. */
    4 `/ A% C/ x; @/ k; X
  25. int SystemClock_Config(void)5 I0 R5 z& c" M% \
  26. {# R0 \% [( }. I/ l
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    9 ?+ j/ L6 g: D2 c
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    2 ?2 o( w! I7 q+ u9 R
  29.     HAL_StatusTypeDef ret = HAL_OK;
    ( X/ q. V$ U1 d& C( w$ o# x; d4 N

  30. 1 E/ g9 ?: F/ y
  31.     /* 锁住SCU(Supply configuration update) */
    ' i; |. i+ h( }- G5 Z) {
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);( s$ o" F* R3 o7 L. c& H

  33. : h' W" I& F1 w; r8 w3 d
  34.     /*
    3 W! v6 f* @1 [7 Z" U7 [0 r
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    3 F+ C8 H$ K# ]' k8 f3 H9 i3 \
  36.          详情看参考手册的Table 12的表格。
    $ B2 ~$ P% v' H2 c  T9 n. o
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。7 l" p/ E8 F7 T  H
  38.     */
    , B4 B% }( a6 F# D. q$ N1 x
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);: v; I) l7 I4 g, r1 |
  40. % b6 c% g. X! N% z
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    / n2 s" D1 l- j. ]

  42. 0 u$ U- B6 Q$ u5 N+ ^; B( `$ A
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    " e+ N, P5 B4 S) T6 a  B; E
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    0 `+ @. b) ]% b' o4 v3 I
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    2 M$ ^' `7 G2 P, l; q8 r
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;/ z2 S& r: H# i, }! C  s
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    1 K: c; p$ s. g0 \: w5 T
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    9 ~6 P! F4 f& h7 y3 B
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;' v0 j9 {' H" J" y
  50. 0 ~3 }8 R; h( L; |9 Q; ]
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    1 m  \/ Z" I0 k, C/ |4 D
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    / C5 z) s' J) r
  53.     RCC_OscInitStruct.PLL.PLLP = 2;8 W" Z! E2 a2 J7 c" d
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    , k& L3 U: J7 v1 o4 D/ T. L1 z6 c
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    ( g' {3 g* `  a/ I  R
  56. & S+ ~* K4 ^7 N/ N% Z) X" k
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;# N6 {# T. a: |" ~) }* o0 R3 A
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    6 i0 @; }2 e, l9 _; B4 C
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    * q9 {9 _( u& T" ~' [4 f9 `/ X. n
  60.     if(ret != HAL_OK)
    1 b: B3 e4 a5 a6 k2 R# L; q, p( D5 i
  61.     {
    5 c. D$ s0 b; }  \: c
  62.         return 1;        
    # w1 e8 O2 p3 E2 d5 M& ~
  63.     }
    3 c. n# f6 x" W, C
  64. & ?8 n( X# Q! L
  65.     /* 3 Y/ _# |, ^+ @5 c- v  Z5 e6 J
  66.        选择PLL的输出作为系统时钟8 d; @' s, ^' W+ O
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟9 p( |) g+ x' U5 n% M; z
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线1 w' y1 A; @' }) ?
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线! g: L4 ^* o& V
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线2 h* G8 M9 U" }5 |& l1 `
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线3 H9 o0 a. \# I6 X5 A
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     # x: V3 }4 C* a+ E
  73.     */
    ' n' f; a6 B$ k8 n/ h( \
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    ! u' b: b% f& [2 K5 d' r2 I
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    4 r; H+ f/ @' s. ~2 a0 r

  76. 3 W5 j2 m0 {. E# y0 ]( ~8 H' l9 Z
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    . i: \& |# p( w  Z
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    4 Y8 }! w3 b: d/ i6 A  ]4 ~
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;( v5 z4 m& ~1 I0 J" H
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    / R) Y1 T% `/ H/ Z5 ]5 B, K. A
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    9 O  k2 ?5 j9 _" g) F" Y& L! \
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    ) ?: s- b* {: O' F
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; . \( C0 c* M3 t+ m; w+ V

  84. # |8 [9 O7 s; R& M6 H, C
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    0 m$ l7 A: P+ A
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    1 x. X. T& j- ?' y) [7 C$ Y
  87.     if(ret != HAL_OK)4 |& n+ C# W4 `+ `, R
  88.     {
    8 i$ ]' Y0 N( ~: S# S
  89.         return 1;$ A1 |) p# X4 E3 ~, G" ]
  90.     }; M# y# B( D+ w3 z0 l- J
  91. # ?# F. }9 z* J6 @- ?1 y
  92.     /*
    , {1 u* }3 I* j4 E. v7 P  m
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    : M2 t4 f7 |- f- K% ]
  94.       (1)使能CSI clock$ D( F1 [9 N" K- m# R
  95.       (2)使能SYSCFG clock
    ' e9 [6 z8 m8 J5 A, ~9 L- A
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit05 @; A/ @' D; y  b* m
  97.     */0 D( L3 l6 n# d# W
  98.     __HAL_RCC_CSI_ENABLE() ;
    8 h3 \' d' b3 ?# H* f
  99. . ]6 s1 D, [( s$ m
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;6 x. K1 k- ?7 I( m! Y4 K; _

  101. * w$ d, p1 {: p' Q. I+ w
  102.     HAL_EnableCompensationCell();" {" t5 A; r- M! z' \* _2 l

  103. : z5 x# R: F$ b+ w
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();) H  n! p% P. T7 p: ~: i
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    9 f& p8 Y7 g$ s$ c' U
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    4 K& W* `* n2 ?, [6 H( Z

  107.   x: G7 T/ s6 _/ B
  108.     return 0;. a0 J% a' h& d1 k  \- k
  109. }
复制代码

! I* P1 Q0 }1 J+ M! x, s( \80.4.5 第5步,配置文件FlashDev.c的实现
5 x. k, x/ l0 Y配置如下:
3 d  b& ^9 @4 U5 Y% }  ^7 I5 s% |5 ]5 t7 h) _9 b
  1. struct FlashDevice const FlashDevice  =  {# R3 }. g* }( Q4 O
  2.     FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */1 X% O2 L+ `5 ^$ e$ S
  3.     "ARMFLY_STM32H7x_QSPI_W25Q256",   /* 算法名,添加算法到MDK安装目录会显示此名字 */+ Q# \) L- B9 _8 K1 o6 j+ n
  4.     EXTSPI,                           /* 设备类型 */& O7 H* p9 |+ t2 J. k3 B4 h, e' [
  5.     0x90000000,                       /* Flash起始地址 */
    : k* i) X+ S: Q0 ]# d5 V+ B  i
  6.     32 * 1024 * 1024,                 /* Flash大小,32MB *// B4 r* E0 j3 L( z+ ^0 j! c
  7.     4 * 1024,                         /* 编程页大小 */
    / n/ U% C) _3 V( n4 \' W
  8.     0,                                /* 保留,必须为0 */* f" G! f, K0 U$ [$ {% @9 X! G
  9.     0xFF,                             /* 擦除后的数值 */
    1 A" f' l- h2 v$ P
  10.     1000,                             /* 页编程等待时间 */3 U5 M8 L7 ~3 t9 ~" y
  11.     6000,                             /* 扇区擦除等待时间 */
    $ v, x  Z8 }) R6 ^9 o
  12.     64 * 1024, 0x000000,              /* 扇区大小,扇区地址 */
    - h8 v( T8 x2 u. Y3 Q: ]" j
  13.     SECTOR_END    ! X* X/ }" x, b+ H* Q" B- q
  14. };
复制代码

; p9 i9 b/ Y  H% d注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:
- M9 `& H4 ]1 D) R9 s' M  R! ?- h9 Z! B9 E0 y& M
ffed7a581be4c5da388ee29fb67249b4.png
* w8 j& `+ }! E3 n, O1 A
4 L$ r6 N6 o2 e! r: a0 |
80.4.6 第6步,编程文件FlashPrg.c的实现
  M2 r1 s. k' F: s/ P  q+ ?$ u下面将文件中实现的几个函数为大家做个说明:
) [/ ?4 `: `/ }5 `* M8 \; `
3 }/ S1 o9 t- d, \4 j+ o. j  初始化函数Init

) C* h/ G/ p/ O# R) z. u
  1. /*
    0 r8 Q$ P& a* u
  2. *********************************************************************************************************) `. o% r7 S3 b$ t( j0 l& m4 Y4 e
  3. *    函 数 名: Init: c" f5 K; c" X4 ~0 M  b
  4. *    功能说明: Flash编程初始化2 ]7 |; [0 |+ v# `
  5. *    形    参: adr Flash基地址,芯片首地址。; J1 a4 G5 z' \7 h3 e; s
  6. *             clk 时钟频率  u3 e  {* n/ {; _- `7 ~/ V
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify  @& T2 w+ x/ ^9 |0 X4 b$ Q* S
  8. *    返 回 值: 0 表示成功, 1表示失败% {# ?- V2 ^& @. m7 e3 \( V% @
  9. *********************************************************************************************************% |: r3 P- w) V7 i4 z" n
  10. */' s$ Q+ E4 e" g( S- E0 H
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) + Z" ?7 r8 L- K2 Z2 o4 o
  12. {
    5 n4 e, Y7 V6 `6 z, C
  13.     int result = 0;4 K. T& B  L1 R. X

  14. 3 I, a4 d* g% q4 j- ], g( {* v( `
  15.     /* 系统初始化 */4 E# x) x% F. m) Q$ F1 k+ I' O( c
  16.     SystemInit();
      Z; q8 U6 H/ F1 R
  17. 9 C8 C1 k8 }( r  X6 Z( Z9 i
  18.     /* 时钟初始化 */. I/ ?2 }2 |; X( Z9 a
  19.     result = SystemClock_Config();/ m* n/ ]' ^9 U, \. r+ }9 x  |
  20.     if (result  != 0)5 M. N, }) P+ H# }/ U1 q( B+ {
  21.     {
    + |: M7 u  y, j6 n5 g6 M' u: I
  22.         return 1;        
    ! {/ L$ M: }( R- v5 @( N# U
  23.     }
    2 {* |, N7 l$ z" i- w

  24. / X* p2 U, s9 [9 O
  25.     /* W25Q256初始化 *// l, ~6 \- k8 x2 `* |
  26.     result = bsp_InitQSPI_W25Q256();0 @- D5 q1 I2 c& `$ f1 \: Z9 b
  27.     if (result != 0)" H% n0 A! Z; L2 K- |% x
  28.     {9 _' b2 X. W* \2 W* s7 w
  29.         return 1;0 [6 C" A4 ?+ r/ M" S
  30.     }
    : y# j4 o7 i0 ~; j, U

  31. ' Y* j7 K  B0 M8 V1 Q" h' y1 w* p
  32.     /* 内存映射 */    - P" ?  ?& p9 v/ Q" Z3 t9 e; j
  33.     result = QSPI_MemoryMapped();
    2 q3 k$ X& U) w' ]; l1 c- l) j+ a, I
  34.     if (result != 0)! Q7 X) q- F9 I' ~, L9 d) V1 X& j
  35.     {
    ( y7 u2 z2 R: i: t4 [
  36.         return 1;
    9 |- h+ p1 o0 \
  37.     }
    , D  A% i* ^- H  Y, _' a7 S
  38. " ]2 I: S0 |7 c! h$ d( p3 a
  39.     return 0;
    6 [9 R3 L- N, J* D7 {
  40. }
复制代码

! `; H" H4 ]( c( V3 R初始化完毕后将其设置为内存映射模式。% O7 V# k/ J( `5 C. J

9 h3 j' p1 t& B' X- g  复位初始化函数Uinit
! R7 G& N# E0 H# p  ]擦除,编程和校验函数后都会调用此函数。
, b0 \7 \+ J; A  i* L5 o* v" J5 ~7 g' h2 u- y
  1. /*2 I6 d: r) r# h, ~0 X
  2. *********************************************************************************************************
      |2 ~# [. F' z1 v' [
  3. *    函 数 名: UnInit
    ! k& p7 B& l1 H; L% C3 ^+ E, m3 \
  4. *    功能说明: 复位初始化
    6 @" e, t$ R* X4 d7 Z3 u# E
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    1 f" ]. O" Q- o& ^$ V) [" c6 m
  6. *    返 回 值: 0 表示成功, 1表示失败: L% b* H1 |6 ?$ F% \/ J
  7. *********************************************************************************************************8 {% `* d1 g  N3 D0 J4 T
  8. */
    # D* r! w$ l+ C: E
  9. int UnInit (unsigned long fnc) - g& A; g/ B0 `8 |" N3 N2 Y/ u
  10. {
    . j: Q: B$ Q; q7 F* n
  11.     int result = 0;8 t% V2 y: i+ l2 R

  12. ' B/ ]6 j# {, g8 M+ C" l* a
  13.     /* W25Q256初始化 */
    5 v2 E7 u4 B. Z8 B! J3 Q7 T  m1 ^
  14.     result = bsp_InitQSPI_W25Q256();
    8 a' K  C. }4 T# |) T& L  V! s
  15.     if (result != 0)2 B6 s/ x" _* E
  16.     {
    ! k+ K3 K5 G* h+ b/ N
  17.         return 1;& s- V, |4 h5 ~- U
  18.     }5 d$ p- B- u: k9 Y0 x: }8 ^& o

  19. % u1 O# m7 t* Q9 p- D
  20.     /* 内存映射 */    ) {# {' \, k' s) R% i
  21.     result = QSPI_MemoryMapped(); 4 y" P. T1 A  N% Q
  22.     if (result != 0)0 j5 B. J& i, t6 I: {1 y7 `
  23.     {
    1 X$ ]) J2 E( T$ q, c
  24.         return 1;
    4 W5 M7 h" o1 H8 A2 `1 O& O) f
  25.     }4 E2 ^2 B5 B1 |( s- J" Q% G, n

  26. / x' ~8 g# k) S+ ^
  27.     return (0);: `5 V) N9 R; U( q# q  E, C6 z* b6 P
  28. }
复制代码

2 c1 _1 `) p: Q3 L: j  ]复位初始化这里,直接将其设置为内存映射模式。3 G. u" T- h0 x# \" H/ ^
; V4 f+ D! J: O! f# e  }! O
  整个芯片擦除函数EraseChip
5 x6 T) A6 t1 |$ w1 K* ]如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:  ]2 e  ^; q9 @

! P4 J& c+ z: |# Y
f623e662bf09233ea594f820f4c7952a.png

( G5 U  Z( w# ?7 ^; p/ P1 p- L2 m. k5 X& m+ o+ p& R
实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间,比如32MB QSPI Flash整个芯片擦除需要300秒左右。
2 i0 P4 t6 O7 E8 B% L( ~3 B" F  L6 i4 U
另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。
! d& Z& x1 |' F; y1 w  X( h& K! S5 H
  1. /*
    ( K% O8 [9 g9 X; f
  2. *********************************************************************************************************
    $ Z3 n2 n9 c/ @
  3. *    函 数 名: UnInit
    7 w5 n3 P- S2 \" ~# g' V
  4. *    功能说明: 复位初始化0 r8 J: v8 U7 p7 U! w2 O
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    ; n1 N; \: y; X9 _9 \
  6. *    返 回 值: 0 表示成功, 1表示失败
    : v; W% Z4 _# c% y3 v0 ?5 t
  7. *********************************************************************************************************
    ' X9 H( ?7 n1 ?4 k  R
  8. */, r! K) ^' B& s: k5 C
  9. int UnInit (unsigned long fnc)
    * w* T$ U2 ~& V( e2 a4 ~5 M1 n
  10. { 8 {( v3 B# Y, k  Q/ f
  11.     int result = 0;# A; g9 @6 O3 P( g% O

  12. ; H8 X$ `( Z' d2 s1 Q2 f$ a3 K
  13.     /* W25Q256初始化 */8 ~$ r4 U2 `2 t6 i
  14.     result = bsp_InitQSPI_W25Q256();
    8 f# F! s# h/ m3 n
  15.     if (result != 0)& u% A* a# j" S6 ~8 d
  16.     {
    0 z( T8 P" @! \* Z8 N! @( Y8 W2 o
  17.         return 1;
    : z7 X8 b% {% H2 q$ m3 \
  18.     }
    ) N0 t: p3 c% H1 j

  19. 4 U7 b; X% @* _( a; I$ r
  20.     /* 内存映射 */   
    4 l: X  _, M8 F# |4 q2 s* e* q  j
  21.     result = QSPI_MemoryMapped();
    . h. m9 w1 ~+ [, A
  22.     if (result != 0)
    / a0 g# k$ Q+ n
  23.     {
    ' c  @7 |$ }6 \5 C% b
  24.         return 1;  I( [* J0 [9 `6 B! {
  25.     }
    ! K  i9 z  E  g% N/ y" X1 L
  26. 8 a. r$ C% ~" m1 h! \* l
  27.     return (0);
    : L& ^+ E0 R" e0 L/ |0 a
  28. }
复制代码

  E- i' H# N7 Q% ]* a: R6 k. U# K  扇区擦除函数EraseSector
# r4 y& ]! ~5 A4 D3 h; C& [如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:
$ y2 w* w. i6 _4 Z1 z2 B; Y) Y1 d( r7 q9 z  ^
733d5af26119f64bff0261515013d3a6.png

" Q2 w4 e; @2 `' ]+ A8 [+ Y) P2 `9 [$ l
  1. /*. p( _9 }3 `0 ]+ S
  2. *********************************************************************************************************
    ; o! d. R2 {2 Z0 g6 ~: O% C
  3. *    函 数 名: EraseSector' C" T: L; G7 N
  4. *    功能说明: 扇区擦除
    ' Q$ H& z0 c7 x+ n/ g
  5. *    形    参: adr 擦除地址: @7 @; Z2 y1 A. {: ]# G
  6. *    返 回 值: 无
    , z! T6 y& {  L" {0 Z& v3 `
  7. *********************************************************************************************************0 R) t' b0 T$ R5 p
  8. */5 [' }7 Z8 C7 s2 Y: [$ [6 p5 X: j9 ~+ O
  9. int EraseSector (unsigned long adr) 5 N! C, j# H. {% c$ I8 i
  10. {   
    : {7 i5 L, V. G0 u2 }
  11.     int result = 0;7 {# @; h2 X) d, ?* E, z2 {* g
  12. + R1 a* W; b$ \3 |( F2 W
  13.     /* 地址要在操作的芯片范围内 */
    . n# ]4 j" V* E5 l3 ]9 }, R* Z
  14.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)9 ^7 t, K" y  k/ H5 [5 h  G
  15.     {7 g2 v4 k. g/ w4 j
  16.         return 1;% Y/ u0 ~7 n0 g6 \0 }! ~7 Y
  17.     }
    * n* n. t3 s$ I6 n' n4 x
  18. % s0 v& L, a/ b# u
  19.     adr -= QSPI_FLASH_MEM_ADDR;5 g6 P' R$ P7 C) x( R& P' `
  20. - t5 E) j8 Z2 o* I. z
  21.     /* W25Q256初始化 */4 _& D) p& e0 k+ f6 O
  22.     result = bsp_InitQSPI_W25Q256();
    * L* }! r9 q# x. s! T
  23.     if (result != 0), B, D0 `1 G$ C4 [4 z
  24.     {
    : Q" p  H$ M1 x- g
  25.         return 1;- t" _' J- d# g
  26.     }
    & o6 Y- g+ e; u

  27. 9 n* M! l, w. b
  28.     /* 扇区擦除 */0 ?6 t- B# e6 U0 @- L
  29.     result = QSPI_EraseSector(adr);  
    2 j% @% j: R3 E' N+ G
  30.     if (result != 0)$ U" u5 y# g! b% s
  31.     {
    0 M( |  Q/ ~3 O1 `
  32.         return 1;
    # c( a+ j- p& ^3 _. q
  33.     }   
    2 y; N$ L" x3 B# f0 x& E
  34. . L  G2 m- P9 S; i8 y! Y
  35.     /* 内存映射 */    2 }. b8 q/ C% X1 G$ F
  36.     result = QSPI_MemoryMapped(); * ?1 D2 T& ]9 T4 Z6 v) |
  37.     if (result != 0)
    ' j8 n+ {8 k7 C- f% q$ w
  38.     {, U+ d% e2 Y6 y; @3 [
  39.         return 1;8 E8 b0 y! w; b. f6 u) N- }: ?
  40.     }8 v* [6 v2 i% _" R; C& D
  41. ) q' @) B& l7 N( \1 @  O% E; G: I
  42.     return 0;   : m3 H$ E' F$ e5 g& n
  43. }
复制代码
! D: j7 F+ U: G, y
这里要注意两点:) I% Z. N/ O' B7 B

- _5 ~* }3 ?* J" {+ `' w; \1 a(1)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。1 A2 e% l  H9 `8 [) Q
4 q  M% `- i; G3 `1 X$ k
(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。& p$ K; e" h5 O% y$ ]% l$ O% a

2 ]$ u5 d, d+ N* b! l& g  页编程函数ProgramPage
3 P1 e9 c6 }( Q# w. W; Y& b8 m
页编程函数实现如下:
0 ?% ~/ w# N. ^$ q* _1 a7 X8 c- m( v/ j$ W
  1. /*
    , }, C0 [, h3 w$ v9 }
  2. *********************************************************************************************************5 e5 \) z/ N% T7 I
  3. *    函 数 名: ProgramPage# `: k7 j+ \5 Y8 d6 y0 s1 n. v
  4. *    功能说明: 页编程
    ; m; w  o3 o, Z, [4 k2 M5 }. D! \
  5. *    形    参: adr 页起始地址4 C& L$ N! }6 u% ]
  6. *             sz  页大小
    : q) @: ]; H1 r) Y
  7. *             buf 要写入的数据地址
    7 n: L9 P0 J: t/ Z2 [5 H! U. T4 k
  8. *    返 回 值: 无+ |; }; j; U% Y& u  j( V1 e5 N# z
  9. *********************************************************************************************************1 [+ h9 `! F2 d& d, b. U! i' v
  10. */0 E7 b/ j6 j0 Q+ K- B
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
    ! b1 ]) \# X9 s7 _7 g
  12. {6 |6 _4 q4 r3 H: y$ d
  13.     int size;
    . V3 B% d8 G# ~6 [& J2 c
  14.     int result = 0;3 C/ e) \( o# w$ L: y
  15. ' |4 _8 v$ O2 [9 x9 t$ p8 I
  16.     /* 地址要在操作的芯片范围内 */    . L0 X$ S# C5 n
  17.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    8 h4 |* W5 o% Q9 @7 _0 \8 K
  18.     {
    . v& D+ `5 l- {6 O: ]" ]
  19.         return 1;8 K7 X! Q( y9 C' q' H
  20.     }
    7 d/ i" K/ b. |
  21. 9 z' H, d/ z9 K6 i, y+ Z+ q
  22.     /* W25Q256初始化 */: W6 y& T- o  P' l8 Y* o4 ]
  23.     result = bsp_InitQSPI_W25Q256();9 p% @- w) ?* F& w
  24.     if (result != 0)0 h6 V* U/ h* {, E3 v1 r! n& X$ L3 c
  25.     {
    " _  d. e- `+ p+ v6 X/ w" X
  26.         return 1;
    5 G% [! |8 @: L& o/ C
  27.     }5 l. y2 p" }  d+ j" ^# W, Q+ R) g; G
  28. ' g) {# U% B0 {: \1 X1 A) f# m
  29.     adr -= QSPI_FLASH_MEM_ADDR;3 f4 k8 Y  t/ c! s2 x" G0 `
  30.     size =  sz;7 r1 |7 R& T) ]. c. B* Z( M* c

  31. " h2 `9 _7 G% W9 o. K$ Z# ?
  32.     /* 页编程 */7 ~9 y+ ?$ ~( V# A  U- }% Q
  33.     while(size > 0)2 z5 w' f' X, G3 J3 Y  }
  34.     {2 B9 D" j( \- J7 S
  35.         if (QSPI_WriteBuffer(buf, adr, 256) == 1)
    ) h3 |7 u# w" |3 Z. v
  36.         {
    5 @$ W, g' J+ a5 g2 d2 o: ^
  37.             QSPI_MemoryMapped();
    # S- p/ x, D+ v& h/ r0 y

  38. . [5 M6 h% |9 [6 M( L" f
  39.             return 1;   
    % J( t; `; V( ^( @
  40.         }
    % c6 ~6 R- d9 K% E1 t9 e
  41.         size -= 256;8 m1 g! b- r/ t# y5 [
  42.         adr += 256;; p0 e! i- j' I8 d: K
  43.         buf += 256;
    7 j: c+ `! H- y" g& }
  44.     }
    ( j5 j9 F6 H4 I  }
  45. 3 L) L) s! b+ T/ V. T( n
  46.     /* 内存映射 */   
    8 i6 D8 Z% z7 S- e( P% ]2 K
  47.     result = QSPI_MemoryMapped();
    ( j% \( |& g3 h% Z6 @: C
  48.     if (result != 0)
    9 {, ?* [& z1 P! E2 A5 R% y
  49.     {4 m2 x" q' f% ^6 k+ t
  50.         return 1;: n. D4 P- n! H! n
  51.     }$ r6 {5 H) k" z. x$ ], J

  52. 3 e, x$ M* p% T- n7 {' Z( l
  53.     return (0);                      2 {1 |$ n* w1 l
  54. }
复制代码
4 D# e  m& `' G: ^+ a3 M/ V
这里注意两点:- ^3 }& ~! d" G' r4 }& x8 p" N7 R3 p

5 J$ V5 l4 M4 |9 O7 M# Y2 V2 N; a3 u(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。# m9 H3 n6 f2 K. M8 s, Q
8 B% t8 a( W/ c. W; M( j
(2)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
2 c3 `% P1 Q, X) ^5 V/ Z
" l  S$ I9 j0 j8 D2 Z. ^- R  读取和校验函数, F" x/ C9 [) Q% U) P7 X! u
我们程序中未做读取和校验函数。, `0 I1 i. l6 s9 O' s5 B
2 f# i% k! J( Q( R# G
(1)     如果程序中未做读取函数,那么MDK会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
" M: ?8 o5 N/ l/ X
) R0 Q4 q9 J/ o& o. ^6 {+ u(2)     如果程序中未做校验函数,那么MDK会读取数据做CRC校验。
3 s% r9 P# K/ H* n8 N
' ~* q* \$ k4 ]) N6 {6 n! x80.4.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)

: ]* j# {/ v; ^! U' F4 Y# }* H最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):' Y* G( ^; W3 |/ {
) G  V6 D; f( `2 F9 }9 o
  1. /*
    ; ^# M8 Q! o" H( K
  2.     STM32-V7开发板接线
    3 g  H& k& D1 |# W3 o4 a) T
  3. " L: O0 u4 e( u! p2 ], i8 D9 d
  4.     PG6/QUADSPI_BK1_NCS     AF10! n0 `- P# |4 ]/ B$ i, J" E' x' X' Y
  5.     PF10/QUADSPI_CLK        AF9: P$ I( L. e: n! \. y( w- P1 w
  6.     PF8/QUADSPI_BK1_IO0     AF10
    & \  }* n# l3 f
  7.     PF9/QUADSPI_BK1_IO1     AF10! y1 Y1 g1 h* w7 l
  8.     PF7/QUADSPI_BK1_IO2     AF97 a! H* ^+ c2 ^; G2 u# n) M6 [
  9.     PF6/QUADSPI_BK1_IO3     AF9
    5 p7 o6 e- @2 i( j9 X

  10. 0 W9 U/ T1 F+ o! [/ W7 r* L
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB8 X* u: x( C8 }
  12. 7 c7 a" l$ k9 X
  13.     H7-TOOL开发板接线5 a% i4 s0 }  h3 E
  14. " @& f- U$ q) h- s& T8 F- u
  15.     PG6/QUADSPI_BK1_NCS     AF10) ?* V# x5 q% F/ d3 G6 [; I
  16.     PB2/QUADSPI_CLK         AF9
    9 b' w5 E9 k# M) m
  17.     PD11/QUADSPI_BK1_IO0    AF10
    + q- C6 l" B& A2 `- v
  18.     PD12/QUADSPI_BK1_IO1    AF10
      ?" f( E. P2 J0 m$ W7 N- `! Z0 i
  19.     PF7/QUADSPI_BK1_IO2     AF96 M5 G! A$ e- u* [1 p
  20.     PD13/QUADSPI_BK1_IO3    AF95 f7 f0 [. [  P) f" p( E
  21. */
    9 ~; p  z6 v( S! @6 u) S
  22. . B4 u7 @3 I, p  O' [2 @7 u) {
  23. /* QSPI引脚和时钟相关配置宏定义 */) w3 P8 W+ w- G+ D3 h! x; ^& {
  24. #if 08 m( D) c( {) s) F5 Q. ~" v/ J
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()) v8 ], `6 i6 r8 x7 n. {5 P
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    " ~5 O; x1 G2 a6 W, d5 @/ |
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE(); b! ]% n0 U) E" T7 K( _/ @2 w! J4 X
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()( u4 W! P8 K8 [
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    ! o1 H. C/ E9 D) O# y0 S
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()$ q1 X* [. Y3 i9 Z' |, h1 g
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    3 o$ _# k& R: G( Z
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()! v% X6 R/ o/ O

  33. 5 \* ^- x" P* h' K/ [1 ^
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    2 \  x( M& L9 j& r2 l
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    4 V3 g0 b0 {3 I5 A% H7 s
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    + m5 {0 |$ x$ R  |3 s

  37. 0 C2 U3 f; n, a" E1 V
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    ' k. P) X5 ~- k: `$ H# N
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    & t, C: D% R9 g! y
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    & d0 }4 v0 h# b" Y2 i! x4 O( B+ p
  41. % E& H# ?5 t3 a- D7 z+ G% Q
  42. #define QSPI_CLK_PIN                    GPIO_PIN_24 y0 H7 ~2 a# Q3 h& K5 O
  43. #define QSPI_CLK_GPIO_PORT              GPIOB3 o5 I) h; E3 x# B- N" z
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    - s/ _. U$ ~# D% a

  45. 6 ?6 N" |0 W$ W7 q: ^  ?' [3 T
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
    7 F: k7 y6 D' F. J( p, l
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    ' p/ b- \6 K' L3 l& }
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI
    & C; @/ S1 B8 B  p

  49. & l' l0 `' O5 D6 O1 a- q; w( H
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12
    # W8 e) ]; f* v8 |
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD: @* N% G1 }# ~! p/ z* l
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI
    $ X( l, D+ O- @( c2 h1 ^$ R
  53. 3 G5 D. [; d3 ~3 ]4 _/ Q
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_71 Q. F9 X: e) v9 W5 ~
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    3 ?# C5 j* k+ E" Q; L
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    9 {7 N! h1 C& c. T3 ?
  57. . B9 f0 i, G) K: b
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13# o0 m% d# N: ~4 ]5 P) I
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD
    + ]; M0 ?* K% ^3 S( V
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    4 g6 k% q1 b& i( @
  61. #else! A, Q, X" D$ ]: p* T
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()& `! u: N$ _) V. F9 e3 w% e! ~
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    0 ?1 j. T$ u' n" ?, ^6 L) ]; T& n7 E# h
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    ! @0 F) C& P8 e6 B
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    & v8 T: S1 E5 q5 }. |) B" [
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()4 t# Z9 s1 P. q; F0 p/ ^+ I2 X. P
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()$ M0 ^0 i+ j% \/ v
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()% J, f! H* l4 k4 J# W/ K
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    " |6 g- S7 y7 B7 _5 i8 T
  70. / f$ b* T1 p5 `9 l: d
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()2 m% w. b! Q# g* T, G+ h# R
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()5 R4 ~5 |5 f7 V; H
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    ) q! G" X1 Y# U, Y0 C
  74. 3 I; O- C; e8 q. M
  75. #define QSPI_CS_PIN                     GPIO_PIN_6- I) ~! g  a% ~  j  W& N- H! E
  76. #define QSPI_CS_GPIO_PORT               GPIOG/ f* |. i# h9 j2 k+ z2 r
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI. E# @( W& x# T4 z7 _, j3 Q4 i+ m% @
  78. + A* x% a! C2 |3 p/ S4 R
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    # @* t+ b7 u$ k: l. H2 K
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
    4 `& w" G  M3 s# O6 e
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI/ i& m: T0 a- e$ }# o+ j9 R/ ^

  82. ' |; S3 B( N5 E. f# P
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8  C% b* u( i# O, O
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF
    " G* f& g5 }% G' E/ H0 ^
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI
    7 y+ n# v5 H2 q! t. c0 U* l
  86. 9 W5 ~+ Q' n: ^5 E9 H' ^
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_96 ~$ O3 y5 |2 x  A" a" U# M; D! z5 R
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF
    8 s1 h$ l* h$ A: h' m, v
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI( u2 u/ i% n3 j: Y9 X

  90.   ?+ Z3 `3 q' G* a$ B% c! d/ c* K
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7/ J: A: U# w+ p, ?6 p: y
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF) d, ]5 E9 e7 U5 P; k4 X! d7 I
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI3 q: a3 f7 K' b, z

  94. " A. F0 P+ E( q3 X) o! q
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    , f0 n; N1 O1 C2 M0 B8 T$ U4 @
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF- b, P, s! h( Q+ V* t1 K: O' y4 T
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI2 U( _0 @; b8 q. r5 i/ W
  98. #endif
复制代码

4 _* O# Q6 B/ _& B$ R; ?硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
- d2 U" L; E$ W  i4 e1 p& ~8 J5 \8 ^& @4 q) r& p& ~
主要是下面这几个:1 e  Q8 k( F5 s; _3 l& i. X, ]

+ j6 l* Z% T3 W
  1. #define QSPI_FLASH_MEM_ADDR         0x900000006 q( R" f- ^0 O/ d# w2 D* [
  2.   o/ M- K7 X% i# V' a& g' d0 u8 W
  3. /* W25Q256JV基本信息 */
    ; K, p6 R- \8 f0 W2 s" L/ ?/ |
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/
    ( K! z. _4 K, n- b7 }9 X) ?: {
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */( R5 Q* u% ]0 e) k
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */$ J+ {, \7 M! K7 S+ W
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    ) F) Q- }0 d. I7 v1 B1 Q
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/3 A% [, a- ]% w4 A* u0 `

  9. & {7 A" S. r: k( G/ ~4 ]/ L
  10. /* W25Q256JV相关命令 */
    1 W/ N6 u1 E2 k/ t: z
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */
    : B7 u7 l$ N& s6 ^
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */
    + H$ E# m+ O4 R- S6 `
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */
    " E8 B: Z% B+ F: z$ g0 ~' Q
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */( f7 F+ b2 k/ ?/ z. z' P4 B' J
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */
    $ }: ?2 B8 j% z5 g
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */  s  A/ o, ?8 ]1 B; @- q+ |
  17. + @' w9 `; |: b; w/ L
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    # v. ~/ @3 ~  p! P% u( y
  19. 2 f8 v) z7 T8 `$ Z8 n6 A) k$ ?
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

6 p$ Y: K6 k, f! ?7 a1 A7 U80.5 QSPI Flash的MDK下载算法使用方法
5 O$ B& v/ _& }3 m$ K编译本章教程配套的例子,生成的算法文件位于此路径下:
0 I# I) a8 G$ Q) _& L; Q( B* s. \5 e1 \3 ]  `- a
ed1f9465c60f92522e906639b76b29f3.png

' y0 s4 i3 x1 P* x4 M' l& r5 w& x5 `% ~- a9 x, \
80.5.1 下载算法存放位置4 W: I9 e% w! E& ?2 `7 J) {
生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:7 k: J: Q6 N2 Q$ P

+ i1 Q5 W0 M( C7 X& Q0 P  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。( h, ]" c+ \6 N0 l
  第2种:MDK的安装目录 \ARM\Flash里面。
- v, j' c) |& e0 k3 Q, M& K9 b* ~$ G8 _
9d5d65cb90fb28ad36464cb1b8368759.png

, u( e. h: c$ r/ k3 l3 N% ]5 _# v
80.5.2 下载配置
3 H, U5 \2 z) U2 p# l' Y" f' s注意这里一定要够大,否则会提示算法文件无法加载:
( {" Y. v; y0 n1 s0 v4 x7 s0 q+ a9 B' D
8eefa57c2d7f128449000c2fb4e788d9.png
7 k9 ?) X9 `9 i2 S
% K4 v: M& G% v* n1 t3 O* [7 W
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。5 o/ Q% h, x; c; M4 P7 B

3 ^# N% c% n) O" l0 M# {( L如果要下载程序到QSPI Flash里面,需要做如下配置:1 e% {$ l4 C, C5 x8 _
9 o# ?% U  u+ u0 c) l
caa01b15ccf9a9700690d2ad1bdedecd.png

3 A+ q! r( M# C. e
  q9 q& B. q: T6 y8 U80.5.3 调试配置
* P5 |" c1 }# }! }
注意这里一定要够大,否则会提示算法文件无法加载:
4 ^$ I. _- I! c+ z: R; |8 z" Z7 j; R* s/ q# P+ E
f23244d646498fc30aa5f1301a327430.png
- [* f8 K+ ]! l( _' M
7 v5 e! i& `2 P9 p( C- u( Q; `
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。
+ T& e/ K9 ]: c1 r
9 O, @9 E6 E( x如果要做调试下载,需要做如下配置:. [: |2 k! d& p* Q7 H6 k/ S

7 {' j- y. Z6 X1 V8 \5 R
84ee4945e5faabd776fedd6f8f7d96d2.png
& }# y# g; H7 N$ t' I
( J! A, }: A+ G" I
80.5.4 验证算法文件是否可以正常使用
" X" H. |) ~! f/ O为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。; o& k6 i- R% p/ U, }: B9 W
3 S. [" t  F/ n. A! H& D
80.6 实验例程说明
2 C/ d! a" T2 P
本章配套例子:V7-060_QSPI Flash的MDK下载算法制作。! {. |# J8 V. ~2 V, r

7 Q: v4 H2 z3 N& X' C) h3 h编译后,算法文件会存到此路径下:
) `9 ^% n& _# s8 ?+ N: D8 R' r/ |* L3 T9 G8 k) k2 v* l

& Y8 S2 g+ y; N; X; A% y  x2 c+ F9 L) u6 K2 n9 ^/ y
80.7 总结
& g: x) g- h+ q5 }0 G0 s, F, E本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
+ j' P1 ~( R7 Y" Q* a  D8 q% n3 Y6 ]) l0 L$ H. b

0 n7 |; B% `! j9 C: n$ ]) m* x0 i# Z9 m$ ^4 R' ]. K
$ [. c0 n% H) _9 w# |' m6 W0 ]

) t' R- S9 u6 b4 M
b69a199f1b4a12d880e64c7ba134070c.png
收藏 评论0 发布时间:2021-12-19 16:00

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版