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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 16:00
80.1 初学者重要提示
4 w" s+ u! A' V7 s: i7 N5 r  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
" q+ p$ D* I: G& U8 W& w$ l80.2 MDK下载算法基础知识
4 d) ]  W& C2 `3 M) cFlash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。
$ l+ @  n$ \5 g  M) v
% w/ d  p1 {$ E$ F1 V2 V. D80.2.1 程序能够通过下载算法下载到芯片的核心思想
) R2 S* h+ S, T/ V2 s4 S$ X  t: ?% T认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。6 n  ]* g0 Z  N

: I. w  ?9 R2 n80.2.2 算法程序中擦除操作执行流程. L) H, O8 ]# j0 g9 W9 n
擦除操作大致流程:
8 ~) `, d. E2 n* Y
) E7 c, Q9 C2 \
d4b4af6b23f5f1d5ee3f3387ccb8fbdc.png
7 I8 D* }2 ^* m- L2 q2 [- ^- ]

, J# M3 t7 o2 Z1 B/ d) [  加载算法到芯片RAM。1 j  \3 a8 M3 t; t/ P' P2 n1 A1 r3 O
  执行初始化函数Init。
2 h4 S: F5 R0 D  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。8 H: _3 B4 @5 B( N- [/ N
  执行Uinit函数。* F, z% P4 r0 l# U9 r
  操作完毕。
- E- [) m) _+ A4 n% V/ C! N: T
) S6 ~/ r: I! j80.2.3 算法程序中编程操作执行流程
9 T% @9 G% C1 J! k' p编程操作大致流程:8 _; y" J9 T' [2 o3 w
( e3 r) H/ Q% ^
d3f601364bb033d42e275ad686c03726.png

/ ]+ s; W7 e9 h1 H& N! z# r1 p# Z4 g5 w) L. o7 F4 W2 r) R# P
  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。& O4 v; t4 Y1 {- e1 j
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
- s1 m0 ?% h. v  加载算法到RAM。
1 i- b% k! p4 _) w  x  执行Init函数。
6 e* `) p+ g! p6 h  加载用户到RAM缓冲。
" \3 M* a3 E/ N2 ^$ r  执行Program Page页编程函数。9 e! Z. L' f3 k
  执行Uninit函数。) @* c* W2 a: m, W9 b
  操作完毕。
4 k* q" ?3 V6 i. P/ _% t0 c0 ^8 r$ |' U0 \- D
80.2.4 算法程序中校验操作执行流程
0 N' z1 @4 T' p校验操作大致流程:4 X- W& N; x: ~. G. E: D
- U3 N: a1 f7 o5 O7 j  U  s
c7b2f373d946bc7d166ab1c1ba9b67e4.png

% ?: y' e! w; w! `. z8 h! c
8 C/ E7 _5 _# N; F8 E) F9 s  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。( v2 y' Q! i5 i: Q1 l& O( \
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:" S0 c6 J2 Q3 |; ]/ e& T! E& y
  加载算法到RAM。
5 X% `4 Z' X3 V; g7 ^  执行Init函数。
( _! w: p0 O1 d4 }  查看校验算法是否存在. n. j9 t8 n. X
  如果有,加载应用程序到RAM并执行校验。1 @1 J) c2 G( z5 T' U- q
  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。9 ^6 O; H) j' H
  执行Uninit函数。4 l2 j- {9 g' ~5 Q! k
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
0 x( T4 I0 L" o. L/ @8 q6 }4 H  执行RecoverySupportStop,恢复支持停止。
% w, [3 Y: |. `& z1 L- a: t  执行DebugCoreStop,调试内核停止。
) _& l2 }- F$ |9 K3 ~- `  运行应用:
. t* k, F4 z. \2 i5 c  执行失败。4 w: T% u+ a3 F: @( [0 X2 k
  执行成功,再执行硬件复位。& C& \$ ~+ ]$ r. h5 h! E# {9 d
  操作完毕,停止调试端口。
9 h0 u' k, n1 w. N% D
) q) s$ D3 y: F' `80.3 创建MDK下载算法通用流程
( |4 W3 j( j0 r+ L下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
7 Z( p/ f- l3 ]5 @" s: ^- t( g% M
7 c4 I1 ?% Q# V1 f' }80.3.1 第1步,使用MDK提供好的程序模板
" ?! T$ r$ Z! f  S  P% r位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。1 m$ I2 S% J% I& ]2 V; R: G' R1 {% ^1 V

  z1 ]& K& ]2 T; V5 Y: Z1 o效果如下:( w) x9 {/ V2 b- I8 a) W6 z

3 Y' K& q# p  Q* Y
8e08d948d34b38592e3699136725ade1.png

