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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 16:00
80.1 初学者重要提示
; B$ W# L! ^9 P! L% B  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。# A7 a: o# z4 i) R
80.2 MDK下载算法基础知识+ c0 {2 n- J6 i' _# y0 U* i0 z; l
Flash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。# Y. S$ G9 u( i/ Q9 Y) e

# [: ~; d# f+ \0 F80.2.1 程序能够通过下载算法下载到芯片的核心思想" b7 e; J5 o7 b9 w
认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。# Y  }8 j9 ^: \; {3 s" U: U

# J+ F, o. w7 O2 M2 l9 @. P80.2.2 算法程序中擦除操作执行流程( U1 s! {2 P0 n6 z8 U/ b
擦除操作大致流程:& S, E0 C8 ]; X1 a0 {, p$ g

6 _. n+ B  ]7 H  q, H3 \
d4b4af6b23f5f1d5ee3f3387ccb8fbdc.png
7 M' A  v( d9 P( r9 w& e2 F) ^/ _7 g

& F- L% A. @' b; @9 R; A" P6 @  加载算法到芯片RAM。
( R) [- Q% ]# C- N  执行初始化函数Init。
* v1 h9 L, s8 ?% ?. ^* f  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。$ e! c4 i( O7 n0 e0 L3 }5 M! d
  执行Uinit函数。. f% Y+ k" N- ]4 M9 m
  操作完毕。; z  X/ ~! w/ a! e8 g& D: v

* ?' _$ P0 |2 f% ?6 @( t- R5 c80.2.3 算法程序中编程操作执行流程. L9 i+ L) e; I$ Z/ t
编程操作大致流程:
& \7 v1 I$ h& X5 M$ f1 G) T& x4 K
d3f601364bb033d42e275ad686c03726.png
$ a, U+ u& E$ h" t5 ~7 T
0 e1 C: {) \1 U8 M# b; X) f& w0 g, N8 l  \
  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。
& T' p7 g" R+ u5 _" W9 ?" ?  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:) k( \; @) P, T0 \6 H) y
  加载算法到RAM。: X2 r/ N- a& @2 T; l' v" y
  执行Init函数。
  H; d5 C" S( r3 G, `! u  加载用户到RAM缓冲。% E( k1 p7 d! o7 z" G8 e
  执行Program Page页编程函数。! P1 h1 e; w. p' O: |# |- ^
  执行Uninit函数。
: u% V9 U8 Q4 E8 I  操作完毕。9 e. }, ?& N1 Z" ?5 M8 }* y
  v/ k/ r$ e2 h* u
