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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 16:00
80.1 初学者重要提示$ R* w& f) [5 q! ]8 H2 E. v1 _9 L
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
6 P/ F* O4 c, ], b- g2 u80.2 MDK下载算法基础知识
: J8 B& H. y" H- VFlash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。
) p, j2 G0 k' C: Q$ {; x' O* f# B# D! w' C3 g' X
80.2.1 程序能够通过下载算法下载到芯片的核心思想8 I/ w+ o; h4 I" O' Q" C1 j
认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。* U# ]+ S( n' @  j& r2 N

: L5 T  s6 q' x# P' a' \80.2.2 算法程序中擦除操作执行流程! k, w& E" P, w5 G$ C2 w, P
擦除操作大致流程:3 G. b# i* j2 O, |/ i& }( P6 P" R

/ t# s: F3 [4 n/ @% t
d4b4af6b23f5f1d5ee3f3387ccb8fbdc.png
( h" }  C% v9 I& Z' b. d* ?

# k5 y* a* V+ u1 R  加载算法到芯片RAM。# n2 |, L5 o5 F) O- q
  执行初始化函数Init。1 _7 w% A' F$ W3 i, p4 c
  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。* R& Y" q% Z4 z* s; y
  执行Uinit函数。9 H" H7 n$ b3 ?* E6 z" l
  操作完毕。
  ^: j2 @4 s6 Y7 S. d
: |* ]4 r$ q: Y  j' C80.2.3 算法程序中编程操作执行流程
) j9 L+ ~$ P8 P6 X编程操作大致流程:  e, Q+ `" H/ r/ N, u
0 ^( J( b; A, T# [( L
d3f601364bb033d42e275ad686c03726.png
  m- f% N9 ]! |/ T! Y

9 J- H9 A* J* u4 j; _5 [1 ~  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。" O* p+ n& D* V' w
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
& Z0 f2 y, U1 J; z$ T: Y  加载算法到RAM。7 h& H/ g9 I; t" ~
  执行Init函数。( x' c  O! f- k4 N6 n# j
  加载用户到RAM缓冲。: x) Z$ g- _: L" D
  执行Program Page页编程函数。
  D/ H% t. s1 ]7 q! O  执行Uninit函数。
' o2 T5 v# h9 u. R# H3 Q+ ?  操作完毕。
) w5 T9 ?9 {  l# I
' U/ R' G; ]- B7 w# E( m, ^80.2.4 算法程序中校验操作执行流程/ [! H9 C2 o- h) V
校验操作大致流程:$ `) @- }" a6 G9 t3 R- L
' C: X. _3 ?# V1 p2 x$ {6 o* O3 v
c7b2f373d946bc7d166ab1c1ba9b67e4.png

1 f' h: r+ G8 G! k: e. V. ~( Z: I* C; C* ^
  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。: }& T, a1 i3 E& u, w9 B
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:& A  K/ ~4 q. K
  加载算法到RAM。
3 J( @" X7 E2 ?4 C, r  执行Init函数。) M1 f( \; z' d' P
  查看校验算法是否存在
! J4 f9 R: [% l: s) S2 A8 C  如果有,加载应用程序到RAM并执行校验。
6 o; W9 ?9 v) u5 a2 Z  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。
7 X; D* p' Y- N  执行Uninit函数。2 i* U* p& `$ ~- v5 z
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
& e/ p6 l0 r1 |  执行RecoverySupportStop,恢复支持停止。5 n" F) F, P/ k4 M
  执行DebugCoreStop,调试内核停止。- M* E1 @, ^5 ?/ ~  z
  运行应用:
$ s) y9 L7 m( _$ t( J9 L5 m8 h# l  执行失败。
9 z5 p2 G& A# w8 _( \. W9 D) m  执行成功,再执行硬件复位。
& }( a/ z0 X1 ?! @! ^  操作完毕,停止调试端口。
1 z& N  J4 O  ^7 i) R/ Q, r: ~- q% b* `( e
80.3 创建MDK下载算法通用流程8 t* [3 E1 L5 Z  ~
下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。) n8 `* t6 \) |  A4 f! q

: g7 C  d" F$ H) H: g7 G2 s80.3.1 第1步,使用MDK提供好的程序模板
9 x) b! y% Y5 R位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。
3 L: y/ L. [, j& ]" S( E  g' ?
8 y8 g! ~- \5 _# T  J) W) B效果如下:+ Z. I: @1 K8 M9 X. K
2 a+ Q$ t9 a3 K
8e08d948d34b38592e3699136725ade1.png

