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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 16:00
80.1 初学者重要提示
' |5 ^% _2 r) {( R- _. D6 H# V! y  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。2 y( C! k3 R2 B, ~/ k9 ?
80.2 MDK下载算法基础知识3 g9 x8 B: q/ s# u6 j7 ~* T$ F
Flash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。; t. }; d, f% h; i7 o

4 j! U3 L1 W1 U; _80.2.1 程序能够通过下载算法下载到芯片的核心思想
3 U( X4 U+ a$ C4 H认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。: X% o& }/ O1 K* g- l

& M! J/ @! R1 M80.2.2 算法程序中擦除操作执行流程
- p: t: L# _* a) ^8 p* h1 v* q擦除操作大致流程:
- M  B# \& v/ B8 X9 k6 ~6 ~# C5 w, d
d4b4af6b23f5f1d5ee3f3387ccb8fbdc.png
) {/ s1 D; S9 C" d" U
6 p  E5 ^6 i+ X+ c7 s) {/ A8 n
  加载算法到芯片RAM。
1 s" a1 g- G# `, o' z0 b! J  执行初始化函数Init。
% X) `7 c$ s3 [5 h& A# Y  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。
' y- }* W0 |2 D. M( t  执行Uinit函数。+ l" ^/ u. N3 T8 w/ U7 \, m( C. m
  操作完毕。
3 \5 K) ^4 M6 X! E2 }
1 ^+ X, Q; Q. O' t. w" P/ J80.2.3 算法程序中编程操作执行流程$ e7 b  V3 s" |
编程操作大致流程:0 W; ?) @- L$ e7 Z- Y, X1 U

2 a' C. i( D; |; E$ f! B
d3f601364bb033d42e275ad686c03726.png

( J' q; l- I+ j' d4 P: i; G* {7 G1 S/ ]8 C, ~9 x9 ~. G- y: n7 j6 x; h
  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。: Q8 A* a, a1 V. `$ |2 c  M0 x
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:0 [5 N1 G2 Z( V0 e. a9 q. H. a( t& r
  加载算法到RAM。1 K! \/ @8 I2 U; h- D
  执行Init函数。5 E$ z; m2 d! j. S
  加载用户到RAM缓冲。
. `5 `3 X/ H$ o  执行Program Page页编程函数。
* X/ _* U, p, u- [  M' i( ?( B; r6 d9 V  执行Uninit函数。9 m  r1 M# @3 e. Q) G4 |
  操作完毕。- G! t7 A3 ~+ D

7 |6 j0 o. V% ^8 ^3 u4 C8 `$ u80.2.4 算法程序中校验操作执行流程
* u( C! S) F* ~& K6 S- B* G& W& k) @校验操作大致流程:, r& U- I. O+ P' @

. V1 K" A0 s0 T1 Y' w6 }
c7b2f373d946bc7d166ab1c1ba9b67e4.png

  c' f. C( Z/ m( v  n0 ]( N# J1 F$ k; J2 q; H
  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。  g$ k% V( S6 A3 U
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:& Y* L3 l9 {( |2 G0 T
  加载算法到RAM。
; L# A. d& G; n9 B: P2 @$ p  执行Init函数。' Y4 x+ f  `! O8 V& L- t. F" `
  查看校验算法是否存在
( L" }, U1 R1 O8 M' y; d  如果有,加载应用程序到RAM并执行校验。
& ?. e: f- X# R5 [. T; }8 \; D  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。6 ~! u) G1 `0 Y/ |  y( T7 q
  执行Uninit函数。) p. V. ^& |. l5 o
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
8 b8 q, \" P4 D: D* K  执行RecoverySupportStop,恢复支持停止。
* b+ |0 f1 _0 k- G" v  执行DebugCoreStop,调试内核停止。
$ i$ }& d) T% @* P) ]4 U$ s$ D$ i  运行应用:  L4 J6 _3 W: [- Z# I2 [
  执行失败。( N/ J$ c" X# O! |% G7 R
  执行成功,再执行硬件复位。' P& H( o) q6 e" G; X0 F" K
  操作完毕,停止调试端口。2 Y" O* B) B3 i% Q% ~

1 o3 Z3 s$ o! ~' r5 \/ u+ [80.3 创建MDK下载算法通用流程; B* X" x5 F$ r) Q* H
下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。! C1 L8 J: M- g% S

. g& K4 k: L  [% i" F/ B" Y80.3.1 第1步,使用MDK提供好的程序模板
; Y5 V" _4 Y0 v, a! M位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。" p, B# Y2 Z% j; A- C9 ]

& |6 k2 K6 `" N& m( r# F效果如下:
. O/ R9 ^4 k5 j& l9 C
9 }$ W+ y) `9 z: I0 ~; W! f% p
8e08d948d34b38592e3699136725ade1.png
, u3 F. s6 O. d' y9 Q- {" C  ?
- _3 @$ ~/ w# H) a
80.3.2 第2步,修改工程名
- Y) k0 W% r/ u4 yMDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。3 [& z+ l" i  h0 Z: C$ [+ h& B$ y# N
  U# {: {& U( e0 E
80.3.3 第3步,修改使用的器件
- i3 @3 H* G2 Q  |! r5 Z( l# z  O在MDK的Option选项里面设置使用的器件。2 ?9 j1 u* a* I! Q, u, K2 P

% ~% {2 o: t  Y6 o
347d6def457f6e101f1c2555f9156cda.png

" }, N* b2 M) n
9 b9 n- j' x3 W, c, o- H80.3.4 第4步,修改输出算法文件的名字! }" }* k' \  K1 b; v+ t' Y1 L
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。
( r. z, R4 U) q/ P' P1 h/ F2 t1 O/ Z: F
07d754db49256bd0b73f4538a328f3cd.png

; W- f( u2 [" y. q& f% _( g' f
注:MDK这里设置的名字与下面位置识别出来的算法名无关:
/ `+ w' m! J- C0 z0 T/ I' q/ D3 y/ a9 Z9 J# r9 V- q
a00b0ff99a0a691f19dfc2565fde811a.png

  i$ s4 u$ M9 Y8 c& r' L6 _# @1 P- u0 w
这个名字是在FlashDev.c里面定义的。9 v" h: t' M3 G# U" c# w, T
0 j4 w7 L$ s% {
80.3.5 第5步,修改编程算法文件FlashPrg.c
) o: @* T: o% \6 ?. G% @  B/ l9 C模板工程里面仅提供了接口函数,内容需要用户自己填。8 R) A7 h! _0 ~4 a8 l2 q8 H

& L" U% o: i! S0 `- R5 \- x" V  J
  1. /* $ W5 X; n" c1 V6 d) U6 r. H$ Y
  2.    Mandatory Flash Programming Functions (Called by FlashOS):( W5 n+ ?( O3 i5 I+ i# H
  3.                 int Init        (unsigned long adr,   // Initialize Flash
    - B' q# w. F0 ~& f& @5 W( Q
  4.                                  unsigned long clk,
    , [9 T: G4 ~% j: R
  5.                                  unsigned long fnc);( p* N9 ]4 {" x
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash
    ! N* ~( `0 }- k1 ]( w2 O1 m
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function1 O4 g$ r1 S4 v1 ~5 j2 P& }- |
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function
    ( u) B4 D1 b  E, P6 x/ y) j
  9.                                  unsigned long sz,% ?7 d2 r1 G- A  U
  10.                                  unsigned char *buf);
    . x2 g) H1 f" w& T
  11. " R0 F/ b- H' j
  12.    Optional  Flash Programming Functions (Called by FlashOS):
    - [- w$ V4 p  a  O, {
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check
    5 u6 V6 C( t( K" W6 J% o7 y* `
  14.                                  unsigned long sz,5 n% |7 Q0 Q3 D8 g1 b$ S& |( |5 h
  15.                                  unsigned char pat);
    : t' C7 B8 @; ?: n! c0 }
  16.                 int EraseChip   (void);               // Erase complete Device% G2 v9 k& w5 k; F4 k# ~
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function& S* z- K) Y( ^& J
  18.                                  unsigned long sz,
    8 p! w- w! ]) m3 M4 z* y, ~4 u. d8 H
  19.                                  unsigned char *buf);
    . e1 w5 e; v6 t1 \' `% T
  20. & K! }; S" U' E# y2 J  Z4 `
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space
    6 w: n/ E" ]% g. b. `8 V) y! O" t( {
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space
    5 ]1 l# e% y) Q( `7 y2 |
  23.        - if EraseChip is not provided than EraseSector for all sectors is called" G6 J8 s" H- B# I8 @
  24. */
    ) y! a6 g1 y, d* a9 ^

  25. $ H' ?4 f1 Z+ n" B8 E# J
  26. /*
    3 l" t. N9 v3 G; D. X* g
  27. *  Initialize Flash Programming Functions/ x* W6 u. A8 F8 U! Q
  28. *    Parameter:      adr:  Device Base Address% |* Y1 `8 K1 n* L* n
  29. *                    clk:  Clock Frequency (Hz)
    ' E  G7 O) i- ^8 v
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    / p$ f% e+ |& M1 @! e9 Y
  31. *    Return Value:   0 - OK,  1 - Failed; L! C( t. z; ]# G0 Z+ |. S4 ^
  32. */. e' K8 t, @( g' [

  33. ; M3 s7 \8 U& q. l% B
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {" Z7 {' H3 Y2 A- ~9 P
  35. ; ^9 \7 U- Y1 K8 S& ~+ E
  36.   /* Add your Code */
    " Q( d2 `( E, K) {* i
  37.   return (0);                                  // Finished without Errors
      g3 K; ~) C' f. J4 r
  38. }
    7 {$ j1 u2 c" m* i

  39. " W) t0 M7 e, ?7 X7 ?0 f

  40. 8 R  t. v" j, }
  41. /*- y# h& J  y, @3 m# C
  42. *  De-Initialize Flash Programming Functions
    % ^  `# K; m8 D! J5 a
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)1 [5 w4 L6 I4 x( B
  44. *    Return Value:   0 - OK,  1 - Failed
    3 O  G. `2 e, I5 h8 x; ~2 B* Y6 q
  45. */0 k: w8 T: ~2 f" Q2 U

  46. 6 u/ w+ e: z2 N% R% S+ _
  47. int UnInit (unsigned long fnc) {
    6 S: p" D2 @8 X/ S5 f: i- J; c* X' A
  48. 9 @  X4 n7 `& D& r1 u8 t
  49.   /* Add your Code */7 U- q: I. {$ F7 T2 Y) Q; l! l
  50.   return (0);                                  // Finished without Errors
    4 `# z) e: ?* ?
  51. }' B+ C' H, ?: T. N  {' d
  52. ! \+ b$ w( g0 y5 X; o5 X) ^
  53. + ^( Y+ w* C* n( P4 A) D: z7 y/ m
  54. /*  K6 ^, d- o. D9 \
  55. *  Erase complete Flash Memory( A4 ?# y" q0 D) i+ x& M
  56. *    Return Value:   0 - OK,  1 - Failed
    - B4 |9 `7 P5 y' ?) [
  57. */  T4 X6 D, {* I' B& i
  58. + M* B* S. h1 W9 U
  59. int EraseChip (void) {
    - M" C: C/ u& X* t4 b6 k
  60. 0 G2 y' w4 S( E$ N% }  K8 P
  61.   /* Add your Code */
    & b  ]  G. {% n
  62.   return (0);                                  // Finished without Errors
      J$ h9 |, t8 F+ W/ W- _
  63. }
    ' @' m  A9 X# }/ v
  64. % g( H" n2 `; v0 F+ J' l

  65. $ C; A# C4 D8 E3 C% F9 t6 Y% J
  66. /*
    ' r" n  D+ W2 ?' D7 u
  67. *  Erase Sector in Flash Memory7 Q' I9 S) Z6 X& _; i
  68. *    Parameter:      adr:  Sector Address
      T4 w- t+ b* i2 t
  69. *    Return Value:   0 - OK,  1 - Failed
    ! c) ~9 S% o" s8 Y7 E  a
  70. */, g6 r7 g5 e7 w4 P. r
  71. 8 c1 U3 s' H% H$ Z2 Y
  72. int EraseSector (unsigned long adr) {2 N/ t# S0 c4 c) _, U( o! Z( I

  73. " P/ k2 P4 i# k/ e; T9 Y+ \
  74.   /* Add your Code */
    * O7 T* c8 Z+ O' c1 I6 P0 G" Z! g
  75.   return (0);                                  // Finished without Errors- [8 z. j3 S; ^3 H) V1 ?
  76. }8 }8 g; Y: e* o  ]: @7 Q

  77. % O/ {& J' E  s+ I
  78. 9 @+ |2 y5 b9 _8 ]" O1 \4 B
  79. /*
    % u9 ]% j$ h  g: s. c
  80. *  Program Page in Flash Memory" x: z: P! r3 H& b' o! R) D# `
  81. *    Parameter:      adr:  Page Start Address
    2 m2 T8 ~" B8 ~3 l
  82. *                    sz:   Page Size8 {0 C/ K, J2 B" v8 b% C* I
  83. *                    buf:  Page Data
    , H! a* R5 R0 A5 H3 z/ x; |0 W
  84. *    Return Value:   0 - OK,  1 - Failed
    * P' O& D9 Y! K& B
  85. */3 w* f# Y: L6 b
  86. . I( o6 h0 t' I" Y' X, ?
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {* m  {) D- \" L1 `
  88. 2 U- ^5 g' V; e) U' N9 ~
  89.   /* Add your Code */# h$ ?, X3 c- _( a+ L9 h5 `
  90.   return (0);                                  // Finished without Errors) c. G! r/ Y9 O9 {, k
  91. }
复制代码

- _/ x' W- E! K80.3.6 第6步,修改配置文件FlashDev.c
( K. |5 w- w1 o. U( R模板工程里面提供简单的配置说明:
6 v9 I2 t2 P+ r: j$ N
/ B8 l" [9 v- B
  1. struct FlashDevice const FlashDevice  =  {
    * ~) X' r" N" G9 F% D
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!' S9 V  s/ ^3 v
  3.    "New Device 256kB Flash",   // Device Name
    ! n! q/ m0 H/ r
  4.    ONCHIP,                     // Device Type
    # O3 Z5 \6 @. C: e4 y
  5.    0x00000000,                 // Device Start Address
    9 S. {; N# G% L) ?
  6.    0x00040000,                 // Device Size in Bytes (256kB)% w2 K8 H; b! m( b2 P: w) \
  7.    1024,                       // Programming Page Size$ T# Z/ v, J  l+ p5 E; J6 Q7 M
  8.    0,                          // Reserved, must be 07 C6 y% }- ^7 R- D7 G* i7 |8 H
  9.    0xFF,                       // Initial Content of Erased Memory! s+ f* j& `8 c% i$ z6 V
  10.    100,                        // Program Page Timeout 100 mSec6 i7 ?4 v5 m; _0 F+ E
  11.    3000,                       // Erase Sector Timeout 3000 mSec/ J; ~* A( o$ g8 d2 j
  12. ) A9 _  F. y! z9 G5 D; O
  13. // Specify Size and Address of Sectors! D/ c% l% c4 F5 D  A7 I: A& {
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)* a+ T& e! y6 [; q% t+ y! N0 c
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors) 4 u+ F3 _! k0 g9 D3 n; E- d
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)
    $ s* e, q3 U) k- `
  17.    SECTOR_END
    # \. {7 U4 X8 ^8 [1 e3 ^
  18. };
复制代码

- D7 L; q* ]' a" a. D注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。! I4 R  j0 r6 Z) Y- E' H2 {' Y$ c
& I# l: q/ B6 J2 o# h
80.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关; z0 |5 y) P$ [5 Q" c7 W8 o
C和汇编的配置都勾选上:
- ~0 R6 T, H9 O9 V7 o  p! @1 J
5 a" R- _6 @/ n- N! T% [
1d27a7314fc6c472937ad43852722b6b.png
+ b0 K, g/ ~: M# v: ^7 s! p8 Y

7 U  ~8 k) O) o汇编:
: ]9 d4 {8 \0 P, q
2 m! {' U( `/ ~
bbea604970c4ef63c77f10e553e28ea6.png
4 P7 u$ W3 w! H. Y: |! U

3 Y/ T/ c) r  ]. I0 y- S8 R  J7 f$ d* ~如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:  I. a# V/ {& F& n! {

0 A0 ^8 ], P& E7 t$ O7 O(1)加载以响应运行事件。
7 Y% x7 G* l" O! ^. T( o1 i
+ V& T) C) V0 d' n/ u# v/ e(2)在不同情况下使用其他例程的不同组合加载到内存中。
" _6 [/ L0 h0 F0 c8 k$ b7 {
8 [( e# N$ g/ J2 U(3)在执行期间映射到不同的地址。
7 }; v+ U4 p) S% y) |1 ^0 {' t, _% R0 l" t
使用Read-Write position independence同理,表示的可读可写数据段。
3 y+ @# Z3 G( U- R* E4 ~' G/ k/ L4 G- S4 S% m" Q0 ]
80.3.8 第8步,将程序可执行文件axf修改为flm格式
6 c- ~; z& }, |1 }; G
通过下面的命令就可以将生成的axf可执行文件修改为flm。
. X' O2 }9 t% ~' r% m6 {! ]/ m/ P% T/ V3 M& w
6b504444979e9fa3a3477c6284abca8b.png

7 l* V1 z9 l8 L( K, u$ R
! w3 E% c  M4 ^% d3 r  I80.3.9 第9步,分散加载设置
8 q  D. c4 m2 Y我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。6 E: a  P1 S$ @6 o' K
; j  u' F) A: L
e8e79854b204b618bbda32af92f28bf2.png

$ \0 f) l+ U( N* [% j1 `0 p$ J5 N0 |7 S& M' P
分散加载文件中的内容如下:
3 Q2 Z# ?2 |. e4 w) M
' \, e+ L5 g* b3 M3 i1 f0 A3 R
  1. ; Linker Control File (scatter-loading)
    1 J# s3 j+ S$ M9 O2 M6 O! [: e/ \' D
  2. ;; P4 R8 k- `" s
  3. ; y- O' l! |) @& e) d& u
  4. PRG 0 PI               ; Programming Functions1 ]" V& j$ Q7 ?! \, c
  5. {
      k7 J5 Y0 h9 z% }9 d7 |
  6.   PrgCode +0           ; Code
    ( k8 d9 S7 r- L  d, o$ f: t
  7.   {
    7 T. Q) N& Z9 T: Y
  8.     * (+RO)# n) F/ [7 c5 \0 k$ h- j2 e
  9.   }
    ( S/ C8 U8 _, W) i
  10.   PrgData +0           ; Data# c' \* ]& Q2 v
  11.   {
    $ j$ H6 ^3 j$ B+ |6 W! k$ P7 t
  12.     * (+RW,+ZI)  J5 l% ]1 a" v$ L
  13.   }
    * @9 A* t; d* {9 y* n; e
  14. }* t  v& L" A7 l9 T* Z  U! w3 B% x7 H4 {
  15. * R* X  ^/ g3 b. m& ~) C
  16. DSCR +0                ; Device Description
    ) s+ h4 E2 Y1 G8 ~0 ^0 v' ], l  [
  17. {6 k4 G, c- F" ~3 j" Y* `5 u
  18.   DevDscr +0
    4 T  M+ j( u8 {4 Y  G6 X
  19.   {
    : H# m* [; V. A( s
  20.     FlashDev.o3 C/ p( o; M) l8 w' Q* M
  21.   }
    % e5 o6 w$ \; `) m! A8 {4 p+ o
  22. }