80.2.4 算法程序中校验操作执行流程
5 H) B' s/ r% T' O校验操作大致流程:
1 F- e) _/ n8 T5 [/ A7 T( a
; X# a! Y0 y1 `. H
c7b2f373d946bc7d166ab1c1ba9b67e4.png

0 s" g& y3 b( P0 i/ h. b7 v4 ]# S! }
! N; z* P; p' ^7 N& Q0 @  F& U  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。
9 Z, u, P! W: N" _3 C3 [" [9 }  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
! t# `6 b2 p% I  j  加载算法到RAM。
0 \8 F, x; Y9 x' F7 U- z  执行Init函数。0 g% P+ d* s  K; b; J
  查看校验算法是否存在* Y+ q; a3 z9 N$ P% V
  如果有,加载应用程序到RAM并执行校验。
" S% ^) U& m" y8 u! C- h  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。. O! q- G+ r8 K: K9 c1 {
  执行Uninit函数。
, z& r+ \+ v  k6 J! Z% q! ]: e( j3 b  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。3 M) b: Q; x# L7 S. b
  执行RecoverySupportStop,恢复支持停止。
3 V) u0 j  e! {* L, t; a. z  执行DebugCoreStop,调试内核停止。
( F8 G' @( q" Q2 h  运行应用:9 v& h7 L7 [# o+ ~
  执行失败。- b2 X& C/ o" ?7 M4 K* B
  执行成功,再执行硬件复位。
' c- m5 S$ u- l! J5 J) o; B2 g  操作完毕,停止调试端口。& q% E  ]/ g  g" ~7 C2 e

: b# O2 X, `9 C) C$ i80.3 创建MDK下载算法通用流程
6 D0 {1 X: N  t. K下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
( V5 N- J6 L5 Q) O( A% d/ `) K$ p: W7 F/ r: O* D( Y6 @
80.3.1 第1步,使用MDK提供好的程序模板" U9 Z" Z% O: ]- t. @! r9 t
位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。6 r. m; d3 O6 w# |
# [. m% G/ h1 d" C
效果如下:
! f4 F& G0 p5 u$ {: c' |( W2 H* F0 O" \
8e08d948d34b38592e3699136725ade1.png

. d& L. B; \! c) ~0 F3 `% ]! w3 W# ~( V, H
80.3.2 第2步,修改工程名' v! ]- Y. x) A
MDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。% l" v2 K( A8 J, |/ x. V3 b) l# _

2 Z" x4 D/ Q# E" t- M2 p) I* D3 q80.3.3 第3步,修改使用的器件
, g+ `& ^1 f( F& ?6 o; c在MDK的Option选项里面设置使用的器件。7 r( t+ v  Y6 G
+ g2 ]* f- f+ D
347d6def457f6e101f1c2555f9156cda.png
1 `8 \3 }" F  J4 Y0 q$ p4 a

2 m% c3 _; n+ l8 w  l/ B80.3.4 第4步,修改输出算法文件的名字* g; a! p6 _+ z( t& p: q
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。4 U9 v7 B- W  d7 k1 C$ m) t
; A. s4 x9 P" @2 Q. I
07d754db49256bd0b73f4538a328f3cd.png
- a2 z0 s6 U$ q$ `

  i# u. P9 f- u% P$ i' E/ b注:MDK这里设置的名字与下面位置识别出来的算法名无关:1 s" N4 T/ J9 ^6 F. J& K! M, |
8 D# i3 ~: b% _( m3 l
a00b0ff99a0a691f19dfc2565fde811a.png
, z9 u8 k0 t% S3 N- t' O2 I

8 b, S4 y" ^2 j6 B8 v这个名字是在FlashDev.c里面定义的。! F9 x+ [: B: Q1 Z: F
' u- i6 t' E# }. v( u$ @; @
80.3.5 第5步,修改编程算法文件FlashPrg.c$ j. |. t& C5 z1 n# F) U
模板工程里面仅提供了接口函数,内容需要用户自己填。% d1 H: u# F7 c# ^( I% X+ {
+ m/ z6 V! Y- ^. B, c. ~
  1. /*
    5 J8 U# }4 l9 N7 v7 s6 R
  2.    Mandatory Flash Programming Functions (Called by FlashOS):7 H% `  ]: D' ~) R$ Z. }" H8 y
  3.                 int Init        (unsigned long adr,   // Initialize Flash
    , K/ F9 O6 `1 ]
  4.                                  unsigned long clk,6 E0 w0 _3 V; y4 y0 ~" X0 Y
  5.                                  unsigned long fnc);% W( S) K; D, Q/ b$ j, _+ _4 c4 {. i% u
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash
    ; n  \" I4 e% I& C2 ?. {" F; w# F
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function0 q& q' L* V3 j0 A, k
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function
    6 `/ M0 A9 w+ k+ H( b/ |) n
  9.                                  unsigned long sz,% c8 b' ^$ r& o, L
  10.                                  unsigned char *buf);
    % o2 R& |, S2 _& s7 B

  11. / ^: `" r& f3 w
  12.    Optional  Flash Programming Functions (Called by FlashOS):
    # n: w- Y0 o: h: o) p
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check
    3 |( d" y6 h9 C" r) I
  14.                                  unsigned long sz,
    # z! U8 j  \9 c' O
  15.                                  unsigned char pat);( X. V, Z$ h9 _- v, {0 K+ K
  16.                 int EraseChip   (void);               // Erase complete Device
    3 M$ t  O3 h8 T% ^# l, \
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function2 u! h( K7 |# s9 _; e
  18.                                  unsigned long sz,
    % |6 S& R* {, W8 E
  19.                                  unsigned char *buf);6 ?) y8 J6 Y/ e( W' I
  20.   V5 ^+ [# y1 T$ }# q! i! v$ D9 ]. O
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space6 ^1 ?2 I6 a, @* w1 J
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space  H& x% l1 @7 j! ^
  23.        - if EraseChip is not provided than EraseSector for all sectors is called
    : N( E" M* I5 P8 @; \, z7 |3 h
  24. */2 E1 `: k0 r; p4 ^& @7 e* v; _! K# \
  25. # E* B& ~6 P9 Z5 }7 P/ q" D5 Q
  26. /*
    * d: O' k0 _9 V& b4 U
  27. *  Initialize Flash Programming Functions6 ~. G& U8 Y4 ?0 \
  28. *    Parameter:      adr:  Device Base Address' i" c. x- S- p/ @1 R" Q
  29. *                    clk:  Clock Frequency (Hz)
    6 |) j* z5 v9 T7 V5 k
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    . i# x* I& t8 ~, u5 M
  31. *    Return Value:   0 - OK,  1 - Failed
    $ k2 J% E6 T/ w& f& q
  32. */& `% K6 ?, O9 }# \" a  s
  33. ! s% T/ H6 K* b- u; H
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {+ n/ {& e0 Z: i" F
  35. , D6 u1 \) f4 v
  36.   /* Add your Code */
    & \) Y- F" c1 l  m/ z0 X
  37.   return (0);                                  // Finished without Errors
    - M4 T( h* J6 b8 q+ ~
  38. }- u# V) K6 |; K* L6 j

  39. * M9 i0 v# X/ J$ b6 D- k5 B: P

  40. % Q+ e0 c+ C- }+ D* y6 S, R! f6 }
  41. /*
    , P7 ]5 ?+ h4 p0 x! W
  42. *  De-Initialize Flash Programming Functions2 J3 p) S4 O$ S/ p
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)( C6 W4 ?4 g7 r1 a% t$ W' c+ G6 R
  44. *    Return Value:   0 - OK,  1 - Failed8 I6 L* \8 E1 h5 v8 @/ P
  45. */
    % g9 @: O1 q. ]; p0 k

  46. 3 k; c$ a6 o8 H  K% G1 D
  47. int UnInit (unsigned long fnc) {- x) q9 s& H# V3 i
  48. 8 A& E) ^+ m9 |/ G6 _+ w" B  n
  49.   /* Add your Code */
    ) D2 G$ @# ~4 M# U4 k* ]
  50.   return (0);                                  // Finished without Errors
    & b7 q9 L7 Z8 U& ^
  51. }' x6 k* J& A7 j$ [7 t: C: X5 r
  52. + s- R& L: ~( ]: [5 c0 d
  53. : _6 V( V" O1 f+ d  y9 X( ~; }
  54. /*
    1 a+ L& x- E  n
  55. *  Erase complete Flash Memory6 r" x! Y5 U' ?9 V
  56. *    Return Value:   0 - OK,  1 - Failed  c5 Y$ d, u, E& N. p4 A+ ^; v
  57. */
    $ c1 F) x+ B0 A, w( ]/ M, N: M8 F
  58. : u5 i' d3 E. T& V, w2 V
  59. int EraseChip (void) {
    1 A; G& @" r% [' F" h9 S0 h" x0 r
  60. ; }3 E/ D5 B) U. f* U* `: ~
  61.   /* Add your Code */
    . Z# _2 L" a8 c
  62.   return (0);                                  // Finished without Errors2 i; J! \% a2 {8 b$ _
  63. }
    1 n- a, E% @& e* j% |5 H' t- ~
  64. 0 N$ Z( \& f: K" k4 I1 @

  65. ' N7 X7 G7 g2 T8 t2 d+ d/ o6 S2 E0 z% V
  66. /*
    ( `, Q) F4 k( z/ g+ S# E. J
  67. *  Erase Sector in Flash Memory5 P7 f: r" y0 H, j% }
  68. *    Parameter:      adr:  Sector Address
    0 v! R6 V/ z; I' f+ @3 t: y
  69. *    Return Value:   0 - OK,  1 - Failed. L# ]# ]9 W# @3 r: ~+ P  B5 l7 _- V7 H
  70. */" s- m3 s% u* N$ T
  71. 7 B! q0 g3 @! C$ L3 f
  72. int EraseSector (unsigned long adr) {7 v$ g# Q! K: `7 H) N

  73. - T# r% A$ W9 ^
  74.   /* Add your Code */2 e+ _6 k) Y# |! T
  75.   return (0);                                  // Finished without Errors
    ' K9 J. S$ _; P: f
  76. }) b1 U' a3 w# A3 K- I. L! m* M2 O
  77. + a( ]8 m+ A9 y$ _

  78. , I' T3 y$ o) ?: h* j3 }
  79. /*
    / ^" h- g5 i% F8 E; B/ f( r
  80. *  Program Page in Flash Memory/ j6 Z* X3 b- ~* n
  81. *    Parameter:      adr:  Page Start Address
    4 z& g7 P# z3 r+ C3 a$ f6 m
  82. *                    sz:   Page Size5 h# b4 M) P* c5 E
  83. *                    buf:  Page Data
    4 {9 p8 w! ~- f4 f) t, P
  84. *    Return Value:   0 - OK,  1 - Failed
    % k$ v3 I  ~; j/ B
  85. */
    % X% @! I1 `  a, C' m" b

  86. 4 f5 V7 S! O/ ^9 M- l
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {9 r9 U! o' w3 c9 V8 A

  88. $ D1 `5 S1 b. x9 ]4 o: g
  89.   /* Add your Code */! W/ h3 e$ M( T$ f
  90.   return (0);                                  // Finished without Errors
    8 J7 L) X1 h- b
  91. }
复制代码