/ m3 V& x. V9 R7 A
% P1 Q' Z) O5 V80.3.2 第2步,修改工程名4 V- B$ z+ Z+ Z/ ?, R; `( Y
MDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。
3 m0 V4 N- h' H2 M) J0 E
' W' Y- e6 F/ U$ d& @80.3.3 第3步,修改使用的器件' z% ]- c1 U/ F! e8 o8 L
在MDK的Option选项里面设置使用的器件。
% Q: }4 |& M6 R, f3 X8 A( u5 k
  s; s5 ~+ V" H5 q
347d6def457f6e101f1c2555f9156cda.png
6 @( ?+ U( i4 J4 d1 @
4 T% s. {& O" q. m1 h
80.3.4 第4步,修改输出算法文件的名字; v, X2 S3 g" K7 W
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。+ q3 E9 l% C/ B5 P

3 y) u( H) ~, q( g8 q% \& Q* ^
07d754db49256bd0b73f4538a328f3cd.png
8 k7 I3 \+ D. l
# ~% }  i4 q, E! F1 F: l% E
注:MDK这里设置的名字与下面位置识别出来的算法名无关:. i' e9 V/ v0 r5 [4 C
5 B$ X+ @* Q; X$ M3 ~; E7 [5 t
a00b0ff99a0a691f19dfc2565fde811a.png
- Q8 ?5 u& [4 e

" E" {- w1 z8 g/ p0 V这个名字是在FlashDev.c里面定义的。$ Y+ m) q- c! q0 l& N+ q
$ [' G( E/ j. G: ]4 p
80.3.5 第5步,修改编程算法文件FlashPrg.c- d. r8 \, F2 K. U6 `
模板工程里面仅提供了接口函数,内容需要用户自己填。( N# j6 \- O( \$ @8 r0 B
' |4 A( g( R; p& W0 L
  1. /*
    / j* f- h+ B; s! v+ o2 \( N
  2.    Mandatory Flash Programming Functions (Called by FlashOS):
    , k6 W# L3 i, s  H7 F
  3.                 int Init        (unsigned long adr,   // Initialize Flash5 o' m7 K& [+ \$ {% s* q  F
  4.                                  unsigned long clk,
    ! s5 B% R3 x# A' B" I
  5.                                  unsigned long fnc);1 M6 N$ X3 k! d  k/ U
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash
    ( L' L# t! b& Y7 g- Y* [5 h  U
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function3 C- i6 ?4 ]+ v, Z! ]. j
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function
    / R- t+ O* J# W2 m$ J
  9.                                  unsigned long sz,
    # F# }5 Z- a/ p( i
  10.                                  unsigned char *buf);
    8 C* E4 _1 ?4 f) O- g' Y

  11. 1 v: J! W4 p% V5 ?
  12.    Optional  Flash Programming Functions (Called by FlashOS):
    0 K3 K& X* |: y& J& p. l/ k
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check, C* [) j1 U) T6 F8 a
  14.                                  unsigned long sz,
    3 b1 `0 D+ \# v+ m
  15.                                  unsigned char pat);
    , h3 t0 V+ ]6 q# [- @
  16.                 int EraseChip   (void);               // Erase complete Device
    & K7 \4 L2 W- s4 p3 ^
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function
    # w/ T1 |  _- G/ X
  18.                                  unsigned long sz,
    ; M2 i. ]& Q' w) m4 q
  19.                                  unsigned char *buf);; @* ^  |0 R5 f3 u6 G  z4 i) ~1 P: B

  20. , M1 u4 Y" b& s
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space# z5 }: M7 h+ W  Z  S
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space
    8 X& `. x4 ~! B- z- O
  23.        - if EraseChip is not provided than EraseSector for all sectors is called8 a# Q! l/ s$ g8 y6 ~
  24. */
    8 p( w- E7 i7 L$ ~7 |- M
  25. 3 [4 M! L/ ]2 y" R* B4 b, O
  26. /*+ F0 Y( p' |( @/ n+ C
  27. *  Initialize Flash Programming Functions
    $ J  x6 g5 c& J1 E, F. A
  28. *    Parameter:      adr:  Device Base Address
    0 o, P: s. R4 `: b9 i
  29. *                    clk:  Clock Frequency (Hz)
    9 K! }  `# ~6 c% V4 q0 Q4 [; R
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    9 ~4 [* D* |% b4 c# L9 D
  31. *    Return Value:   0 - OK,  1 - Failed
    5 j. W+ K  ?2 [% M1 c, R5 `, }
  32. */9 f* g, |$ ^5 @, S; l8 i
  33. # C$ [7 u# ]; l7 g$ n, a- K9 J5 T
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
    : W  ~, X" g% S) ?. y

  35. 7 K, _5 ^4 j  M" C" Q
  36.   /* Add your Code */
    6 j" _/ A2 S7 \) n3 q5 ~0 ]4 ?
  37.   return (0);                                  // Finished without Errors
    " g+ [( s# \; G$ \5 @
  38. }7 c0 W: }$ z( Q: t& q" m8 w

  39. 7 Y$ J0 {3 @" @$ L: O$ x4 g6 D# Z
  40. 3 d6 T# ~% L" d. ]
  41. /*4 U$ S% p2 z- {9 {# z% x
  42. *  De-Initialize Flash Programming Functions
    , ?1 \3 r4 Q" L. a7 R8 k; }" B% s$ S
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)6 S- z! j0 b7 u
  44. *    Return Value:   0 - OK,  1 - Failed0 X: ], E8 ~. m- q8 e( p
  45. */
    ' L. M+ z5 o1 p
  46. / Q# H, Z. w. N$ c$ ~( d
  47. int UnInit (unsigned long fnc) {' l  t$ x* S2 L, j/ B8 ~
  48. . K" z7 q. w6 W0 k" b* o
  49.   /* Add your Code */% d* N5 N. V+ D1 ]! L$ h9 r
  50.   return (0);                                  // Finished without Errors3 A2 M" ~- O) |% G) O* _
  51. }! @5 t/ u4 s3 u4 K0 `+ G: c

  52. . s- N4 E  ?1 c- w! |' O0 D) F

  53. & n, V: I2 R4 l
  54. /*) r3 C! N: W( S; V
  55. *  Erase complete Flash Memory
    6 E6 I* O# L+ M. v$ V0 R( Z; L
  56. *    Return Value:   0 - OK,  1 - Failed
    ( ?5 X( l+ M) ^; H
  57. */7 k5 R1 _7 s- k5 k; y: _6 R, K8 J6 v
  58. ' ^- W6 Q7 B' G9 J4 E
  59. int EraseChip (void) {
    ! b- O+ X6 M, Q: B# {+ }

  60. ) y( _7 m/ _: x- w7 ]; ~( \
  61.   /* Add your Code */2 f3 H: V6 N7 B3 o
  62.   return (0);                                  // Finished without Errors% A; H4 i3 r/ o
  63. }
    ( r4 |8 g4 J3 x# [4 ]2 z- m
  64. 5 U  [7 l9 V. Z, B- c
  65. ' o2 ~" \/ j" w& I# [5 n4 l% c7 M
  66. /*& H( \4 z9 ^& c6 @' Y: x8 l
  67. *  Erase Sector in Flash Memory% J: L6 L8 I* E
  68. *    Parameter:      adr:  Sector Address
    ; Y8 j+ X( ?. X- B2 r9 M
  69. *    Return Value:   0 - OK,  1 - Failed
    ! @, p9 p) d0 M+ i/ ~4 U3 X
  70. */3 q1 [5 K. k: a

  71. 3 B# K) o/ a% E" I. n# k* ?
  72. int EraseSector (unsigned long adr) {) e. k8 W! v- m6 C
  73. / k  ]' g4 ^  q* S$ k. O; z; g/ ?3 D
  74.   /* Add your Code */
    # ~  A7 T' O% @* p& O
  75.   return (0);                                  // Finished without Errors% ]) R- D. K" [1 e# B8 q0 v
  76. }7 ?5 @. _* u- t
  77. & \" [1 g8 E1 f+ M4 s; ?

  78. $ o$ G# l. H" M( t
  79. /*& }: e: s/ R+ {& ^0 @0 n
  80. *  Program Page in Flash Memory: q8 ^: e! O" W( ?: f, t: E
  81. *    Parameter:      adr:  Page Start Address
    . E; D' z* e2 k* b. @
  82. *                    sz:   Page Size
    ) D; N( R6 a7 @1 F
  83. *                    buf:  Page Data
    # f- @& V% h5 c& L6 u( ?
  84. *    Return Value:   0 - OK,  1 - Failed1 l# J  P. c% L! W
  85. */7 Y1 P  N# u( \" |2 C3 q9 A: a
  86. ! G3 [2 a# F" H" p# V" u9 R9 b
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {/ I* }+ F% h0 |* E

  88. & a# B% |* b' I8 W& e2 F
  89.   /* Add your Code */
    & D% i! z# M2 Y9 O- R
  90.   return (0);                                  // Finished without Errors9 t) N, I: F' Q
  91. }
复制代码

8 d) j: x3 U9 n! P7 F( ^0 }80.3.6 第6步,修改配置文件FlashDev.c
3 ^0 y+ P4 y9 @8 [* s3 O' O模板工程里面提供简单的配置说明:
- C0 L1 G: L' ?2 |
5 i8 u' d% d7 D# `( h' \9 G
  1. struct FlashDevice const FlashDevice  =  {" s4 \2 y7 p; i' N4 M
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!
    ! ?7 ]: G- U* H% v7 z! V' L
  3.    "New Device 256kB Flash",   // Device Name % S1 r, Y- c" f1 N, O' o. X, S9 v
  4.    ONCHIP,                     // Device Type* Y, {, x+ n3 T! q; g1 p1 S
  5.    0x00000000,                 // Device Start Address
    0 ~& x5 D0 m; o% e: l1 }6 u% x
  6.    0x00040000,                 // Device Size in Bytes (256kB)  h* }( U; f, ~2 \/ N2 b
  7.    1024,                       // Programming Page Size6 [  U% q* O1 B& I9 s- t
  8.    0,                          // Reserved, must be 0
    9 J& @; w( x9 N7 l& s
  9.    0xFF,                       // Initial Content of Erased Memory
    . l9 q( X) \7 n% Q0 E+ v. n
  10.    100,                        // Program Page Timeout 100 mSec
    / n* m, `+ V; J5 r' W: Y
  11.    3000,                       // Erase Sector Timeout 3000 mSec
    ) p  o9 E* a" P; \. \! P- d# ~

  12. & z5 R5 V$ R0 i
  13. // Specify Size and Address of Sectors7 _! g# @  s$ A8 X8 E! \
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)
    9 N7 P/ h# O1 f1 d+ e' \6 y
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors)
    ; ~2 W6 \# g- b& r0 M  X* T* _
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)
    & s7 W  D+ r1 F5 ^
  17.    SECTOR_END
    0 W9 x1 D* |- q. K1 p: N; `8 ~
  18. };
复制代码

! [: r- ^! B  T注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。
5 K. `- h2 k! e' k' {% l
  ?# W( c7 i! V. j7 @8 r( A$ k80.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关
9 e4 \2 s9 `. u3 d' O2 XC和汇编的配置都勾选上:
% A6 r2 P& J& k! R- ~  {
3 }# p5 v, x$ I
1d27a7314fc6c472937ad43852722b6b.png
/ r- a. ]2 {5 E) X# E
% n3 e+ a3 t- w/ ?- p+ O1 o6 n9 v: Z9 m
汇编:
9 m! V0 x; a% K. ^% d3 K3 A  j: A: k5 v7 C& C
bbea604970c4ef63c77f10e553e28ea6.png

' b: t2 M; k9 |* x: b) P! n/ Q2 B. \
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
/ s. M$ q4 H! V( S4 j1 S% A6 [& }& C  }, r
(1)加载以响应运行事件。6 d" C! k% |" X- s, A4 f

2 l/ Z8 W6 D/ B% H(2)在不同情况下使用其他例程的不同组合加载到内存中。/ X  U5 Z1 ?% n& ?$ J; g  C

4 u  o# U: h% h2 M(3)在执行期间映射到不同的地址。  h$ }# J- r! T7 Z8 c. _
& k) O' g, a" \3 N% y- T2 H+ M
使用Read-Write position independence同理,表示的可读可写数据段。+ r+ r! r" \# U- y: G; g

