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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 16:00
80.1 初学者重要提示! A6 `  a: _2 \9 [
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
$ n+ i, M: I! n, b) M8 v$ n0 V+ Q& B80.2 MDK下载算法基础知识
2 M  J+ o/ U& L0 VFlash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。5 s1 L4 z' m. {; J/ O/ p
9 l1 i( H' b/ D
80.2.1 程序能够通过下载算法下载到芯片的核心思想
; f1 @! A$ ^* B+ {/ a4 b" g认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。, M8 B4 p( x0 }3 S8 F9 i1 H6 w
. o, S3 A* @3 y5 v
80.2.2 算法程序中擦除操作执行流程" R# q$ o7 {7 u# U4 w/ d
擦除操作大致流程:
  g$ g3 u; N3 V( ]; w3 z) F$ |
; V/ H) ~6 T) S
d4b4af6b23f5f1d5ee3f3387ccb8fbdc.png
/ s- E9 b' z  K* K
) r8 v' ~- ~, F# n1 {1 J
  加载算法到芯片RAM。
4 `( M' i# N' \3 T9 ^  执行初始化函数Init。
5 F( q/ n2 d3 t% C  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。
, V, n( V' q& U8 Y' l  执行Uinit函数。4 z. g- V7 h. [' P  z/ V: O# V
  操作完毕。
1 o# ]) T# D8 w" k
! _1 y- b  O7 K9 A/ N' q80.2.3 算法程序中编程操作执行流程) z1 S& Y# n9 t* ?
编程操作大致流程:3 v9 i( V" }. p2 z0 R
4 z3 C! O. ]. M3 B. L: b* V2 i% }
d3f601364bb033d42e275ad686c03726.png
9 ~& p+ y$ w. ~

. d! T& c! J; |" O8 U  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。
' I  I$ z! h* u# A  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
% G& R* m% n  ?  c  加载算法到RAM。! d: p1 V/ @+ X& C0 \0 D
  执行Init函数。4 c# L! U4 Y; Y2 `0 s* J
  加载用户到RAM缓冲。. R  C  O5 _% N6 W0 G
  执行Program Page页编程函数。
$ d; ~/ S, ?, r+ a  执行Uninit函数。
) p" Z/ u; s. ]3 e3 O( Z/ K7 o# D6 q( u, W  操作完毕。) @/ E' t1 v  x1 V
* r# [- C2 u1 c; l/ |9 N
80.2.4 算法程序中校验操作执行流程
4 v  S: ?& D( F! p/ D6 \: E5 v& W校验操作大致流程:
# H4 c( t! f" a  |  d$ h# R% z; y
c7b2f373d946bc7d166ab1c1ba9b67e4.png
/ l/ ?( A8 m, i' j, H
3 a- O5 |7 W; e8 @/ Y
  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。
% e! M% V2 U9 @3 E  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:- h4 j, v+ C; r9 Y. Y& D! ~
  加载算法到RAM。& m) y2 L4 H/ ^. [( w
  执行Init函数。* }% J' ]' N5 n
  查看校验算法是否存在
2 T! j0 c* ?, g  如果有,加载应用程序到RAM并执行校验。0 `# M2 F- h6 y2 _3 q! ^! B
  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。( H/ g/ d9 t. L# x
  执行Uninit函数。
. K' o1 M) H* S  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
' v+ j. q" F& b1 W/ ?7 P  执行RecoverySupportStop,恢复支持停止。
. M6 ^  U  [& i: d8 S  执行DebugCoreStop,调试内核停止。
5 n' B8 k  w' s5 N  o) U  运行应用:
8 k' j% C& s# m% X) m) }  执行失败。/ l! L1 y3 O) n
  执行成功,再执行硬件复位。4 d& m" F/ r0 H$ K  \, e
  操作完毕,停止调试端口。
; d  V& {% M  q5 l# G0 F# m) c4 T9 u7 h; Y2 T
80.3 创建MDK下载算法通用流程
0 y1 o, s9 z' l' u下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。5 \% J3 {$ M( m, Y- ?3 D4 l* q
6 k; l+ p# O0 d, H; ?9 N
80.3.1 第1步,使用MDK提供好的程序模板
7 X5 U  t  h2 v' w- x6 s* F位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。
  n& Z& h7 u5 G1 K- e% p
$ E5 ~) F8 Q! t. R% k; F! j/ C效果如下:
: R( u$ z% A8 _* q/ w! ?. g7 a, \  [+ i- k+ N8 Z" w
8e08d948d34b38592e3699136725ade1.png
2 v. ]/ D: F3 G- D0 e, H: P
& N$ B  j! l9 z6 {; [
80.3.2 第2步,修改工程名7 a- R) h, v- k/ A
MDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。1 g$ f& C& Y. ]1 O

0 f+ m' l4 k! Q& P9 K80.3.3 第3步,修改使用的器件6 y: J; B6 Z+ e. e  {
在MDK的Option选项里面设置使用的器件。
5 V3 ?( v) n. O( `, O- o( n
# h# i; j2 @- d. Q6 J; }. \- {* G
347d6def457f6e101f1c2555f9156cda.png

4 E: C' {" V, ]( d5 G5 U  T  _5 [% Z( Y5 ~' e2 c0 j7 X
80.3.4 第4步,修改输出算法文件的名字
0 O2 K4 W, M3 n+ F7 @; W这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。
, {( U& T0 e+ T5 y% `( p1 K
7 ~* f& W- y) @8 l9 `8 ~+ U
07d754db49256bd0b73f4538a328f3cd.png

$ z" V7 F- p/ ^* J" X+ W& `" S( d( {& h. m; S- F& d
注:MDK这里设置的名字与下面位置识别出来的算法名无关:
+ _# _2 e3 q0 F+ h  _
3 S+ G$ ~; c% [9 p
a00b0ff99a0a691f19dfc2565fde811a.png
3 ]$ T- a5 S6 @1 i0 F3 A
; T, v# f/ P- V/ J9 ]  n6 |0 j6 K  r
这个名字是在FlashDev.c里面定义的。
7 _4 a$ C: J! W- j! I7 k) p) y" e9 i5 J* V/ X' S+ v# E' y
80.3.5 第5步,修改编程算法文件FlashPrg.c% _/ W2 E; F3 k% g7 Z# B
模板工程里面仅提供了接口函数,内容需要用户自己填。& ~: v5 ^# i* g# e$ |

5 ?  u, g: ^) {4 K. U
  1. /* " U/ `4 _' @: T  s# D9 n
  2.    Mandatory Flash Programming Functions (Called by FlashOS):
    # B: J2 D; R1 z' T8 E; z
  3.                 int Init        (unsigned long adr,   // Initialize Flash/ }: z& q5 c) ^6 {- _
  4.                                  unsigned long clk,: W" A4 Y' ?* p$ B6 e% G$ ^
  5.                                  unsigned long fnc);
    3 j) Y2 l- g. M3 k
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash
    : x7 v7 `4 T: |0 ~
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function
    5 b2 i& c1 A# x( `
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function1 D! l, t$ a- f+ |" r3 E8 P) \" i* w" b
  9.                                  unsigned long sz,: T# K4 z9 ^7 I3 n' B3 y5 d
  10.                                  unsigned char *buf);$ q% ?" `- l& T. K

  11. 7 b9 H) R. |5 ~: r! v7 e
  12.    Optional  Flash Programming Functions (Called by FlashOS):" b8 z: u1 Z; I! W
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check1 S2 o1 b& X" a2 A* K: n- g
  14.                                  unsigned long sz,- d1 [9 z% Z6 `
  15.                                  unsigned char pat);5 _( o& k; h% M8 Q5 v) X
  16.                 int EraseChip   (void);               // Erase complete Device( o' {4 H3 Y8 g, r; W9 z! r
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function" z( A' F; z+ T+ z9 q
  18.                                  unsigned long sz,# q# @, o+ O! }
  19.                                  unsigned char *buf);
    # B! ^; r) T" I2 N. [/ x
  20. 5 u9 z( E* r2 h3 c
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space2 n4 J9 P1 A5 q
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space! n% s6 N+ P* O: N3 G/ x' H
  23.        - if EraseChip is not provided than EraseSector for all sectors is called
    $ `$ h5 u+ D9 l& H3 [
  24. *// t, e& x# Z( O7 W& h4 \5 s
  25. : S9 F2 {/ n, n) e% {5 u
  26. /*! p6 I* M: r6 Q- {
  27. *  Initialize Flash Programming Functions$ b+ G2 v1 E' l. S0 u! c4 F
  28. *    Parameter:      adr:  Device Base Address; |  I- d" T8 q( F
  29. *                    clk:  Clock Frequency (Hz)+ K# A9 K' @. Q9 N
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)1 |# ]! X  U+ r; L8 w  a! u  b7 C
  31. *    Return Value:   0 - OK,  1 - Failed
    9 g" u- W7 A# j: w
  32. */
    ' h: B6 i2 F7 {/ v" r0 S

  33. : \$ y* m9 Z$ L# A( J/ A
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {; Q& b: a; {- W+ T- M
  35. $ f4 U6 c( b( u0 I
  36.   /* Add your Code *// w9 T2 c$ V8 [& B
  37.   return (0);                                  // Finished without Errors. j7 B1 k2 ^: ^( u& g
  38. }
    ) T; S" o& N" I
  39. , P" }( j; L1 f4 }  U8 f4 i; S4 i
  40. 3 j" |% H9 z1 c/ S
  41. /*, e, I& Z. j7 \! ]
  42. *  De-Initialize Flash Programming Functions
    8 f  U: f; D, O4 V4 Z/ m8 x- v
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    8 P6 s4 w5 f' \
  44. *    Return Value:   0 - OK,  1 - Failed5 a. _+ I8 t$ @1 @* ^/ A
  45. */
    ' {% m: A' @9 I" b, }3 Q
  46. 0 W$ N8 t: x8 \" }& P2 L
  47. int UnInit (unsigned long fnc) {
    ; N% @- E/ T; ~) O

  48.   B8 A6 ^# P( m" y
  49.   /* Add your Code */
    6 @8 ^4 X- {7 y, t8 e. Y' ~- ?, n% b
  50.   return (0);                                  // Finished without Errors
    0 ]  f; y8 o8 W/ i3 k5 h
  51. }! ?% f; P% [0 x; ?9 ^( ^3 E8 \
  52. 7 F3 e6 h0 e) f9 q  G( h5 D
  53. , s$ g7 u) T0 d' B) ~
  54. /*
    ; F& P6 |* e9 F
  55. *  Erase complete Flash Memory) r% C- H, t9 V. q& d/ x* M
  56. *    Return Value:   0 - OK,  1 - Failed# j% ]% \( W1 P, q) E* X
  57. */
    ' d4 B4 k5 n7 m- j
  58. / S/ R/ U9 y/ \3 j( Q( E/ Q: K6 r
  59. int EraseChip (void) {
    ' Y' U4 m# i. O
  60. 4 e4 G- b# m& {/ x/ e0 w. K
  61.   /* Add your Code */
    2 K( [4 W) K3 B( B1 {4 w! @$ ~0 G
  62.   return (0);                                  // Finished without Errors! P( I( s  _! C. }- j3 A
  63. }& [& n7 U8 t# w( I7 O( \

  64. ; K5 R# O( C1 G# X+ E

  65. , `; ?8 `( W# r9 i  C, i
  66. /*
    ( [; o" P0 X. H8 {
  67. *  Erase Sector in Flash Memory- E2 L0 q% R  F  [% k. b
  68. *    Parameter:      adr:  Sector Address" g. y3 K3 N, n" G6 V' r: L
  69. *    Return Value:   0 - OK,  1 - Failed5 c* }  h% R% Y3 m
  70. */8 m5 ]! q7 Z3 U# I. `2 @+ l

  71. # y* l$ K4 _# `( A( P/ T
  72. int EraseSector (unsigned long adr) {7 ]7 L8 L% X, n9 L
  73. , O/ i5 @+ V- M" p  k8 [( u
  74.   /* Add your Code */
    ! X7 E9 Y: I6 D
  75.   return (0);                                  // Finished without Errors3 B  ~: X* p( o, _' c/ Y- ^
  76. }, ?! }$ Q3 z/ Z% Q3 }

  77. % w) `- y! `' W2 f& `

  78. 5 s: t; _2 H6 s* r# r
  79. /*4 r. O, `9 t* }- @: s3 J
  80. *  Program Page in Flash Memory1 S, J" A6 N, |, Y! u% I
  81. *    Parameter:      adr:  Page Start Address7 t! i/ z' n1 k6 R# x& k
  82. *                    sz:   Page Size
    , m$ L' `  H4 V
  83. *                    buf:  Page Data
    & x9 B% f1 `; y% D0 T- X
  84. *    Return Value:   0 - OK,  1 - Failed1 F0 b2 ~! O6 l8 L% B2 v
  85. *// M/ T! X/ J$ n

  86. % @- h5 a  M" `
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
    9 {  Q0 T) z, N1 x% ]1 ~+ u

  88. : w# [% Q3 C% ]9 I& Z+ Q8 J+ O0 q
  89.   /* Add your Code */
    % B4 t, j( |1 F/ H6 N' E
  90.   return (0);                                  // Finished without Errors3 A+ y6 N* U, e" ~5 m. A
  91. }
复制代码

( H2 n9 U, \3 M/ [- g. R7 {' X) T3 P: G80.3.6 第6步,修改配置文件FlashDev.c. \6 T0 F1 A# u* {7 [
模板工程里面提供简单的配置说明:  P5 V' B! J2 ?' i
) |$ B  L: d6 m7 B1 P+ {7 `- m
  1. struct FlashDevice const FlashDevice  =  {5 N' z0 R/ I9 F5 X9 E* P
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!
    0 ^( M/ ^6 A3 A: n
  3.    "New Device 256kB Flash",   // Device Name
    , g" r/ k- p, ]% ]+ ]
  4.    ONCHIP,                     // Device Type
    8 l; k  L3 T( O! \
  5.    0x00000000,                 // Device Start Address
    ) p$ a  r  P$ |( s
  6.    0x00040000,                 // Device Size in Bytes (256kB)
    + B$ k; S9 N3 O, M
  7.    1024,                       // Programming Page Size- k( e/ s& V# l5 i0 i0 r0 ]# q5 C
  8.    0,                          // Reserved, must be 0; |& b* a( c0 s* c. i0 C6 S/ P
  9.    0xFF,                       // Initial Content of Erased Memory
    7 M7 G; E, {, Z
  10.    100,                        // Program Page Timeout 100 mSec6 D" V! [, ^+ E' m9 D
  11.    3000,                       // Erase Sector Timeout 3000 mSec6 @  ?6 i4 V' O# B

  12.   g$ o2 o$ L; Q+ I- w  s8 V: N
  13. // Specify Size and Address of Sectors
    1 P. [, t4 Z7 I9 D' A
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)
    8 W% t# ~7 Q  f, S" b2 x
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors)
    " o0 W8 E* U' W8 W2 b: m% [4 b
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)) d# Q% c0 t" D
  17.    SECTOR_END- w" X: b  o6 T- [0 b+ M7 c
  18. };
复制代码

' @, f6 T4 \; ?; D) |$ _0 p注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。# C3 c0 o+ P6 n! [- S5 D
. T3 q+ N1 l4 Q1 u+ o
80.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关
* }, v6 S6 h) n9 r# \% v) S* A2 @C和汇编的配置都勾选上:
/ [: h+ T1 L7 J- {# q5 Y! V0 S+ _1 v8 v! ]; D* D
1d27a7314fc6c472937ad43852722b6b.png
% N/ A( E5 N0 `3 e( ]2 H8 c, d, Q
+ R5 Q+ R( I+ S, @
汇编:1 k# d' v4 f( J

2 ^3 Q: P8 o0 X
bbea604970c4ef63c77f10e553e28ea6.png

  f; y# a3 H, }7 A& Y4 e
# |$ b2 x" A2 Z9 w0 P" e如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:% i6 o- h2 o! k- }- k8 w& r  s

9 e6 V, o2 l5 B) |' A(1)加载以响应运行事件。8 E& G( ]; V8 V) D9 N
6 Y4 s* Y' \% x. Q9 {& w, A0 R
(2)在不同情况下使用其他例程的不同组合加载到内存中。
3 I/ W6 ~: b) j6 Z2 V; w$ V5 r( |+ `- y4 D* v
(3)在执行期间映射到不同的地址。
8 {6 y/ q' |4 @4 u  |5 h# E1 {; W3 M4 C, B. _0 b: B: `
使用Read-Write position independence同理,表示的可读可写数据段。
: Z5 ]5 G1 |/ d& b2 t8 X/ N; D0 M7 f1 {2 X
80.3.8 第8步,将程序可执行文件axf修改为flm格式
! I6 f4 b3 k( S! U' l+ }( V: R
通过下面的命令就可以将生成的axf可执行文件修改为flm。
- l, b# Q6 S) s
1 L) L* o/ z; A# o# t( F# f# O
6b504444979e9fa3a3477c6284abca8b.png

. P$ H0 j. d! t# l; O' j( K0 g% l! X. A  r1 o7 e: `
80.3.9 第9步,分散加载设置
! N; Y0 \, w# G! z0 F) p' g我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。" A3 F8 |+ h# g# `- r4 l

$ j3 q6 `- I0 \1 u
e8e79854b204b618bbda32af92f28bf2.png

* O& [3 E5 ]8 d( r, [8 [! N4 w5 E# w! G
分散加载文件中的内容如下:3 m' q2 |- W8 o3 C3 H" s& k

, H' j! _( y# z$ e. F) e2 U
  1. ; Linker Control File (scatter-loading)
    + @5 o5 G& ?7 l8 j' t/ u' ?6 K- C
  2. ;, _& G$ C, M" l! K4 r

  3. 2 y. {2 O5 A1 ^4 a) K* {
  4. PRG 0 PI               ; Programming Functions8 A0 j% S2 P1 [
  5. {$ i  o3 q: t7 i+ p2 X: _+ ]9 Z% c
  6.   PrgCode +0           ; Code& r5 a3 d# g* E$ M- a
  7.   {9 E5 L; y5 L9 [: B; q7 k' f
  8.     * (+RO)
    * s; U) z" S- l  \9 J1 @6 J4 H
  9.   }7 _6 i4 o) y" ~5 l
  10.   PrgData +0           ; Data
    ; d: [0 j9 l0 ?5 w: J! E; [
  11.   {
    4 q1 F9 X+ O/ L2 e2 ?
  12.     * (+RW,+ZI)5 r9 ?: n6 k9 V
  13.   }2 l: {+ W* A! _7 N' h
  14. }* t6 w: V8 o* A; s% [
  15. ; o) X4 O9 U- L, q/ Y' r$ g6 o* v
  16. DSCR +0                ; Device Description: X5 n6 Y7 T$ ?: k* E
  17. {& ^  c# x1 E; H0 U1 b
  18.   DevDscr +0
    ) M4 r) b/ @2 j! a& s
  19.   {. F* n; l: W7 H+ _% w, j! W2 \
  20.     FlashDev.o% h% j' W4 I. _
  21.   }
    1 k/ |: |. X. _
  22. }
复制代码
5 D, S1 w6 T" ]* z4 L2 I9 n
--diag_suppress L6305用于屏蔽L6503类型警告信息。
/ A& P9 U5 C: Z9 p, x0 x0 R( l( i0 T1 x
特别注意,设置了分散加载后,此处的配置就不再起作用了:7 B4 E3 v2 w7 B0 F/ h4 a6 M6 T

1 I* H$ Y2 [4 ^& V1 m
b249eb40e5562b04ac3eb05ec0219c1d.png

/ }5 G0 s7 A% N! D
, H; G3 y: B# ^) y5 K80.4 QSPI Flash的MDK下载算法制作
) ?1 t8 n) O$ p/ c' u- J下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
. e4 E9 v) H# Q
3 J4 }1 u/ \, N% E) G80.4.1 第1步,制作前重要提示
2 u9 x3 N) [" {- c: T' Z这两点非常重要:
/ G2 W$ \5 @6 ]* j
6 t2 I, q* t, K  程序里面不要开启任何中断,全部查询方式。3 V- N4 X8 U# o. o. O" h
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。$ V& c# G. G- ^
( r- u& W  y: v" H' ^4 O  g
80.4.2 第2步,准备一个工程模板/ ?& H( j: O/ {0 o* e7 N! g
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。+ @) R/ ?0 _8 b; Y( ~5 t
6 E) \% g+ z4 t/ l% w7 k3 v5 e: E4 x0 w
a91aaf84a25d730b77273624d2b502f6.png
1 h  w$ }4 U5 @8 R% h$ p3 N
2 ^( `0 j# t- ?
80.4.3 第3步,修改HAL库
" J8 J6 m9 X* X. X8 c. d; u这一步比较重要,主要修改了以下三个文件:
8 `2 m9 C# F- B: C* F
4 b6 D2 U  z. o8 a2 s7 L
4471afac6bbc201866d0d9a8ea7d876b.png
( v3 l( J+ V5 X. o$ R
" C* n! `! W$ B
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
2 R  H8 W# y6 _
8 C: e" ?0 n0 I9 T80.4.4 第4步,时钟初始化  ?2 b: N9 w' L+ [
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:7 i3 T6 g3 g3 E# Y' |6 A& y5 B3 d

  x- d2 }; H: P- B4 W8 {8 a2 g
  1. /*5 `) T9 d" x. g  H
  2. *********************************************************************************************************$ v" s" a6 `% M* W. @. ^6 [
  3. *    函 数 名: HAL_InitTick
    : D! H7 E, |- ^7 @4 G) f
  4. *    功能说明: 重定向,不使用
      S, ^0 ?' K% V7 x" t- k
  5. *    形    参: TickPriority% H; n" f, J; ?* s6 H$ ~) F1 W8 Y
  6. *    返 回 值: 无( I; G1 r6 X# I& T% @
  7. *********************************************************************************************************# i! g: \$ U; H6 F
  8. */
    0 V! @2 E7 j0 P$ R
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    0 k7 n1 f1 p7 C. ^% e
  10. {4 S# T$ H: p* X. j! b; b
  11.     return HAL_OK;
    ! F# y; w! ~& [' P
  12. }
复制代码

) B4 r5 l/ k" B然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:6 T" p. k% e5 g* h2 \8 l
1 y3 j! a: s$ ^8 t0 Y
  1. #if !defined  (HSE_VALUE) % S7 _3 |" x! m  e' N  d- _
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */5 _  H% K6 W4 Z* h' G1 z( J6 G
  3. #endif /* HSE_VALUE */
复制代码

9 R6 F2 J- p' S( `( |, J' |  C$ l8 \最后修改PLL:- R1 M2 c0 b- a, R

9 A/ y6 B9 w' U
  1. /*5 T: y4 @* F5 h3 `* Z; I/ J" U1 ~
  2. *********************************************************************************************************3 C' W! U- V; b' d1 y5 a
  3. *    函 数 名: SystemClock_Config( }6 n7 }5 S' [' I+ O) t
  4. *    功能说明: 初始化系统时钟! V" W' q( D3 O9 N, m
  5. *                System Clock source            = PLL (HSE)
    5 W/ `# U; {. K0 R2 t
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)+ ?" d* H* S0 x) k1 z* n
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    , \( e9 z$ [" }$ Q
  8. *                AHB Prescaler                  = 27 [" y& G9 {- l  h! k. u. f
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    . y+ Y- y7 }8 D+ U( l" i& _% {5 d
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)1 M% m5 X! Z9 j0 R9 k2 T
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)( q; I/ w5 V2 K0 G: i! Z
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    4 u* Q: A1 t1 G
  13. *                HSE Frequency(Hz)              = 25000000
    7 Z& ~: \$ D) B# p3 R7 g5 F$ o1 o
  14. *               PLL_M                          = 5
    % B9 O8 k/ r& s" c/ }! T
  15. *                PLL_N                          = 160
    ( z8 K" V* j- [$ ~  ]) Q& k! K
  16. *                PLL_P                          = 25 U  E+ _1 u2 E1 G7 `: q
  17. *                PLL_Q                          = 4
    - m6 H7 x6 n# ?, \2 v; F+ [
  18. *                PLL_R                          = 2- ^8 S" l7 ^% o
  19. *                VDD(V)                         = 3.3
    & ~4 L( m, n6 C6 `
  20. *                Flash Latency(WS)              = 4
    ' p. L+ p5 h% k4 M0 g4 Z5 f
  21. *    形    参: 无) I3 ?8 f$ v' j! k' q5 ]9 p
  22. *    返 回 值: 1 表示失败,0 表示成功: J$ N5 S0 d' r. @; e- B, r
  23. *********************************************************************************************************
      M; L6 b* L! B1 ^
  24. */- }2 {$ A7 |' m6 d* f5 g/ u8 T2 z
  25. int SystemClock_Config(void), d. D8 M2 b7 o% b- K: D; I7 O# j9 c
  26. {- s6 G' w3 a3 ~1 g- Y  b
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};2 j6 j% a- }4 u: O, Y8 B
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    3 F& s  O' J8 o+ s3 I; r
  29.     HAL_StatusTypeDef ret = HAL_OK;. d# N! C8 O0 w7 p6 @8 L' F

  30. 6 ]7 i. @, ~0 t! H. p
  31.     /* 锁住SCU(Supply configuration update) */9 }0 z$ o  g# N" e) E/ ?
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    7 e0 d5 B! z$ S) H0 _1 ]% f
  33. 4 x2 R, e5 [8 S; V2 [" a  S* U, |
  34.     /*
    4 a0 u0 ?( N% \+ V5 U
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    5 |6 J! y- C' J) J% l+ r( @7 F
  36.          详情看参考手册的Table 12的表格。+ H, q2 l5 K9 n0 {4 O( |8 T
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。; S4 Q5 |* K8 z6 B! U/ m' x' q: f9 o
  38.     */4 W2 @) R; g( _# Y
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    - X1 B0 @0 ?1 ?4 o- t! z
  40. + y9 ~" K) ?3 B& O8 C0 c2 x/ w6 r
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    + [( B) M4 \5 j6 ?6 y" I6 c, N# ~
  42. 3 l) r' Y+ I6 ?4 k6 K& `
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */: O  B; y7 D4 G+ Z* w' V1 i
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
      b- n6 i- V( |5 H  [
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;% G9 f0 t6 o% R$ L$ g% R
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    & x5 e% S' r) S  Z, S
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;% Z) N4 a4 \7 w! F7 @
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    ! ]5 r. E6 y* N
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;1 ^5 E% [* ^( {' X

  50. 4 s7 W4 o# \- W5 J! }- f' Q- I+ L2 F
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
      O/ G! Q( }* J
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    0 A9 Z& A1 R8 I  G3 B* v4 l3 V
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    ! v% \4 y- l( S/ g0 q8 A1 M
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    7 c5 T, f0 k% {8 z2 F) D
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        ; B. b( s' \+ O5 b
  56. 8 J# d' i; X* s% A
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;5 R3 J6 X5 ~: f2 m; k
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    2 e0 r: |& b: L2 D, C
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    ; ^0 x$ [: U: [" j1 y6 `8 }: ~
  60.     if(ret != HAL_OK)
    . {$ H8 i9 r$ f0 [$ g
  61.     {' H& h- |( U0 I* X; E; f
  62.         return 1;        , _2 E. t$ x2 l. x) p5 {5 A+ \" l2 M
  63.     }
    ; `  }. s, |# V, {
  64.   P+ I& R2 q0 G+ f( h
  65.     /*
    1 B$ |3 D) b, p
  66.        选择PLL的输出作为系统时钟* T" A$ V8 |6 |* x8 h  q
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟9 o# J1 c; u# }" T1 V: O. l
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    " F2 S6 n7 G" @: x8 u6 Q0 {! N
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    ) X6 j: a% b7 a* W/ Q* n6 z7 {
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    9 L6 ]; P: b# f; R) V
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    / h6 ]( p) V: g8 U  e3 V; S
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    ( m  r' T$ [! c. Y' }7 ]- j
  73.     */7 v9 w9 z4 y, `1 q" W# T
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \! f+ D' p) S# B- K9 ?, T( h
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);2 o' `  F2 V+ \: _

  76. 2 r# c) k; n* N- ^
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;6 K3 f) ~1 h" M9 n/ a, J) W
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    % Z' b7 S4 M' k, ?5 l3 o$ g
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    8 Q: i) J, f  x$ e: l
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    : q. }0 w# m' X- l6 x  g7 \3 i
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; 9 W5 |, G) T/ H" i
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 4 _( |1 l% ^, y* q3 r6 Y' |& i
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; $ k0 ^6 X7 m) h
  84. 5 e, D9 X6 E" ^) w+ p
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    + o3 c7 e) x" D4 G' K
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);3 J/ P6 ]! I5 K; O
  87.     if(ret != HAL_OK)
    # M" C6 Y, y0 i% A5 h7 k, n
  88.     {
    0 ]3 l- }2 R& P
  89.         return 1;" Z# A+ d. A: \% f; T" m
  90.     }  l4 d2 I8 g  I7 ?' p9 l  _# D

  91. % {  n4 v- D5 H* L# I6 K# J4 L
  92.     /*: Y$ m/ @% G  ?; g+ A% T7 m0 y
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 / F& q* l" b* d! J4 L- k
  94.       (1)使能CSI clock
    : f5 k5 |  ^9 l( d2 m/ j
  95.       (2)使能SYSCFG clock% \9 x6 }. y1 ?2 ]: e. g- @
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    ! w1 i$ E4 e1 _+ p" H
  97.     */
    1 A& B4 {/ y/ ]0 G2 D& O% G' Z
  98.     __HAL_RCC_CSI_ENABLE() ;
    ; x( Q. \& V# D) g% ~4 e$ ^" e
  99. ! S: V7 s% j+ @1 T
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;+ ^- S4 s; q' ?  _0 ~' W$ @# P
  101. 3 |1 ^) M) r0 q7 J2 x9 i
  102.     HAL_EnableCompensationCell();
    4 @* _- X3 `3 v  a

  103. $ H7 O) K+ B3 X  \. X2 ~
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    9 k5 {7 _. D% J; S) m: ~+ f
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();$ n! g8 j" E7 e7 o0 o6 g' [+ z: a
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    ' x' `5 d$ K- M) J! S+ g" P

  107. 9 D! t. @3 b/ A' ?
  108.     return 0;
    ! u- }8 {3 t/ C9 ^( f* L2 l
  109. }
复制代码

& q5 B; H% W* {80.4.5 第5步,配置文件FlashDev.c的实现
: y& o! z- o8 a2 t  G2 J配置如下:9 H7 S8 f% r- n, o& M$ K* ]& L3 e

# o9 a0 Q% M1 u- ?
  1. struct FlashDevice const FlashDevice  =  {
    7 y+ V; a7 ~0 c
  2.     FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */
    9 u; n6 h  v$ V+ q! S' p
  3.     "ARMFLY_STM32H7x_QSPI_W25Q256",   /* 算法名,添加算法到MDK安装目录会显示此名字 */% G+ d) M- N$ _/ c
  4.     EXTSPI,                           /* 设备类型 */. n1 |+ u" `8 l( b! @  }
  5.     0x90000000,                       /* Flash起始地址 */
    4 P" K, y% N+ F6 @
  6.     32 * 1024 * 1024,                 /* Flash大小,32MB */
    , N: q  i3 C5 o
  7.     4 * 1024,                         /* 编程页大小 */
    ( w( ]& W! i9 s! A; t
  8.     0,                                /* 保留,必须为0 */8 V/ y7 b; Z  ]0 q/ a* A+ n0 ~4 j
  9.     0xFF,                             /* 擦除后的数值 */0 ]6 P* H6 Z- C
  10.     1000,                             /* 页编程等待时间 */. X& h. G# T  G0 ~- i3 b
  11.     6000,                             /* 扇区擦除等待时间 */7 p1 M: T( F& X% U$ v! ?
  12.     64 * 1024, 0x000000,              /* 扇区大小,扇区地址 */- C' {9 [! [  N& O0 M- `
  13.     SECTOR_END   
    $ I4 N/ S6 F, T1 E
  14. };
复制代码
# N( K3 o) E  X1 L+ c! L4 g, k
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:% I5 V8 m6 s. R2 c& \
/ s) c5 Z7 k8 ?- W
ffed7a581be4c5da388ee29fb67249b4.png
. p! h3 |: C6 n) }/ `
' `. m% R0 `! T) e# c
80.4.6 第6步,编程文件FlashPrg.c的实现* j: J9 s" l% c( Y" T0 D
下面将文件中实现的几个函数为大家做个说明:
' d/ x9 Z2 j: j; K# G/ r& b2 T5 a$ O6 t% `  U3 j9 J8 z7 N" P
  初始化函数Init
5 E8 }4 i( V! D0 S0 v, N  A9 Y2 ^
  1. /*$ `' Q$ V) b% S7 P: r
  2. *********************************************************************************************************
    : Y, k! z) x/ Y0 \$ \( W( ^
  3. *    函 数 名: Init1 r9 Y7 T5 I) l4 d6 n
  4. *    功能说明: Flash编程初始化' v5 B) |' q% p- m3 v
  5. *    形    参: adr Flash基地址,芯片首地址。
    5 G% e6 H5 [1 S; u% i
  6. *             clk 时钟频率1 C8 L5 ?  S! C" V' ]0 q8 R
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify% i2 O! c, W, z8 v$ P9 x
  8. *    返 回 值: 0 表示成功, 1表示失败) C9 Q: i/ F: C4 G; X
  9. *********************************************************************************************************
    ' j1 A! @/ [; X: @/ N3 h* H7 i
  10. */
    . J3 G; A+ N" z+ G8 T' v% {- R
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) 0 A+ _. Y, @' O1 a5 ?  f0 y# n
  12. {
    ! G! T7 Y/ N0 y+ g2 _' F' j# u; T
  13.     int result = 0;& U$ g# ?- O2 p8 J2 o

  14. ' \1 f  g8 ]; T$ V3 R1 F- I5 b
  15.     /* 系统初始化 */
    + L" _% Q2 f, W7 R/ g' Y& H
  16.     SystemInit();
    : E6 _+ J- g/ D+ ]3 {

  17. 8 {' N  G$ n8 C* I( W# e' t
  18.     /* 时钟初始化 */$ y* |% |9 r( s5 `5 U4 f! ^! z
  19.     result = SystemClock_Config();
    % h$ s, T$ c& `  v2 F
  20.     if (result  != 0)
    ; b3 Z! d9 F6 R- @
  21.     {3 S) A7 @: V) |1 l: H/ x5 w
  22.         return 1;        % e7 r2 v* y1 i) L8 ]! R; H2 }
  23.     }
    # {, n1 W' ^2 A9 E5 J* r/ q
  24. ' ?% e, J$ c$ t. I' d
  25.     /* W25Q256初始化 */
    , n. F/ O: ]$ X. ]; x9 Z+ G
  26.     result = bsp_InitQSPI_W25Q256();
    1 V* j: ^$ E3 p: J- W
  27.     if (result != 0)
    # m& m( b3 `& c5 _9 S
  28.     {) q5 O8 k: I0 z
  29.         return 1;7 Z9 [; X% f7 m3 Y1 c7 D
  30.     }
    5 A" V5 R" M) j: v- S8 f
  31. 8 b4 q5 s" [, h% |0 g. C
  32.     /* 内存映射 */   
    9 m# v4 s/ D. x+ k' B, M
  33.     result = QSPI_MemoryMapped();
    ) U7 s& v9 J; E+ t3 c/ H
  34.     if (result != 0)8 P7 r/ N5 G* N( L; t( f+ W
  35.     {
    5 W# D- b0 A* _. q
  36.         return 1;4 j8 {; G6 H( Z# Q/ ?% \: P
  37.     }$ W( I0 ~! I5 I1 i( |6 D0 R$ ?
  38. 5 y/ n: I" Y% t/ M) R' W. W
  39.     return 0;
    $ H7 [; u$ {: n
  40. }
复制代码
/ U9 C: {6 k& T% F/ M- j/ m  b
初始化完毕后将其设置为内存映射模式。
5 k7 A* ?: }7 ]1 G) P9 B* M4 g# |# @/ M! d! @# {; x
  复位初始化函数Uinit5 q' e* E" g1 s0 s0 t. O- _
擦除,编程和校验函数后都会调用此函数。
( u8 z. D; h6 o8 a
$ E+ c; y5 N; ^) d0 d
  1. /*; ~1 k' g' l" Z+ T) h1 v) W
  2. *********************************************************************************************************! c& L; @% K& Y2 X. j* u) U( B
  3. *    函 数 名: UnInit
    7 e" s! \; T' N6 `& ?  S* ?% l
  4. *    功能说明: 复位初始化
    : ?! ], G0 F- m8 m* J% z5 {
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    . R; t' K5 N5 q. w/ I  s0 H- z
  6. *    返 回 值: 0 表示成功, 1表示失败
    - T0 ~2 S5 J# c6 U+ @/ p
  7. *********************************************************************************************************( ~0 I9 X7 j+ a  |0 e8 z1 {
  8. */% Q5 x* I& Q% T2 C
  9. int UnInit (unsigned long fnc) / g( C% p5 T1 |7 m3 n/ j% P
  10. { 3 X* C7 u$ G# e3 C; A
  11.     int result = 0;0 l2 K" e! a) j

  12. , c3 u2 X0 V/ p5 s2 L
  13.     /* W25Q256初始化 */
    , N* K, A9 Y/ k) h% f
  14.     result = bsp_InitQSPI_W25Q256();
    , w/ a. o2 ?3 A7 M) ?4 P2 V3 F9 ~
  15.     if (result != 0)& x  k& N+ r+ @1 q; O3 t- |) T
  16.     {
    / @, j2 w0 A( I! S9 u# ~
  17.         return 1;% K- D, g( q0 A5 L( [
  18.     }
    " F! b+ Z- V, _- M: h

  19. 5 @( E8 S3 h( O) ?; A
  20.     /* 内存映射 */    9 H0 ~  z1 d0 Y7 X* U9 Z' ]
  21.     result = QSPI_MemoryMapped(); % S1 x  j; A6 s
  22.     if (result != 0)  |' N' h* B9 Y* r" n3 v, J% J
  23.     {* ~- R, g3 F  Z1 Z1 s% ?( a
  24.         return 1;
    ( W) D1 _1 R1 a* a2 _
  25.     }
    : ]" K  ?2 }5 |# K- f! W' T
  26. - Y" H8 i, r9 g2 k
  27.     return (0);
    5 O$ Q7 p) Q3 d) M) l, U
  28. }
复制代码
4 U2 r& f2 O/ `
复位初始化这里,直接将其设置为内存映射模式。
  L$ |' R& e% }( f) Z3 c; g8 ^; D  [# Z8 J0 t
  整个芯片擦除函数EraseChip
- ?/ m1 M' z  J3 b8 R( i& n* ^如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:7 a5 @$ W2 N- N  Z

" q8 b! q+ ?" Z1 M
f623e662bf09233ea594f820f4c7952a.png

5 a; V/ O* J) d# ]! p) x# i; f- y9 E3 |& o
实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间,比如32MB QSPI Flash整个芯片擦除需要300秒左右。/ J6 C2 b5 a" e. J/ h
+ C9 [8 t/ @/ \& z& u3 n/ O: \
另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。
: Y3 j+ l/ O% z# {6 T$ _6 a% E( o
  1. /*
    3 I5 C: y2 Z3 K0 G! V
  2. *********************************************************************************************************8 |. Q4 ]" D! q5 }2 a" m
  3. *    函 数 名: UnInit
    : X0 j/ ~: V2 M; a
  4. *    功能说明: 复位初始化- J/ M0 ^% i' c, z
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify) @$ g0 F3 }0 K3 M$ D
  6. *    返 回 值: 0 表示成功, 1表示失败
    $ ^/ G/ p& D8 t( i8 S
  7. *********************************************************************************************************
    - k1 W/ V; i7 U9 h
  8. */
    ! W! ~9 Z% s! j2 f5 I% N5 r0 b: M
  9. int UnInit (unsigned long fnc) - {3 ?; o' a0 x1 f# M& }1 d
  10. { 7 r' X' g1 i( x, x$ Z
  11.     int result = 0;1 z4 Q! K) B8 `, ]  s

  12. " V6 U+ p+ w& w" ^+ q  `  d# A
  13.     /* W25Q256初始化 */
    - m  \9 J: b. ^/ d7 W. f
  14.     result = bsp_InitQSPI_W25Q256();7 M0 P4 V) P6 K; G, {+ P" }
  15.     if (result != 0); [  \: B. H2 i
  16.     {
    . I% U! u/ `. ^5 h2 z1 S
  17.         return 1;
    % Q* {. u  d3 ^2 {2 _
  18.     }
    2 a4 q# S, h  s
  19. 7 r" W* X( X% p' `
  20.     /* 内存映射 */   
    + T  p7 }5 r$ t) Q! P. O
  21.     result = QSPI_MemoryMapped(); ( c1 S0 j" M5 l# ^) e, e( @* P( u
  22.     if (result != 0)- ~- R% G2 q' X  z4 B& C) l
  23.     {
    1 n9 _3 l+ a/ J. x4 Y
  24.         return 1;2 y) e! m3 a4 ]+ }2 A- ]
  25.     }
    ' [8 ^! i6 c9 |' b1 H

  26. % _( p( A# D: c: M$ H
  27.     return (0);
      j2 q  Z! ~+ p( M
  28. }
复制代码

" o3 s$ Y; J1 W. o  扇区擦除函数EraseSector
/ B" x& z9 E. E( l/ j如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:7 C+ ^' K6 ]  N- c* v

1 j$ ^/ H& H& v+ |, [& ^
733d5af26119f64bff0261515013d3a6.png

& U; \& O6 j4 [# P+ V, y8 Q& W8 y7 m6 L% ]" O- ?. K4 x* @. u, p
  1. /*/ j, [& r* e* q- ~  u+ ~0 ]
  2. *********************************************************************************************************
    + {6 }- W  @- |7 C8 S
  3. *    函 数 名: EraseSector
      d. F* ?* ?+ [2 W5 C: i6 U0 i: I
  4. *    功能说明: 扇区擦除9 j  R9 s- h; o. l  }" I5 W9 |9 T, s2 V
  5. *    形    参: adr 擦除地址; ]0 c! e! Q( j' T
  6. *    返 回 值: 无1 N# G# u4 Q+ g
  7. *********************************************************************************************************
    / t! D4 F' u$ i8 ~! I: o  y
  8. */# q- g) K: i2 x9 E  B/ @
  9. int EraseSector (unsigned long adr) 8 c7 Q8 A: p- _- G; R9 X3 ^! s
  10. {   
      C: F8 t& Q3 z6 ~6 z" q
  11.     int result = 0;
    * k* C8 ^8 v3 {1 D) z. a) ]
  12. . c% p( i+ J( O+ p* l
  13.     /* 地址要在操作的芯片范围内 */, T1 d- j& y6 O$ Q% B. w# R
  14.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    - r  d$ ?2 ^# e
  15.     {+ r! V' p4 b) X
  16.         return 1;" S0 ?2 q) w: H- a
  17.     }
    # p7 X1 }& t2 p$ @
  18. ' l1 [3 H/ j/ f+ ^. _) j3 v' D
  19.     adr -= QSPI_FLASH_MEM_ADDR;$ q1 ]& a. p+ r+ I! i  R- ]! r
  20. 0 g8 x+ h8 W  s& A$ N1 v; c
  21.     /* W25Q256初始化 */& ]. M- j' B* F2 o# a2 {
  22.     result = bsp_InitQSPI_W25Q256();" i. u$ ?4 X- p: K% _* G
  23.     if (result != 0)9 {, J$ q6 n$ @* }" z4 c* C
  24.     {; A$ S# A$ W  i) j5 }
  25.         return 1;
    4 _0 K6 ^* l8 M5 Q* K
  26.     }
    % u9 ?+ ~5 M* A* M6 P

  27. 5 C' h$ I: Z5 x$ N
  28.     /* 扇区擦除 */, D0 q0 ?/ w( T
  29.     result = QSPI_EraseSector(adr);  / B1 z& N) }8 |3 T
  30.     if (result != 0)
    $ K8 O0 g3 Q2 v1 @% x+ E
  31.     {1 a2 y9 f) n2 g7 v6 y! Z3 S+ `
  32.         return 1;
    ; h9 k4 b+ T+ Q: c( e7 ~
  33.     }    - |+ n, u3 k7 X7 v  j7 w
  34. ' m, ]  l& Y( Z% Y/ A7 g$ c8 D
  35.     /* 内存映射 */    . x" G' _" M0 o$ g2 z7 e  b
  36.     result = QSPI_MemoryMapped(); / A  g, o. z9 G& u- E3 B8 H% U
  37.     if (result != 0)
    8 \- R. Q7 z' H$ s, J( l- q
  38.     {0 ^; b. Z1 E! Q' W" u2 u/ R
  39.         return 1;7 Z  O7 x8 q, O5 x  l
  40.     }
    3 M  R  T  a( X  P+ o4 D( l

  41. , E5 W3 q. O1 G- s
  42.     return 0;   ( v+ Q0 J6 B& g) r% ]) g# t
  43. }
复制代码

& E6 C# ?2 |. @3 G  y这里要注意两点:
3 C% ?; V, Q: y# W0 D. T% F6 q2 H' f- C* U/ f4 _# ]
(1)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。6 S% B& i' e9 N  c: }8 n) B) S6 y4 i
* U% Y5 u; D2 Y3 r7 \6 D4 Z+ W
(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。( `* i' w  ^6 k5 q$ q

( N  V/ m, ^+ t. g6 Z8 Z  页编程函数ProgramPage

( v# ~6 H+ q% g页编程函数实现如下:
7 G0 q7 R7 }3 m5 V9 `7 F3 u. o, L4 H" Z  ^: t
  1. /*
    9 L, L9 j; J6 [4 G
  2. *********************************************************************************************************
    8 [6 \2 d! b4 G3 O) a1 f# C: U
  3. *    函 数 名: ProgramPage7 T3 T9 ~& D1 h; D" t' s% G( a
  4. *    功能说明: 页编程' e! e& \- @4 z# E( ]' G/ z% s
  5. *    形    参: adr 页起始地址+ j- h0 j  V) [, r
  6. *             sz  页大小
    1 |% r: D3 g3 i  s- {
  7. *             buf 要写入的数据地址& c* E* o8 }/ `/ k- L) ^
  8. *    返 回 值: 无
    : z2 R& p8 ?% [& E. A* g/ a
  9. *********************************************************************************************************9 e$ p+ V9 q9 q7 D1 `- R
  10. */* [: @" a8 t' l$ u3 U  O3 J& Q
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
    ; V( W; Q( ]/ F* A6 ?5 e. t% N
  12. {8 O! ]' f& ^, N0 G3 B/ ?* c& m
  13.     int size;/ u  ~0 ]9 _4 Q
  14.     int result = 0;) N( K1 L: r/ s' s) P5 `
  15. - x/ G1 y- `0 Z1 `2 w3 K+ ?
  16.     /* 地址要在操作的芯片范围内 */    * R( a% X) ?2 S
  17.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    * L% m- |' t9 _! c* ?) Q" F$ Z9 X
  18.     {' j+ i1 q7 h0 f8 j0 V& u
  19.         return 1;
    $ I" I% o, n6 Z+ C  }+ o& f- ?* V$ @
  20.     }
    , E5 a" i0 }( d- P+ V: w0 C3 c: G
  21. 9 P* |; \9 A% u8 k9 j1 ]
  22.     /* W25Q256初始化 */2 ?# U+ X- Z, ]$ y- t! o7 @1 ?4 i1 t/ W, Y
  23.     result = bsp_InitQSPI_W25Q256();
    5 X) M9 b4 `6 b! s
  24.     if (result != 0)6 a. S1 w( s; K2 s
  25.     {  r7 d4 H6 ^  J) t4 H$ q+ ?
  26.         return 1;
    4 b/ V: @6 d1 }  j  }& A
  27.     }/ D- F' x1 V7 g
  28. 9 N( q" P1 u4 d8 W. D# H& X
  29.     adr -= QSPI_FLASH_MEM_ADDR;) ?' x6 j+ _  P+ j8 L) G
  30.     size =  sz;
    8 q, ^/ ^6 O; ]$ U: L7 z$ b
  31. 7 K- \" |* G7 Z( J' J( `9 r! N
  32.     /* 页编程 */
    # M. C& I$ f7 G$ u$ Z
  33.     while(size > 0)
    , Z- B( l4 m' E1 A
  34.     {& ]) H+ P! q* h
  35.         if (QSPI_WriteBuffer(buf, adr, 256) == 1)3 \0 ^6 U% Y3 R8 ~7 {/ @" X2 Q
  36.         {$ ]3 P- m: s7 `
  37.             QSPI_MemoryMapped(); & S9 C* ^1 R- o( o" D. \

  38. * }6 m8 z; \& o  ?" W
  39.             return 1;   
    4 M& Q9 e$ p+ m% a. f% ?/ X" e  L
  40.         }. m$ d& G. n, O8 Z5 M) U
  41.         size -= 256;# }( L  `& E  H$ R2 Y8 i% J
  42.         adr += 256;
    ( a7 F# \! ~# A1 P
  43.         buf += 256;7 U+ c7 @! b. V8 c
  44.     }
    + a; ]. I  |- Y1 L$ c

  45. ! G% e1 r) o  ]  P
  46.     /* 内存映射 */    + B: ^1 X/ f  A  n: {( D
  47.     result = QSPI_MemoryMapped();
    " N3 E0 N$ y6 S% f0 r+ E( h% |2 f
  48.     if (result != 0)
    ( n' D, f/ v$ E/ m
  49.     {, E' Z: D2 V4 S" N; f6 n
  50.         return 1;
    ) F% ?$ g' D, x% J" a4 [/ L. {
  51.     }: ~0 c( g5 l7 Z9 y% ?' s& U

  52. 0 l; d0 }4 g: G1 J0 M& d; M
  53.     return (0);                     
    8 c2 P+ H: U" ^2 d8 w
  54. }
复制代码
+ O& ?. v2 F6 q2 ]* A* k
这里注意两点:% z7 {- D0 j/ R( l

; n  p% w9 @4 B4 H( J9 @, ~, K/ [3 h(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。! F- Q/ u  |. s+ }) B  u" p7 ?

' X2 X. P0 @9 W, Y- b(2)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
. x$ X( K6 ?5 `3 P/ q7 h1 C! I/ A
  读取和校验函数
* k- ]: c3 j. z* e) s0 `: J# O; p& g我们程序中未做读取和校验函数。1 v& p' [0 ~  Q. P+ r* L( B$ X

3 I7 s7 e2 h4 y  h% V7 Z! Q# E(1)     如果程序中未做读取函数,那么MDK会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
, ?4 B5 U  E; {1 z) C% M
/ Q. H2 Y3 e' U8 k" U' L(2)     如果程序中未做校验函数,那么MDK会读取数据做CRC校验。
( ]; \9 z5 {5 F( n! ^; Y* C/ j& W* N- L' p9 P7 v
80.4.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
! q' o3 y1 `1 ]" m
最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
- t/ S8 ?$ k; S6 f/ d# y) o- }
4 U9 ^0 X) v: }: h* h2 a
  1. /*
    8 x% {, @4 h( {
  2.     STM32-V7开发板接线! t0 N, Y* k. H+ H5 U# b: h  X
  3. 4 j: ^- f7 r7 Q
  4.     PG6/QUADSPI_BK1_NCS     AF10/ n2 V- J; E( H9 ^8 K
  5.     PF10/QUADSPI_CLK        AF97 l) ~5 @6 t  f. }: }
  6.     PF8/QUADSPI_BK1_IO0     AF10( x. }: C+ R: c8 S8 M# f! x
  7.     PF9/QUADSPI_BK1_IO1     AF10
    4 d" y" l- y& K* G3 l% B" B2 b
  8.     PF7/QUADSPI_BK1_IO2     AF9
    + n( y' X+ j; @( `
  9.     PF6/QUADSPI_BK1_IO3     AF9
    / _5 N9 l3 J; r; k4 t& y' B

  10. ! h6 _1 g; f9 L
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
    ) T" f! z6 ?$ o: B2 P

  12. * e& P& G( ^9 _$ O- ~+ v' S
  13.     H7-TOOL开发板接线
    * k4 Y3 ^# C4 G+ a' }3 Q
  14. : e1 m9 f3 _' |1 Y: K
  15.     PG6/QUADSPI_BK1_NCS     AF10
    1 I2 c; X3 l4 y2 S7 `% ?( i! P
  16.     PB2/QUADSPI_CLK         AF9
    2 J1 Z9 L& Y6 Q& b
  17.     PD11/QUADSPI_BK1_IO0    AF105 O; `' l2 U3 O* L( k9 O8 G# t
  18.     PD12/QUADSPI_BK1_IO1    AF10& ?, ]  F3 x, x. q$ n6 m
  19.     PF7/QUADSPI_BK1_IO2     AF9
    0 E) ?3 t: K! s4 w; t4 L, e
  20.     PD13/QUADSPI_BK1_IO3    AF9# s9 |: z$ S9 R7 u+ n
  21. */
    7 |# W; O9 @* q  {

  22. + P- b* A, D8 q- b2 w+ z- j
  23. /* QSPI引脚和时钟相关配置宏定义 */6 J' e  u/ \" I* x7 m
  24. #if 03 I# [4 j2 ]; T& W9 R: ^
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    # }0 ]* S/ Y/ E
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    1 @! z# J* x. A" r& T
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    5 A2 S3 F$ M" b6 k+ L7 _
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE(), b+ s9 i; c' @* \8 v* }
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()& G1 ?" T& U) U9 [8 l3 B, M
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    9 U9 ]/ u2 g( w) |6 X: H7 o/ b
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()  A; j9 S% J# F6 o' I6 {5 x
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    . ~) M; V$ b! Y/ O

  33. * q' ]$ h" @5 }% {1 D
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    * I! }, ~% P# C2 E; L8 [! O
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET(), ~! ]. A9 j; N2 }/ ~' C) [
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()$ ]& a* m3 p; m9 f1 v
  37. ( [5 m% d4 K6 n. ~3 M5 ^
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    , |- i+ n$ }# h* G
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    # b9 O3 F# I8 s6 r5 O0 j
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    0 @; O# i* h8 l! ^6 S: H
  41.   d$ Z. n0 A( Y$ A. Y* p
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2- i* ?* q8 G" |( S  y* H9 a
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    " N' ?+ d2 m% |1 ^- E3 \- }( _: u- E
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    & n0 c7 z! a& r& y/ g

  45. 9 {2 d7 {6 R. D& V1 u" d
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
    4 P5 r8 ^2 k, U$ P1 P; c# Q" ?; R$ ?3 J& s
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    ) y# o! a$ c% x: c
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI: C& T6 O  U0 O7 N# @! j, W
  49. 8 s) u' [, W5 a
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12- ]+ d1 K! i5 B# x$ ]6 y" s8 d
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD
    & v9 t1 N/ \* N1 Y  H: `
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI. t7 T% o9 Z- o7 c& a
  53. ; r& q( a* F; G
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7+ U2 o, x2 ^9 U+ s! d1 M7 v
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    1 b; N/ q) p; [! `6 n, n
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI5 U; l$ g, O6 q5 I) {

  57. ) j$ i$ F5 [% k1 n% M: Y
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13. o$ Y% s3 K+ k1 H& c
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD( q; |) M. C& v' K
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    , O5 {) T/ w+ O6 v* j6 O
  61. #else
    # v; S9 k9 g( y7 e/ t
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    " n. M+ c2 q/ ?. c3 Z8 [
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()5 s! {, d/ U8 a' a' ^3 s
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    7 R9 Q& \: R* u( g
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    8 r7 c' a2 o$ |0 `' E( k! h
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()# _# A- n; [- i, \: j8 M7 @: _2 C
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    ! D# G$ S/ `1 ?  I, A- l- W
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    & w! i/ Y; B$ t' u
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    ( e+ [: }. z. n$ A: ]" ~# t# x
  70. $ N( n- l$ {+ |/ v. M, U( x
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    + j/ w2 F% _, t: x4 g3 I
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET(). y/ W3 i  U* E/ u
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    8 U# J2 ?8 U, r1 N
  74. ; x5 D) Z' _5 N" \; n2 r
  75. #define QSPI_CS_PIN                     GPIO_PIN_60 V3 u. K# a( T9 |# f
  76. #define QSPI_CS_GPIO_PORT               GPIOG
      W+ O  e2 Q& d2 r! N7 Q* Q% b, ?! ?3 X
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    # r. K# Y+ O4 _; Q$ v7 N

  78. 7 O4 E+ c% X" i( v# }$ m
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    % q* {7 t8 A$ }7 a$ @8 z
  80. #define QSPI_CLK_GPIO_PORT              GPIOF. Y9 {. y1 C, f- c$ F
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI2 f/ y. ]+ i" Q: @8 u6 U
  82. 8 \2 a1 W. s& ?$ D5 m/ T& x
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8
    : a, ]2 z1 v! \) a  k9 p
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF
    ; `& x( q* o: M9 s
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI9 K5 p7 F0 [0 [
  86. 9 t, W2 ]( ]; l8 g4 b4 R4 _* G
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9+ v% }4 T' w$ z: i7 Q
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF
    - u% D  j& s& K. i1 N3 c& a: M1 d1 S
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI  G/ f3 Z# C' Y& V5 P
  90. " ]7 z+ O4 g& V
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    " V' G, n- c1 [& z+ ?' ]5 v
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF. G* b( g- D& M$ o& Y1 K
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI$ {+ `- K2 r' F$ M* u" w  V# k

  94. 2 l& h, H# X. f+ ^9 B. R% d6 Q
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    ( c* V+ E1 u5 H& z
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF, ]# ?5 s/ E7 m# g
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI: g4 S' E1 C% t: l% U$ G0 F
  98. #endif
复制代码
1 g! ?, U$ o0 |& m; K
硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:8 A8 ]: W$ m" V8 e' G
0 y6 ?$ e3 b6 ^9 I2 d4 b0 J
主要是下面这几个:/ M5 W. |% B2 G& i5 y# j

0 t; c+ U+ m7 ]0 G
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000$ ?* e1 B. y4 }" R1 Q5 C

  2. " x; D, \/ C3 a" \: e% ~) }
  3. /* W25Q256JV基本信息 */& r  Q; m$ H! I, X
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/
    2 ]; X" ]/ g7 z+ R. y
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */
    $ T  d  c6 i2 h# [
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */$ Y; q* k1 P6 [, C) E! }
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */5 E( @7 I1 m/ {- _0 M
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    3 B5 b" c! }1 j
  9. $ e/ K  g, Z1 p) \' y8 r
  10. /* W25Q256JV相关命令 */5 ~$ G$ a+ @' F
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */
      u5 N( a! K) F# r% }- ]+ l
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */5 X6 K0 l' I" a3 X" O: Q
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */- Q* Q- V6 M9 L1 v) o
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */+ j1 s1 w( B# c8 M" D% X9 S) M+ Z
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */: |2 b' v7 P: B: h* L, D: X
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */, q2 Z3 P" i0 {' E2 J
  17. 3 e; Y' ?7 K& p# ]  T
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    8 B# z( Y3 X  h; P  M# e# L
  19.   R0 v/ ?6 }4 f: @8 ^' A2 ^+ D
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码
1 Q5 Q! T& t9 `6 d
80.5 QSPI Flash的MDK下载算法使用方法) t3 A( D$ ?9 m! N2 m
编译本章教程配套的例子,生成的算法文件位于此路径下:
6 {8 u( J% n+ b$ L/ R" X! {9 s+ d3 f4 z1 c
ed1f9465c60f92522e906639b76b29f3.png

# M  X, [/ Q) I2 p. n. p, X4 S1 w5 T" [5 s  w4 v9 q0 T
80.5.1 下载算法存放位置/ S6 g' U7 E! g& v& t6 P' v
生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:- S) a6 {) F8 P8 k
! ^( L* c' J) D3 y0 Y, [5 L
  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。; h1 w8 x" v( O, S- _/ \1 {$ B3 O
  第2种:MDK的安装目录 \ARM\Flash里面。2 J! q; P/ _0 U- q
7 O$ `2 @! D* F/ I: K- Q5 ^
9d5d65cb90fb28ad36464cb1b8368759.png
7 R9 g# e8 X# o% D3 @) d$ S& M3 i1 U