. d; n2 q- x/ d; Z! @* H) v
7 v! b- W* y$ h: v* L80.3.2 第2步,修改工程名6 A* H6 E, B2 n4 g, q
MDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。- g$ X5 g3 |. {2 y
1 ~# v8 F! K- `/ ]) g
80.3.3 第3步,修改使用的器件1 [6 `( _( X3 t8 i, s
在MDK的Option选项里面设置使用的器件。5 S2 X- _9 p3 X8 q# M, V

* u3 T. X2 F5 `% a) H4 Q
347d6def457f6e101f1c2555f9156cda.png
  X' X$ ^1 n2 @8 U9 D/ h
0 X. o5 N6 U7 G  g0 ?) ^
80.3.4 第4步,修改输出算法文件的名字
7 i% E+ O' c1 J1 F, G这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。
7 c0 R" ]! y# o4 m2 E  C# Q; e! Q0 c) w0 U7 Q% m9 r
07d754db49256bd0b73f4538a328f3cd.png
6 S" y  }. R  |" n
! F5 G4 {- b7 x3 z& e7 ?, C+ H
注:MDK这里设置的名字与下面位置识别出来的算法名无关:- W! Z' {0 @6 |

! B: Z' C: {. G5 S* K1 S
a00b0ff99a0a691f19dfc2565fde811a.png
8 Q6 B" H. k! d+ ~
: P) F: A" d$ m- L; C
这个名字是在FlashDev.c里面定义的。2 f+ `3 l, L1 u' X$ C: l- Z  h
4 A! S2 R9 Y+ R; Q& G
80.3.5 第5步,修改编程算法文件FlashPrg.c+ }: G; n) z  [3 p7 Y
模板工程里面仅提供了接口函数,内容需要用户自己填。5 y, \5 Y: k) H3 i& ?& ^

2 t  h9 p3 X+ ?, `1 V$ L3 r
  1. /*
    0 _+ e1 Y! I$ m& Y: U5 q
  2.    Mandatory Flash Programming Functions (Called by FlashOS):  K# M" ~( b+ K6 W4 P* j
  3.                 int Init        (unsigned long adr,   // Initialize Flash  @7 ~( k5 e+ a- [: @! D& N+ H
  4.                                  unsigned long clk,
    + z& o0 W$ h' z2 w" B6 \# n- ~7 l) V
  5.                                  unsigned long fnc);
    9 f# t' ^* z- A/ y8 P
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash
    6 C/ K  ?! c$ ^2 P- I" i
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function
    * k9 v6 A* X- p! e/ `8 f
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function
    ) z$ l% Y% ~2 d
  9.                                  unsigned long sz,
    1 @! o# C) @, M! y  V% U0 q
  10.                                  unsigned char *buf);
    % ^* G0 b6 ]9 o5 r$ O7 a8 n

  11. 9 W' @  W3 X# u9 @" o& A
  12.    Optional  Flash Programming Functions (Called by FlashOS):
    $ e. S# {8 B% S: R, X3 Z
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check
    ! \4 p, _0 W4 q. z) u- m6 l! p! ^0 r
  14.                                  unsigned long sz,3 l! U, h) R2 k3 \) V$ v
  15.                                  unsigned char pat);
      L. s$ c  u4 R5 A
  16.                 int EraseChip   (void);               // Erase complete Device4 [  J# k1 D$ S3 v+ B
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function
    9 ~6 h5 X5 a/ o2 g% R' X2 ^% E
  18.                                  unsigned long sz,- I2 q# n9 ^, J# p2 Q3 Q( {
  19.                                  unsigned char *buf);
    2 w. S/ G/ |0 W+ w0 O
  20. 9 k  k& Y( v3 J% }" h' u3 u
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space; J- q1 f$ C" ]7 Z
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space( u; J  x: r* x3 k7 j; t/ M! n6 C! V" |( d
  23.        - if EraseChip is not provided than EraseSector for all sectors is called1 U; ^) O7 v9 G0 V
  24. */  m5 G3 Y; L& Y/ g- }$ f

  25. ' {: n9 \$ D$ N& ~9 M
  26. /*2 E+ ~3 u+ q7 U6 R" k! g& Z8 I% c
  27. *  Initialize Flash Programming Functions! \2 p9 Q! p$ E
  28. *    Parameter:      adr:  Device Base Address
    & j. S5 w: x% x. _: z1 L
  29. *                    clk:  Clock Frequency (Hz)
    . t+ Q+ |# L7 V, P% `
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify), `( ?. L6 K  w3 r
  31. *    Return Value:   0 - OK,  1 - Failed( A( e" z. k+ e8 R& B$ l4 [
  32. */
      Z9 p2 H1 {5 l6 ^! ]
  33. # d  b( e9 Y. P' H/ y
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
    / c' K! x' _6 ~: h

  35. 9 x) v1 v% f; M6 p/ R! X- W/ d; g* p
  36.   /* Add your Code */
    % m$ s9 E/ {5 J
  37.   return (0);                                  // Finished without Errors
    ! R* S; Q6 k: f9 z4 ~( |
  38. }$ O4 E  g' u# `) t! k9 w2 `+ T4 V

  39. ' h/ w3 d9 D" a7 S  q7 M2 ~* _/ X

  40. * T! M+ O& R. ^( _
  41. /*
    . q+ f6 T5 B# B+ ~' G7 f
  42. *  De-Initialize Flash Programming Functions
      G. \1 d: [3 E/ X+ ^
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
      `; N  l$ \6 z6 F
  44. *    Return Value:   0 - OK,  1 - Failed
    4 P! s1 R1 \. e4 a5 k- r$ {, x
  45. */0 `9 Y' _$ d# ]  l  X0 Z

  46. 4 E# d  j" I4 U; x0 L6 m4 \# ?
  47. int UnInit (unsigned long fnc) {
    3 X$ T3 B  w: z/ o
  48. 6 L4 s+ r8 W2 N
  49.   /* Add your Code */
    ; o$ s+ ?; A, Q3 i" ]
  50.   return (0);                                  // Finished without Errors
    & Y( U; C4 V2 ~8 N
  51. }
    9 H( A4 f* V9 l6 o# h! V

  52. ( v4 E+ w! r% K) |' I1 o7 G
  53. 8 E, M; W+ v" T9 a
  54. /*
    # b' {, \  T8 J2 _9 w# {% L$ t
  55. *  Erase complete Flash Memory
    2 L3 X2 e  A6 b; \7 w) G- Z, k
  56. *    Return Value:   0 - OK,  1 - Failed
    4 x5 h& u, p. P
  57. */
    ( X* I' w+ ]0 S- t  [' D, n

  58. / J# P6 I  a3 L% o( U
  59. int EraseChip (void) {
    & T! S7 |0 E5 c

  60. 2 P  W& C- m0 b6 Q
  61.   /* Add your Code */
    $ G3 S5 L) Z, M, X
  62.   return (0);                                  // Finished without Errors
    + a% k0 X9 J7 d( @, ^& `( g
  63. }+ R5 f: H8 ?, `6 R

  64. 4 j4 I  o  b  \3 I
  65. 5 v) ]7 }/ ~# x3 n$ ?, H
  66. /*, R5 K& X4 x& A! ?& `- L
  67. *  Erase Sector in Flash Memory. Z( `4 r2 \& ?; q3 t. w
  68. *    Parameter:      adr:  Sector Address9 L3 j# q+ D1 C7 I, c3 e- R
  69. *    Return Value:   0 - OK,  1 - Failed6 h! [" k4 D0 O9 @! _& y3 X
  70. */0 n+ D" V+ v( T& W$ Q& D

  71. & B6 K2 _/ [: b% p9 p
  72. int EraseSector (unsigned long adr) {
      p5 N  y/ m6 v: b; f" R
  73. , m' m0 q7 [6 R- @
  74.   /* Add your Code */
    & D3 o& b5 _: O( N/ U3 ~9 q
  75.   return (0);                                  // Finished without Errors
    $ Q5 H  P$ m- o, p2 v! P; B" J
  76. }
    # B4 v0 w& g' s7 P2 R- r
  77. 9 R& k( z$ b9 \( e- v/ E
  78. 0 `# c3 m: |2 \4 B' l5 n1 o
  79. /*
    6 t0 h- q1 b$ U
  80. *  Program Page in Flash Memory$ r2 z4 Y/ b! @+ v+ f  V' O
  81. *    Parameter:      adr:  Page Start Address
    6 u& E2 F7 n2 a  \! a
  82. *                    sz:   Page Size
    8 h$ z" W$ ]- z% x# C
  83. *                    buf:  Page Data
    - S& r* j8 p5 U  C- x  g, d" t3 D9 k
  84. *    Return Value:   0 - OK,  1 - Failed( Y; R2 H6 a6 n
  85. */, J; b6 k. C. I* h* w/ Q
  86. : N3 r1 Z4 u3 J/ }( @% Q, q8 F% s
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
    2 A& C0 b, I* @1 p1 x
  88. - P! ?4 M/ k4 H* v( B: o% b
  89.   /* Add your Code */
    ' O2 @5 {1 V7 X
  90.   return (0);                                  // Finished without Errors
    9 m- J/ v( T4 m; x4 D3 H+ o
  91. }
复制代码
) T% t7 U0 W1 J1 I
80.3.6 第6步,修改配置文件FlashDev.c: Y" D( f- y7 W0 Q  s" W1 ]
模板工程里面提供简单的配置说明:
6 B) U! P: `% B. C3 Z" r2 `' n
8 l0 M$ j# F. N; G3 f" _& X
  1. struct FlashDevice const FlashDevice  =  {
    / t6 C( s+ M/ e& i  p- K8 J
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!$ j, {/ }  b/ C. Y  F
  3.    "New Device 256kB Flash",   // Device Name
    . g6 I4 M+ x% _- V
  4.    ONCHIP,                     // Device Type
    5 i( M; S8 e% g/ _1 C) c! A
  5.    0x00000000,                 // Device Start Address) y- l- g  q3 V* H! g
  6.    0x00040000,                 // Device Size in Bytes (256kB)3 m8 e: `/ e( B7 r+ C' ]" \3 N/ V0 d
  7.    1024,                       // Programming Page Size
    ( A  t0 ?: R* ~  F5 N
  8.    0,                          // Reserved, must be 0/ f' B+ A8 d' ~4 V+ q! v' f
  9.    0xFF,                       // Initial Content of Erased Memory
    3 f4 N9 J: ]; ^& q! p# O
  10.    100,                        // Program Page Timeout 100 mSec6 S% X0 m$ r! r3 M, U$ z& O3 I
  11.    3000,                       // Erase Sector Timeout 3000 mSec$ s5 t" g  d" E2 T- _

  12. ; N1 t8 ?" }2 x6 W/ H
  13. // Specify Size and Address of Sectors
    / K9 w+ n8 K3 C: @
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)6 M- I1 m4 ^( J& @8 d9 ~
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors)
    $ y* J4 r" g  e3 W
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)7 Q  w! S5 P% B4 K/ r/ ~: e
  17.    SECTOR_END9 F0 Q) y* B1 {
  18. };
复制代码