& h, G. l7 G% Z* q+ j/ g80.3.8 第8步,将程序可执行文件axf修改为flm格式

: |1 C) C- I! ?; A- g9 x2 ?通过下面的命令就可以将生成的axf可执行文件修改为flm。
7 }5 f; |$ Y7 i  B  |" z; n6 X  c7 }
6b504444979e9fa3a3477c6284abca8b.png
8 d8 f" V( L- T" Q+ ]& Y

* q8 I2 n! v  @: f80.3.9 第9步,分散加载设置
" y% O* S3 E, ]/ W7 I- Z; V我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。2 `1 ]& R  |' T* ^' |

' l# T$ m2 ]* {& `- }, r7 m
e8e79854b204b618bbda32af92f28bf2.png

( D7 |: o9 S# y% Z, ?: Q# L
4 N3 e: d# {/ h" k8 x分散加载文件中的内容如下:3 {: }2 F& M1 b7 L4 q% Y3 ^

8 I- b$ H% r: A* @0 p4 W- P# }0 P, S$ Q
  1. ; Linker Control File (scatter-loading)
    ; @) K0 b3 e9 L! G
  2. ;
    5 ~/ ]7 S4 A4 V9 s

  3. 3 e! I" d# t& ~: T% ]; _4 I+ f" a
  4. PRG 0 PI               ; Programming Functions, i" ~* d- i9 |3 N  v/ g& t
  5. {+ b( z: J- m7 A6 }  d/ L* m7 m
  6.   PrgCode +0           ; Code" m/ c4 _6 u, J: y$ d; s$ v
  7.   {
    % c* u8 Q& d  F) u+ m6 L' ?
  8.     * (+RO)
      k  K( [/ p- k5 o  ]- y: f, [
  9.   }/ [, o) \- g3 j8 l5 i
  10.   PrgData +0           ; Data
      k; g+ `7 {: C0 Q
  11.   {' U0 J5 [, Y! ?9 ^
  12.     * (+RW,+ZI)) M2 `' l+ i" S# P; V. u
  13.   }
    1 I6 G7 [$ w$ r: o: b! _
  14. }
    4 J0 A4 q. q$ T- f* L4 `
  15. " t# o2 Y( r4 A! c6 o2 p
  16. DSCR +0                ; Device Description5 W  T- D! F6 J) A5 `
  17. {8 S% d) t$ s1 o+ j0 b7 r
  18.   DevDscr +0
    ; F& a/ }9 q; a* [
  19.   {
    ; z* {) l% X. {  o, p
  20.     FlashDev.o2 C; L# E' ^4 ~" o9 E' W  q
  21.   }) @" o- x% c, I1 v4 x. Z3 D: S5 G
  22. }
复制代码
6 @" ?5 `7 m! ~9 D  u6 g
--diag_suppress L6305用于屏蔽L6503类型警告信息。) r, d- _9 X  D. v$ d