复制代码
- z6 [: @$ n* V7 P- \  r5 R
--diag_suppress L6305用于屏蔽L6503类型警告信息。
# F9 u( r- a* {; ~# [0 E% ~  w, c& s1 s* e7 U
特别注意,设置了分散加载后,此处的配置就不再起作用了:7 L" e/ R3 B& H, A

9 z% j# M) c7 r  a( K) v" }+ i
b249eb40e5562b04ac3eb05ec0219c1d.png

* j+ j" B. C, ?% ~% M9 X* e1 \5 r
80.4 QSPI Flash的MDK下载算法制作$ u( f! h- `$ v5 ]% H
下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。% {, m5 H+ `- G2 [6 ?
' ^  k/ T- H1 z6 H- G) I
80.4.1 第1步,制作前重要提示* B+ v& L6 o' g' G, S3 N* ]+ R% F7 q
这两点非常重要:$ U* v4 e1 Y- W' T3 S1 S$ F5 D8 h* S
: m. V/ A3 z; B$ g& {
  程序里面不要开启任何中断,全部查询方式。; N; ^4 q  A2 t- G- R4 k
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
! [0 ~2 ?- C) e, u: R/ N% s& o$ D! `: X. n) Q$ V
80.4.2 第2步,准备一个工程模板) A9 f( m/ p; d: c! v7 X
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。" g; Y$ G0 c6 C

4 ?# o* h8 X2 n; b% ^5 _% Z" n
a91aaf84a25d730b77273624d2b502f6.png

3 t: h( W6 G3 X. B1 w6 ?5 [7 C, u/ a7 {. \5 X# |, U2 m
80.4.3 第3步,修改HAL库9 h2 }! a/ S* P: T, G
这一步比较重要,主要修改了以下三个文件:
" z5 X( O$ I9 {6 P
2 N! {; g! d! L1 M+ q
4471afac6bbc201866d0d9a8ea7d876b.png
( f9 n+ Z- }4 X3 w& o/ q
7 }- H' o& L: M( T1 c
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。3 N. }8 y5 S9 W, c( c
. X" l0 o- L; P+ G8 R. R6 ]
80.4.4 第4步,时钟初始化6 ]7 w, q  s. ?# }& i8 P
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:) {- ]; ]& s9 l# ^7 U

' m  B# x0 \- F5 N: Q0 D
  1. /*0 n4 K  y* t' @6 p2 i5 t3 T
  2. *********************************************************************************************************+ u  h' {# E% e) _: x$ _! i5 X' J: U
  3. *    函 数 名: HAL_InitTick( s; y) l" t/ b( b/ l; p/ O: M& j
  4. *    功能说明: 重定向,不使用1 U# }4 Z% i6 z4 @
  5. *    形    参: TickPriority( {7 E4 Y* X! l
  6. *    返 回 值: 无; m1 d$ ]2 U# o) Q( @
  7. *********************************************************************************************************
    4 C0 `) Q. m. c# d# \; I( E
  8. */; A8 d. \! R5 h7 r& T
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    3 z: U+ A+ ~! A& e$ A
  10. {
    1 v* E1 F  q7 d
  11.     return HAL_OK;
    2 u" L* \7 y5 W
  12. }
复制代码

+ R# H4 m9 V: g. I( W然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:& }. b: P6 Y/ I+ C# u0 `  @

$ Z; [2 x7 L8 x' U/ {
  1. #if !defined  (HSE_VALUE) 9 j" e, z+ K; f: N2 U; `
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */) L8 d; n: i% B! E1 G: V
  3. #endif /* HSE_VALUE */
复制代码
% W7 c" L8 N: `
最后修改PLL:
- E( b; H5 x4 g& k3 I5 d& E
& N( w1 S1 Y, J& B. I* m/ F7 `
  1. /*7 \) D9 g% s3 B+ V) d6 O% U% S, [8 j  C; a
  2. *********************************************************************************************************
    # J4 y, P0 w1 Y4 p' q# p
  3. *    函 数 名: SystemClock_Config7 I. Z. q3 ~# g
  4. *    功能说明: 初始化系统时钟! @, \! [6 s! r5 p+ H/ C4 t, ^3 f2 [
  5. *                System Clock source            = PLL (HSE)
    ) I5 Z0 w1 n  T, V8 b# K# t  ^/ C
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)+ ?% X% n/ y, x3 `! |5 V
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)% K5 N! r! I- L; D
  8. *                AHB Prescaler                  = 2
    1 q4 e% R( g4 {  o% k
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)7 J' e+ {8 m9 B' I! c# n: ~
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    . N; B; w3 ~$ r* _8 t0 P
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)1 u5 Z. ~! h  R3 g# j
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz); u* _5 D4 g4 N: z' n1 b
  13. *                HSE Frequency(Hz)              = 250000009 p) f3 i+ Z' F" H2 W# C% y  `: m
  14. *               PLL_M                          = 5" V1 R0 }4 {+ K! r& ^
  15. *                PLL_N                          = 1608 C: n, N8 R: K, j) o1 T2 u6 z
  16. *                PLL_P                          = 2$ }* o! |" p7 P9 h% f2 u; T
  17. *                PLL_Q                          = 4
    ! q' `( M% V# a4 b6 J) f; ~2 a
  18. *                PLL_R                          = 2
    1 S& r1 B8 L. ~, H; T
  19. *                VDD(V)                         = 3.31 @/ {' q- m. E9 [3 q  f, V. M: p
  20. *                Flash Latency(WS)              = 49 p3 r4 C' _. X' M9 X8 p) J' x
  21. *    形    参: 无
    - R4 S( F0 p5 e5 Y) G7 a% e. E1 ~' U* {
  22. *    返 回 值: 1 表示失败,0 表示成功  k0 r' N7 A  q6 ]( g% D0 G/ k+ S
  23. *********************************************************************************************************
    2 \5 A. E! W) f
  24. */
    0 M6 J4 V) S0 [2 ?) q
  25. int SystemClock_Config(void)
    + c! v: F. Z  ]+ c& s
  26. {+ h( i) X0 A9 v  w5 n# k
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    ) [" A- K, f/ m0 Q3 t2 P
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};1 b* G0 ^% o/ w
  29.     HAL_StatusTypeDef ret = HAL_OK;( R" `  _- H; T* j$ q$ B

  30. ' e# ]" B0 j+ l* }
  31.     /* 锁住SCU(Supply configuration update) *// u, ~0 w5 O% ^$ S5 E
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    % y9 @+ N3 {2 S, o

  33. ; P1 ]5 f9 F: n, B
  34.     /*
    . @- A* V; o& B( \& h/ C
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    " D1 Q2 ~# d+ |, M2 o" j( z3 z: _1 I
  36.          详情看参考手册的Table 12的表格。9 T% d8 D6 L# X$ h7 Z* z
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。; m5 m6 J# d2 r" O! k+ U
  38.     */1 o/ @, N/ i4 h0 W3 b
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);; V+ j: H" q& x4 Z
  40. / f3 K5 R, D( @/ K
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    $ e. j, w) h. c% y
  42. : I+ q0 p: [$ F! [3 N
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    4 Y( o* z2 R/ N" e
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    8 ?. W) c- t4 i/ V
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    1 _, [- B$ F0 ]* v" V
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;  R! Y  b. Z$ }! _! s- N+ x
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;, H4 h9 R+ ]; l1 ~
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;0 _& E7 j! g& Q" w0 F( C9 j
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;# k: ^4 I# m! O# w3 L* ^
  50. , D8 |+ b+ g! v$ z" w
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    ; o9 M4 X, }3 E; E! V0 H
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    1 z1 m6 n( _" q4 s; l
  53.     RCC_OscInitStruct.PLL.PLLP = 2;' {' {; T# n, l+ @- w
  54.     RCC_OscInitStruct.PLL.PLLR = 2;! Q  X2 Q8 m. i% i. R. n
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    & G0 R; x& w- \: N# ^% D& L
  56. 5 h% y+ N7 w: _5 r2 X
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    " z5 m) l4 F3 J+ ^7 ]/ v
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    ) V) T8 L: R9 l$ f& b$ H' W
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);% V  \& `, e0 O1 i- h: t: m  Y
  60.     if(ret != HAL_OK)
    9 }6 \8 _# K/ o$ r/ r
  61.     {
    2 ]2 P7 [+ k$ C1 g2 h# l: Z7 e
  62.         return 1;        ; O! W% [# S) G! P3 U( J
  63.     }* ^1 R' f: V  Z5 z( F

  64. 7 o' P0 ?( {4 R. G# Z% n. \
  65.     /*
    2 y4 N* Z3 M) h
  66.        选择PLL的输出作为系统时钟
    8 `9 W8 U0 O. k1 O; I' E- l
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    . |! Y# I# _* A
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    - E- ~2 j. l4 v8 @8 u- E0 O; X# `( m, Z
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    2 Z/ L6 d" N# D, L" A% o
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    ( J: U4 H7 o0 l
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    . n; A( @6 n* c" i" q5 l
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     ( V" b! L% d5 c6 H2 o$ a! f3 @
  73.     */$ O" _' e7 H6 p  X' j* a! w
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    / W% z' u* b& V' D
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    0 j, s' V: n1 I

  76. / V, ]4 P4 v; H& N# c
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    ) C; E" Y6 F8 i, F
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    1 I& y3 t% Y: W9 _( e# O
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;8 P6 |. V( t! y0 p* I
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    . |, N. D4 }" {; f6 }
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; 6 F/ W2 l$ X2 \$ }3 W: M! g
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    6 x! Z# O4 q, Z6 \
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; 7 Y! R% q! `% n: {
  84. / w6 X2 b/ }9 ~# R
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    ! y* Q5 Q, G* Q: a
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    - G* U5 C" b+ a  r
  87.     if(ret != HAL_OK)
    6 o5 {/ g: w6 W# |# d3 a* e
  88.     {7 A+ U. A$ O4 U+ `
  89.         return 1;
    ! j2 [/ ?' N9 C+ D( P# i
  90.     }  l' x' \, s8 H$ s, h
  91. 2 X( d  b: H" _" {3 ?3 u
  92.     /*
    1 \- t7 {% s+ v9 }' Z; R9 Y% A, \
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    5 T8 E/ u( n8 l# s6 Y
  94.       (1)使能CSI clock1 W/ f1 I9 ~; T2 H0 ]4 [
  95.       (2)使能SYSCFG clock8 p* p$ T$ s9 q- Q2 {
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0+ d8 ~( n  ]* c2 |! E, P
  97.     */
    . M' h/ i0 \. c; w2 a
  98.     __HAL_RCC_CSI_ENABLE() ;& Z. O9 F7 K+ X6 J/ M4 i4 Q
  99. ) j) J6 L# ]& r2 A
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;. J4 g! o8 h8 U1 ~$ ?
  101. 8 l( \5 M- K  N% x: I: P) Q
  102.     HAL_EnableCompensationCell();
    : t/ K4 N4 Q' H$ v
  103. 7 t( S; j7 N- b5 F+ E
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    - g5 p/ g) h/ v" }4 @! k
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
      d/ p7 d/ [1 Z# q$ X8 D
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    * X* C9 D- m- B0 J8 V
  107. 3 Z; w* a: C' V. v
  108.     return 0;
      x4 Q- z% {$ K0 P7 e. }
  109. }
复制代码
" z) D2 u; \, m1 Z. R
80.4.5 第5步,配置文件FlashDev.c的实现" i7 }! b7 G" K" E( D6 a( o. f
配置如下:+ {$ u/ w+ N7 U) A7 g! D! P, S) K3 r

  }) R' N3 ^1 [+ U3 ~! l
  1. struct FlashDevice const FlashDevice  =  {
    % ^' G2 V' O% \  }; N# g3 M
  2.     FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */1 H/ z& K; {. r7 p0 [# K
  3.     "ARMFLY_STM32H7x_QSPI_W25Q256",   /* 算法名,添加算法到MDK安装目录会显示此名字 */9 m1 K; T0 t0 a( g  f
  4.     EXTSPI,                           /* 设备类型 */
    " c) d$ I8 X2 b, K2 r6 t' [
  5.     0x90000000,                       /* Flash起始地址 */
    1 U. a/ A' `5 `: `
  6.     32 * 1024 * 1024,                 /* Flash大小,32MB */3 Y, x6 k; }; c/ l6 N( l
  7.     4 * 1024,                         /* 编程页大小 */
    + c, j! _  U6 R5 p) x
  8.     0,                                /* 保留,必须为0 */
    4 ]# h* x* ~$ j6 N/ z' ~6 }, A5 J+ v
  9.     0xFF,                             /* 擦除后的数值 */
    + f9 ~" [. @3 S3 ~3 z
  10.     1000,                             /* 页编程等待时间 *// `; Z1 ?' n# C
  11.     6000,                             /* 扇区擦除等待时间 */% _( u# G' c3 |
  12.     64 * 1024, 0x000000,              /* 扇区大小,扇区地址 */
    8 \, V3 E9 G' g7 a6 X+ E8 s
  13.     SECTOR_END    3 x+ G& N2 m2 J! e5 e. z7 q
  14. };
复制代码
0 {6 M; T! b! @# S& z
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:
( i0 w  k  g8 v# t1 k" p+ ]! n  e' z
ffed7a581be4c5da388ee29fb67249b4.png

7 z) I! o1 i/ R/ Z: @1 _$ _4 p# p
. ~6 ?# t: d& V- z6 }% ?80.4.6 第6步,编程文件FlashPrg.c的实现
. M8 x) L  Y. m1 D下面将文件中实现的几个函数为大家做个说明:6 J" \3 t4 ?* z% b1 H" P, T
. F" r. z0 x) O7 u4 k
  初始化函数Init
% s/ Y/ n' s( \* y3 w2 M
  1. /*" H" {  X0 K. d2 e% L
  2. *********************************************************************************************************" `% {1 I, {! T! A( t+ ?
  3. *    函 数 名: Init
    2 R& c) g" I/ T
  4. *    功能说明: Flash编程初始化3 U5 E8 V# ~3 v4 `4 j2 R
  5. *    形    参: adr Flash基地址,芯片首地址。6 R( K) k0 O8 w4 F( v2 N
  6. *             clk 时钟频率1 r6 c6 a- j5 l% a4 Q7 U
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify! }  J, ?& t4 [8 A$ Z: V3 U
  8. *    返 回 值: 0 表示成功, 1表示失败5 ~4 N! w  m+ [5 Q8 O3 K, L2 F2 A6 T
  9. *********************************************************************************************************  J5 b; p% b; d6 D4 V3 E3 `: p
  10. */; C8 d; g( y9 o- q( G
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
    : j" {' L" i% Z. c9 p! G
  12. {
    % {8 v4 Q' z- c+ s4 t' Q; X; r
  13.     int result = 0;* c$ y* v5 k( q- B
  14. + {7 ?4 j& E6 j  d
  15.     /* 系统初始化 */
    8 W- v( v3 g/ ]
  16.     SystemInit(); 1 `- Q8 D  }( y% g4 h, N
  17. + ^' P8 b  z/ u8 n* k5 ]) N
  18.     /* 时钟初始化 */6 \  X- M! K/ b- L
  19.     result = SystemClock_Config();" E! \3 s  N$ `5 v- _
  20.     if (result  != 0)4 x6 s: d$ y2 P4 J- g
  21.     {7 `3 v4 c. y0 E( Y( S) ~
  22.         return 1;        ) N' ^/ [! l' \' [. `  S  B
  23.     }
      ?/ R! }9 g- n. _$ N$ g$ S8 P9 D

  24. 5 H! Z) X: L% _
  25.     /* W25Q256初始化 */
      x1 r3 r7 Y5 |7 n
  26.     result = bsp_InitQSPI_W25Q256();5 s* Q. m9 i1 m. G! i  b
  27.     if (result != 0)
    # q0 b2 ~, M& h1 c
  28.     {
    5 T2 J$ O- Z; t: _
  29.         return 1;& ?+ e# n; [% Y4 X! t: J8 ~4 O5 k4 R
  30.     }
    / F  ]8 Z) E, _8 [  q

  31. ' W" @; I0 c6 \0 ?6 r3 E- {! L: R: U
  32.     /* 内存映射 */   
    # A; L* W5 e. A  L8 D0 F' P' W
  33.     result = QSPI_MemoryMapped();
    8 h' L" I3 C7 H  D
  34.     if (result != 0): S; S4 D, O7 Y# u( i
  35.     {
    1 f& O. s( Q& r" p, }* F
  36.         return 1;4 E1 K- H1 R0 ^5 J. y9 S
  37.     }
    5 O9 C$ }9 o4 W

  38. 3 @/ Z; {# J  y: S/ ~; C
  39.     return 0;
    / P; `! f1 n# `
  40. }
复制代码

5 p5 m! U* f# E  K) s0 v( v初始化完毕后将其设置为内存映射模式。
# @  D- C6 K& `$ T1 }+ G0 h( C- p  n8 Y3 m9 D( _: J% w
  复位初始化函数Uinit( v9 S+ v8 ?& V3 ^: T7 ^
擦除,编程和校验函数后都会调用此函数。
) n$ ^; ~* F2 k4 |% _" m& m& S# z! I) W6 l' l
  1. /*
    ( n8 @* S: D6 b9 `: ^
  2. *********************************************************************************************************- J' T$ d+ v" t# j1 Y8 C: k
  3. *    函 数 名: UnInit
    $ M+ K; X2 B4 l% e1 [
  4. *    功能说明: 复位初始化
    . Q6 k9 v" g, |
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify4 m7 l0 Z* Y7 N! n. {% X; ]
  6. *    返 回 值: 0 表示成功, 1表示失败
    $ O9 Y5 ]& f3 A( \' D+ m
  7. *********************************************************************************************************
    / ^' _. h! h% o; i* e( _( I
  8. */. q; G3 }: J5 j+ F3 c/ w
  9. int UnInit (unsigned long fnc)
    * z3 y; q4 ?1 f+ G3 l
  10. { 2 R7 R3 i- a0 Q
  11.     int result = 0;
    $ ^; @8 C. ^8 l2 ?

  12. ! f: J& e! s% R' G+ m: {6 l
  13.     /* W25Q256初始化 */+ B1 i2 o: k( \* J4 P$ ?% ^
  14.     result = bsp_InitQSPI_W25Q256();1 O+ G1 Y+ D- D- ~3 @
  15.     if (result != 0)
    9 f# B  S1 e5 G$ o4 a
  16.     {
    2 m8 M/ x# x  Z5 N
  17.         return 1;; i: f8 h9 D  b
  18.     }- V! P3 P; v0 f
  19. . C. X2 l: `5 E3 T) ]
  20.     /* 内存映射 */   
    ! \( b( u- n: d: Z( q' h
  21.     result = QSPI_MemoryMapped();
    3 g/ i& V5 \0 K/ `$ }) m( w+ R  I
  22.     if (result != 0)
    . D: j5 }' ?: l( G0 X# `
  23.     {( a6 ^, A6 O( O7 d3 m# `
  24.         return 1;
    * b0 C, k% G4 [. ]
  25.     }: l, h2 ^& J! c( B0 w# x. t

  26. 6 R* x  n# l2 y- L
  27.     return (0);  S/ B, t! S! N9 i, }, o! Y
  28. }
复制代码

' F3 p$ o1 n9 ~3 m8 y/ u3 B& Q复位初始化这里,直接将其设置为内存映射模式。
2 \  L6 X& H4 i/ n5 t# P7 t6 I1 m
  整个芯片擦除函数EraseChip, P( z% T) i8 {% M6 @
如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:* `$ B% W( K$ s  x5 H# P* F
" ?8 ]9 T0 T; k* d( U  S
f623e662bf09233ea594f820f4c7952a.png
8 S4 F7 ]! y2 J! }! J" Z3 p
: D. l" t, L' a
实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间,比如32MB QSPI Flash整个芯片擦除需要300秒左右。
- l* Z# g( U- B( f# R
" X; X) i  S" q另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。5 j! u0 j; d- E/ Q

) g/ t( Z9 {3 z, J& F
  1. /*' H4 U( _9 K: Y8 L; c" t- [  f
  2. *********************************************************************************************************+ ^4 K% _" H1 y. ^2 Z' C
  3. *    函 数 名: UnInit
    % w$ k. U3 V& s3 O
  4. *    功能说明: 复位初始化
    * g& M5 ~" P6 Q. }9 [
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify% a2 l- Q! r$ b; I0 S4 I/ v
  6. *    返 回 值: 0 表示成功, 1表示失败& P4 c( a' K* H+ `0 \5 E
  7. *********************************************************************************************************/ H" B/ t0 J  f9 u& F6 F
  8. */
    + @4 u3 M0 D; Q6 N% T
  9. int UnInit (unsigned long fnc) # i% e* f' b9 l- Q+ }
  10. {
    ) _  b9 ^3 C, U$ L; k, @
  11.     int result = 0;% L' |& B/ j7 X. K# |8 G# N' I
  12. " k; Q; g  g: k, ~/ c( ?* c
  13.     /* W25Q256初始化 */. j9 |7 t  M$ i" h# {( B
  14.     result = bsp_InitQSPI_W25Q256();4 V( y1 r4 P8 q7 X  W
  15.     if (result != 0)6 ^* o, o) Y9 V' @1 X' s! z3 ]7 U* Q
  16.     {+ K& O; h8 q1 r: B3 x8 l& o# ^
  17.         return 1;0 C: Z' D; v- w2 H: H; F0 P8 i
  18.     }
    1 k& a4 @/ y2 k% v% h5 b

  19. " V, _& t" c1 p; Q! ~1 X! C
  20.     /* 内存映射 */    2 v/ S/ X2 X; [  K- b$ @% f' W
  21.     result = QSPI_MemoryMapped();
    & S* z8 Z8 C1 Z0 u
  22.     if (result != 0)2 ?9 g& k& O1 H, z, {: N
  23.     {, N; \8 Q$ {) d
  24.         return 1;* K" v% r9 n7 U8 \: g
  25.     }& Q. ~$ I  {' i' h( P9 V

  26. ) B; |" U1 F; u$ i; e: [
  27.     return (0);
    ) q1 x, v1 X8 x! W$ s( G
  28. }
复制代码

8 N: l8 A- x$ l5 t+ n( b0 {- N  扇区擦除函数EraseSector- {& x) h5 [) s5 k: c% e; Q
如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:
* ^* W% y; \2 Q( G  Y9 ]8 Q  w
3 j' [( L: E0 Z6 X
733d5af26119f64bff0261515013d3a6.png

  H, b4 Q" n8 R" S8 _7 [" h0 D: M, j7 c/ w& }/ e5 Q2 f: w
  1. /*
    8 b! h' [8 I4 u4 J& B
  2. *********************************************************************************************************
    5 G) q6 A7 m5 g( D$ }2 `9 x
  3. *    函 数 名: EraseSector
    ' S+ Q0 e/ d9 f2 X5 d
  4. *    功能说明: 扇区擦除1 t4 R) k8 q" h; D5 g, U
  5. *    形    参: adr 擦除地址
      R2 O" e0 H/ o# E
  6. *    返 回 值: 无
    " \% z) [7 u/ `! }! ^
  7. *********************************************************************************************************5 L4 ^( z6 W8 |
  8. */! T( J! h5 ~9 Z" w: e. w4 n8 z
  9. int EraseSector (unsigned long adr) & M. ]4 y* D) @/ g0 |
  10. {   
    + D2 K7 h6 b' t; `$ D
  11.     int result = 0;# V! H& }$ q: P, q- U1 t, K( p- M

  12. ! E2 Y$ B- V) h' T7 \
  13.     /* 地址要在操作的芯片范围内 */- g" |! ]+ K1 j& T7 h7 c
  14.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)" b2 `" c, c. ?9 {% x
  15.     {0 P& h9 x& f$ ?4 P- x5 a
  16.         return 1;
    8 x; G# b- T. n1 f
  17.     }
    6 o' `; v( @! B* s% v
  18. . l5 L( k, ^8 ~( i
  19.     adr -= QSPI_FLASH_MEM_ADDR;
    2 p8 V. [+ }; v' ]5 j6 l
  20. 9 s( Y* {9 t: ]4 }+ }) l4 O
  21.     /* W25Q256初始化 */
    / ]/ ~7 G  D( @, l
  22.     result = bsp_InitQSPI_W25Q256();
    ; ?$ c6 D+ q0 b9 w: U
  23.     if (result != 0). p  j6 K6 i# i' m2 _4 b/ z0 b' w4 ]
  24.     {6 R3 G: W) r7 ~. j5 f; z5 w- |9 H
  25.         return 1;
    * d) n& Q; r+ \# \4 `, f: m
  26.     }
    8 }. O( W/ m9 ?9 M

  27. ( r! u9 A6 x+ t
  28.     /* 扇区擦除 */- a. T: S7 ]+ ~. a% Q
  29.     result = QSPI_EraseSector(adr);  
    / B& Q& s( \, M# M$ @2 K6 C
  30.     if (result != 0)
    / A' O6 d; x3 I0 u4 K
  31.     {
    0 ]  L5 G2 l5 i  C% K3 g& B$ H
  32.         return 1;
    ; @) f* |+ V9 Z6 t2 O- {0 L
  33.     }   
    5 p' A' s( ?  y- C$ u$ @
  34. / w# V: ]  M% ?( t3 ?- L
  35.     /* 内存映射 */    4 e; E4 |* ^* V. w1 T
  36.     result = QSPI_MemoryMapped();
    : E) |+ J( _7 Y# ~0 W
  37.     if (result != 0)
    4 l) u7 \8 J: v# _& t9 p
  38.     {
    9 ^6 y; K: a- O* D& e* V
  39.         return 1;4 V- K+ x6 V1 n3 x# U5 A
  40.     }
      f0 O- q6 X" j- v

  41. ' Z, b: X# ~- ~( L% l; e
  42.     return 0;   * W: v( B$ G4 y, P, v0 @4 ~
  43. }
复制代码
  q& f1 r0 G9 X+ i% f  u* B; e
这里要注意两点:
( [: ^- b* ~: H' b. D# O& [4 m- b
2 a8 k1 G4 S' m& B1 [: o(1)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。( t9 Y/ |7 Q4 A0 |: n4 ?. b# n

, \# R/ H  e- {. b( O6 c# {8 G(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。
  K6 ]1 o; t' q6 ?" }2 q
  _0 _$ A" k( y( ^. Q& H9 D: c  页编程函数ProgramPage

: {9 Y$ i$ F) C8 c页编程函数实现如下:( |$ t9 @% M; x  U
5 j' J5 R/ m: @" S
  1. /*3 x. X2 Z, i; m0 p4 z0 ~( @6 o5 h
  2. *********************************************************************************************************
    3 I2 {) }+ [4 M9 I/ k
  3. *    函 数 名: ProgramPage2 K/ Z) C, ~& l$ I
  4. *    功能说明: 页编程& y% m5 h2 M$ V' y( F) b
  5. *    形    参: adr 页起始地址
    4 C3 D9 u1 G) E0 D& R
  6. *             sz  页大小
    ' Y" [8 }0 h5 I' a$ s, ^6 D: ]
  7. *             buf 要写入的数据地址
    $ S1 b/ I; U" t$ z. g$ Q
  8. *    返 回 值: 无
    ; Y& Q& m' z- O2 e1 j! ^3 Y! Q
  9. *********************************************************************************************************
    & ^" `" ], i% l' o: I( e
  10. */, F" D: q, \' [5 ^+ E; q$ [/ [
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
    : {; q4 V4 S2 a/ b9 A  d
  12. {
    ) ?, {: y& @/ c7 {9 g
  13.     int size;
    + ~% Q0 K% U4 Q1 X- z, U
  14.     int result = 0;: [/ \$ l0 H9 J7 B- [3 U
  15. 2 ^3 I4 I" @! k: L7 w
  16.     /* 地址要在操作的芯片范围内 */   
    - X* b2 |( {( u! I
  17.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    2 z' h) Y4 D, b! g$ d  \! N
  18.     {  C" g# B) e* W) q
  19.         return 1;
    & x: c# S# G  w& @
  20.     }2 u1 g5 B# e( ^% ]( p

  21. : ^4 F/ Y  c7 T* W& c
  22.     /* W25Q256初始化 */
      [( f* Z# M- P+ J# g
  23.     result = bsp_InitQSPI_W25Q256();; Z8 {1 X( X) Z' a' [! K
  24.     if (result != 0)
    ) [; v) m9 Z  K9 ^2 D
  25.     {% K) {& v+ B+ j' ^- |# ~. q
  26.         return 1;
    2 _7 C3 }, `% J3 ~+ U
  27.     }
    6 t& B- J5 N5 x8 U- g. O
  28. 6 Z8 E4 c3 J8 Y. A
  29.     adr -= QSPI_FLASH_MEM_ADDR;, _: g3 x; B3 g) i* Z) S
  30.     size =  sz;7 {! Q1 e9 o, h0 y8 o; E3 i
  31. 5 W; v1 I# B9 Q) @" r
  32.     /* 页编程 */6 J) z( Q; D7 H: i, \( T- S
  33.     while(size > 0)( I& \& T3 P6 w
  34.     {
    ! c2 F( f& P7 |# K3 T" T
  35.         if (QSPI_WriteBuffer(buf, adr, 256) == 1)
    & d! b2 x+ O5 Z( b, }( C* S
  36.         {/ R' n4 N) [* L5 a- {1 t
  37.             QSPI_MemoryMapped();
    % D+ V: C. h2 q% B

  38. 5 @; T- K5 T  `
  39.             return 1;   , G* d+ ~& r6 M$ ]
  40.         }
    6 D* m" l: e' Y
  41.         size -= 256;
    / W6 L/ L1 `9 a' E# g5 c
  42.         adr += 256;. |1 w# J. Y/ J
  43.         buf += 256;
    6 J/ H* b# @6 N0 _
  44.     }
      a" D8 P! C" G- b

  45. ' s+ C" Y! e7 [% r6 G9 v" f+ I% q
  46.     /* 内存映射 */    * o* ]: E+ `9 b6 d9 t0 J
  47.     result = QSPI_MemoryMapped(); ; R( B/ b3 m" ]  a6 G
  48.     if (result != 0)6 }: P; u8 z( D' ?5 D9 p  _3 y) t
  49.     {
    " I; t& I9 o& }" e% J5 U  D
  50.         return 1;7 ^0 F' ^' @5 u; P
  51.     }& z8 b3 I. j% o
  52. - ]! a' a; M3 b0 Y
  53.     return (0);                     
    8 T; U0 D  L% ]2 b
  54. }
复制代码
$ [9 c& h8 @1 a
这里注意两点:% u$ P# d! v4 H- k

# ?: A/ U+ u) Y(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。
5 Q- \" r' e1 Z8 x8 ^; j3 a$ D0 l8 [
(2)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。) G6 N. v" t9 R

! Y& P. a# N  ~2 {$ |  读取和校验函数; r  ~# G& Z+ |& V. v7 s
我们程序中未做读取和校验函数。
0 F. u( O- C8 q2 m. R. {7 R: J1 j$ H7 A! p* \4 N5 `# H
(1)     如果程序中未做读取函数,那么MDK会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
5 P3 r5 p; n! g5 ~( w# V
" w1 e" k' s* q% |* _" Y(2)     如果程序中未做校验函数,那么MDK会读取数据做CRC校验。
6 i# ^7 D: L" H7 ?4 g3 b2 u! n6 B& f2 v; @3 [
80.4.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)

5 E4 ~- ^  R/ x) J0 w" I最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
$ J$ u* ]  W2 U' I" y
5 }, K4 m* Z3 _, H4 @: g
  1. /*   V8 Q- \6 V( i$ e+ M, @- k+ Y" Q
  2.     STM32-V7开发板接线4 f6 z6 S. }$ v- D% |

  3. $ i6 F0 a9 R" Y  D6 R; I5 Y$ w/ x
  4.     PG6/QUADSPI_BK1_NCS     AF109 B9 ?( z# F1 {5 Y
  5.     PF10/QUADSPI_CLK        AF9
    3 U3 G9 \! \2 _  s9 z! y
  6.     PF8/QUADSPI_BK1_IO0     AF10# \7 _$ \' Y4 n7 {
  7.     PF9/QUADSPI_BK1_IO1     AF10
    0 y, u2 X* g9 r8 S% W
  8.     PF7/QUADSPI_BK1_IO2     AF9
    " W: x9 o8 n* `9 h
  9.     PF6/QUADSPI_BK1_IO3     AF9! U* L  s$ \9 t% W+ o1 p
  10. 1 {2 v/ k& L% j# o1 G, y
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
    3 u0 ^/ q4 U, }- M$ Z1 h, c
  12. 3 F; g" P8 }; {5 P
  13.     H7-TOOL开发板接线
    " M+ y0 I. G8 y9 E- T3 u9 V+ g# t

  14. $ e, i9 D' \5 I+ @9 [6 M* [0 W+ s
  15.     PG6/QUADSPI_BK1_NCS     AF10
    , ]& d4 q. L: O6 q0 o3 R7 `
  16.     PB2/QUADSPI_CLK         AF91 C# |: ^! M8 J1 q& n: U1 M
  17.     PD11/QUADSPI_BK1_IO0    AF10
    ( W; C- k& E$ J6 U+ U. O
  18.     PD12/QUADSPI_BK1_IO1    AF10
    # U" ]. {% z6 |6 Z: z) F. q
  19.     PF7/QUADSPI_BK1_IO2     AF9, o* D! w$ s3 x
  20.     PD13/QUADSPI_BK1_IO3    AF9
    + Y# m# r$ k. \1 b
  21. */) X! R& ?8 f$ k! B9 x4 F
  22. ' \6 z! [# k8 u, R( |: }5 j
  23. /* QSPI引脚和时钟相关配置宏定义 */8 r' d$ w, F0 D( N1 r4 K. T
  24. #if 0
    , Q" m* |; x  ]" i6 K. {$ N& n5 J
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()" E+ v2 @8 _* A  I
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE(): x3 R# I4 |9 _; P4 Y
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE(), J& y3 L2 j) j( _& j5 y4 \
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()( P, ]- X6 S8 h: i
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE(): P/ n  V: Q) P' Y5 G+ ]! o9 G
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    ' l4 f- l4 b* v# C
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()# ]7 A, u8 m5 i% J8 |" D" I9 x  w
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    ) H8 m5 C1 ~, J# v; M
  33. + a; k4 i# l4 {, r' d. o
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()7 N# G- o4 G$ [9 r. V
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()" u  n1 I$ n" M; F9 z, u1 D1 s
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()0 E2 I7 _# I( l! B* t
  37. 2 ?3 Z! L6 q5 m9 h3 R' z4 k" ?
  38. #define QSPI_CS_PIN                     GPIO_PIN_67 i0 U  q3 g  t% q2 v$ L
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    : H9 ]7 y! V. L2 n- c0 u
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI# r+ L  o' F+ J: [% O0 N( K# a

  41. $ _5 [/ u; x* ^9 v1 Z
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2- z7 y4 Q$ t2 i3 e* o1 N
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    ( x  n1 G* _0 W
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
      _7 z& h& W# }$ P5 s
  45.   @$ P, ^* h5 ?. F; Q: X* N, z) ?
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11  S. d+ y; v) ?5 d) ^
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    2 L" ]8 R8 U, [; C5 j3 x
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI! B8 E  ^( l; \6 E" _

  49. " d1 w) U& i- k4 X6 a% x
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12
      P2 k. }& {# ^1 B  V% o9 L' O
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD
    1 ?; `3 T) A) p
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI1 o/ K( {3 f- _# ~7 c
  53. ! a( t% E! b; d/ o) z) h( D2 T
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7/ I. \1 S, ~( R! E. q( C
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF# {0 d; p$ a4 L& w
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI* H; K$ m4 N7 X: x' j

  57. + k/ W; r; |% n  c
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_132 p! N) d5 V2 e3 u- p1 i, r5 p
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD  s! a! H: q$ M& [7 M
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI6 |4 X8 n* _  a! n! B( ^9 X
  61. #else
    * z% l3 {9 [6 q" u3 ?3 K
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    $ ^1 ]# W  m. e( d8 M) c2 \# x
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()( X) d7 E8 C( ^  }+ Q0 Q
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    # {2 k! j1 Q- O+ F% l6 A0 l
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()* A  c* L' v% Q, `2 b) B) |
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    4 }3 Z" @$ W4 |8 z+ o
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    & W  A+ `0 X, K* c3 w+ v/ j; N. S! g9 ]
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    ) y" ^# Y( \0 ]  y9 X
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    7 V% ^3 A1 `( y6 j5 V( P4 [; i5 P

  70. 2 |. w5 u9 J4 T: V* P6 C
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()" ]3 m+ p. v& M, A7 X
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    4 Y2 u" W( z. Q7 [- n! e( o5 X
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    # T& _* P& e( S' x

  74. % z$ F5 @: j' u* t+ ~
  75. #define QSPI_CS_PIN                     GPIO_PIN_6' G! L% @7 @/ B7 u9 h2 [
  76. #define QSPI_CS_GPIO_PORT               GPIOG
    $ g2 O0 g  L$ O" r$ I' \6 p1 _7 o
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    - H4 J2 [; c# {' F7 H. {- l' M7 f5 J
  78.   L% L( \2 f; K# [+ n1 [2 E8 o
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10% I- Z+ U& D3 _) u! \
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
    6 u1 x2 P8 V# I8 Q
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI% B, p- U0 j% S+ m! i
  82. : t) E1 k9 L, N+ ]1 J
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_86 w4 ]( C7 O5 B6 I7 ?
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF
    ! F& _9 `& O0 B1 f: q, ~6 o
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI
    ; G# y6 I$ p6 W: b
  86. 3 Y; _+ }( a- X( }" z! k
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    2 j# G7 R0 w$ }" N: y7 e; B
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF. g# d+ ]0 i: i4 h* n
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI/ l% z3 \$ ~6 I1 o1 f! z
  90. ' k2 d/ R0 g& w
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7- E" x+ [* r- D0 t% a4 R& g$ ?1 V6 h  u
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF2 J6 z2 s2 f/ c+ E
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    ( v1 Q9 b( Q2 D

  94. $ r0 s3 F' F3 u6 l/ g) w
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6* P( v7 C5 n, T! ~5 n* |
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF: y: C$ n* I9 V0 x5 [8 e" T4 d1 C
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    / r, M+ o/ ~: F: ^4 ]
  98. #endif
复制代码
8 b5 I+ l1 p+ u# J
硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:" [7 i* y& q! I4 G
6 z7 ^8 T3 H/ Q$ Z' _6 ^2 a
主要是下面这几个:8 P5 v% [( f5 @- g. \
9 y6 n$ {' ]3 b9 ^& p# W) K" s
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000
    & S; M5 H, m2 I* V( l; i9 @
  2. 9 @: N/ r2 f6 d) p8 c' B
  3. /* W25Q256JV基本信息 */
    ! ^" A" P! X  \" l* Y$ t, B
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/7 ^0 v# [- X" j! q
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB *// |1 S; `* s# o. O% B3 B4 D
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */. `1 c9 Y4 \# K$ N+ ^% V
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    * t9 }4 B* ^% J" x
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    , _' r2 d6 h3 W
  9. 5 @' `$ R, M9 Z6 V4 w$ o$ E
  10. /* W25Q256JV相关命令 */
    ) p- q0 ^9 W3 K, @
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */6 P, e  c- y# ^& ]5 t4 a/ l" K
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */
    ' E% Z: J9 P/ o5 o
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */  j4 ?  D9 r# t; c* O; C/ X/ I
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    , g8 t' _5 V$ o$ r3 ~" [  M/ ]+ @
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */8 j5 @) e: b% V) i+ W
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */
    1 J- y* v$ F# ^2 h4 _

  17. $ ^0 B7 @: M( h0 F
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */4 c- B" }* d1 I. b5 A1 w
  19. 8 O  @5 A  g5 N( A
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

% k$ \+ `- _1 d+ }" G& _, |80.5 QSPI Flash的MDK下载算法使用方法) k& P7 f0 R5 {
编译本章教程配套的例子,生成的算法文件位于此路径下:2 H9 N) {( a$ c0 T0 S, E

2 g4 {" ], P  s- H. c
ed1f9465c60f92522e906639b76b29f3.png
" @8 k% I- [8 V2 ?5 {  z
) j; E& M7 G8 U% r& c  c, y
80.5.1 下载算法存放位置
  V/ L6 g/ z% p5 R4 S' ^生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:: e, V0 R4 p% ^7 s

/ E  D- e! \# W  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。
( u6 L) {8 T' x8 O$ |  第2种:MDK的安装目录 \ARM\Flash里面。; p* }3 d9 e6 C* ?: b" O* d

) t: {2 n* j/ }, ~
9d5d65cb90fb28ad36464cb1b8368759.png

9 L8 I: u$ F5 U, a) ]" L7 _( c8 F/ A/ x! i
80.5.2 下载配置
) y( ]& V$ T$ u7 v7 B8 o注意这里一定要够大,否则会提示算法文件无法加载:6 S: z% `# D" [' {, C1 m* l
+ Q6 H, a/ o6 v" c  y9 |1 V% L
8eefa57c2d7f128449000c2fb4e788d9.png

' q$ L# p9 a- V9 q  o" }5 H5 W: U, ]$ m# H, \
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。+ k' o! s6 Q0 S0 C
- R' d, N& {. a& h
如果要下载程序到QSPI Flash里面,需要做如下配置:
2 G( b5 n& z& h% I! B( X3 M* N# Q& @4 l1 K, J. O) H9 d
caa01b15ccf9a9700690d2ad1bdedecd.png
* o3 M% ]0 d6 u; c

+ a( w' m7 K( `) Z" h$ f* a( q80.5.3 调试配置
: S& j; w' B1 ~6 r# i
注意这里一定要够大,否则会提示算法文件无法加载:  G( G9 |, y9 s: r+ y$ _1 i4 J" a& p
$ f; g0 G4 a: w# v) ]
f23244d646498fc30aa5f1301a327430.png

$ o* D  X2 X+ u4 B3 j: Q+ t
- p( P; o0 Y( b# X$ v( `) {我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。
, x" ?: H" W: @
0 g" v- K3 D* Q2 X# M如果要做调试下载,需要做如下配置:
) p. c: `" s8 o% \% b, r% @' f4 n$ {* z, i# \# O* ?) u
84ee4945e5faabd776fedd6f8f7d96d2.png

. _2 |. p0 z  g1 m! x% p, j
/ U) k" ]+ i' j4 J80.5.4 验证算法文件是否可以正常使用% r: s  Y* ~6 v" ?1 N0 [
为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。# r7 q  _: N6 X4 N- o
/ _9 C, C% T7 Q" ~. R
80.6 实验例程说明
/ C* M2 I! [; Z5 S
本章配套例子:V7-060_QSPI Flash的MDK下载算法制作。% S4 I$ L. Y' D- ~) l9 T( _' W

3 p, ^2 T9 ?4 R! h* F7 n/ |编译后,算法文件会存到此路径下:# l0 N) m3 n! s, H2 |2 Z, s

* i* l- m9 U* D5 t

( P- [/ b1 w" p8 z: M* w5 @7 D+ B7 r3 V- y- Y
80.7 总结! r9 V: f6 u; J0 Y) P
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
& i3 t- W, [8 u% c- Z% b% l" G5 G# g

. D4 V. o/ c% F9 C$ y1 D" ^
% v% z8 G4 x, N/ G+ v5 Z. @8 Z" G/ w: N) t
1 s" D& B6 l+ ?, n8 q
b69a199f1b4a12d880e64c7ba134070c.png
赞 收藏 评论0 发布时间:2021-12-19 16:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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