! w) O; p( a( U& a! K5 l- R) u; m注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。) G: K- B' ^. g( W" i7 m& L. s
0 x, d' k# o' `. _& |$ w& O
80.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关! y# U! B* R5 I5 Q8 }- i8 _
C和汇编的配置都勾选上:
. f4 D0 m3 i' F" J% ?) R1 U0 W% B
1d27a7314fc6c472937ad43852722b6b.png

/ J9 W9 ^6 L. m+ a; y* ~2 D) {- M6 m* g6 \; c
汇编:) g. }' T  E: f$ h' O) P0 x

% ?( R8 ^% Y( [! ^" M1 f9 q
bbea604970c4ef63c77f10e553e28ea6.png
/ D- `/ h, I) o
: o8 j' v8 ]3 z% x9 h, H- }
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:! j% O3 x$ ]9 F, Q% k

3 G0 ]9 z( X' [9 N(1)加载以响应运行事件。
1 f( H8 n: m& w. f
, ^& U- D8 v2 v: n(2)在不同情况下使用其他例程的不同组合加载到内存中。& Q" [& T& Y, G5 H# W2 `8 \
+ I+ H* X+ W+ w. x/ T  E! l
(3)在执行期间映射到不同的地址。
0 A! R2 z: W1 }4 W  M5 Y: {0 T+ J: ^+ B
使用Read-Write position independence同理,表示的可读可写数据段。
8 @/ J7 v) q# x3 q7 U6 x5 b( e& N
80.3.8 第8步,将程序可执行文件axf修改为flm格式
6 U3 D( g: Q( H* P3 f1 L
通过下面的命令就可以将生成的axf可执行文件修改为flm。
# p1 ^. F+ V* ?9 E/ V" \
4 b( N4 z1 y$ T$ n' [) w; \
6b504444979e9fa3a3477c6284abca8b.png

! s( Y( i) H  `# u4 Y( `3 I
+ G- T7 L9 N, C- y7 Y. H) U80.3.9 第9步,分散加载设置3 i4 w6 [* d4 {1 ?8 G) `9 ]
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。
3 U* C  q1 T! f% S# F% Q! f, }) r) N( B& Z4 X' E; {2 X3 L2 i4 K1 Y
e8e79854b204b618bbda32af92f28bf2.png

) }% ?; ~1 q* R2 J- u) D7 ^3 W6 W7 D$ k7 P! l
分散加载文件中的内容如下:
4 I% X1 `( W( W- w( m% L' W) c" q; V2 a& Z) U! n/ U' z6 [
  1. ; Linker Control File (scatter-loading)
    9 B% C3 y$ X. u+ ~
  2. ;# H/ E+ ?1 q' z
  3. 7 l& [  V. R, q% c' L0 x; ?/ N
  4. PRG 0 PI               ; Programming Functions
    ( I. p5 k/ T* R  m1 i
  5. {& \; }3 K3 Y' t
  6.   PrgCode +0           ; Code
    9 J, m, V* c+ T. c( y
  7.   {
    1 S. P* B. m( \: T# A" W  w' j% y
  8.     * (+RO)4 V2 l" ]  b% A( p) T( B% N" f( R( j
  9.   }
    ' Z) \' |/ C# h/ M; }
  10.   PrgData +0           ; Data
    1 T  c9 d+ T7 h7 y
  11.   {( q/ u! M; Y0 N8 z: ]8 I2 V
  12.     * (+RW,+ZI)( n/ O- N2 B4 d9 e2 D  J8 I$ S9 R
  13.   }" E4 s" C9 i/ T, Q6 d
  14. }
    9 B- _' y/ o1 f; _0 D

  15. 7 G7 Z' [. P# g; c9 H1 Q5 H9 b5 B, `
  16. DSCR +0                ; Device Description8 Y' F$ O/ D: @
  17. {
    1 i0 r( Y% ?/ s: a% L" p3 n/ Z
  18.   DevDscr +0! |, U# J# @6 Z% `1 w
  19.   {  o% B' A1 U4 m4 E9 _4 j- y
  20.     FlashDev.o
    6 `! w6 k  B7 w4 S1 Z$ n5 G6 p5 j
  21.   }' I2 x( d' E9 [4 n: K9 Y3 @' G
  22. }
复制代码
# P9 W% d+ u$ k8 y
--diag_suppress L6305用于屏蔽L6503类型警告信息。# e7 c* Q! n1 L! g
' @( @: t' l1 [$ p
特别注意,设置了分散加载后,此处的配置就不再起作用了:6 j7 Y, R  Q8 t6 {

6 {& b! V/ J/ i% u$ T
b249eb40e5562b04ac3eb05ec0219c1d.png

" ^; l: V* N" {: q& {+ }, }
. s! t) v2 X; t: n/ R7 a80.4 QSPI Flash的MDK下载算法制作6 e2 L- j5 C+ t0 K2 S
下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
1 I6 `  _( Z4 X" K, i+ G, o8 I) N- q$ O5 o, F
) X( w4 D7 p+ K: b$ X/ t& j, w80.4.1 第1步,制作前重要提示7 T, M7 S8 K5 l2 g2 H5 R1 W. y$ \$ l
这两点非常重要:
% ~" X- q; B1 t6 ?4 b4 L6 d7 d
  K  i( e* ^7 }5 K+ r" z3 D! Q+ ]  程序里面不要开启任何中断,全部查询方式。: [1 Y5 ^% P  u* {! R- y& f
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。( {8 |$ h7 s7 a! _! i( t4 p4 \- Z

" M8 e3 t7 P5 E- v80.4.2 第2步,准备一个工程模板. T, G. r5 d/ }. _. E5 p4 F
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
1 j) e4 o0 q; x$ [2 Z5 ?
1 p& w7 I1 [# i: M) T7 K9 i" {
a91aaf84a25d730b77273624d2b502f6.png
/ }" u- e) A$ {5 e" C
0 P& F$ ^" _9 z4 \5 C/ j- {% T1 ~
80.4.3 第3步,修改HAL库9 B! S- U& L9 j% j) p% U7 ]
这一步比较重要,主要修改了以下三个文件:
8 b4 ]0 O" F, j- D. }7 e  m) D0 }# I& X2 {
4471afac6bbc201866d0d9a8ea7d876b.png

' H* [( i& g! g  e
2 q, E+ p5 O. s' q, t6 m主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
% E: j- \' K' S" ~  ?5 Z$ _% N; n2 h* N$ x! T- n( B& F
80.4.4 第4步,时钟初始化
- f! r4 a& b* V* }; g. H我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:5 n) n+ \1 G8 s( X& E1 v

9 {7 z3 U" \, o/ y" }. h7 b2 o1 a
  1. /*& H7 \2 {% T' b- _
  2. *********************************************************************************************************
    + M7 t8 d, B, ~& ?
  3. *    函 数 名: HAL_InitTick0 ]9 ]" j' X, e; c) @2 I
  4. *    功能说明: 重定向,不使用
    6 `! E: P3 }, B0 S
  5. *    形    参: TickPriority
      A% d0 I- x" k& C
  6. *    返 回 值: 无% ]  C: W7 K% Y' [$ h+ G# n' D
  7. *********************************************************************************************************
    4 a7 ]& _8 Q+ L: Q. U( x) ^
  8. */# i* A5 D9 w. r  j) E
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)6 q5 J. |6 ]6 N7 L% s( z4 d
  10. {- k$ d2 G" Y- m6 m3 x, M" F* r6 g
  11.     return HAL_OK;% x3 U2 {# x# p! e# v1 p
  12. }
复制代码

) o. H; R1 j: U4 \6 T然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
, f1 m" W$ |* M+ ?$ \8 C7 n) {- u7 T
  1. #if !defined  (HSE_VALUE) 5 b9 j4 C6 B8 j( O# ?2 X
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    " [* m) e  b" {  P% l
  3. #endif /* HSE_VALUE */
复制代码

& I# J$ u, j& G: Q5 B& j* `  h) K最后修改PLL:: ^% C" E+ g7 w0 ^! h+ `
8 a2 K  E6 J; f) S
  1. /*
    % [4 A; b! A$ Q+ e
  2. *********************************************************************************************************, P  Z! M6 E$ q, @" l) O8 Q
  3. *    函 数 名: SystemClock_Config) }0 h9 a% Y, \2 Y! ]
  4. *    功能说明: 初始化系统时钟9 P  `& y* [4 e4 h* X' x8 ]
  5. *                System Clock source            = PLL (HSE)
    * V1 y  V4 |  h. J
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    7 V" a' @1 O5 E3 P! Q6 f( E0 L
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)) T. @! U! W% K6 w
  8. *                AHB Prescaler                  = 27 ]' {0 O: A! V" b6 v
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)1 ]& ]0 {  E4 Q0 |) d
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    1 ]9 \: s- |) z8 ^$ y  s
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    9 m, P; l' R+ K
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    + b+ }% J% [. R/ @1 B; ~/ \
  13. *                HSE Frequency(Hz)              = 250000000 _9 B3 _+ X9 L6 ]
  14. *               PLL_M                          = 5* i4 @6 v) R+ Q# F" j
  15. *                PLL_N                          = 160
    ' c/ J3 p8 T- E: s) L$ H
  16. *                PLL_P                          = 2
    5 \$ x: v+ N6 B
  17. *                PLL_Q                          = 4; x7 a7 C+ g) B6 m, M  [* Q  b/ c
  18. *                PLL_R                          = 2: {- w' T. f/ L! d' d$ j
  19. *                VDD(V)                         = 3.32 D1 @% \, h! J7 g* ^, |' ^
  20. *                Flash Latency(WS)              = 4! A3 |0 [4 q$ Z8 `8 v2 ]4 M
  21. *    形    参: 无
    2 m& R3 \9 E6 y6 i5 c- u7 z
  22. *    返 回 值: 1 表示失败,0 表示成功* \$ O. f5 p, [3 u$ ^& A
  23. *********************************************************************************************************9 G2 r$ l. B  s0 h; }$ l8 r
  24. */
    ! Z6 b  T) Z3 _( T( ^4 a2 H
  25. int SystemClock_Config(void)
    , \. G6 V' `! h
  26. {
    8 f4 x/ p# H3 t. Z, Y# A
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};; l6 a8 K! y' x. V
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    0 y6 B( n1 p7 R5 y
  29.     HAL_StatusTypeDef ret = HAL_OK;. `8 D2 R' }+ m. h+ ^& X

  30. + x1 X; R6 W8 E- h4 R* h5 q
  31.     /* 锁住SCU(Supply configuration update) */# `8 R6 _! ?3 |( C
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);% I, A6 P/ }" q9 T

  33. 1 _, C( V0 Q& P9 N) q$ E. z8 O
  34.     /*
    ! _; h( j1 w$ a$ H/ V% `
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    % g' G. }! {' d* G2 q
  36.          详情看参考手册的Table 12的表格。/ m: r, l# a! `
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    4 d, D- u$ q7 ~7 D7 k9 D' \
  38.     */9 p& T! M4 ]& w2 D. R4 ]
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/ M5 c; k4 j  e8 {1 p
  40. 7 L. E# ^1 f/ E0 S( H) K
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}9 j/ I3 [- ]" a: J) a
  42. ) G2 k1 M0 a7 `1 Z6 W% }
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */3 g5 Y/ p0 b6 W% ]1 A; u. \& }+ A
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;& x* b/ s; |3 X+ f1 c
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;: a7 ~# U9 G, h/ _# k
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    6 d9 q1 ?4 i$ }$ H3 l, i4 Q' v
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    . B" ^! z, }' n& s
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;' l3 l7 O: T' }: q
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    7 H8 `, i. o7 B7 [9 L& D1 a

  50. 0 a/ c* P- V$ @$ {
  51.     RCC_OscInitStruct.PLL.PLLM = 5;  t/ n/ U) v) w+ X$ u/ U" w' J4 M
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    + P( `- i; l, f# A" [
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    7 m; [' e) h% A. Y8 X6 A% H
  54.     RCC_OscInitStruct.PLL.PLLR = 2;; F! D1 j2 _1 p. v# L3 {! l
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;          w  O% v0 M* T3 \6 F* y! q; J

  56. " W1 p, @, y8 W4 v
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;6 \1 C! _5 Y( z) \
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    ! t# s; D! _* C# W) N
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);1 t0 n- ^& r* q6 F3 w
  60.     if(ret != HAL_OK)
    7 e9 P  I1 O* r9 t9 c0 |4 K
  61.     {
    9 l* o5 F" M" a" {0 L- L
  62.         return 1;        6 Y7 W+ q2 O2 x; ^
  63.     }) I2 a: t+ P0 Y3 f8 T; }' J8 ^) k

  64.   o7 @4 h9 G2 `/ J2 @
  65.     /* ) S8 Q, h" R* B3 k5 L5 c! r
  66.        选择PLL的输出作为系统时钟- h& d/ q  P; e% j5 Q1 D. @$ F  d1 M) O
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    6 a  g5 A1 F) c- a1 |% e- J
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线6 \% ~7 `" M6 F1 B$ Y3 J7 b6 u5 W5 S
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    ! A  Y8 L' q1 |) E
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    + y0 l; g3 m/ A; W6 G3 N
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    ; T, Y# C! v% }+ m
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     $ A; W( t$ N- d& U- U: h$ G- ?# |/ u
  73.     */
      `  c" z" V/ b5 o/ d, d
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    ( a) n0 E( ^. }# _8 ~
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    ' k- V/ i" U7 w8 J: `  p& u
  76. " q2 f3 X% i* w
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;, @9 T& ^- x5 T: _* e
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;/ Z; Q3 |- Y( d# x4 b0 h, F
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;9 T9 N7 h, u' {' ^( s  _
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    5 s! O+ |" S. w( |' e$ x
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; . V8 [9 ]9 x2 A) t4 d6 f. q* C
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 8 i& h$ a- _! K* y
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    - W; Z' C4 t4 b/ x0 c! P% g7 ^

  84. ! |% I; h3 o: E( s# s2 @6 p
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */* |- d0 e% F' R" D* a/ R, ~9 B( h0 n
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);& g: t; ~( C4 U, P3 K% Z
  87.     if(ret != HAL_OK)
    & o( @; n. ?! N! V, a5 @& a
  88.     {: [, A2 }* w  J  F6 j, g
  89.         return 1;
      r( E$ ^) {( w  ]* j1 m0 }' P9 B
  90.     }8 n8 L( u# a/ |6 Y1 ~& q2 p/ o, p

  91. ) w6 t! |9 ?8 Y* }
  92.     /*
    . `. Z1 ]8 `& O
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 , ?2 X' p% t4 j- W1 n
  94.       (1)使能CSI clock
    0 v+ A: S+ X3 t& `5 a0 M3 C& I; A
  95.       (2)使能SYSCFG clock
    , @# X3 r& e$ `( f2 P! B; H! v
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    & c0 z- ?- N9 f! e9 E: t
  97.     */
    9 V) h+ g% s* K% G5 N8 A
  98.     __HAL_RCC_CSI_ENABLE() ;! p9 K" a* Z; \8 ^; K$ z0 g2 L, E) E

  99. 4 u; |/ \9 O0 m* R
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;$ D6 c) \2 C) _9 K( T

  101. % c. z, `2 g# }; f
  102.     HAL_EnableCompensationCell();
    4 ?+ Y  A3 _+ B5 N

  103. & I! k$ d0 B) n: U
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    , m6 I3 c! X3 J) V
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();- \4 T  p3 k6 k) y" Y' T" d
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    9 ^4 ]; w# b1 d' w# H6 }. }% q+ B
  107.   o/ x7 L8 J) E2 o! y3 G
  108.     return 0;0 r5 W/ U# g$ H
  109. }
复制代码

. x" I! z0 R! y: y80.4.5 第5步,配置文件FlashDev.c的实现
- N. ]$ o- f6 E配置如下:+ W5 ~3 ]/ u( R# ]' K" A/ T

$ l/ z" n3 S3 ]4 r' ]% v
  1. struct FlashDevice const FlashDevice  =  {1 u7 x2 k( O) ?$ A
  2.     FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */% v2 O' k1 p2 b( b8 Q& T7 L1 W7 x
  3.     "ARMFLY_STM32H7x_QSPI_W25Q256",   /* 算法名,添加算法到MDK安装目录会显示此名字 */, L$ h+ s" l9 _9 E7 ^2 b  ^
  4.     EXTSPI,                           /* 设备类型 */
    / `+ M1 ]4 [9 {8 z
  5.     0x90000000,                       /* Flash起始地址 */5 z4 N. O1 c# J0 Y9 W0 P
  6.     32 * 1024 * 1024,                 /* Flash大小,32MB */
    2 o9 [( {( D9 B: R0 i
  7.     4 * 1024,                         /* 编程页大小 */
    3 A: R5 B7 }8 w2 r! G2 D9 k
  8.     0,                                /* 保留,必须为0 */9 {0 G1 X$ r  n* S) b, i2 V! `
  9.     0xFF,                             /* 擦除后的数值 */. Y/ ~' O* o7 J# H
  10.     1000,                             /* 页编程等待时间 */! R# P5 h& I$ d, ~
  11.     6000,                             /* 扇区擦除等待时间 */' G/ s5 n' t- L6 M6 l# a
  12.     64 * 1024, 0x000000,              /* 扇区大小,扇区地址 */
    3 o1 P1 u& ~5 v, [3 T8 ~, Z
  13.     SECTOR_END   
      x) G, T) ?/ I0 C
  14. };
复制代码

9 H0 E( x) s: \0 W. y7 r注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:: o* J) Q8 Y/ |1 C' }
' ^) Y7 w# {# }8 l: `
ffed7a581be4c5da388ee29fb67249b4.png
1 A" l( ^' f+ |- n3 |
0 j) Y' W9 `. M- D# l$ ?0 T
80.4.6 第6步,编程文件FlashPrg.c的实现- o  v9 Z1 @7 U% B5 e
下面将文件中实现的几个函数为大家做个说明:
& ]" z" N3 T( e7 `) C  i) z4 H% s2 v2 R: o
  初始化函数Init

9 v. R4 F. w4 N* E/ Y2 I: q( Z
  1. /*
    # A9 V" P7 X4 V/ L7 _  _3 A( C
  2. *********************************************************************************************************& ~5 x5 T0 k6 [/ Z
  3. *    函 数 名: Init
    6 @, ]# [% p! @  z
  4. *    功能说明: Flash编程初始化
    * R4 t* [0 z2 O
  5. *    形    参: adr Flash基地址,芯片首地址。
    & f" F- B$ O; E3 O6 r( v" u
  6. *             clk 时钟频率
    6 m! S4 s2 r) d/ _; [6 o" V
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    ! m0 N3 R8 d: P6 i, D9 }4 q/ z, N
  8. *    返 回 值: 0 表示成功, 1表示失败- D9 e4 j  h& `8 \) k
  9. *********************************************************************************************************2 U& o( V! U7 I" ]8 V. W' _/ x) q
  10. */8 G+ M8 r, |( W5 L/ @. P* L
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
    ! J- m: f4 u5 j, B
  12. {
    , L: ^! _( i" G& A8 e  C2 D; X! u
  13.     int result = 0;
    + \. ]- b- C+ }( P; N
  14. % i5 M' B1 J, [0 ]
  15.     /* 系统初始化 */. S9 |9 X& @- r; v; V
  16.     SystemInit(); 8 j, F* i" P6 }2 j; y0 c

  17. ; T9 y6 t& a! ]- u% K3 i9 W
  18.     /* 时钟初始化 */
    - f3 M3 r+ {3 }% T: h, d% l
  19.     result = SystemClock_Config();
    * h, n8 o) J3 n: U/ s' b: i. E6 ~
  20.     if (result  != 0)  |) y' i' |! E( ]  l
  21.     {" h' c& g8 y) e
  22.         return 1;        
    . H* _6 X- ^# n8 e+ J
  23.     }8 U! `3 N! R2 a: _% g1 ^

  24. 6 F& Z. |: n  s2 [% S/ f5 _
  25.     /* W25Q256初始化 */
    2 D6 F& }# a8 M) |9 n% k8 B" G& U  d
  26.     result = bsp_InitQSPI_W25Q256();
    - r3 c7 D# h! a0 v
  27.     if (result != 0)
    . K% p$ w5 n8 g1 Y0 D1 [0 J
  28.     {, k; D; Z% s& B% ^. S+ n
  29.         return 1;3 R) E& ?7 G  W; [5 ~
  30.     }# d: A; t" }4 u2 a; p4 x4 o' T8 Q

  31. # o# C* k, D. i: r$ ^
  32.     /* 内存映射 */    9 }1 q$ o! O' b( f" [  g9 v
  33.     result = QSPI_MemoryMapped();
    * J8 T9 r& w# Z' k1 S
  34.     if (result != 0)
    , f! ?( A* d1 u2 }) ]+ P
  35.     {0 y) f' l4 h) A, ?0 L
  36.         return 1;
    ! T( t# C  T# g& F
  37.     }
    7 Q. H' C+ J" V1 U8 Z1 I4 L
  38. 0 t. e3 j1 {8 ^0 Z  X# d
  39.     return 0;. |( V$ {" B2 @! s
  40. }
复制代码

: l& x3 D9 R1 _3 a4 u6 a7 A初始化完毕后将其设置为内存映射模式。
* c- A1 P$ s; f) J3 l/ [4 ^3 ?& G% W6 C  K* P0 }  s6 d# }" }, x
  复位初始化函数Uinit
  q) q7 B7 d/ C7 o4 g. U擦除,编程和校验函数后都会调用此函数。4 j% Z9 L: e! F2 J: B1 e& a

9 e0 q- K: T2 Z. H( y
  1. /*
    9 e0 F- a( n4 @/ r2 l
  2. *********************************************************************************************************
    / f9 p# u) F! b% ~1 _3 B
  3. *    函 数 名: UnInit4 d: p( F2 d/ `
  4. *    功能说明: 复位初始化
    6 y* d2 u1 \* a, U+ v  {
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify0 E6 _2 y' i! _, p! T* z5 |2 Z, i
  6. *    返 回 值: 0 表示成功, 1表示失败8 F& |$ V  B2 d8 H3 `: T% ?
  7. *********************************************************************************************************
    5 Q$ ?" k& j  H. t: X' c4 e
  8. */
    . O0 H$ D' \) Z& h% V
  9. int UnInit (unsigned long fnc)
    # h1 j% F0 v/ L. |: ~
  10. {
    7 [$ S) k/ `# w5 Q: ?0 V
  11.     int result = 0;
    . J9 g0 K. r. P5 `, \6 v" y% E& U2 o, t
  12. " z0 c8 c( a, ~) O
  13.     /* W25Q256初始化 */
    6 C. J, E- w$ g( l: f2 S
  14.     result = bsp_InitQSPI_W25Q256();
    / |3 b' V' W; R9 n3 W
  15.     if (result != 0)
    5 d' a0 U; C9 z
  16.     {0 A0 V$ @: k; m4 Z: i2 s; L. G
  17.         return 1;: X! R* @* p4 }, \
  18.     }
    3 l* k3 Z) I3 K3 A# M0 L* S

  19. 4 `. y; R1 c9 I6 D5 h
  20.     /* 内存映射 */   
    : O: g6 I) F- |. k. u& u  B! ]
  21.     result = QSPI_MemoryMapped();
    3 f3 Q" k+ z* D
  22.     if (result != 0)0 e1 e# N6 V: s0 [# Z8 }
  23.     {
    $ C8 j- n6 u' ?! H+ E
  24.         return 1;8 J& e" v2 t# y2 W7 [
  25.     }
    % x5 v. C3 P: e  q" r
  26. " T+ _0 R" h9 ?$ P1 ~! q
  27.     return (0);
    ) ~8 ]7 Z, l# e
  28. }
复制代码
0 l5 K( o7 {3 ~8 h/ B6 F  c; ~
复位初始化这里,直接将其设置为内存映射模式。
9 ]) f! T/ K6 j% B; c+ @
3 e# @4 X+ }) \  整个芯片擦除函数EraseChip
2 I2 x3 r) g8 t. B& M5 ?" l" L4 ?如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:
1 k! p/ P: m- b
: \, h# l0 W+ \1 T# B. \8 m
f623e662bf09233ea594f820f4c7952a.png
: c) x- n. u9 u- h9 G

6 B2 a3 z. \  Q8 n& p7 W' |) b实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间,比如32MB QSPI Flash整个芯片擦除需要300秒左右。
$ e- {% t. @9 c! ]0 i- N$ v" w& z( j
( ~4 m) c2 V  v8 b. s1 e另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。
3 v+ z# o/ l% T$ T1 p
9 n. `+ T# q5 K# _1 L# n
  1. /*( d& Z" a3 o1 z, K& n) y! |
  2. *********************************************************************************************************
    ; d( ?( V; [) s% c8 [9 X
  3. *    函 数 名: UnInit
    - v& i, ^+ H. T6 x- p
  4. *    功能说明: 复位初始化5 Y1 P9 j% u, U% }' A
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    4 ^5 A9 y6 Z5 K! N+ r0 y' ?
  6. *    返 回 值: 0 表示成功, 1表示失败
    , w. ]( e) d8 g2 J. x
  7. *********************************************************************************************************0 e: q8 ~5 u; K$ B* \9 N# x! b7 U2 h
  8. */
    3 U2 w: T  {7 @" o7 `# l9 X$ R' a
  9. int UnInit (unsigned long fnc) ) A: ^9 j& f. f. C0 e% p# l
  10. { # q0 x, }; l; E1 E( r( u
  11.     int result = 0;& ^) J, W1 q  C0 r% `

  12. 8 b7 S8 i/ @  J8 M  U
  13.     /* W25Q256初始化 */
    9 ?' \5 m1 U7 J5 m! y+ d( u
  14.     result = bsp_InitQSPI_W25Q256();1 }+ P& R! T2 O" t" R! a
  15.     if (result != 0)
    % f( Z* o1 t* u3 `& C
  16.     {
    + g7 t7 \% ]& K) |: c7 H0 N
  17.         return 1;* w2 J* Y; Q+ P5 ^: ?' p6 i
  18.     }
    0 ?8 _: [6 I; J

  19. , Z7 l5 X) C% ?' d4 \, E0 H
  20.     /* 内存映射 */    ( [6 g; n; w) P6 J$ J
  21.     result = QSPI_MemoryMapped(); ' ]+ e! K, Q6 a' B2 Q) {6 J
  22.     if (result != 0)) H( j% S' Y* g  E" J
  23.     {
    # q2 o4 \2 f6 I
  24.         return 1;( F. w: ^; x0 b1 l+ P
  25.     }
    3 f  O2 C9 d5 y: g" B% m* k

  26. 1 c; L5 x6 e0 o! y; a$ B
  27.     return (0);) w* ~" T* L: @" H+ C
  28. }
复制代码
3 @; _: f* J. }6 s( M$ B: R5 u
  扇区擦除函数EraseSector* n: i2 n! U6 ]; w- G
如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:
9 Q5 B: i6 x% Z8 O- `2 d5 P& g. K- q2 v: w$ d* `" B( L$ `
733d5af26119f64bff0261515013d3a6.png

- B" g4 Z. v4 r
: B# D4 B9 }2 d  D
  1. /*' D" O3 f& |/ [) }- C. y% C
  2. *********************************************************************************************************
    0 h; c- a# d, M0 j4 Z1 `+ A
  3. *    函 数 名: EraseSector
      K# m, ^' ?3 U+ r
  4. *    功能说明: 扇区擦除
      {8 _7 |" E- T) C0 M
  5. *    形    参: adr 擦除地址% n4 K( ?% R8 O4 \
  6. *    返 回 值: 无) P; {5 ~/ ~6 U* f! q
  7. *********************************************************************************************************
    3 W" i* a; C4 J/ r5 J$ i
  8. */
    . E+ j5 I# j4 K' }0 F+ v6 M, P' Z
  9. int EraseSector (unsigned long adr) 8 ^8 i$ x& r" i) T- Q
  10. {    " u% j! `5 b3 e3 ?
  11.     int result = 0;3 K% X6 [: r1 r' U, Z

  12. $ f2 J6 q& G9 I0 C2 [% v6 e
  13.     /* 地址要在操作的芯片范围内 */
    ; D# ~3 r; M) e4 e! {( p% k' i$ c
  14.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    ' x7 r$ M, L1 {; W2 T
  15.     {
    - ]& F9 c* T. p% Q! w% G
  16.         return 1;$ v# ]0 C' b* u; u
  17.     }) ~2 w( D' m5 h. |  t0 X9 m

  18. 9 U: p8 u- G  e: D' i  k
  19.     adr -= QSPI_FLASH_MEM_ADDR;& F6 i4 F$ v! w
  20. # \* @# ~1 a% |
  21.     /* W25Q256初始化 */: y( e* k. L' ^0 ~+ _1 i
  22.     result = bsp_InitQSPI_W25Q256();
      z# V3 `- m2 L, G
  23.     if (result != 0)
    7 f" f" H" z8 L$ J
  24.     {* A- e: F! v2 s( O* l+ W
  25.         return 1;
    $ ~9 d; Q8 |: _, e# s) j
  26.     }
    0 Z* O2 W! Q8 }# R8 {, _- m9 m

  27. 6 S" o5 [/ Z" }0 j
  28.     /* 扇区擦除 */5 g5 g/ @6 V. @
  29.     result = QSPI_EraseSector(adr);  
    0 @3 b7 d5 g. N" \3 |
  30.     if (result != 0)
    - n2 x% M, |& V2 o4 L6 _* s7 x
  31.     {
    # H7 g0 x( C" w6 i/ @
  32.         return 1;
    / z$ V4 v" z* y0 I$ Z+ [
  33.     }    8 T% b  y( J. a! H+ t

  34. 5 J+ x3 O2 }' H7 F; t5 f
  35.     /* 内存映射 */   
    8 `( u! X6 C4 X, U" h9 \
  36.     result = QSPI_MemoryMapped(); & n6 P$ b! P0 J  |& S3 j+ ~# Y
  37.     if (result != 0): B% `$ |! n! E, }8 e- M( \
  38.     {
    $ l6 v; L4 Z1 _3 z+ h
  39.         return 1;% \* I- V4 @, |0 Y/ l
  40.     }& X% M  T5 ?+ W( l7 [. C4 N
  41. ' `0 H$ x: ~6 g2 a& ^
  42.     return 0;   9 \" c5 _* ]5 n- \# Y) ]9 B
  43. }
复制代码
( m6 O* {. F+ Y$ B& p* P$ O% ^
这里要注意两点:
, `' ^, ^% w% n7 [% G. s8 \3 ?! [1 M. D
(1)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。, p- J' H0 c5 Y8 N* U  K
5 D+ c& c- B  Q- D+ a/ H
(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。
) @$ p6 `& f3 N. u4 S3 Y, T* L* c, t, b! F' v7 C1 a
  页编程函数ProgramPage

1 _3 P8 M4 n: [# Q  ^+ z页编程函数实现如下:
  E" X8 l# d  C" R0 u, [3 Z# w: X: D4 i  B& n$ N
  1. /*
    * U' P/ E1 z& [# [2 B
  2. *********************************************************************************************************- K, `6 p5 m$ Y* J: z% b2 V
  3. *    函 数 名: ProgramPage8 t# O; p+ n' o! C+ }
  4. *    功能说明: 页编程9 D& B! H/ c/ @, Y$ X4 C
  5. *    形    参: adr 页起始地址
    3 l. v& ~8 W3 y
  6. *             sz  页大小6 g4 v% e6 O! _  p( K2 D7 |" p
  7. *             buf 要写入的数据地址
    * a5 B  o. N5 h4 s) |
  8. *    返 回 值: 无. `+ u4 h: S% v7 R' l( g# ?$ W
  9. *********************************************************************************************************6 ]( ^9 |  `$ X! A
  10. */7 s2 w" c5 F, N; I% X  Q
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) 8 B, i( s1 S2 h& S
  12. {, M+ S. H9 N1 I0 \3 `  V
  13.     int size;" L' K; z$ |! N2 d- P& ~. n
  14.     int result = 0;0 a2 ^( g6 p. i+ j9 s" t! N
  15. . p; J( ~  H, R- i9 O
  16.     /* 地址要在操作的芯片范围内 */   
    3 d( h8 @, S5 S2 U8 e
  17.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
      g! E- ?( W- s) j% g% x5 K
  18.     {$ s3 ~7 l- h/ l7 ~; f- ]; `
  19.         return 1;
    / _0 ]9 j3 i5 @
  20.     }
    8 N% [& s, ?2 r+ N$ U

  21. 4 Y1 a3 W8 Z4 M+ {+ l
  22.     /* W25Q256初始化 */' _$ |  P8 E4 Y0 z  d
  23.     result = bsp_InitQSPI_W25Q256();* v! i! X) I7 I7 ?" ?
  24.     if (result != 0)& V7 L$ k/ d# j9 _# _1 G" V
  25.     {
    & T# P- b8 h7 P9 p* t6 j9 }9 n) M
  26.         return 1;% Y4 s0 n; W4 \  N
  27.     }
    - {* C6 k8 k" a9 r0 s
  28.   C$ W* k, L3 C8 u$ r
  29.     adr -= QSPI_FLASH_MEM_ADDR;, Z, ]  Y# h7 K
  30.     size =  sz;& c- Q1 i6 i$ n8 _: z8 ?1 H

  31. & _; D0 e* y6 M
  32.     /* 页编程 */
    , ^  g5 I( ]$ P0 T! f; e
  33.     while(size > 0)
    + n( ~6 A+ l) \5 U: s
  34.     {$ h; b9 v  L1 v" |$ {# Y: i0 }
  35.         if (QSPI_WriteBuffer(buf, adr, 256) == 1)1 ~1 x5 k/ `  |  T
  36.         {
    4 F$ D0 ?6 y4 c5 r- M/ I7 }
  37.             QSPI_MemoryMapped();
    + Q' O) S' w2 I6 g" p

  38. " S4 u5 A* D$ A* _
  39.             return 1;   
    % l* E+ @$ S+ J8 x$ c% T
  40.         }+ p6 n4 [. t/ H& }- R
  41.         size -= 256;8 y: ~# f- X3 m" z/ ^3 B" ?
  42.         adr += 256;
    1 L# J! q( G( j  f
  43.         buf += 256;
    3 c# |: @3 O# K8 ^# L( x- j. n6 z
  44.     }* T& j: [# O# K/ R. `
  45. / w2 `3 o/ x. {6 O
  46.     /* 内存映射 */   
    6 ]4 P2 |; I* c9 O9 S+ T
  47.     result = QSPI_MemoryMapped(); 3 y  N7 ]1 W8 V' `
  48.     if (result != 0)
    7 T: Q6 v) |% W3 Q
  49.     {  q' z7 h) R4 W
  50.         return 1;
    # P2 y) ?3 o( B$ n. f
  51.     }
    3 H- Y' a$ {1 b8 M% I
  52.   f) l% ], Q3 G. J
  53.     return (0);                      & u5 }, C2 E$ @4 u  m' j
  54. }
复制代码

4 n! q1 x) X8 R0 l/ M! K" H这里注意两点:
2 y9 z' M& @2 X& l- h. i
# e6 [2 k; p  [(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。
1 S  M' \3 b+ t8 \5 W0 J, Q* A; a& B7 w1 _
(2)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。% N/ P, S. }1 I4 w1 s
) h7 Y. g7 g: ~
  读取和校验函数
( m8 V9 |, u8 ]( g7 V8 ^我们程序中未做读取和校验函数。/ [5 W+ w! n; h. Z
; R) \& P' \# i  K4 ?0 T
(1)     如果程序中未做读取函数,那么MDK会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
7 v! Q# Z7 |; t
' J& ^7 @7 ?6 t" }) t(2)     如果程序中未做校验函数,那么MDK会读取数据做CRC校验。
0 m1 g5 K0 y+ K- _7 p
* W7 J6 F6 g3 n, ~1 O80.4.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
$ {/ a1 e& U8 {
最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):& e- P; ]6 R/ ^
! o! k. O, N1 O  E! l$ m# r
  1. /*
    : t' g& e8 L9 n7 j: c
  2.     STM32-V7开发板接线# n  v( u3 n9 j2 {
  3. 8 U( {+ H) y) V8 a. B% p
  4.     PG6/QUADSPI_BK1_NCS     AF10; J# ]. P: f* ?
  5.     PF10/QUADSPI_CLK        AF9
    ' x6 V3 h/ C3 @4 _2 A
  6.     PF8/QUADSPI_BK1_IO0     AF10
    . t- q0 Q4 [6 x. G, h0 x. B6 e/ P
  7.     PF9/QUADSPI_BK1_IO1     AF10: A0 X5 \' P  p" x: g5 M
  8.     PF7/QUADSPI_BK1_IO2     AF9. \& T  {0 t& E( N0 r
  9.     PF6/QUADSPI_BK1_IO3     AF96 q: X* ]. r( q1 n

  10. " f& U; Z/ ]% o7 q
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
    5 Y- Z$ |" U# }! r+ ^$ d
  12. # b* ^7 M8 j) W& h
  13.     H7-TOOL开发板接线
    # B) r! E; w! ~3 _2 s

  14. 6 o3 v! K8 L3 r. `6 x9 d2 U
  15.     PG6/QUADSPI_BK1_NCS     AF10% ]# F5 i, x$ P# Y' Q) v* N8 I) N
  16.     PB2/QUADSPI_CLK         AF9/ `; r% v1 y$ m/ N# r. Q
  17.     PD11/QUADSPI_BK1_IO0    AF10
    # L5 O6 S* W6 C  X' ?; M* H
  18.     PD12/QUADSPI_BK1_IO1    AF10
    ( X. B: e9 q1 l# c# r
  19.     PF7/QUADSPI_BK1_IO2     AF9
      t6 y9 K8 O& v  u$ E% ~! j/ V
  20.     PD13/QUADSPI_BK1_IO3    AF9
    ( ^& U0 A3 y7 c" `, l: o
  21. */
    $ s2 k, E6 o# m! g2 `; q# H

  22. & s  ?6 P2 N& w6 H
  23. /* QSPI引脚和时钟相关配置宏定义 */, v- [, w3 [  ?( k3 ~
  24. #if 0
    : o) F& s7 F- _, O9 ]' \
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    ! N7 t  w' m! l+ l
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    1 R* i" _/ }" j  Q# t$ W
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    7 v, |8 d' }* L- W9 R# J0 Z
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE(). N# `3 U$ M: [/ E
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    / ~. m" k0 Q: b; |0 D* S, M
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    ) g0 S  c2 g% t
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()! |  K9 ~2 D- ^8 T' T
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()" K* p" _! p0 \; Z& e% n9 B2 M* D( ?
  33. 9 v- B7 ^- ^% Y3 l8 A: N* R
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    , k! e1 l; b* d: P5 `
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    # v. w8 [; C* y
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    ' J' |: C, J$ ^5 U
  37. $ D7 {; j6 I% f
  38. #define QSPI_CS_PIN                     GPIO_PIN_67 y4 D' x% f" @0 B9 X
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    . ]  x! p9 @. C* Y! d
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI1 ~; ^- M+ u% y8 V

  41. 6 ]' x+ Q8 \  i' v2 S/ q! C5 y5 G- y  A
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2
    % O5 a: V3 R; `4 V
  43. #define QSPI_CLK_GPIO_PORT              GPIOB0 N& |& E( c# i  M9 w' r
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    : ]5 o0 g# ^* K9 _2 }5 @8 k$ m
  45. . |9 Z0 X: |5 T
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
    + g' l. u6 n1 Y# c& C5 }
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    - V8 V9 q. e$ Y
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI' ]# D& c; o$ b4 A' P+ W

  49. " h0 b7 H$ a5 _- {3 f
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12
    0 x; P1 @3 d) o( [1 @
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD, x+ p, N$ l3 S7 F/ h: Q
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI
    7 x3 w) M9 @$ f: J2 X! c. ~( U
  53. % _! J% R. ^& f) X; e
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    # U0 L0 b9 h+ j: i: s- M
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    6 w9 b5 K: T# k
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    1 }" O: a% p7 i7 L1 r8 o, s

  57. $ t! K- R0 W9 F. Y
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13( d  M; @$ z/ b; v
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD( i# _9 Y. r4 L1 Q/ l( r4 v1 t9 f
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    + ?3 c& y! e, q. p  ^% J* _/ ^3 l. [
  61. #else
    . D$ h" s! O8 D1 t7 n; M/ A; X; C; A
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    5 ]/ I. b! G! i' u, {# a* c* b  g$ Y
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()& i: M1 f: e7 x; D" H
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    ; n& K# N- ], i0 n) j- W
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()& V9 t( c) v4 X! H5 n: {5 e. O
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()+ v, x. E/ }. J  |' H& o
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    " L6 m0 }) ~4 [0 n; Q7 P9 s: V
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()+ X+ t* W0 J7 C3 G
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    1 y( A  V+ A+ m1 `( K+ m# c8 O7 T
  70. , i. ~% L- w, E9 ~+ J, Q  g
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()  i6 [6 W) R' R) U( n9 Z
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    # Z* R! Y' d' D8 z
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()' T) h# T9 Z6 R$ V3 B9 U6 d" ~9 f8 i

  74. 9 c, l8 l0 V% G
  75. #define QSPI_CS_PIN                     GPIO_PIN_6
    - C5 w. x( d$ o( X
  76. #define QSPI_CS_GPIO_PORT               GPIOG; N& E0 _8 f" K2 o$ z( G# c# ~( c
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    & _  b  ?( p2 }& U+ \+ F
  78. ( O1 Y  r/ l. K# b( d7 p: `
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    - H' j0 N7 f1 U' u3 a+ x$ V( ]0 O  W
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
      m" f% D7 F$ p/ \. Y& L
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    9 u4 ~% Z' @6 P
  82. ) b3 p- U. d2 n! X! R* p' q
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_88 p7 X7 l1 F/ l
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF7 P& l- ^: m/ n2 I1 f
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI1 b2 S# s; b6 {( }8 F* s9 Z. V

  86. * v7 p# \2 `. G: k5 _/ ^2 M' I/ s
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    + v# n6 q0 `* j# |
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF4 k# r! h: n: e$ @6 o/ k* h
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI" X0 w5 \1 e" a0 c  d% c
  90. $ P# l4 S; l' J, k3 o% p
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7" o3 Y# \! ~1 B
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    8 B$ M4 U8 S' H  G& {
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    6 R$ L: O% D; [( N

  94. : J+ \* i* N* ~$ x& _& l! f
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6" d- T# Q5 X! ^) Z  ]6 V
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF  l; n+ P8 T& m/ ^, t
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    * [! _; B4 A, y! v% f3 N3 m
  98. #endif
复制代码

* E8 ?6 Y; k3 v' W1 B/ B0 \硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
5 W. i1 [- E# D* j
1 m% W1 K2 l+ J6 G  ~主要是下面这几个:
5 {+ D, B% f. b' I& G2 n1 Z( f% F1 i8 e' T  q
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000+ {9 p6 _3 E) Y7 }5 i
  2. 6 A* `8 t$ m# Y! j
  3. /* W25Q256JV基本信息 */
    3 w/ [# f" |1 r/ z. t& B; l7 k2 ?2 H0 q2 l
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/
    " `2 ~8 S, ?9 o9 D# v0 N
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */
    . v. O7 G$ M& T! s9 X4 ^8 E
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */
    " i9 l1 \0 ^, l. g
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */" a# S" t/ C# U8 L0 p
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    1 G" f& M& j+ p
  9.   V$ G1 l: U3 A% r) a
  10. /* W25Q256JV相关命令 */
    4 S0 A* D5 {( U2 V' }
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */
    6 q) @: B4 s: x, F5 R! w7 V
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */1 ~# N6 `* R; |3 ]
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */: V% A& k6 A2 P( T4 C" _5 X
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */3 x: A- j) k- x0 ^5 L! ?/ I5 v
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */
    * U% W7 i% h8 E8 G# V
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */
    " k  z! H$ R5 p& @
  17. * ]/ B  i! z, P' y0 S
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */' Y4 p, O" D6 I
  19. - U" k' b) U7 X0 C0 C1 d
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

" v6 F  O/ g4 O7 w  o& @2 V80.5 QSPI Flash的MDK下载算法使用方法# K+ {- T1 ~$ f* r+ B$ Q, w
编译本章教程配套的例子,生成的算法文件位于此路径下:0 s# `: S+ B" J

# i$ P: b0 @6 v: ~2 z& n
ed1f9465c60f92522e906639b76b29f3.png

1 q8 |# |5 `* f* Y& F
: z. P  @  g3 \. u3 b+ X80.5.1 下载算法存放位置: _$ N- r8 G2 h- g5 ?" n
生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:, v" ]  F* p- D

$ f4 s3 V% G+ j" S( @  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。7 j& ~+ B. N! ?$ N
  第2种:MDK的安装目录 \ARM\Flash里面。0 B# Q' Z7 P6 l, o

+ y; `2 x) q- p0 p% f2 B9 [
9d5d65cb90fb28ad36464cb1b8368759.png
* p1 r3 B- ^1 j& l
  F  T, w. G' w$ s; L3 a
80.5.2 下载配置
8 d: ^. c/ c* g; K0 H注意这里一定要够大,否则会提示算法文件无法加载:6 ]1 Q. V8 `: \% p5 ^8 j
* T3 T3 s  Z/ z- ^" ?9 H  S
8eefa57c2d7f128449000c2fb4e788d9.png

9 d3 t5 \8 }0 r. N7 |! r! M6 A, c, O! U& E, Q
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。
- @; M# ]# m2 y1 v0 [6 K1 Y. g
1 L0 q3 L4 J# `" O+ A7 d( J  R如果要下载程序到QSPI Flash里面,需要做如下配置:+ o% g8 Y8 k6 L9 \5 t% a8 ?. |

( u+ j9 o) i+ y" V3 M1 T2 g
caa01b15ccf9a9700690d2ad1bdedecd.png

# h; B$ K' n% d: h
& O" A2 V" E" J. w80.5.3 调试配置
) O6 z6 B- Q( B& v
注意这里一定要够大,否则会提示算法文件无法加载:
9 j0 @( A$ s7 L$ V# g6 n: |; q6 v+ {( u  F, [  J
f23244d646498fc30aa5f1301a327430.png

) X* _! s7 m; y% z3 E; L3 N3 t* o: y# X
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。6 Y% J* T! w" ~) r9 f0 p

# R: [' h* p* a& h$ J$ L2 C如果要做调试下载,需要做如下配置:/ o* ?0 n! |7 m  r' O
* b1 z3 u9 S8 u+ k9 G
84ee4945e5faabd776fedd6f8f7d96d2.png
8 W. t. b0 j. w  Y" `
2 G8 l& L. e# L' D) f: j
80.5.4 验证算法文件是否可以正常使用
2 A0 r* H! M. _1 S为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。
) c% F: z4 ]$ [/ n$ V0 W' t# K: z0 p( X$ ]# C$ {/ g- k
80.6 实验例程说明
2 m8 s# L: g1 {* p) H
本章配套例子:V7-060_QSPI Flash的MDK下载算法制作。0 t" F8 a. E( ~9 T( l
8 D% V- A& M. ?8 W7 _
编译后,算法文件会存到此路径下:/ }- [5 Z' C4 x1 d
+ R  j8 d4 W! m* f+ y+ z  c* |

2 j' Q# h: d2 c) T6 w9 K. t' Q7 m1 Q' |6 @" y/ \1 A' o
80.7 总结( w( {# X; H2 r  W
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。0 h' _& r4 E  Q4 n' i
/ C0 E5 `; t, l: u8 F" d5 |

, z7 [. q7 u" w  @2 X* g; ~- |; H! e5 M' L* h

$ n# h9 Z3 h9 f- y7 \% F
- O6 S  c1 M7 G% Q8 n4 O
b69a199f1b4a12d880e64c7ba134070c.png
收藏 评论0 发布时间:2021-12-19 16:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版