* Q/ n) X; T, p. ~" m4 K/ y9 z5 g特别注意,设置了分散加载后,此处的配置就不再起作用了:
% s  ^- J  ^1 k  i0 a% U4 o! R2 c( @3 o# L$ N
b249eb40e5562b04ac3eb05ec0219c1d.png
$ O- d. o* n" e+ B
3 L3 B" w! s- @, ], x
80.4 QSPI Flash的MDK下载算法制作
) M, E+ a) Z% F# t$ m6 |下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
. x+ B# x+ [: t( z3 Q, Y
6 e3 T: b6 G2 h* x0 H% s80.4.1 第1步,制作前重要提示
6 ?9 H( n; V' K( o这两点非常重要:
7 I0 M5 [5 p+ p" Q' ~9 _. D5 l; k0 H: F7 J3 Z- Q
  程序里面不要开启任何中断,全部查询方式。
0 Z" c4 Q1 Q: j* n6 e" @& r  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。- O3 i6 X7 G9 ^# H

6 s! `6 v! u$ L9 {80.4.2 第2步,准备一个工程模板
3 X7 b! L( x8 p. j& Y 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
5 ]( Y  a- t+ q* u5 U& r' c) |2 V$ k) e1 R
a91aaf84a25d730b77273624d2b502f6.png
- Y0 e. }8 R; R2 `  X

( Y5 h1 i  |& l80.4.3 第3步,修改HAL库5 |+ y3 @4 p1 R
这一步比较重要,主要修改了以下三个文件:
- l6 K( i6 [& k' Z) z, _# B$ Z5 h' v& C
4471afac6bbc201866d0d9a8ea7d876b.png
  H! G- X9 d5 a$ w' S
( _/ `$ U  c( Q$ L+ h) |3 k! V
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。, g+ b. i# A6 o1 e( a+ t

0 g  V+ n( C( G80.4.4 第4步,时钟初始化- P8 G5 u- J: Q
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
9 y- N4 A) T& T# M! j& K" R) R
7 f0 y% }& E  T% {
  1. /*
    * S9 [5 l) _7 _1 S& A
  2. *********************************************************************************************************& T: |4 I! N8 Q0 Y( a
  3. *    函 数 名: HAL_InitTick7 [3 F* c: e5 i8 n8 E) T
  4. *    功能说明: 重定向,不使用
    ( }+ R! S7 V! N0 R! A
  5. *    形    参: TickPriority% {2 c6 T& }1 X' S
  6. *    返 回 值: 无
    4 T! d0 A& m. s+ ~$ M5 s
  7. *********************************************************************************************************7 ?7 w( S' H' C2 z& K5 f
  8. */& z6 c6 ~+ w; \; G
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    3 H; L  Z' O% l8 @9 l. b
  10. {
    ! |/ a5 u3 C8 f) p
  11.     return HAL_OK;
    / p9 t8 g; |& u( j  G
  12. }
复制代码

9 Q6 ]( t( Z3 y; z6 y+ K然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
0 a/ w' [: {' j4 X4 \
# l( g. ]- r" S" F
  1. #if !defined  (HSE_VALUE)
    + \/ M8 [' C5 v2 T; R- T9 x
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */* a4 \# @' l2 n" g$ K
  3. #endif /* HSE_VALUE */
复制代码
2 V. j: c7 \: m' }9 L
最后修改PLL:* K8 Z' e. Q9 }4 E7 v7 g# U( Y4 [. K
2 i& _& l  A) v9 p# s
  1. /*
    3 B  I8 {- `: X( |
  2. *********************************************************************************************************
    & L+ {: ]$ o- u/ x; ]- B
  3. *    函 数 名: SystemClock_Config
    # h7 c( [7 w2 J; A& r
  4. *    功能说明: 初始化系统时钟
    9 y  [/ M* q  _% k0 p' S: B
  5. *                System Clock source            = PLL (HSE)
    4 u* G. A+ Y4 p0 b- s, b& C2 d: J
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)4 P* N- O9 ^! |: f4 G
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)3 T, U1 G8 Z. c& t+ T4 K0 J
  8. *                AHB Prescaler                  = 2
    8 b9 X. I( ~) s' ^8 S3 K
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    $ E7 {" L% b9 J0 P* [6 L, n" w
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz). H3 G# O0 J; L$ t
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)# r$ h3 i& T  N
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    6 U6 M' r1 p- V! ^% {) I" C0 V$ M3 j( i
  13. *                HSE Frequency(Hz)              = 25000000: J, z$ P$ l  F' L! k, i+ I
  14. *               PLL_M                          = 5- F$ Q$ G% O# G
  15. *                PLL_N                          = 160* b% }$ y8 S9 X: J0 z% q
  16. *                PLL_P                          = 2
    6 s! @' ^" T: B* G, N# M
  17. *                PLL_Q                          = 4
    ! C: ^8 g4 I4 o5 \5 q) {7 `
  18. *                PLL_R                          = 24 J4 n; w$ x0 H4 d; l+ L
  19. *                VDD(V)                         = 3.3
    & i3 P) L: x) |
  20. *                Flash Latency(WS)              = 4
    ; s2 L# R% X8 ]9 W# i( [
  21. *    形    参: 无
    ) r% O; q8 N: z' c/ w% f5 N
  22. *    返 回 值: 1 表示失败,0 表示成功
    * y6 n" i/ h$ o9 T
  23. *********************************************************************************************************
    3 b  G$ P1 H/ R( y; x1 y7 z
  24. */
    ; @6 H3 j& E2 H& g
  25. int SystemClock_Config(void)& t8 J( ]8 L; j1 j" G9 W* V8 z
  26. {( d' P+ ~5 [0 t$ Z: k8 U9 H
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};  L9 Y/ v' H$ z
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
      m9 q: @( `  B0 l: _8 {0 e
  29.     HAL_StatusTypeDef ret = HAL_OK;$ o! {) R& ^" r: |" A/ W* O  [2 G
  30. 7 j; ~0 E2 u) w* h5 B
  31.     /* 锁住SCU(Supply configuration update) */
    % ?  R8 w$ {/ Y: }6 c
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);4 I! y  o/ x* j5 P, p/ \/ K
  33. 5 j' D# z4 A2 R# t9 Z
  34.     /*
    ) S4 \8 ^4 X) Y0 w
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,7 }1 i6 m% F8 e9 ?/ Z7 r
  36.          详情看参考手册的Table 12的表格。' A: l3 D6 Y5 q/ A
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    ! d% [" X, ]$ L! }# {
  38.     */
    , U) Y+ L0 v2 w. h" l$ Z! O0 n
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);# I5 {/ M8 {) |+ p5 G4 w
  40. # e; \3 O  y) u, i" {* ?9 |
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}. P- g6 d) y4 p8 M/ T4 Y1 M

  42. $ R' \! z" _+ O% n* N
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    . d5 \4 Z* [' Q; y; g* L
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    $ s/ s( X. T4 D# U  Y' W" x
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;( ?" r" j: ^& r0 w7 P
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    . k" O8 W/ H6 x# h$ O
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;" |$ q2 Z/ {0 U
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    # a: ]: P, v4 Z0 p4 ]1 g
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    6 ]9 i' i0 L. ]1 p; U4 y; T# {5 b
  50. : E) C* j. o0 e3 F$ ?; m
  51.     RCC_OscInitStruct.PLL.PLLM = 5;# Y8 M% }0 M0 K# C& ^" p4 j
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    & q. b' V5 N7 ^
  53.     RCC_OscInitStruct.PLL.PLLP = 2;( O' \; x+ {9 e9 c
  54.     RCC_OscInitStruct.PLL.PLLR = 2;0 _/ N7 L4 v2 e: v6 b$ T
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    1 `0 _0 X0 z- Y* \$ Y2 J9 C

  56.   Z* Q5 v3 X- l: X9 f8 k
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;3 R( U. P& `# r" z/ ]/ y1 y
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    % ]7 o7 J) Y# T4 k7 ]4 V
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    ) T3 F8 h6 W% ~% @
  60.     if(ret != HAL_OK)
    " q) O9 I! j1 r. U' m" b
  61.     {
    : [8 |  d7 S# v
  62.         return 1;        0 X' i: y  Y2 |9 D
  63.     }' W7 L; X( H: S6 U  r& ^
  64. % T2 U+ M4 x* {. @
  65.     /* ; Z7 \7 y0 s. i4 A1 X
  66.        选择PLL的输出作为系统时钟0 F  T3 l9 c2 I% g5 V
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    $ L( r9 p* l# M& R/ H0 g/ I# d
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    6 V' V3 B2 w9 x. R! P' s. w
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线8 \, [3 g$ |) X* t9 F( O
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    & ^' b* P5 _( y2 r5 z# O5 g
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    & T! `& m& b9 K) s
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    + J. |1 Y4 w4 X3 P; u8 [- y
  73.     */
    8 H' j/ E$ [# j+ }) k
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    2 a+ z' N5 Q/ ?
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);9 F# D/ ]2 o$ c$ R4 ^* ?4 N3 m% j
  76. / `! ~! k$ f8 P# t: \
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    & p8 Q- t' p0 C5 l( [# b
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    . r, H8 E2 t+ }( y# j
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;2 m* q5 r9 D, K4 Q" v
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  , Z5 I: A9 m& i* x" L& |
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;   _0 q1 l% T6 O" E1 i" T
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    5 k/ ^. S' D# H$ t% p0 B7 I
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; , i% n) |- q, B

  84. ( ]6 y: w- ?6 s7 H  m
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */, H" S; g. i# N
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    . }* p# }, U; G% z6 W. I* Z
  87.     if(ret != HAL_OK)7 u" P5 R# B7 A* Q
  88.     {+ f, t/ b3 y, @9 B
  89.         return 1;. W) F; z! k' g' ^1 U
  90.     }: p2 _9 W( D% E9 M8 M: d

  91. 2 i0 E. [, t9 C6 h! ^6 f
  92.     /*
    4 b' ^; M) j2 c
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 0 m6 E4 B( L) q8 }, h7 m. O" {4 B" c# p
  94.       (1)使能CSI clock' Y) V% @0 o. R8 y. o8 S
  95.       (2)使能SYSCFG clock
    6 a* H6 D8 ^0 r; q; J: G, d
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0  l4 M- c6 L$ R: m
  97.     */
    9 n3 O: P( ?0 ~; _
  98.     __HAL_RCC_CSI_ENABLE() ;
    # K+ G+ y5 i1 N- c

  99. 8 r% f6 ?5 O% _9 e* b: n! U) w" z
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;* L6 a; M, g1 Y/ K' m  p
  101. # N6 `; f; i* j5 E
  102.     HAL_EnableCompensationCell();# y5 P( o) Z2 }3 i3 n1 I

  103. % f8 b- ^, Z2 b3 u* `. c
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    ; J! V% ^% W- H6 N! z2 C
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    # W, P  a" e, W. ^5 x
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();0 l$ x4 f, V' a3 u' Z5 M

  107. * U1 O3 p& I% n
  108.     return 0;
    6 M3 u4 T6 |' P. O0 R) _
  109. }
复制代码
4 z- H0 l3 g* \* K6 c1 E6 R
80.4.5 第5步,配置文件FlashDev.c的实现
" H. z! W5 e8 f$ e- \配置如下:& Q  X( d& z0 O6 A+ A
; X2 J( X; H  ~! H, B1 G3 k1 U4 r
  1. struct FlashDevice const FlashDevice  =  {
    , o' p$ ]2 u5 ]$ ?* ^4 x" X, F
  2.     FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */
    9 b7 h$ g/ [% L, C" [# M
  3.     "ARMFLY_STM32H7x_QSPI_W25Q256",   /* 算法名,添加算法到MDK安装目录会显示此名字 */
    ) J+ B& c, `3 m2 O. o: b) J
  4.     EXTSPI,                           /* 设备类型 */6 H  U' D) r) Q* f1 c4 _4 j! P
  5.     0x90000000,                       /* Flash起始地址 */# I! D8 i, ?( I/ U2 W
  6.     32 * 1024 * 1024,                 /* Flash大小,32MB */  `' c0 C, a3 x3 f6 X. U
  7.     4 * 1024,                         /* 编程页大小 */5 s; Z; m9 F( M
  8.     0,                                /* 保留,必须为0 */
    0 G& P8 T9 I5 d- [4 P! ^
  9.     0xFF,                             /* 擦除后的数值 */
    - t" `: O$ [* {) z- v+ f  `
  10.     1000,                             /* 页编程等待时间 */
    + [+ P$ y- g5 {/ {8 o8 ?* `* \
  11.     6000,                             /* 扇区擦除等待时间 */
    2 Z3 w, D+ @/ ?' C
  12.     64 * 1024, 0x000000,              /* 扇区大小,扇区地址 */
    3 }9 m& I& i8 a' i# I' {
  13.     SECTOR_END   
    3 b  V( c$ M5 ?% E2 B( X( s
  14. };
复制代码
2 _; v* f7 z/ z1 v% V
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:# ^2 m) J- e  @1 e: Y& a
1 ~2 S7 i- }' o+ y, q
ffed7a581be4c5da388ee29fb67249b4.png

' s! J! d# V* k' F+ Q2 q, [0 N# H, \2 p% l
80.4.6 第6步,编程文件FlashPrg.c的实现( u2 m9 ?  \  \! j2 @
下面将文件中实现的几个函数为大家做个说明:, `2 P* ]5 p* h$ L

2 G% V; W6 K- y  w' _1 A  初始化函数Init
# D; Z1 i# {0 v  M. _
  1. /*
    4 u2 a% ^8 g) C, G" U
  2. *********************************************************************************************************+ Q6 h% m9 b; ]7 h# u
  3. *    函 数 名: Init
    ) ~: `. |; @* s& `1 V& U+ @. J
  4. *    功能说明: Flash编程初始化
    1 M9 \& \7 G& e* p% c$ C$ s3 h4 `
  5. *    形    参: adr Flash基地址,芯片首地址。! a+ P' F9 ]; E) ~% W/ h. c
  6. *             clk 时钟频率3 D2 ?9 g! T% D" j
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify  W$ m, {- b9 a/ Q/ o( @4 Y  r
  8. *    返 回 值: 0 表示成功, 1表示失败0 e6 g! x- O* M2 m* n
  9. *********************************************************************************************************3 w1 Q# q, b' g1 D
  10. */; D; {$ g) y5 R5 W" Q# Z
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) 7 D5 V8 J$ R: _+ T( C7 Y  S2 D
  12. {5 e* E7 a+ u0 v  i. x
  13.     int result = 0;
    * q: M) U0 w7 W% d1 e% X

  14. $ w3 h0 g, U& r9 e5 b
  15.     /* 系统初始化 */
    * `+ k5 A' Y) e) z) U
  16.     SystemInit(); - ^+ u" y5 q7 {7 m( M; P  _

  17. 8 B0 x& j. P& h$ S, o3 o3 b# V
  18.     /* 时钟初始化 */3 s0 {0 t) H  V! Y! Q* Y/ l
  19.     result = SystemClock_Config();2 ?3 l  U& \5 p& H) k
  20.     if (result  != 0)" ^& ?& m( M2 O. e  y
  21.     {
    ) z* e& s5 ~- d7 T# p) ^$ V! p: j
  22.         return 1;        
    - Q: d: z6 K" ~, i4 C
  23.     }0 ^- W3 @  H: r! d; @8 S

  24. 9 `) W2 J) q" m! g: B! F" a0 U
  25.     /* W25Q256初始化 */
    4 |1 q8 T8 n5 H; `. O# f
  26.     result = bsp_InitQSPI_W25Q256();
    9 D0 s. K" t  N& W/ ~* q/ J
  27.     if (result != 0): k) l7 Q2 O" _! |
  28.     {7 x1 i  `% X, n" W, D7 |
  29.         return 1;
    6 ]4 A& x8 A  E! w& ^+ z
  30.     }7 k' L6 g: d7 H2 |3 \$ `3 A
  31. 4 S1 d5 Z; j% B5 N* p& t
  32.     /* 内存映射 */   
    , Z$ u' l! T' B
  33.     result = QSPI_MemoryMapped();
    + E/ @- M/ `/ ]8 y2 V% ~* t) I1 v
  34.     if (result != 0)
    7 x' y$ ?6 l. E5 y4 s8 c, x  H
  35.     {% f, B. E3 |  X3 w
  36.         return 1;
    1 a& F  v( N# d$ k7 X7 G* \$ H
  37.     }
    - \% B- U: {8 V/ G& i4 T) b, z
  38. & Z6 N1 N! O: N8 R0 i6 c
  39.     return 0;
    # q9 N2 ]& C: p3 A
  40. }
复制代码

# x3 L5 e" d8 e  T' D7 n' J初始化完毕后将其设置为内存映射模式。
  c! w, \6 y) e! ?3 c( L: Q# m6 Y1 M1 ^# j0 P
  复位初始化函数Uinit. c; K  @  p- e2 s( h" k
擦除,编程和校验函数后都会调用此函数。
- n1 f3 Q( M5 i- ~& D! f  ]& a9 [
: n/ r1 X* _  E) P, Y0 Z
  1. /*
      e0 b6 i) Y+ |3 o
  2. *********************************************************************************************************
    / V  t1 R" l& T) Q$ q7 m9 J! ]0 f
  3. *    函 数 名: UnInit
    5 U  f! f8 t1 C$ u  G# z/ c% o
  4. *    功能说明: 复位初始化& \0 F1 J& a: \
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    * v% Y: R  f# f* T: b6 |! W; P- ]
  6. *    返 回 值: 0 表示成功, 1表示失败/ ^7 q# l- y8 O3 I( e
  7. *********************************************************************************************************
    & r9 e+ i7 P+ a+ ?# ~
  8. */& d1 b8 v8 [/ M7 X4 d- l5 m% D
  9. int UnInit (unsigned long fnc) 6 _0 a1 [7 H& i6 @
  10. { - ~; I5 l9 @8 F+ k
  11.     int result = 0;
    ; w6 J- K7 V$ C+ K; p

  12. 9 N& i0 L+ I9 w
  13.     /* W25Q256初始化 */
    7 u$ j; }9 Z; V: U' ?5 u$ f
  14.     result = bsp_InitQSPI_W25Q256();% c- j9 ]' G+ r
  15.     if (result != 0)
      W+ s5 X9 i, Z. l+ H
  16.     {, V: e. z" C. ]) P* f0 P5 w
  17.         return 1;  i: y, K# W0 x& _4 p2 D
  18.     }
    / m7 L1 i* z7 a3 p" Z& E; w

  19. ! X9 j+ ~. O# l1 _- g0 B& i% [
  20.     /* 内存映射 */   
    6 Y( q1 G' m7 i9 q1 \( i
  21.     result = QSPI_MemoryMapped();
    $ t' L" z/ O5 [, a- t) U7 f8 ~) f8 n
  22.     if (result != 0)) r  a. @2 b  h
  23.     {0 M  o* N4 ^% _; [( g& e
  24.         return 1;+ i4 j' Y" _& g; c) n9 P
  25.     }1 Y0 z9 F5 B: V% e* p' D' \

  26. ! k7 f$ i1 z4 U4 M3 T' b
  27.     return (0);; T8 F* V6 e9 t  \# _& u$ p5 @
  28. }
复制代码
8 B9 J, r2 \% _* j$ y3 E# ]) y* s
复位初始化这里,直接将其设置为内存映射模式。
% ?% H6 c% T5 R( `7 q  T. T8 |+ e+ k5 m. I3 C
  整个芯片擦除函数EraseChip
4 ~4 D+ j' Y( ~. Y1 `2 o如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:
0 S7 y. K( [, m. N
$ }" J3 }  A' K
f623e662bf09233ea594f820f4c7952a.png

' m( {# c0 D5 O- m8 w# [
" {5 X3 @6 Z% B8 p& M/ Y+ r$ ~实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间,比如32MB QSPI Flash整个芯片擦除需要300秒左右。
2 x! _; |1 R$ \! ?) r
% L9 F8 }5 {. q另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。
4 [; X- u" t+ f5 t. N9 [. F' O; `" X; L4 `, Z4 P, T+ s1 X
  1. /*1 O% q- z7 w# J2 U1 ^9 k
  2. *********************************************************************************************************
    & B/ p5 _( x. v" D3 Q2 F/ s2 F: \
  3. *    函 数 名: UnInit
    $ m" m# ?* S7 M( C1 U
  4. *    功能说明: 复位初始化
    & o4 e% E$ a) m( k' `- w$ E7 x
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify) K% G& E1 P0 R7 t% h, y
  6. *    返 回 值: 0 表示成功, 1表示失败  A! V# ]1 A; @7 i4 S7 m; e
  7. *********************************************************************************************************, t* a8 l. Y0 P7 j- B
  8. */
      x; N% Y. K  B# F: T" m$ s+ N
  9. int UnInit (unsigned long fnc)
    & L/ t3 c- b& p1 ]9 s  G% I
  10. {
    4 b+ v, C  y% W9 J1 s. |
  11.     int result = 0;
    1 P  w# z, C1 t

  12. 8 E: }5 m' n$ a) E
  13.     /* W25Q256初始化 */  R; l5 `# ?8 P( _
  14.     result = bsp_InitQSPI_W25Q256();) ^  c6 j# w+ ?+ J: E; w
  15.     if (result != 0)% E7 k: y' q: p, T
  16.     {
    , X' |6 e% q. j# }
  17.         return 1;
    ! J+ E$ y, p7 g- `, P2 m9 F- {% C
  18.     }4 q% P: X1 G8 P* V/ Z& {

  19. , B5 T. p) a+ ?& Q4 `
  20.     /* 内存映射 */   
    1 y  r& T8 u- H
  21.     result = QSPI_MemoryMapped();
    , Y% G+ T5 L3 v1 ~4 R7 s
  22.     if (result != 0)7 G1 {2 j! u+ E& L% y% O2 h
  23.     {
    3 T0 C( L! _, ~4 e7 l
  24.         return 1;! [( B: ?' `0 e  E  _0 V
  25.     }* z4 Q$ ^2 T% A9 _3 Q$ u- T

  26. 6 Y  N; R4 `8 ^. k
  27.     return (0);
    $ Z/ |2 L; C" f7 k7 U4 s# M0 Z0 R
  28. }
复制代码
  |/ ^. P0 o5 @8 y
  扇区擦除函数EraseSector- [& t$ v- f5 o1 h; q
如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:
% ~# `& T' ^0 F4 r& H! d
+ E; Y" @' _3 y/ i
733d5af26119f64bff0261515013d3a6.png