2 b  O& N" j3 m2 C80.5.2 下载配置0 c$ j6 M% v0 I. e( s0 ?. t
注意这里一定要够大,否则会提示算法文件无法加载:/ O) ~+ R# p' W2 k4 H$ x7 r
5 ~: W6 k& ]/ D$ P$ o% [. `- |
8eefa57c2d7f128449000c2fb4e788d9.png
3 u- [0 F3 Z7 ]! f# a* ~9 g: o. w2 w3 I

0 [" `% N$ E0 z% Y- J我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。
! a! G1 s. G3 N1 Y; x- F+ Z  ?# {/ A7 F# d. s' z
如果要下载程序到QSPI Flash里面,需要做如下配置:6 T  z% f# U4 f& q) O. @0 c
) q' b4 d9 ~% j: ^! d3 `6 b) E6 m0 }
caa01b15ccf9a9700690d2ad1bdedecd.png

7 g6 _# D' W5 O1 J8 Y: R1 v& ^# ?1 D6 d! [) V7 n# L
80.5.3 调试配置
  t+ {0 ^& w* L2 ]6 i; f
注意这里一定要够大,否则会提示算法文件无法加载:0 Q/ [! B, [; C, a' T

1 w, D+ c. P5 t3 P* O" K4 ]
f23244d646498fc30aa5f1301a327430.png
# F) O% ]. {5 ]
" e/ n( ~! a- `. y& T7 v  |  _
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。6 ?1 e* q( K) C: [

( u! A: `' e1 f" K) N如果要做调试下载,需要做如下配置:
3 F( i. w# E; X) ?9 `; |* V0 F& k6 }7 K# r
84ee4945e5faabd776fedd6f8f7d96d2.png
/ d* S$ ^& T& H
4 I- |$ D% S1 D
80.5.4 验证算法文件是否可以正常使用
+ c  k, ^3 F% }+ W为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。
0 o' ?" |: f; v8 y4 ?5 F+ `; I: O2 @
3 G; _' o) B1 `/ v8 Y- n80.6 实验例程说明
7 Y( L9 b- I3 t# k; m, |2 ^- ~. F
本章配套例子:V7-060_QSPI Flash的MDK下载算法制作。
3 ]9 Q9 E6 v/ o  m& K5 Q; J4 P  h7 G+ B( B0 J
编译后,算法文件会存到此路径下:
/ V, z" ]1 O0 ~$ H, J  I" D4 }
% D1 Y1 X% E6 S: P; _6 ]

/ F$ |  d7 [) T5 Y3 r3 Z! F( o% P2 S& u, F( @, F
80.7 总结+ F  |) i1 H# p7 Z5 }
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
" \1 x% `9 m0 w/ ?3 f6 v, Z! w
) ^0 `( m$ s( I1 V3 G7 w
! s) N3 ]2 O1 E5 R, z' g+ U
+ t9 v/ V- F' U- T7 _+ L5 f% y8 [) \3 {8 z% U, ^: r) e8 [: Z8 x: @
1 p4 t) @& B; n5 Q3 z$ q5 x
b69a199f1b4a12d880e64c7ba134070c.png
收藏 评论0 发布时间:2021-12-19 16:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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