, a2 ]) w# c8 C  j80.3.6 第6步,修改配置文件FlashDev.c
( E6 f( Y1 F0 @) D模板工程里面提供简单的配置说明:
* g3 y2 U5 o  U5 w- j. J4 k0 F8 ^& q" h
  1. struct FlashDevice const FlashDevice  =  {
    8 ]6 s- P; u1 N; g' x' y/ l5 N( b
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!" S  s; c+ E' U: p
  3.    "New Device 256kB Flash",   // Device Name
    6 ?5 W# D3 }9 K4 M9 l
  4.    ONCHIP,                     // Device Type
    ( n+ E  [9 M/ V# D& z
  5.    0x00000000,                 // Device Start Address3 F- W5 W! Q* W" T" ?; z( l, j
  6.    0x00040000,                 // Device Size in Bytes (256kB)
    $ V) f0 z4 N7 I9 C
  7.    1024,                       // Programming Page Size
    : z" Q) W' j, i8 ]
  8.    0,                          // Reserved, must be 0
    1 J; [4 E& C1 O7 |- ]4 U$ ^3 p# B7 V
  9.    0xFF,                       // Initial Content of Erased Memory/ e! M7 z) i1 h3 N/ A
  10.    100,                        // Program Page Timeout 100 mSec4 j% W+ W' Q6 O3 X- B
  11.    3000,                       // Erase Sector Timeout 3000 mSec
    1 P5 X& y" S, F. u6 @4 s

  12. ' [. Y$ V; Y( d/ k4 [7 t1 J3 `
  13. // Specify Size and Address of Sectors" k# c* @; y) r8 q  f$ m
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)- P; \* q9 ?7 g; ~! n6 A. ~
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors)
    : c7 V0 n' z+ F. v! Y( \
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)
    2 s# I- D' e4 R7 I8 p$ f
  17.    SECTOR_END
    1 H+ C8 a; n* `) g
  18. };
复制代码
) W9 u8 a3 j4 d/ r
注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。
+ m6 `4 y& O$ p1 b9 e0 l3 Y1 D% D1 Q& e; j! I( Q
80.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关( A' p' l( n1 c" q6 s
C和汇编的配置都勾选上:2 z% e: N( }; g3 K4 H3 \3 ]

) [# w, ~8 f, T( f" O
1d27a7314fc6c472937ad43852722b6b.png

9 c3 e, P+ E7 k: z( n& b
; y: X  L( e' m+ j9 ?汇编:2 _- Z7 |9 e! A# L8 W7 n4 w
# g, D3 D# {# o1 p, @* @
bbea604970c4ef63c77f10e553e28ea6.png

' ]( c" m# S$ e  C" ^" F1 E% L, @% f  _/ w3 Z
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:. L( g2 d: l8 q& u$ U. Y. l
- w2 C6 d! C) \1 o. f/ n% B( Q- {' `
(1)加载以响应运行事件。: X% T7 ~% V5 D
# k2 n8 }5 n- V8 m; d1 i& r" J# [; r
(2)在不同情况下使用其他例程的不同组合加载到内存中。
7 }( y8 j, M% q, g: j/ v
$ H& Z0 B- E( a(3)在执行期间映射到不同的地址。
/ p' F4 C( @& F+ R
. h2 H- a9 w4 a! P9 X# y使用Read-Write position independence同理,表示的可读可写数据段。
6 {* Y& L1 r6 C, U" Z2 w6 ?+ @: A" [* r) O# b8 k6 ^4 @
80.3.8 第8步,将程序可执行文件axf修改为flm格式
" m( V. P$ a$ \5 g. ^* B
通过下面的命令就可以将生成的axf可执行文件修改为flm。
9 @% w3 Z( r) f$ L% b! K: A& Z9 B) I8 n
6b504444979e9fa3a3477c6284abca8b.png

  N- Y! x  V- e( @# c- ]2 z/ b& e0 Y2 p1 z
80.3.9 第9步,分散加载设置
; E6 L$ B+ Y/ g我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。2 f: @8 z5 M6 y! j# R' A# J, M

( K. @" n, Z  I2 _$ A- X" d' ?) m9 _
e8e79854b204b618bbda32af92f28bf2.png

3 e. j  W- K3 S7 [- {" j8 I! X% c* l
分散加载文件中的内容如下:( ?: _- e7 I* C
, y, B$ Q! }" ~0 Q
  1. ; Linker Control File (scatter-loading)6 n4 K4 K; y9 q6 j* T) c! ~: K
  2. ;2 P. o+ m  E. A  Y! {: Q
  3. . E2 j' _2 b) ^9 U  c) P) T# j% y
  4. PRG 0 PI               ; Programming Functions
    9 Y& K- ], a8 z( \/ D5 x
  5. {/ {$ T/ M6 {5 d9 D0 `/ w: A- T5 h
  6.   PrgCode +0           ; Code
    . V9 Q! t, G$ t3 B. E
  7.   {2 s! Q: K0 x  |( h+ O
  8.     * (+RO)
    " P( f2 ?0 l0 e' }
  9.   }9 t# w* f. {+ Z: r: {& {- W3 L
  10.   PrgData +0           ; Data0 j8 l6 p3 s- G' z! ]9 f
  11.   {1 \$ [- K+ O7 N& E* f
  12.     * (+RW,+ZI)
    0 N/ ]/ c0 X4 o0 O
  13.   }* d, x& B$ i0 n4 @, s0 q
  14. }: t; W, \+ v, G& ]- a
  15. 2 N& [' w$ m* f( @( u
  16. DSCR +0                ; Device Description) E2 F' j8 |* r9 ^
  17. {
    ' A0 _% u$ p: @1 |, V( Y
  18.   DevDscr +0, r  x+ m$ F/ f/ U; Z$ w; r) e
  19.   {
    4 L0 }: y7 |' ?0 Q# X# M/ y# k
  20.     FlashDev.o( u" m: s; x4 T
  21.   }9 C& c4 w! r: G" M6 b% o4 M7 ^3 \
  22. }
复制代码
" j- ?6 P1 O# {8 X
--diag_suppress L6305用于屏蔽L6503类型警告信息。6 `8 o+ t( D  ^/ {# h+ M

* J: W. @# O0 `4 O特别注意,设置了分散加载后,此处的配置就不再起作用了:
# `' }2 O$ S8 \: \* {$ ?2 K9 ~4 C
! R0 r( Z, O' [' O" c9 ?- ^& m
b249eb40e5562b04ac3eb05ec0219c1d.png
5 a0 W  k2 z* G0 I6 ?. X8 Y4 Q
3 R2 d! p" s6 e0 a
80.4 QSPI Flash的MDK下载算法制作
1 j! D) [' l; L4 I下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。; h9 e# n) T& ]1 ]% a0 e5 \

" c+ [4 {2 m1 c' N  L- Q80.4.1 第1步,制作前重要提示
( ~- O6 x  A5 z" n这两点非常重要:4 t" ]7 j. P3 j/ t% d( `
' U$ `) Y& L$ V1 U  [; t/ U
  程序里面不要开启任何中断,全部查询方式。
4 f& g/ t; X9 G1 {  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。" J! P  C: [# @  b# B1 M( e; f  T
- U& n( s! l/ c* ?3 L
80.4.2 第2步,准备一个工程模板6 O* V% G- K! m1 x5 G
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
0 p+ F% l* K7 m2 D# d  U
5 F+ G" Y! i8 Q2 B$ d- J
a91aaf84a25d730b77273624d2b502f6.png
- W! i; Y2 m. \! G$ u. A
" V  W) x: Y& o- d( d4 g
80.4.3 第3步,修改HAL库7 ]3 a2 Q5 O" t$ M% I" N. r
这一步比较重要,主要修改了以下三个文件:+ n1 S) S4 c3 D% D7 k. P

9 j1 C2 ?4 E  n7 a
4471afac6bbc201866d0d9a8ea7d876b.png

: N( ^7 X7 K- ]$ z, P* S; ]
- e9 ^, ~& r* ~( H主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。% f5 k( G& O8 |# \& d3 r& @

/ M, m$ k- |9 G9 V80.4.4 第4步,时钟初始化
; e$ {6 _+ d  ~- k* t# C我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:7 U' g9 H& K" P! m( |
3 i6 \. S* K& C" j; l
  1. /*
    9 M: v; J' i  \. Q4 d6 ]
  2. *********************************************************************************************************/ [6 i9 ^( K+ I" ^1 j. x
  3. *    函 数 名: HAL_InitTick- x, T, V) K6 d) a
  4. *    功能说明: 重定向,不使用
    / X0 M1 J, @" b; u& C5 C
  5. *    形    参: TickPriority- h- d- _: M0 [- o% O4 j
  6. *    返 回 值: 无
    & G, ~/ O7 ]  F  y, h
  7. *********************************************************************************************************4 ]0 A" g; X; }: H* h2 S  b# [8 D4 v
  8. *// U9 B, a- j. H6 c. k
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    - J' i& C8 H, A9 I4 g0 f
  10. {
    % y) U: z6 L+ D3 V, s' x+ E
  11.     return HAL_OK;
    7 X, M  A7 c  z' s: V- D8 ]
  12. }
复制代码

* u4 j; G+ O/ \然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
) j0 g6 v1 C/ G. Z; b: ~, \% Q8 H; w% [0 d/ G4 V
  1. #if !defined  (HSE_VALUE)
    ( l* X9 Y( \# o, A
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */1 w6 w% U, Y3 n
  3. #endif /* HSE_VALUE */
复制代码

, K6 }5 z% F( k: r/ `* e最后修改PLL:
. D' w% @" A3 s+ B; N* S8 n: d
4 W+ k% Q  Q( p) Z
  1. /*8 r  J; R: T9 c  y$ A
  2. *********************************************************************************************************
    * f& ]+ b9 C  c4 r
  3. *    函 数 名: SystemClock_Config
    + S) c. z2 b1 m( z
  4. *    功能说明: 初始化系统时钟
    $ R3 f( q3 Q' ^/ R1 t& a
  5. *                System Clock source            = PLL (HSE): m& f  F% w& s" I/ C
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)! [) I5 v+ z1 t- Z& q6 R: J) M( A
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    0 g3 N5 S2 l9 z( m3 F
  8. *                AHB Prescaler                  = 2* A7 s& n" J6 u" T: s' b) k- ^
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    $ A- {2 b% Z8 T: ^) T- Y
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)5 q2 e/ y7 Q+ E' f9 w
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz). w4 K+ g, f8 i5 H/ F5 [% G$ ^/ ?
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    5 J) ^  `6 @" [7 R4 ^! J
  13. *                HSE Frequency(Hz)              = 25000000$ J9 Y# {' N2 C# P7 `5 E% K9 p/ f
  14. *               PLL_M                          = 5
    , ^" {# q. A) O$ C! T
  15. *                PLL_N                          = 160, z! G2 ?: X& Z. \  S; }
  16. *                PLL_P                          = 2
    ! b+ e' f) x1 `/ c, p
  17. *                PLL_Q                          = 4* W! W: ~2 _: K8 ^
  18. *                PLL_R                          = 28 s& \) ^2 N( k7 K' C7 N" f
  19. *                VDD(V)                         = 3.3
    # K2 J8 Y* v2 R; v' o6 {
  20. *                Flash Latency(WS)              = 4
    ! e7 h2 _) U$ f$ |0 q. Z: o. ?
  21. *    形    参: 无4 K7 p0 a$ R% z+ ?
  22. *    返 回 值: 1 表示失败,0 表示成功" O: x. g) L5 a' o  a
  23. *********************************************************************************************************
    7 r4 M0 y4 V7 \1 Z
  24. */- l1 y' E+ ^* M
  25. int SystemClock_Config(void)2 a+ P) d4 |# K% V$ c3 u: m
  26. {2 q3 n/ N( ~" l# `& H! Y. D
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    5 x& l* r4 f- B  d: m! W
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};5 i5 `% L4 G$ I9 S$ x
  29.     HAL_StatusTypeDef ret = HAL_OK;" k$ L8 @" U1 S: G: s. |
  30. # D! s- a. ^) }  E
  31.     /* 锁住SCU(Supply configuration update) */
    1 Y* H2 o0 `; U
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);- _3 t( z3 @8 S: k# ^

  33.   u  o' _! l! `( p
  34.     /* ! P, I# Y! b4 S- n1 f8 W, _1 J; c
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    6 r* p- O! C% H5 J( Y2 F, v
  36.          详情看参考手册的Table 12的表格。
    : u- F9 e1 s+ C: K; Q) ]: z) j
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    % T' `9 j( e. ~. @  R  P
  38.     */4 }" I/ K1 a: n' r4 q/ N
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    8 J0 B3 Q2 W' K2 P$ G5 X0 B

  40. : m( ?6 A) H2 x9 u
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    ' x! z" c3 k& {% Z: o' H
  42. / j* T' u9 ?7 |+ t
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    ! p" ]$ p% W) a, n! e
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    4 H" I! P2 ~) Z: h- \8 ~
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;) a5 k8 V& Y2 i0 ~& o8 d% @
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;4 E) r% j! A( m% @9 I' `$ N& F
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    9 R! |( s: `/ ~: @$ z3 E2 M
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;9 y, g7 o; g* \4 w
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;/ N+ a9 o, L& i* L/ k# b' E1 T

  50. $ X; f% M& `3 |4 g$ W% w4 L/ f
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    5 U: T2 H4 \* D0 u8 a+ |
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    * R% L- e: y+ R7 N. `
  53.     RCC_OscInitStruct.PLL.PLLP = 2;/ d, T+ P6 F! o* D. i$ }" u
  54.     RCC_OscInitStruct.PLL.PLLR = 2;+ c$ B1 K6 U5 p7 L0 Y8 l; p9 {( z
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    3 P: l- T5 X5 G3 L
  56. % ^6 }* c. i: j! i! D5 }9 |* }
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;# ]: W+ u3 @" b7 |
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    * ^' |: e5 |5 a# h2 n
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);0 l9 h0 N  ^& p, \
  60.     if(ret != HAL_OK)
    7 d0 {7 z) |1 u% z# s! `1 X) l
  61.     {
    % ]4 F4 B1 l8 P( J$ H. N. Y
  62.         return 1;        
    1 ^0 `2 M7 {5 o" S' z# j+ G
  63.     }1 B* H' R# e0 L3 K; T

  64. ; ^4 y' t8 o/ W+ J+ A% M# F
  65.     /*
    ' t% U8 J4 o9 |( n' B7 M
  66.        选择PLL的输出作为系统时钟
    $ `, D& y3 r) c6 s+ d& u* W, T
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    1 V6 T  m3 i4 U5 n: d* h4 O
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    9 i9 X: t/ a8 U$ A  R0 l8 o
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    * `5 Y4 \; z/ q5 F' H8 K% u
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    ' r, Z; ]( p; {/ \# F4 I8 x
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    9 `+ A' t5 t& {
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    % ]" O: q/ P1 `7 ]( }
  73.     */
    9 A! B7 k0 F: W
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \3 l7 V( t9 F% G3 [# U
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);( i; T0 p* y6 n0 N/ q3 u/ }
  76. % m( R( x1 G) F; J7 i
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    3 D9 l3 f! p) p8 _" q
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    - z! z  k# I5 Y% d' O9 a
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    % q. S8 f) T' o$ M& ^
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    0 e7 `( S8 Y) U
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; 2 m4 d8 V' R5 C
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 0 @/ D$ P: K" Y
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    4 h& b  a: {+ H' {
  84. 4 l  Y/ i- a1 E+ c
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    ' {2 S& \: n( R* K5 d1 m, O! j5 _
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    : p7 d! X' `/ M/ S$ [3 L' r
  87.     if(ret != HAL_OK)
    ( \6 l; O" A: _' F
  88.     {3 [7 V$ L7 n0 V5 I
  89.         return 1;$ b: N* Y  `" W
  90.     }
    ' T# L5 @$ [' f' x' s3 D

  91. : c9 F( [% W0 x( ]9 {$ f5 {  o& i
  92.     /*
    # v  a) x* B( s" X
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 * f5 I8 p' m+ {; y* V+ h8 y
  94.       (1)使能CSI clock
    & h, H9 _7 w/ R, |: C
  95.       (2)使能SYSCFG clock
    ! D3 s1 o& r9 K
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    : m* R1 @8 u: U
  97.     */
    " F$ o; d- F  {: m) D
  98.     __HAL_RCC_CSI_ENABLE() ;
    $ w+ z7 o  n+ {
  99. 5 J' z& z% z5 g1 j
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;1 i" ~% |. o3 G$ ?9 `0 a7 w

  101. ( i, V, z" u2 J& }. N0 e
  102.     HAL_EnableCompensationCell();7 b2 H- l" Y( r5 @- G3 s

  103. ; E2 ^3 M2 P: H' x, O
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();+ X, ]& P; R  w( [$ Z; B" t
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    4 a$ |  @& x0 R7 `! H
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();6 g+ \+ `; k1 P  v1 m9 h* D
  107. " D  s; y3 q1 [+ {! C
  108.     return 0;
    ! I0 m* K& U- t) R
  109. }
复制代码
( ^- S' |; l3 X/ b
80.4.5 第5步,配置文件FlashDev.c的实现9 A/ }" h# \# m1 e/ a
配置如下:
2 }& P; _) ]/ [# {0 d3 S2 Q
8 I, }; I. m  r" ]8 f" N7 @
  1. struct FlashDevice const FlashDevice  =  {7 E. l+ w# w7 T- ~
  2.     FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */, K' ^* H7 {+ \; p# K; x
  3.     "ARMFLY_STM32H7x_QSPI_W25Q256",   /* 算法名,添加算法到MDK安装目录会显示此名字 */
    # x- \9 u3 o' y
  4.     EXTSPI,                           /* 设备类型 */$ D" P# Y' o: E; ~! }
  5.     0x90000000,                       /* Flash起始地址 */8 R+ C9 ~1 C7 o2 e
  6.     32 * 1024 * 1024,                 /* Flash大小,32MB */
    ( `2 ?+ B3 {! `7 \; A* @. A& [: }# u* c
  7.     4 * 1024,                         /* 编程页大小 */
    , m) j8 f: K: k2 x
  8.     0,                                /* 保留,必须为0 */1 N3 v' o/ d* X* [2 |
  9.     0xFF,                             /* 擦除后的数值 */
    5 c- @5 e; L: Y& r8 W
  10.     1000,                             /* 页编程等待时间 */
    % k3 q$ F$ L& I
  11.     6000,                             /* 扇区擦除等待时间 */+ V6 B4 T& g1 S; c: k% P8 I
  12.     64 * 1024, 0x000000,              /* 扇区大小,扇区地址 */
    7 ?, x. y7 K& [  A
  13.     SECTOR_END   
    ! m$ [1 i9 z) D3 R( i
  14. };
复制代码
8 t1 R# _5 C" @1 ?6 F" v
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:# o0 u: v8 P2 b" w( i- p
1 a7 v2 L2 w* K: w" J' W
ffed7a581be4c5da388ee29fb67249b4.png
+ }" `8 @: a+ |) r: c4 v# o6 L
2 Q* x4 W' Y1 T5 q7 I# D
80.4.6 第6步,编程文件FlashPrg.c的实现
4 ]0 A/ r: J" C1 H/ k下面将文件中实现的几个函数为大家做个说明:
0 |+ Z. a) J8 B8 a$ R" `- ?' l4 _! y0 y
  初始化函数Init
- }4 w% t9 D- v5 C6 D- R% s
  1. /*3 \2 P. S) ^/ x, L" N" W$ `+ {
  2. *********************************************************************************************************
    , g% r) A3 p$ ?8 I- Z: j. k7 _6 [7 g
  3. *    函 数 名: Init
    : w7 Q1 e! h! D
  4. *    功能说明: Flash编程初始化2 f" d% w1 r# p) h
  5. *    形    参: adr Flash基地址,芯片首地址。# p+ i6 [0 g: L: b2 _
  6. *             clk 时钟频率
    4 o. E6 G8 e6 U, l  ^
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    / d$ e+ J: E, H
  8. *    返 回 值: 0 表示成功, 1表示失败
    9 g! j% M( V2 R# N3 L
  9. *********************************************************************************************************% J' v! q. N4 J4 r- |; {- M' w) N' y
  10. */
    / Z9 _  d0 \& M; i" G$ S9 v7 w: Z
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) / z' n% \4 J% T% f% Z4 b
  12. {
    . U' d* I5 u1 w, P3 j
  13.     int result = 0;& v) [+ r* o- E$ W1 [

  14. * y, r8 y1 o0 p; E
  15.     /* 系统初始化 */
    2 W8 }) H* \: s- w% \$ ~0 l% n
  16.     SystemInit();
    % Y1 p/ W: y7 w4 I% X

  17. % A' N8 [. j* \* U: F
  18.     /* 时钟初始化 */5 a' y0 a- b1 `' ]. @- i. g+ V
  19.     result = SystemClock_Config();
    $ g& ^' W+ o4 M4 }) |$ y
  20.     if (result  != 0)
    . P! z7 I3 I6 h: ~
  21.     {
    + t5 l7 [- T* o4 Z# L; ?
  22.         return 1;        
    & U; W7 L5 D8 C9 c0 ?# F4 Z8 y
  23.     }$ e! q  t. f& ^: r
  24. 0 q6 l7 w' O- p0 [# ~
  25.     /* W25Q256初始化 */, o# t3 n  Q( g+ X0 J/ f' Y
  26.     result = bsp_InitQSPI_W25Q256();) y1 ?/ [7 l( X
  27.     if (result != 0)
    % Z6 x( ?6 i8 ?! {
  28.     {
    ; Z* j- f) |! Q5 [
  29.         return 1;3 z+ g5 C" P; j
  30.     }& m* w$ t7 n* |: i5 f. t: {

  31. 9 |" K- G% ]0 e2 s7 r
  32.     /* 内存映射 */   
    * h6 [- P0 W+ r& M
  33.     result = QSPI_MemoryMapped(); 7 N' T) [1 o/ @& X, {
  34.     if (result != 0)) u! p: m+ z2 z7 E. e
  35.     {- H! I/ p* F6 t+ u8 Y/ o: v
  36.         return 1;
    0 L' H0 c5 o) k5 B; J6 ^; _
  37.     }
    % Z0 n% z; [# Q: H  A

  38. 9 f2 e) T; b. q3 u
  39.     return 0;8 ]6 {1 J; ~/ I( [0 \4 y  f
  40. }
复制代码

9 h# Y: I' s2 y! H初始化完毕后将其设置为内存映射模式。
' [9 V) p- s* J$ m3 d) m; h# Z8 x  ~
' d/ v4 J7 M( z& Q  复位初始化函数Uinit
5 }/ S* J' c4 g$ ?2 r7 b1 C8 t擦除,编程和校验函数后都会调用此函数。
/ I% d. A' i( b6 X, {+ d3 j- G$ C0 X$ b  J* W8 L1 O
  1. /*8 C. X, ?- z$ F) s4 H0 Z
  2. *********************************************************************************************************) N3 w% n( ^! J+ g
  3. *    函 数 名: UnInit% m0 g- E# E4 s5 b! b" b
  4. *    功能说明: 复位初始化" \# ^. B) |3 R( r9 p- Q" ?4 S
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    ) _6 O2 ?9 e- A9 h8 b, [
  6. *    返 回 值: 0 表示成功, 1表示失败7 X9 ^( U/ w4 a' u: Q( e
  7. *********************************************************************************************************: V# A" o) t7 y4 K" ]' I/ x( ~
  8. */
    0 w$ f4 j9 ^% R& t
  9. int UnInit (unsigned long fnc) # m0 T2 c9 u; w1 s
  10. { + C4 k6 R0 v+ _, ?2 W. a
  11.     int result = 0;
    7 R) d. D9 m% W4 f* `; N
  12. + M1 r, f% N6 I9 g5 h' W% b
  13.     /* W25Q256初始化 */
    9 T, g: X  Y) Z0 o  M" Z
  14.     result = bsp_InitQSPI_W25Q256();& {& w, ]) }, {$ b8 I; ]# i) ]
  15.     if (result != 0)* _) E5 m3 h. N
  16.     {
    ! I. R0 y: t1 g/ b
  17.         return 1;/ N! J! O- l5 [7 n& A. Q- ]
  18.     }3 I% w4 B5 w# m4 h% s$ j
  19. 2 H/ o) G; v3 h, x. H1 D
  20.     /* 内存映射 */   
    ( n0 T# ]4 j8 E6 w( D0 ~
  21.     result = QSPI_MemoryMapped(); 1 q+ R" I! \* }  H( H
  22.     if (result != 0)
    5 f/ A0 t. E* z( F
  23.     {
    ! l; J, ]# _0 g+ h9 ^
  24.         return 1;
    & |; e+ `( W# v4 D/ I4 m
  25.     }
    6 |# G) ^( g) \7 n9 J% B
  26. $ Y) M1 ~% ^. Q) I0 Y5 M
  27.     return (0);* Z! O" J  r& L
  28. }
复制代码

# l1 ]9 @5 R! [4 J复位初始化这里,直接将其设置为内存映射模式。
  Q" G3 r* [" E$ d+ L1 u7 P9 }$ q2 E! j; c4 A, L& S( P
  整个芯片擦除函数EraseChip
0 Y! }  d6 Y9 e/ P5 M5 h  }) P如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:1 J9 o/ g( i4 [- U" X
/ p9 I9 c8 z4 y  x( N
f623e662bf09233ea594f820f4c7952a.png
1 x; I3 a# W5 J, J+ U1 w! }7 o

7 m, ~  ~7 k9 u# @& @2 H# n实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间,比如32MB QSPI Flash整个芯片擦除需要300秒左右。4 Y7 b& P+ G. z% q2 J, M. T( w/ Y
/ t- a6 ^( K3 D1 ~
另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。6 z% q5 y0 v2 T5 G" y
0 y0 K2 f/ o2 K) ~# s8 F: v8 _( f/ e
  1. /*
    & J# |5 U; w5 J, }2 z. o  a
  2. *********************************************************************************************************' ^! {# E9 U* Z: \- l
  3. *    函 数 名: UnInit3 [/ E% s; _7 R" {
  4. *    功能说明: 复位初始化
    4 M5 P4 F  @  x* Q1 m
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    # U( ^! H+ q3 Z
  6. *    返 回 值: 0 表示成功, 1表示失败
    ( g' v) O. \. C1 n* i; w! F% g
  7. *********************************************************************************************************& y* A- k% @9 g6 {# z7 |9 r
  8. */
      y+ o0 _) r* j! ]2 E! S; f
  9. int UnInit (unsigned long fnc)
    - ^) N$ z( J* X  ]
  10. {
    : N; Y/ M6 S$ Z. |5 ?- _
  11.     int result = 0;" {& i9 V$ b8 F1 J

  12. ! s+ t' x8 ]! j4 ~$ r
  13.     /* W25Q256初始化 */8 k6 h4 L4 [2 e* n0 P. P. |
  14.     result = bsp_InitQSPI_W25Q256();. E- v" H/ O: T  f$ b
  15.     if (result != 0)
    " _/ s0 j  t* N% ?2 K
  16.     {+ O1 G( |" }' S* ~3 g/ h% u
  17.         return 1;
    + ~* H# ^3 u- I4 s7 x/ G  ^
  18.     }7 ^6 u6 q' w, t$ R! V  k
  19. 0 Z$ T6 A+ q/ r
  20.     /* 内存映射 */   
    , M% J, F) [! d* G# s* I
  21.     result = QSPI_MemoryMapped(); ) C6 Q; P2 r+ F4 ]. \9 k
  22.     if (result != 0); g/ J' U' l; L0 H& }& \
  23.     {
    % O2 E. k# N% J; a
  24.         return 1;- l6 J) c* q! `  T' O
  25.     }
    : h5 X6 S& j# d+ B

  26. ' i6 ^' k/ E" V. d/ X7 a
  27.     return (0);
    * E6 {+ m! c7 e8 k
  28. }
复制代码

* t+ I4 O: q9 A+ G, f, E6 a! c  扇区擦除函数EraseSector
+ @; g# R0 j0 D如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:
& O5 H) ?% T! i' b* s2 I
: ]8 T, l3 w! ]0 J
733d5af26119f64bff0261515013d3a6.png

" G9 L7 I) L3 d; x  n; z7 Q, d+ K5 A
; b4 Q" C; L. E' O! _. q
  1. /*
    , Y: a+ j2 `2 b/ v+ d) V' ]6 z% Y2 ~
  2. *********************************************************************************************************
    5 M1 i* H' F+ Q0 k
  3. *    函 数 名: EraseSector
    7 p- U( h- d6 N1 H8 v# _' _
  4. *    功能说明: 扇区擦除" }7 s4 Q/ [" F! A
  5. *    形    参: adr 擦除地址
    4 ]5 y5 R( e3 F5 F7 |$ v8 c( x2 n
  6. *    返 回 值: 无
    ) G! X" X1 z, Z; i3 w5 [/ \- d. j6 n
  7. *********************************************************************************************************+ \2 E# r* y6 q4 J1 i# L+ S6 h& E
  8. */0 x6 S1 s! V. Q( ~
  9. int EraseSector (unsigned long adr) 2 [% K7 V' K0 p" O
  10. {    9 q( o: ^8 Q) w' T
  11.     int result = 0;
    9 I# c1 V5 X% {1 h. I/ G

  12. 6 n/ G! i& I( \: u- B) V
  13.     /* 地址要在操作的芯片范围内 */5 \, s9 u4 e0 Z4 |0 W/ D  J# v
  14.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)3 Z4 u$ V( K6 u
  15.     {. s9 r: W. I9 ^  r" D6 ^
  16.         return 1;' U% E' L/ E' k, }" V, n$ W% Y2 T! C
  17.     }
    ; @& Y5 F# J3 ~2 P( R- y
  18. # q6 z8 M8 _, ?( G! w, n
  19.     adr -= QSPI_FLASH_MEM_ADDR;; _$ ^: g( ?" b7 f

  20. . _+ F7 l/ }/ z6 H' L; Z8 S
  21.     /* W25Q256初始化 */
    1 D2 _' a) s$ x7 r; d4 h' \
  22.     result = bsp_InitQSPI_W25Q256();) K, v1 @5 T# z' S2 A
  23.     if (result != 0)
    # A) X; r* {3 ?: c* p
  24.     {
    . p! U4 ?: `3 J
  25.         return 1;" C3 t( U0 \( w* t- z
  26.     }
    ' Z0 n$ v9 Y% J3 l! C6 U8 d

  27. 4 V. G  ~3 }# y! o
  28.     /* 扇区擦除 */3 k) ~2 l3 \( J- {
  29.     result = QSPI_EraseSector(adr);  
    7 U1 U0 E6 t; z9 @* ], c
  30.     if (result != 0)3 x0 v. c4 ?4 j7 e- d9 b
  31.     {
    ( r- w/ S, t) O( M( A4 u  o  I
  32.         return 1;
    , Z& e: }1 T& t0 w; S4 L* d  _
  33.     }   
    7 b3 d" o$ ?8 T8 P6 ?* z# U
  34. , X  _. N3 N0 b9 U& _9 F, A
  35.     /* 内存映射 */    1 I5 `' R, Z) N2 G2 C
  36.     result = QSPI_MemoryMapped();
    6 Q0 u" V" ]( c8 u
  37.     if (result != 0)
    - [# b+ O- V3 H8 n* Q; p0 \
  38.     {
    3 r4 Y2 ]4 [+ `# e
  39.         return 1;
    * P' R; g: |" d- n
  40.     }
    ' F2 A2 c8 T& z" D4 I! a& i
  41. 0 ?+ D3 S& r& u/ c. \
  42.     return 0;   
    ! p* h2 V( _$ M* f9 N
  43. }
复制代码

& ~2 R% F6 s) u& E6 C( Z这里要注意两点:+ X& {) N( _7 g- M. {' R

7 j. _9 b3 E0 S" v( z( w7 q5 o/ C(1)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
' q* l& D' w5 B; I
0 b6 L2 |; J1 p! H(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。
9 Y2 v& ]" [/ ]* R
$ c/ c: C. [2 A0 S! K! e  页编程函数ProgramPage

1 G* l) }/ ~1 \3 l2 M( M2 Y2 x页编程函数实现如下:
& K" l& F0 A: \
& q% T# {! |7 M+ a! ~, u* ~5 E
  1. /*, T9 ?8 u% Z( A7 [
  2. *********************************************************************************************************5 n0 `3 f2 E$ D  G
  3. *    函 数 名: ProgramPage: f3 X5 F0 `: J9 L" n) I  u
  4. *    功能说明: 页编程. P9 K3 ?8 a0 M% j$ C
  5. *    形    参: adr 页起始地址* K8 G8 |5 G8 D- i, [1 i
  6. *             sz  页大小
    * p7 Z# f' N) f) a8 q! G. w0 c
  7. *             buf 要写入的数据地址
    ; U- m0 _" h, g; q$ x! m7 |  Y
  8. *    返 回 值: 无
    ! ^, o1 n1 G; S$ @
  9. *********************************************************************************************************8 @  u5 ~$ [% W6 v1 M3 M0 K
  10. */
    0 }$ |; y1 e. T2 H6 a
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
    : `) \# e9 X9 z4 v
  12. {: u& O9 W% k0 x9 M
  13.     int size;
    ) j' l3 b7 @* T6 L2 e
  14.     int result = 0;
    ) j  W1 M+ d( C8 b

  15. " h: v2 X# v9 K
  16.     /* 地址要在操作的芯片范围内 */    . @8 C) H2 c" v" v
  17.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)6 y" [# G6 `9 c; e
  18.     {6 T# L6 I: i3 R6 C) T
  19.         return 1;  S9 Z; K1 r" j* Z1 [) F
  20.     }
    5 s6 g7 k5 E" v2 y

  21. + Z2 Y8 a; D; w2 o0 U
  22.     /* W25Q256初始化 */4 j& W3 g& b. d8 Z
  23.     result = bsp_InitQSPI_W25Q256();
    3 X1 M0 S' c/ }. `. }; ?
  24.     if (result != 0)+ u2 [2 y  m7 m
  25.     {$ t5 u2 H9 O# E% \6 N0 r& h
  26.         return 1;8 I" J8 R. e* P9 c" s+ o6 V
  27.     }; M5 H, n+ |2 ~# O' a
  28. + ~8 ^, h# m4 S2 r( y
  29.     adr -= QSPI_FLASH_MEM_ADDR;
    * H# t) @% l# V8 D( g! u1 j
  30.     size =  sz;
    0 U, W# k7 h* Z# J4 J8 m( E) N, j

  31. ; Q; t, F- I! a5 o& t- J
  32.     /* 页编程 */
    1 ^6 P  b/ ?+ W
  33.     while(size > 0)  d' |! J% U6 m" y4 i5 _
  34.     {( h% ~8 b2 n& D& j; h9 ~
  35.         if (QSPI_WriteBuffer(buf, adr, 256) == 1)
    * R( D" V# ~* h' [1 g( |
  36.         {
    7 b  @' ^1 S/ j
  37.             QSPI_MemoryMapped(); 8 {6 H- `, \9 H  t5 @& U
  38. ' f3 ~- ~% \1 N0 t# v7 s6 N) u
  39.             return 1;   & w- R: v1 c5 z! @9 B
  40.         }/ P* `3 P; {! D+ G
  41.         size -= 256;
    + x! T& ^1 x2 i! g4 n8 W
  42.         adr += 256;
    % X, K- j" i5 q# @
  43.         buf += 256;3 H# O& Y2 X+ C3 S: F
  44.     }
    - k0 \/ F, q) o7 q6 `. P
  45. 1 d  X% p+ c5 K3 i$ Q* n  i& H+ s0 u
  46.     /* 内存映射 */   
    1 \- J6 v4 B/ r
  47.     result = QSPI_MemoryMapped();
    0 ?0 Y# a2 P- Z0 P: i
  48.     if (result != 0)% U! W+ F. `1 q3 O0 b. J7 n& A
  49.     {
    $ p; P$ ~. P& A$ ]% Z
  50.         return 1;: T# o2 K" ]# v/ E; A8 M9 [
  51.     }
    ( I* {* O! U9 ^8 @2 S3 R' D4 o
  52. ( C' W' }. [  t) m# h/ m2 i- H
  53.     return (0);                     
    7 O. I! _" p' ?7 G
  54. }
复制代码
9 }4 q( D* V  ~$ Y) F
这里注意两点:
$ t1 C  w/ g! M, H6 c4 P& ~0 [# |$ Y7 @" R: X4 v/ p) ]: h/ n
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。
2 k- Y; y0 L5 o" b9 k. X2 Y- _7 v$ u& Q8 s- c' g: a
(2)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
$ t# I. N, ?8 _0 r- Y- E) `) w7 O3 J
  读取和校验函数
+ Z, k0 R# J) O* n- U我们程序中未做读取和校验函数。7 w; F# C' q: e* p

3 I+ ?( E. m" O  ~(1)     如果程序中未做读取函数,那么MDK会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。/ A  E: G" Q+ I& U6 a' `$ E' R* h

8 }5 p6 h* Q" n( W7 l) M7 C(2)     如果程序中未做校验函数,那么MDK会读取数据做CRC校验。! p; {4 e  s2 ^0 [/ b- x, {
# g  ~: U3 R8 U0 ]3 {( k
80.4.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)

' a- E+ `1 h9 w, B6 j3 q' `8 f最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):! H1 ~9 j$ I6 D/ I" B4 D
' Y1 t. @2 w' O
  1. /* 8 l/ @3 k- z5 O# Z' u
  2.     STM32-V7开发板接线5 ?1 l) s) r2 ]9 g+ D% R* W
  3. : R0 t6 R. a% R: }0 p* T/ |$ ?
  4.     PG6/QUADSPI_BK1_NCS     AF10
    ( F  G. ~5 r: |
  5.     PF10/QUADSPI_CLK        AF9) q9 }, U- P% R" f# h: ]  L
  6.     PF8/QUADSPI_BK1_IO0     AF10
    # V  L; x: F& h8 L, U
  7.     PF9/QUADSPI_BK1_IO1     AF105 s# G: ]& v! r, l  W
  8.     PF7/QUADSPI_BK1_IO2     AF9, Y3 f' s4 u  d6 f. ^/ ~* p
  9.     PF6/QUADSPI_BK1_IO3     AF9. R) F! u9 a7 S, Y

  10. + S# d3 P( J  T, }4 L& [- {6 g
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
    - o  z# t* f( J

  12. 5 s. |/ u7 S$ _2 O: l' J+ J
  13.     H7-TOOL开发板接线3 U0 m2 V7 j& N: v  q8 Q9 w

  14. 8 x5 P, O5 H1 S4 B7 x1 N+ Q
  15.     PG6/QUADSPI_BK1_NCS     AF108 ]8 |1 e% S& f; I- ?
  16.     PB2/QUADSPI_CLK         AF9
    ' }$ D& y& D7 K3 n* P
  17.     PD11/QUADSPI_BK1_IO0    AF10
    ( u/ X7 w" t7 g' S; o' L  a, c8 f
  18.     PD12/QUADSPI_BK1_IO1    AF10' Y' F9 I+ q. {/ x4 B& U# d$ E
  19.     PF7/QUADSPI_BK1_IO2     AF94 D, p& C: |& M7 @
  20.     PD13/QUADSPI_BK1_IO3    AF9
    8 m5 U, H# y* f. h) t
  21. *// b, Z' B+ A; N/ k" N1 f

  22. 9 V! t% H. n( }4 x" [
  23. /* QSPI引脚和时钟相关配置宏定义 */
    % e$ @; ]1 V  Z: m
  24. #if 0' D, F8 _( D/ i9 }; s
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    9 w. ~+ g, N4 K8 ^7 q/ B  E0 m" w
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()  q7 L# I, h3 a
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    ; n- U. Z* ?. \4 X' J
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE(); N- O/ S4 ~- Y( e% k
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE(). x9 x' Q' w7 @# \# n. i- F
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()* P+ [; y" B* o! r, p9 P6 m
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()/ l# W  ^( R) G" d
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()1 s) b; V9 _- o2 Q' u: O  U% [; ?
  33. ( J9 Z$ t$ _! M0 f* x1 a5 ]
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()/ r3 \' F2 x$ R0 u! ?- p  g4 i
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    + G  t* I6 r3 w% k* t/ n! g0 S
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()2 p4 c3 m9 K2 Z5 z8 Y

  37. 0 r6 ~; C4 I$ v. w
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    2 t& v4 k- S. B# c0 G+ Z
  39. #define QSPI_CS_GPIO_PORT               GPIOG: p' _$ a' l0 c% L7 G
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    / v7 k7 y; ?& h* X/ W( G
  41. - r7 U3 P! D5 q: N; p$ Z
  42. #define QSPI_CLK_PIN                    GPIO_PIN_26 X, z4 I. ~( i  w
  43. #define QSPI_CLK_GPIO_PORT              GPIOB5 _7 {; ~+ V0 m7 `# a& @+ D% G
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    3 ^) I% a/ s% ]6 g+ ^
  45. 0 q- E2 c" g7 O* J
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_11
    6 Q& x, d2 L( ]8 V: ~: p0 [- p% N
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD: w9 K  o' T0 A  m+ x
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI
    6 b" T7 P; i) t% Y: _& c2 x
  49. + W1 z7 I. ?1 X/ ]+ i/ V# I
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12
    6 l  {* `/ C6 x  M3 H
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD
    ; C# H/ J* f( b; p4 t
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI
    . d) O+ P$ r9 b& n& h2 L9 C( s9 J0 o
  53. 3 e$ r/ b( a7 P% l6 T7 @
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_75 s2 g. \+ \. t, D0 R
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    * f) m+ [4 i$ ?
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    9 s% T: n$ G. i: g& k/ D
  57. / g) z7 `3 S2 [, S7 A! O8 Z* M: z, B; q
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13
    + W" L0 u+ Q( `4 i1 U" C
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD
    4 }0 @! B1 Q  s$ w. ^! K, m3 x- y
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
      V0 f6 b; T  ?" [# d- D, u
  61. #else
    ) I4 T8 a8 Y: u8 j% _7 C$ c0 g! [  D5 P2 a
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()" ^6 W8 h0 \- i5 j. q
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE(): j0 U- s$ H$ h  _: Z* O
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    0 U) a4 r/ v7 F9 g
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
      P/ x3 L8 ~! {" Q
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    7 \  Y1 w/ y( ^' x. V9 J
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    : h; _- A: ^. L# j) X3 C  h2 @
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()% o: t' m3 t, O. r, v
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()1 I7 B1 g0 B7 }4 z

  70. ( u' M% E( e- [) w: q
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()2 X6 L5 X2 W; i- R
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()2 I7 z% y. i9 d
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()3 O$ N; n2 L6 ~$ \7 c

  74. 3 w2 y/ o4 ]& x9 S
  75. #define QSPI_CS_PIN                     GPIO_PIN_65 P- l5 X1 B+ {9 `5 P. p  K
  76. #define QSPI_CS_GPIO_PORT               GPIOG
    6 q' c: z7 E1 Y2 w* Y2 C% C) P
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI% `3 ~- D4 E/ }# U$ u# v
  78. - p5 I* p% H+ j( ^
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    ) {* @* {: r+ b8 s
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
    . t; T# r, ?$ D
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI+ n8 F# X: j+ b. y% z
  82. 8 r$ o' ~7 t8 M! Z4 u" E5 f
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8: j6 U5 }$ S! Q
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF0 o% l9 {6 ~$ r/ u) \: d4 r
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI
      ?! p" T: P# {1 v* g9 p

  86. ' p, w% R9 }" {4 X+ Q
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    ( I- ^+ A, Q/ _" a7 N& y; f
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF
    . h. B# l! b3 O% [  q# p$ q
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI2 A, f+ _. y9 |" ^' {, o* V- m

  90. + H+ N+ P& Y) v8 e# n, F! q
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7  k+ @* m) Z$ q& D0 _1 a( W
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF. S4 k3 d3 P0 q8 S. C
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    0 X3 D1 O1 h  Y2 S( Q3 P
  94. : ~: i! X" Z. ?. c# T
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    9 Z. |# s+ N/ Q
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF
    / C8 z' ^6 B3 X- u' b+ u9 _5 N
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI2 R7 S9 N% }( C2 E$ k' r3 U) r/ I! A: U
  98. #endif
复制代码

  r; ^" t4 s" {! ]8 O1 I硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:" C/ {0 x. q- m4 Q# `" [' l% e

5 f2 P4 a; B! u/ z' C7 F主要是下面这几个:
  y3 G) R. m  ~) ~( l. U7 E
9 J# W2 e% m# Z0 i+ v; w' l( Z+ W0 S
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000! l, t9 T; w( W

  2. 0 p# H% K8 {" l+ m
  3. /* W25Q256JV基本信息 */, i( W% @1 p+ t2 E: @; o; z! j
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/
    7 l2 O: ~/ m3 W6 g+ ^5 E
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */7 x3 W4 \3 F+ {
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */
    # X9 s& f4 y! r0 `& d% a2 ]3 R1 S
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    8 s' x; n% V4 T
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*// Z/ U4 g' p1 c$ N
  9. 0 b" t: R5 f2 R  y" q- h6 G
  10. /* W25Q256JV相关命令 */2 U& u- B9 Y- s2 R
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */  R) u. V7 ]1 C2 u
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */
    % ~; H$ c4 [( [
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */
    1 f5 E& _) g7 L
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    % T' V5 J8 a+ N
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */
    2 o- B$ m; s5 W
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */7 c& d  K% H0 U' D% a  Z, q

  17. $ i- K, ~- Z" u  H
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */! J$ P. g  N, T- c% r7 l

  19. 8 R& N, R7 u: q- d, b
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码
1 p  l1 Q, M2 c
80.5 QSPI Flash的MDK下载算法使用方法  @& {) ~; w& \9 u0 O- i* W- N
编译本章教程配套的例子,生成的算法文件位于此路径下:) J* V* l# T4 T: L# _
! J9 X6 P% ^0 z
ed1f9465c60f92522e906639b76b29f3.png
  G2 m, J$ |# T' U

. Q" r6 w! _2 _5 f* Y: ^6 X1 ^" v80.5.1 下载算法存放位置
- k$ O; \: T+ ~! c7 ?6 j* u生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:
% z' M+ D' v9 c% a3 ]" c% j4 |
. @0 y. }# m! O4 J/ S6 ^- T7 i  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。4 l# y' `% d) h. S9 s+ n. L
  第2种:MDK的安装目录 \ARM\Flash里面。8 U! j8 L/ B7 b' Y8 X" E
& w$ M* D, I" n8 B. b
9d5d65cb90fb28ad36464cb1b8368759.png

) I' C- e7 @# m$ T3 A" _0 }# a  T9 h/ E
80.5.2 下载配置% |. A: o$ b6 ?# b5 I
注意这里一定要够大,否则会提示算法文件无法加载:
  [- X. ~7 S) P* u1 V# q! m5 j5 ?9 v$ H7 ~  t
8eefa57c2d7f128449000c2fb4e788d9.png
. T+ Z) D1 H: h6 @- R7 r% a

, [3 R) |/ c! X3 N8 ?" B我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。
2 T& n# k6 E% d2 \- @
% s# v+ k$ a& g1 h. f/ L如果要下载程序到QSPI Flash里面,需要做如下配置:, Z& P6 C( y+ Q$ P1 ~5 t: S  X

  J* ^% N4 X! k/ H
caa01b15ccf9a9700690d2ad1bdedecd.png
( u( x1 ]/ j5 f, g; X* E
/ P0 P" O: u' I- C
80.5.3 调试配置

8 n4 N! [- C- ~2 ^4 I6 O$ k注意这里一定要够大,否则会提示算法文件无法加载:6 O- S9 A- m3 m- X0 T

- C' u; G! Z# U: y; D6 H
f23244d646498fc30aa5f1301a327430.png

2 |( t' `% l/ O! M1 p7 t* M1 @1 a( r8 {) R; n7 G' T( Y
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。# S+ x1 @3 c* O6 j% Z
: m# F' l6 s" e4 P+ }1 t
如果要做调试下载,需要做如下配置:2 A0 `1 t7 p  L& r% `
: F9 n; w$ y- O  z- j1 m3 i5 _
84ee4945e5faabd776fedd6f8f7d96d2.png

: x$ r# _# f( e
1 f, z: `8 y# Q1 G& k# t- b7 K+ P80.5.4 验证算法文件是否可以正常使用
% M# {9 _( E: B% E为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。- b5 _6 H$ W! D& E6 h. x
9 D2 [1 |  N; b) r5 Q4 s) e
80.6 实验例程说明

( }7 Q8 a" W0 @7 L6 L本章配套例子:V7-060_QSPI Flash的MDK下载算法制作。* l, x) x3 d3 E9 W
8 m( J& D0 L: D$ \
编译后,算法文件会存到此路径下:
/ }$ |; J( W' @: y4 A# r
/ ?! h4 f& e; u& l3 e# Z, i
3 M# a% @1 l+ Z# e. h( C% d

! r, |, ~' v5 C9 x80.7 总结# R0 N& l$ b6 _: X$ A
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。0 H( ^4 b- A: V
$ Z- b/ J2 l1 e) n
) G& C4 d# g3 ~1 S& f  ]% _9 ^

+ Z9 d: F; D7 {) \8 I2 u2 o# P& O" y3 {3 E

# B0 c# W2 g9 H7 r& B; y4 g
b69a199f1b4a12d880e64c7ba134070c.png
收藏 评论0 发布时间:2021-12-19 16:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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