8 i# q1 l* T) e
4 e& W5 X- Z% o: s
  1. /*) r6 O2 K* U9 z$ X
  2. *********************************************************************************************************8 |& a* m5 `( T% J) C: h$ A9 q8 }% Q9 {
  3. *    函 数 名: EraseSector. }6 U: K7 ]( H" J
  4. *    功能说明: 扇区擦除
    / h) A' d- M; k# w- z& [: i6 A9 U7 t
  5. *    形    参: adr 擦除地址3 @. O/ a3 E$ o, I; ?
  6. *    返 回 值: 无3 `7 [+ R4 r/ W6 F
  7. *********************************************************************************************************
    3 W0 b4 u% u4 u& M/ n  D& \& p8 E; ?
  8. */
    ( b! R7 {5 r" k! @! Z
  9. int EraseSector (unsigned long adr)
    : s* D  S( U0 B0 F3 W( T& d( b
  10. {    1 M4 c2 z  P& L: I0 X6 X- F& Q
  11.     int result = 0;5 M% v$ Y% w" l  I3 X+ ^
  12. 8 l$ J' H% k. ~
  13.     /* 地址要在操作的芯片范围内 */) w6 z4 Z, c: N3 O
  14.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)5 O5 p5 p" C& L' }1 F
  15.     {5 P# z/ C$ E" T1 y: V3 K5 Q- ?
  16.         return 1;4 @0 l& h* q: o/ A' ~! e
  17.     }0 v) N/ v" v& x* ~3 S

  18. 1 f: X2 h, y: }/ t- F9 h
  19.     adr -= QSPI_FLASH_MEM_ADDR;5 ~4 c6 y5 u( `
  20. " S9 `, r+ d: g5 B
  21.     /* W25Q256初始化 */, X+ ^/ g6 `( N
  22.     result = bsp_InitQSPI_W25Q256();
    2 S, X( J+ a4 w  X) y6 r2 q4 q) C
  23.     if (result != 0)
    : `& n3 y3 t- Z. S8 p' K0 f, Y5 f
  24.     {
    . x3 E5 x1 Q* d- C. `4 K9 D
  25.         return 1;
    " W  V+ N) D$ G3 I* K4 w
  26.     }
    3 T/ {6 z2 ]; c+ W" z5 q6 z
  27. 4 g# A0 V6 E3 r
  28.     /* 扇区擦除 */- |! ?* v/ n) H; f' N/ m3 [: s
  29.     result = QSPI_EraseSector(adr);  $ b! Z- I0 S5 i+ d$ h4 J. B
  30.     if (result != 0)1 S3 v" [5 r( v5 @
  31.     {
    9 M& R8 }' {# `! d( r3 t9 \
  32.         return 1;
    % N! M6 a9 b& X* G9 L/ L' P# R% x
  33.     }   
    ( R2 d2 s  \2 \6 `! O, Q" q# ]* ~0 Y

  34. # N. {% ^! ?  r* w/ c7 ]; F
  35.     /* 内存映射 */    . S1 h. A  p2 x# @5 R2 i
  36.     result = QSPI_MemoryMapped();
    8 [- c' P$ b# E( D  y
  37.     if (result != 0)
    & b, t0 M) S% m3 t! D
  38.     {  y5 B: w. k3 a8 T
  39.         return 1;; H4 I3 o6 Y1 e$ P& ]
  40.     }
    0 m6 r: K2 L  a( t; L
  41. 8 s' z4 l% |# O* O0 r( `7 S- a
  42.     return 0;   ! ^9 ]. ]( {: ?, b6 S
  43. }
复制代码
$ P+ e  p, N- F! Q7 v
这里要注意两点:
+ n4 n, q% w& _4 B5 }  L2 Y1 h$ l* }7 ~
(1)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。: f/ @( }4 E2 K: O
& Y/ ~4 b3 H8 \* R0 o2 [( ~
(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。
2 Z2 }! E5 P8 q+ W' k: M1 e; c
* g* B8 x% L* G& ~8 x6 L4 k  页编程函数ProgramPage
5 _  C5 S2 Y! D6 h$ z' y, D
页编程函数实现如下:5 J( n+ ]2 c( x
  ?, y8 a6 y4 |1 ~/ i
  1. /*
    ) E/ v" V6 X4 n7 h* w8 B
  2. *********************************************************************************************************
    2 Z) s" q5 x9 P  F  L! [- u
  3. *    函 数 名: ProgramPage; t( v4 ]/ S7 f8 L' d% M
  4. *    功能说明: 页编程
    % L# v$ S& a2 _" F
  5. *    形    参: adr 页起始地址
    0 Q. S3 S- ~& b+ M% N' P2 Y! A
  6. *             sz  页大小
    1 a9 I+ I8 z  C! @% z
  7. *             buf 要写入的数据地址
    * N" X) h: h. A9 W
  8. *    返 回 值: 无6 e$ e. D9 s  v. D6 l
  9. *********************************************************************************************************
    0 b: h" P7 }5 Q2 ?2 W- e% D; Z; ?
  10. */
    / L, `( f" i* p5 ^
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) ( |! P' I4 [" f8 L8 }6 v
  12. {; {/ u4 r4 h' _. Y2 P) G4 a
  13.     int size;
    " y( y8 B1 d# i( h# Z
  14.     int result = 0;1 T( |6 z* h7 u/ j) }+ v4 |9 l

  15. & G# E0 H3 {# h6 W- L
  16.     /* 地址要在操作的芯片范围内 */   
      y2 X. z' S! D' S  s8 C
  17.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)6 v7 T- B4 R, E0 J
  18.     {
    2 W% P/ }5 C9 o+ C" I) k
  19.         return 1;
    9 H6 O' t$ f$ D3 W5 r. C
  20.     }
    5 G  S5 V, }8 y& P: B

  21. + r6 a  n9 c' z+ j- y
  22.     /* W25Q256初始化 */! W+ B0 x5 D  a1 J- z5 I
  23.     result = bsp_InitQSPI_W25Q256();0 G2 X0 e6 ?1 I+ \! q* B; I9 h
  24.     if (result != 0)3 R5 T& R7 ]6 K
  25.     {$ U8 r( l5 e) V3 o' p
  26.         return 1;
    1 i7 d- a/ K% c
  27.     }* p4 }) G2 x% A  y: J( `

  28. 9 p4 d4 m3 g7 M' u$ `$ S7 R. S& i
  29.     adr -= QSPI_FLASH_MEM_ADDR;
    / M, x8 L% Q$ R. B; h5 w1 g  z. c
  30.     size =  sz;
    6 b% T7 [9 E5 S8 }9 ~7 W
  31. * C( c( t' g' c  S; E0 l, U
  32.     /* 页编程 */! W7 ^. V, u" F) P0 r
  33.     while(size > 0)% W- g- ~: B& @/ _$ Q' E- j" m
  34.     {6 U# n$ L  D7 Q  X
  35.         if (QSPI_WriteBuffer(buf, adr, 256) == 1)
    # |5 b6 J: c1 \/ k' J. a% f% F
  36.         {
    # N4 ^6 u+ w' E: b* \' |3 U
  37.             QSPI_MemoryMapped();
    0 n3 x8 D- ?& ]  c
  38. 0 p' c0 {% f5 n9 ^0 k! ?. U* b# ~
  39.             return 1;   
    8 q% P1 b3 ^/ w/ W0 h
  40.         }5 l) z& A. w  @. E$ d- T) t$ D
  41.         size -= 256;# L& W- v4 c1 I3 r
  42.         adr += 256;7 `6 k) p/ |2 m/ \5 ~1 i3 `- G
  43.         buf += 256;$ A+ j* y: w% \- `& K
  44.     }: B$ `0 x) Z  Z5 x' t3 P) k

  45. " n) t4 b3 i; Q  p- F
  46.     /* 内存映射 */    9 H' p7 n( N! d9 a3 K6 Q
  47.     result = QSPI_MemoryMapped();
    " K( v# T, m4 \1 R  \5 o
  48.     if (result != 0)5 S- d6 O7 U# ]* y/ C
  49.     {
    ( T: ]: [2 U! l7 Y' X
  50.         return 1;
    8 F  `5 J. I0 W8 C
  51.     }
    9 n( n6 W' H- z0 u# J
  52. + j  z5 q7 ~' O. z: S; y
  53.     return (0);                     
    $ P! F3 H7 M4 v7 q5 q% `
  54. }
复制代码
- n: [% c: k1 M" o
这里注意两点:9 V: H8 N2 j: o# m2 l/ Q
$ G7 c2 d  |& U: O0 f3 ]- X
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。( E/ r; g' f- \+ Z- c/ Y+ @

; m& Q2 h# o' Y  ^/ C(2)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。' o9 I0 D) g6 o8 Z/ w9 n! L1 M+ X
$ o9 x! i4 p% y) O4 q+ _, @1 j
  读取和校验函数
1 L' ^. S/ W, ]. q我们程序中未做读取和校验函数。
2 H* b! m0 B) I2 E  e, \: i3 L. s2 W6 @% t# F9 S
(1)     如果程序中未做读取函数,那么MDK会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
1 i0 c3 V/ J3 }* B- G( K* \) d' n. Q6 [' ~- u4 S* G# l) S
(2)     如果程序中未做校验函数,那么MDK会读取数据做CRC校验。
1 K/ `7 t9 J# A, b
3 O/ o3 K( G* q80.4.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)

' y; b2 ], r  y# {9 N最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
+ @8 G7 [) D8 d2 P
& p! b  g1 _6 i, Z, i
  1. /* , j0 L6 \0 Q6 ?$ M6 C3 }. E1 s; f
  2.     STM32-V7开发板接线
    : Z0 ]% {9 ~7 {7 v! P, Z) ]  d

  3. : y5 n  f+ p/ U5 X
  4.     PG6/QUADSPI_BK1_NCS     AF10
    7 l: B% e3 o! Z% J( h* Z
  5.     PF10/QUADSPI_CLK        AF9
    7 K6 J  j& K! T0 Y
  6.     PF8/QUADSPI_BK1_IO0     AF10
    : W; H# E0 a* m1 k% f  ?, i) w  _3 y
  7.     PF9/QUADSPI_BK1_IO1     AF10# B( ~( [# @% S: U7 F
  8.     PF7/QUADSPI_BK1_IO2     AF9
    & Z8 e1 _/ ~) {! ~
  9.     PF6/QUADSPI_BK1_IO3     AF9
    , }8 |7 o/ K! f
  10. 6 k* n; a& m) v3 r
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
    2 ]$ N  p) B1 ~2 b) |
  12. , A( ^4 C2 o4 x5 ]& o3 x9 B
  13.     H7-TOOL开发板接线  p! v) s: N9 A" y
  14. 6 w4 ~. K4 j3 h
  15.     PG6/QUADSPI_BK1_NCS     AF10
    + M! C' u) c# U# v2 K$ Z
  16.     PB2/QUADSPI_CLK         AF9" G* g6 \, ]$ M8 |! Z1 r+ Q# t2 X
  17.     PD11/QUADSPI_BK1_IO0    AF10
    - ?4 I6 L- v& g9 O/ @% i
  18.     PD12/QUADSPI_BK1_IO1    AF10
    . h) v$ {; u3 \) z% ^/ I4 o0 C( E
  19.     PF7/QUADSPI_BK1_IO2     AF9' ]. y6 F+ V9 n. y* u  ]* v
  20.     PD13/QUADSPI_BK1_IO3    AF9
    $ N% h0 g  c6 Q
  21. */& `. ~7 ?7 m: m$ Q& C
  22. 9 @& h6 ~) E; L
  23. /* QSPI引脚和时钟相关配置宏定义 */
    + ]% c4 n+ Y$ b; D6 s! J
  24. #if 0* b$ F! V! \% o, |( ?
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
      i5 b. A1 [& ]. f! q
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    0 q4 ]2 s4 {% y' H" D& h& `
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE(): S. c4 Y& n- p- c/ M& g/ O
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()( a8 ]! V. c: @/ `
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()$ p3 `6 Z, Y/ T: I8 |5 O
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    7 t% l5 x. i. @$ q
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    + a2 y0 w5 s$ O
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()* c& d; O* D+ K0 A3 O# ~

  33. 3 d0 h- L: }' W( G2 i
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    * U6 `" K2 M% c$ l
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()7 W* [" Q. ~0 `2 b8 v$ K) f
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()" c) F: c9 s& K6 U  L3 P: t
  37. % ~6 }. ~( K  y
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    . j0 D+ }1 j$ ]* S! D; Q* C
  39. #define QSPI_CS_GPIO_PORT               GPIOG6 Y( n- n# t2 d2 S! h
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI6 C+ d; e3 p$ b. U% c; I; i8 w- |
  41. % _- G! \* G1 n+ C* I0 d6 \
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2
    * r) L4 U8 G4 {9 r$ Q( M4 r
  43. #define QSPI_CLK_GPIO_PORT              GPIOB6 }+ j' z* y" D+ ~8 j: |" e
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    % I% r1 t2 {# p  E* t4 u5 d

  45. $ @+ s; F5 k9 Y" Z: o
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
    ) w" s9 n( P3 e, e
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    6 f2 A$ u% T& V+ o" c/ m0 x  g
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI
    # W' P" {1 k: y! g  \8 N6 A
  49. 2 ?# `$ ^- Z1 R2 Z. ]
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12, p4 v- }% f! I9 ]
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD/ {# v5 x& }' ]7 \  a  }
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI
    1 L( z9 ], g, X; _0 k" h  u
  53. ! I- R* s, s* E# x# Z
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    8 U  U. l- c" M" G! c9 z' B
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    9 Y) S/ J2 g1 H' z. q5 T6 R
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI7 F8 }8 i- `2 Q9 X) d5 g5 b; e! W* Z5 W

  57. ) b, }, e6 H# J1 Q% W. r
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13
    ; L) }1 \1 J5 ^) e9 g  }( \
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD+ o1 \, C; O1 ^0 v0 O0 {0 O
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI; w: {) B! U: |. ~
  61. #else5 ], |# @( e$ F0 a
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()& {% f0 u, T6 U& N, _, E
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()% Q5 o, r: V! f" T
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()4 g: E& s1 q% L0 B5 o9 [
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()( h; g! V/ l* }4 J+ V% P
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    & L" M3 U, e2 X" j; `; U% m6 ^
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()# H, _7 {8 J/ J- \: U& I
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    + o& U% r8 {, ?6 e. @
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()1 ^0 \: @( p7 Q2 r' o, t  G; I6 b1 F

  70. & t( a5 ~: ?5 _0 C2 Z
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()0 q" ]& S8 Y" |
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    ' u3 X- i; q6 o( u# l% O
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    : r# ^- t, \0 O4 C  `! O& C8 [* O3 r

  74. - K5 c' w# ^% H1 {, A0 _
  75. #define QSPI_CS_PIN                     GPIO_PIN_6
    9 y+ K. g9 d0 G* }8 @' U
  76. #define QSPI_CS_GPIO_PORT               GPIOG
    : d  ?; n) Z% P, c. {% f
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    0 K; s0 L: `5 J* l
  78. & u% U4 H# r: ]0 U: v+ L% i
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    ) j2 b/ O5 O* I0 r9 U% F
  80. #define QSPI_CLK_GPIO_PORT              GPIOF# n/ o+ C3 f5 V* n! ^
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI3 d6 [) T2 J$ S9 f% [. k

  82. $ F9 v8 M3 T$ V9 h: q4 \/ t
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_85 H) n" G  D; k, Q: }' ]" s, f/ [
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF
    % v+ H2 M! t! |2 M2 }( A
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI6 |( y  H7 g$ ]2 L

  86. 6 m% q3 _- I; u/ @' Z( o& s
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    $ k& i* S, B2 R
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF
    & g5 g+ w% r' m  P, y( Z
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI3 {- n8 `4 ^) B& D5 _0 p
  90. * N0 q1 A5 L5 |* `4 i3 P
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_76 A0 I7 O$ Q* P
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    ' n' V  M& v# w1 D  V
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI, W' y8 I$ N. ?# a2 J0 f

  94. " q& q3 J7 j, }' s
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    $ Q/ t1 J: A$ T. O3 G
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF$ d  C3 @  {2 S. y. C$ u+ Y% |; t6 ?
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    3 V) P& i& m3 B: n1 ]. `# `0 J
  98. #endif
复制代码
6 V0 I7 Y4 A- J- c
硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:8 P2 P$ c" Z; s
# e5 G/ p2 y* ]0 p
主要是下面这几个:! F- K5 T9 l$ J- m% O) I) T' O
, s4 |6 B# f: K" l6 u" g5 \0 f
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000, R6 W1 v2 C' z  C1 |$ b4 a

  2. , ~2 u/ U2 P% o0 Z. O
  3. /* W25Q256JV基本信息 */9 y  l- F3 T. M' L& u
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/
    8 k: z5 V4 p8 I9 K
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */
    ! P9 J: p2 M' l" q5 B4 R/ H
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */) K" b3 t, U) d
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    6 c( E1 g5 _; F
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    8 f- G5 K% z' M1 V1 b: a8 T5 b1 a

  9. . H' _) d  `  \7 |9 X
  10. /* W25Q256JV相关命令 */2 m) u2 N. t: c% @' L5 }$ U! v) _
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */
    - q( P2 m% C, S# x9 K4 t) |
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */
    ' w( z- ^5 k2 l! x$ {
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */  S; z" h  }( ^  t( u6 d  P8 e
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */! q6 E6 g8 E8 R6 C. W& V* y3 N2 s
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */% v; q$ t) C- a/ L
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */5 g' y& H4 T& f5 f$ ~  T
  17. 9 ]; w' _2 r# b: l# |5 F
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    : ~% Y9 P: G0 a4 ^3 Q$ R

  19. # q( E/ s. {& d& ^$ L  @" A
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码
0 P- a4 q9 s+ L
80.5 QSPI Flash的MDK下载算法使用方法
- b" @+ X. N1 V% d编译本章教程配套的例子,生成的算法文件位于此路径下:
- @1 Y9 S$ E. O( D3 ~) F; F3 r0 |) N' c+ x. K& G0 E
ed1f9465c60f92522e906639b76b29f3.png

) @. E" v/ p/ H: D5 E9 Q# A; l; w* s8 v+ i  W! `: y4 x5 S
80.5.1 下载算法存放位置
( E1 `6 N' N0 m# h- P, v! V: N生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:; U- D* m# |+ d% o
$ A" k# E  Y! C7 |7 O- m
  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。+ i# L; B% S" E1 v% O! s
  第2种:MDK的安装目录 \ARM\Flash里面。( B* m- V& k; p  o/ S+ y

4 p) N$ ^! U5 z  ?: T5 L. W
9d5d65cb90fb28ad36464cb1b8368759.png
; Q, t. [, |6 {! m
! d3 h& m- |7 @
80.5.2 下载配置
! T7 w9 W( x" m! M* E/ f" w/ Y  r注意这里一定要够大,否则会提示算法文件无法加载:: D* t7 o: Z( i8 D

# H( j3 ~4 ]) k
8eefa57c2d7f128449000c2fb4e788d9.png

" R: [6 g0 R8 X: `& U8 _# M) J% p
8 [9 C! m) G+ y* M5 ~我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。
+ q  W- H7 ]. O( n! @% V) H( B6 D, {, W1 D& h" Y; ^/ w% u) K
如果要下载程序到QSPI Flash里面,需要做如下配置:* _4 I$ n. d" ?3 B7 y. D# [
: m4 ?# a3 E& d
caa01b15ccf9a9700690d2ad1bdedecd.png
' q" h. H& c8 J/ k

& Y- p5 U/ w/ w3 I80.5.3 调试配置

- V/ H$ }$ P( Z+ L/ U: m2 L( l注意这里一定要够大,否则会提示算法文件无法加载:$ Y4 ?6 y9 O0 _4 P  c0 \- a; {
) u* f# W7 s4 t1 @) m3 M- z
f23244d646498fc30aa5f1301a327430.png

7 I4 F/ y. W7 W' x7 C/ x. E/ c0 ^0 T
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。, j+ N" r. w# h
8 p6 j& {) y+ V: }0 W7 m
如果要做调试下载,需要做如下配置:1 D: E" U; r1 O6 k: u
0 t) y3 x8 J, [
84ee4945e5faabd776fedd6f8f7d96d2.png

' a! I! ~  H" x+ k
4 L7 L! G' ~1 v+ t80.5.4 验证算法文件是否可以正常使用
' g2 o, C! X1 k为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。5 u2 ~+ R/ X) S9 M+ B7 d  G

1 W, W! A' J8 m5 ?! S/ p80.6 实验例程说明
& f! V$ k& l# k
本章配套例子:V7-060_QSPI Flash的MDK下载算法制作。
7 e! v% T7 N7 |2 D4 G3 `
$ X" M8 O# \- W4 z8 z! y编译后,算法文件会存到此路径下:
0 e/ O4 X9 W3 O3 ]) d' `' J  ~- J: G8 q
# g, ~* x' X9 x% f) H( r

) P% a3 Y% V+ @; _- i5 U" c/ v4 @80.7 总结$ U+ K0 j4 X. `( E; n1 z1 t
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
( [) V1 j1 }$ z/ e3 C! d  v8 t$ D% {. m3 [
& Q& \3 L9 f: K

9 d: g3 F/ K* r& Y% g8 I: S$ k6 B: o) _0 r( N# d8 J
9 \! g9 d7 _! y! [$ `
b69a199f1b4a12d880e64c7ba134070c.png
收藏 评论0 发布时间:2021-12-19 16:00

举报

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