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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 16:00
80.1 初学者重要提示7 O) B: }( k- [+ k! S# p
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
6 B7 i! k! a8 c( e3 V, E80.2 MDK下载算法基础知识" p; G& u0 ]# U# a" b" s
Flash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。1 i" a' g9 P! J( m0 f; r
8 P. Q: w) m6 l7 g  e$ C
80.2.1 程序能够通过下载算法下载到芯片的核心思想
+ n' \+ g6 j' V! T, ?认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。: k. h. }% Y! E1 E& @. b& _

9 a9 {& z* E1 M# p80.2.2 算法程序中擦除操作执行流程
: Z- h2 [: h5 e0 R擦除操作大致流程:
3 @* Q- [5 d2 P0 M# f. R0 P5 B# x3 D7 F; m/ y# g. y; I7 \: }) ?
d4b4af6b23f5f1d5ee3f3387ccb8fbdc.png

' [# R9 E- A" K2 p: S
1 F" i! H* x; E" o  加载算法到芯片RAM。3 p6 ]7 P& }$ h: x7 H; y2 R
  执行初始化函数Init。
0 o4 R/ i: h# C, Y' V7 G& S  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。
/ h7 [0 b' e1 r  ^+ a% s" W3 T  执行Uinit函数。; A# C7 b7 P5 w0 T; |( U6 h( d) I
  操作完毕。5 e, r1 [# ~+ S% @8 P

" H7 M2 |2 [# \/ s80.2.3 算法程序中编程操作执行流程2 m0 t4 R7 B' u" W: e
编程操作大致流程:$ s; n% E( i' L4 G5 A

9 k: e9 y; W+ }1 A4 @* c! G/ v$ E
d3f601364bb033d42e275ad686c03726.png

0 t3 d/ ?3 Y' j$ M9 N( H+ Y$ _2 z& a6 Z- ~, P+ l4 C9 M
  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。
% _: _% a) s3 P, S  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
% O* ^/ [4 \# i+ U  加载算法到RAM。
$ `1 w5 ?2 R7 L$ r! P4 A% j; X0 Y  执行Init函数。' V8 \% i$ W, @9 _- B
  加载用户到RAM缓冲。
( m& b% ~8 T0 t& R, a. s' V, U  执行Program Page页编程函数。& @+ e0 f" g9 C" V1 Q
  执行Uninit函数。9 f! D+ f" v5 I$ `" U$ g* }
  操作完毕。5 j3 A& D7 B4 _" p# P

2 d9 A% A) J! l, a+ a80.2.4 算法程序中校验操作执行流程, K( Z; n* u: B. ]$ v
校验操作大致流程:
" C5 ]: j. u8 u# ?* x) b2 Y1 D6 \7 n, I3 z$ X; X( ^: S
c7b2f373d946bc7d166ab1c1ba9b67e4.png

0 ^5 H( t8 w! n) T* B/ H4 a( K) P; k8 D* S
  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。
/ O- \7 R+ [3 ^8 J8 N8 n- F  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
1 }' f7 n! `: ?- [+ ], ^( b( o  加载算法到RAM。
* Y! L0 M: u. e9 f( e+ q2 _  执行Init函数。
! j$ }/ Y/ }3 k# q# ~  查看校验算法是否存在
0 ^; [/ f7 `# u+ E  如果有,加载应用程序到RAM并执行校验。. Z+ L9 N* y5 Z6 U$ Y5 \7 Z7 f! g& E
  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。5 f+ z9 V" B# k- h1 I' T5 @2 i
  执行Uninit函数。1 s' |7 N) x! t9 m: E
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。  l1 F* E8 k, ~( d
  执行RecoverySupportStop,恢复支持停止。
* X: {- [  y; H/ I; n* F  执行DebugCoreStop,调试内核停止。
+ Y1 U5 F8 t' N" \  运行应用:. z6 R" g1 a  ]- I% h3 x) X
  执行失败。
/ g! _' T! E% x" x0 \  执行成功,再执行硬件复位。) U9 d/ y  ^1 `" }# p
  操作完毕,停止调试端口。
( G# F6 D/ h8 K- m3 l# Q+ \; d0 P) t1 c" m
80.3 创建MDK下载算法通用流程( j+ d- w( O+ Q9 `; @( W1 A
下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。, t3 r# j) n0 ?+ E! A4 |7 n

; s( b  B9 E9 F80.3.1 第1步,使用MDK提供好的程序模板, x8 y. w- u! J4 _" T
位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。# R+ x( ~/ D" w$ [
) H0 Y# q' \/ c: f- s  s
效果如下:
: @# }! x* `; m) }/ k' c# \$ N; M' Z2 J3 t7 e, q* F
8e08d948d34b38592e3699136725ade1.png

/ Q/ @/ u+ L" {# ?6 Y
- m6 `6 E- \) |3 j* E& |6 h* v80.3.2 第2步,修改工程名( m# e7 _+ w* Y8 c
MDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。
+ L: S+ e+ s, L0 d6 P# ?
- L% u3 D4 Q9 g8 I; K80.3.3 第3步,修改使用的器件
4 M* _# e# x3 ~# S3 U, X在MDK的Option选项里面设置使用的器件。
# F: M* Q( z& H/ A9 k8 v: B$ o) y" n. _6 ^, V; F
347d6def457f6e101f1c2555f9156cda.png

7 ~: Y8 ~- W. W8 ]0 E& x+ d
0 N1 W7 @; g' m1 @80.3.4 第4步,修改输出算法文件的名字( J3 [. V4 N% A! `9 R( m" p
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。
' k& Z& Y1 A  _. h) U% ?, i, b3 ?" X! M0 k1 y9 Q$ F# [( Q
07d754db49256bd0b73f4538a328f3cd.png

. A) w8 l+ s8 J" ?
9 n- w6 r6 M0 k注:MDK这里设置的名字与下面位置识别出来的算法名无关:0 n3 A0 G3 D( E% N: M
6 K5 T+ o" E( @' c6 e+ z
a00b0ff99a0a691f19dfc2565fde811a.png

7 w' p2 @; n4 c, K- D
3 T. `  }' N6 D7 R2 J$ i+ B这个名字是在FlashDev.c里面定义的。
9 V% a8 O) R9 K7 E+ a& n5 k$ t( I/ j3 \
80.3.5 第5步,修改编程算法文件FlashPrg.c
! S) t1 W. `: n) }3 u# F模板工程里面仅提供了接口函数,内容需要用户自己填。
1 b* j, a, u3 p% [: w, m8 l7 Y; x9 R  O
  1. /* $ r' s5 [- _* D! g. U: Q5 ^+ T
  2.    Mandatory Flash Programming Functions (Called by FlashOS):- h, I4 X- w: u' k
  3.                 int Init        (unsigned long adr,   // Initialize Flash0 b2 b" [- m  J: Q" k
  4.                                  unsigned long clk,
    ) A4 u: j7 Z% I/ s! [' P5 C
  5.                                  unsigned long fnc);
    9 p' _9 y7 t8 e, [/ F
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash1 b$ v% s! e7 m: D8 B
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function$ m/ o! J/ |! e# `; X! p3 r
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function
      `1 j/ e; J* N% P
  9.                                  unsigned long sz,1 j6 w6 w! g5 N7 X
  10.                                  unsigned char *buf);
    6 F* O* b4 e% Q6 n! e

  11. " O3 Q" |8 B( g8 C7 a: x
  12.    Optional  Flash Programming Functions (Called by FlashOS):
    1 B/ W+ K) C: F
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check
    . C8 `( L9 U1 p" V; C
  14.                                  unsigned long sz,  S/ J0 f* j+ M/ V8 P: H/ o0 C1 \& N
  15.                                  unsigned char pat);( E3 N* C- O7 O/ y
  16.                 int EraseChip   (void);               // Erase complete Device
    % k" ^) l/ Y& E5 L$ c
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function2 G  d) G4 E5 d! l) a
  18.                                  unsigned long sz,
    ! ]- B" U' Z3 Q* `4 `, M
  19.                                  unsigned char *buf);
    0 G2 I- |, G4 R* R" d2 a& W# k
  20. 5 b$ s& o" m# _% h
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space( S6 D0 a* z! |, N" q4 H2 {# N% e
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space) p4 {2 V, X- P9 q) _
  23.        - if EraseChip is not provided than EraseSector for all sectors is called
    7 H! V8 E' ^3 k. E* A3 n
  24. */5 N1 e, o1 h% d; s2 s* u! Y- P
  25. # u2 [% T1 q* F. `1 [
  26. /*3 j* A: t* m( n: w8 C
  27. *  Initialize Flash Programming Functions4 @1 P! u& L& H5 c& H+ T% T
  28. *    Parameter:      adr:  Device Base Address" K: a0 S: v# Y1 g" W
  29. *                    clk:  Clock Frequency (Hz)+ Q6 q* {# m4 z1 w" z, g
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)5 A# V7 U" P  P3 S7 [6 d% p
  31. *    Return Value:   0 - OK,  1 - Failed* K# l( e( [  e( @, \9 s
  32. */% ?: ]5 b  ~/ @. @6 [2 b4 [5 Z
  33. / @/ f2 `) k+ M) c  f. L6 e
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
    , w  m) ~  W1 f

  35. ( a+ t  ?5 h: J5 E5 n; x
  36.   /* Add your Code */
    # h, Y* D% C& f1 o3 w/ Y. E
  37.   return (0);                                  // Finished without Errors
    " Q. D% s: x! I5 c" B$ L, |
  38. }
    5 d' K' x9 |) Z; F' f. I* ]* k

  39. / m7 |0 A  a: Y3 D

  40. - i% l7 L2 _1 g% |
  41. /*
    - t: a! u: V- R: g, Q
  42. *  De-Initialize Flash Programming Functions' N( |" M: N* y" `3 I
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    ; H1 X# K0 g& h* J! F
  44. *    Return Value:   0 - OK,  1 - Failed
    8 J9 I2 C9 V& O8 {0 L
  45. */7 H1 [. K9 H; i( x' x$ }% L6 ~$ K

  46. 9 }9 ^" K3 A/ y
  47. int UnInit (unsigned long fnc) {
    # T, |; n, X: y) j4 K# v
  48. ; m7 n% t6 I, ~1 m# n
  49.   /* Add your Code */. ]) f3 |% W4 X. i  H# f
  50.   return (0);                                  // Finished without Errors- q0 x( r; D) i* X7 H( ^2 \# Z& o. N; Q' @
  51. }0 M! S* `2 U3 m; r! k8 C
  52. ' \: h/ J7 G3 v- u9 Z8 S% E
  53. $ U4 }# f; t- O! G
  54. /*
    " ~' f. i- S5 k# C# p5 m0 E
  55. *  Erase complete Flash Memory& b9 t2 u4 J7 m, K) R9 X7 A5 O
  56. *    Return Value:   0 - OK,  1 - Failed" f+ T/ U, e+ {- ]
  57. */8 m2 T! W- U2 S' q, V

  58. - Y# R, L; f* M0 o
  59. int EraseChip (void) {
    , ^. y$ Z0 e  }/ V- t

  60.   ]) ]! A! h6 r. G6 Q  F
  61.   /* Add your Code */
    ! m' Q4 T+ y# L/ l- n+ |9 c
  62.   return (0);                                  // Finished without Errors2 j3 R% P5 H: b& Q  |
  63. }' i. ~7 [7 Q1 y7 m! L3 W

  64. * U7 m2 ^, E) `% }. V3 I! v( ~
  65. + F: r0 C& X; T) Z2 x3 B* }; V
  66. /*
    % Q# a: }% ]9 `: b, Z
  67. *  Erase Sector in Flash Memory! r! U6 |( E( T* j0 @8 F) C
  68. *    Parameter:      adr:  Sector Address, ~9 Q1 i2 A5 i" J4 c! z* e
  69. *    Return Value:   0 - OK,  1 - Failed
    " b6 q/ A$ o, D2 f
  70. */5 X- X( I' {, a/ s: D, F

  71. $ P9 a! X+ w5 k$ x$ W9 C6 C
  72. int EraseSector (unsigned long adr) {
    & A, ?/ C; }7 D; U! \

  73. : x$ C0 V  f& H! T; K' ?
  74.   /* Add your Code */4 r- l* e% U5 A6 `
  75.   return (0);                                  // Finished without Errors* a2 ~( G, {1 n, N+ @: m& T; r, d
  76. }
    / n- W9 M2 W! y

  77. 8 e) L5 H/ G; g! r) w

  78. 4 y1 \2 C- n( z( w$ j
  79. /*- T/ V5 Q3 F, A9 W: }2 i
  80. *  Program Page in Flash Memory6 P/ h1 m4 Y! u! ^3 S
  81. *    Parameter:      adr:  Page Start Address# I, y6 [2 L2 f" O% W
  82. *                    sz:   Page Size: f% }& Q3 ^' o! Z3 A/ K
  83. *                    buf:  Page Data
    6 ], ^- @# d( R! K0 Y* j- v6 S
  84. *    Return Value:   0 - OK,  1 - Failed) m2 B6 N7 u( f
  85. */# f; C8 |8 f7 j: E0 L& K! ?

  86. " J9 ?8 o# F* A
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
    . ?0 N/ ?5 ]; v  C' d; g0 P
  88.   ]3 d. }& @2 [7 E7 N9 z$ \
  89.   /* Add your Code */  b, F9 X1 W7 f) `& R% n- m/ Y, r
  90.   return (0);                                  // Finished without Errors/ p: H* V7 `- }
  91. }
复制代码
: R; Y8 Y/ p8 a- q! h1 K
80.3.6 第6步,修改配置文件FlashDev.c1 \; r8 ^  N3 D$ b" O
模板工程里面提供简单的配置说明:$ n; I# \, c# B  c  y  ]
# J5 ?3 E5 G6 I- F; o4 N* _
  1. struct FlashDevice const FlashDevice  =  {- v' H3 M& l6 @! s4 O  e
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!
      r4 w, t, i$ o0 p
  3.    "New Device 256kB Flash",   // Device Name - A+ I/ V+ w! b' |/ p
  4.    ONCHIP,                     // Device Type0 b9 ]+ \( J6 B' z# }' S
  5.    0x00000000,                 // Device Start Address
    - [* H1 N: k; W4 W0 y# L
  6.    0x00040000,                 // Device Size in Bytes (256kB)6 x0 ]2 r+ p7 N# v0 T, g$ N$ p
  7.    1024,                       // Programming Page Size; M2 u4 `4 B8 V0 ]
  8.    0,                          // Reserved, must be 0% z6 s9 w4 F) H  o
  9.    0xFF,                       // Initial Content of Erased Memory0 {5 r& H, D) ^! c! X
  10.    100,                        // Program Page Timeout 100 mSec4 M) R) V# v* S. N
  11.    3000,                       // Erase Sector Timeout 3000 mSec1 c* Y+ }' z7 a! V

  12. 5 F9 ]. I# f5 G% t( {& ~  W7 u
  13. // Specify Size and Address of Sectors
    , [( y4 }3 M% c- X
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)9 g3 e" I4 a' C& {( j
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors)
    1 w( h! }) i6 F. C8 T8 U
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)
    - }" h4 {) g4 n/ i& j' y! d* Q
  17.    SECTOR_END6 j( C1 y/ M# }! x
  18. };
复制代码
3 c( f( Y% A% u( M. Q
注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。
" i" }% `' r7 t6 c, u  W9 o9 ^$ W( |5 A$ {4 D9 R0 n6 L6 U
80.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关) v# [) `2 w& m; c; [& v
C和汇编的配置都勾选上:1 B. Z- H- G2 M3 [

+ H! O6 J% k- n1 I
1d27a7314fc6c472937ad43852722b6b.png

3 e( H6 v; B6 v/ g( J
% r( f+ Z: l, y+ q汇编:
+ o( y$ v2 G' z+ V3 C* ?5 Z: {, [$ m" E3 F% h6 v/ O9 j7 I4 y- v- \
bbea604970c4ef63c77f10e553e28ea6.png

0 @3 z# n7 F, o6 I  w0 Z" h! I8 G) H+ L/ k
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:9 ^! L! b% v' }7 E
% U8 O. B: v  N5 S' j% ?1 _
(1)加载以响应运行事件。3 T3 p( Y: F, y7 f& c" ]0 S: D

- J/ r' d  M  V3 R* W(2)在不同情况下使用其他例程的不同组合加载到内存中。" x9 N* P3 r( s) d* {
9 A: Q$ x8 W# e/ Z7 ?4 I
(3)在执行期间映射到不同的地址。. J: y3 `: X; E6 Z  \& V7 u! o& l. F
* w. [, h! u8 W/ t
使用Read-Write position independence同理,表示的可读可写数据段。
$ X& D, Z% w8 d' l  [4 A0 @' [7 Q( }2 c0 g0 l9 D+ M
80.3.8 第8步,将程序可执行文件axf修改为flm格式

% {. L1 c4 D9 _5 L  X通过下面的命令就可以将生成的axf可执行文件修改为flm。
& \9 ]+ o% l; X! U
2 M8 D9 b/ f9 G. B; D( E/ m* X
6b504444979e9fa3a3477c6284abca8b.png
/ O+ W+ a# [: _+ `. N, P8 }

9 b2 ^& P$ b! P80.3.9 第9步,分散加载设置
. u$ T6 p! C, M' W我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。
7 X+ E) s! O: @/ K9 ?5 ]. |( u0 ^6 r  ?( M% I- O% ]9 }
e8e79854b204b618bbda32af92f28bf2.png
# }3 [* ]3 K' y- k& H- g

9 P2 e' T; B" L/ J; \) G分散加载文件中的内容如下:& n8 ?& w2 Z, \/ M( F& e
8 o. x$ r6 C* B' J) o0 J+ E
  1. ; Linker Control File (scatter-loading)
    % _; X: ~( e# ?4 Z: U2 Z9 |
  2. ;
    ' @6 t: O- N7 Q$ t1 f

  3. * _- C, \& i! b( K8 b
  4. PRG 0 PI               ; Programming Functions
    4 S# w+ V& [8 W& H
  5. {
    ! I  X+ D' I2 W, [/ b. T8 M
  6.   PrgCode +0           ; Code
    & K4 B8 X& {- _: @; ]  h: C
  7.   {3 K3 w" |' k( O( k' T0 E/ W
  8.     * (+RO)
    # u- B7 G" i% d, N( v; Q! w, X
  9.   }  _& M- g- H% Q* [1 c4 t
  10.   PrgData +0           ; Data$ S1 b8 Z2 v4 |5 N  x. C6 Y
  11.   {
    2 t4 t: ]' a6 {% F! f1 I; A$ C
  12.     * (+RW,+ZI)" M. y" n! n/ I
  13.   }
    ; ?9 b& e7 o) \
  14. }% S. y. X# v7 c

  15. 3 o" `1 W+ K( g( K, L0 T
  16. DSCR +0                ; Device Description# L8 \0 _: `. f% i+ K: z
  17. {
    8 y! i9 u7 i. @+ m8 b- ]
  18.   DevDscr +0
      |6 E( S' n$ s3 B+ d1 f- C
  19.   {
    , K% F$ Y% d# J; y- B- E
  20.     FlashDev.o
    7 I" q0 s# p3 k
  21.   }& w8 v! c: R; b) h. j
  22. }
复制代码
6 @; R6 _8 f% y0 \/ m% i
--diag_suppress L6305用于屏蔽L6503类型警告信息。
/ D) s  j- @, [% Y& ~# v: _
. i5 Z( F3 R7 s9 g; K5 V6 B特别注意,设置了分散加载后,此处的配置就不再起作用了:
# d$ B5 `( X  o8 z
) [! C5 `: r7 j
b249eb40e5562b04ac3eb05ec0219c1d.png
7 z9 I5 ~! o5 h. B* h- x3 T# z. U
+ |; F7 [, n0 o+ B0 g
80.4 QSPI Flash的MDK下载算法制作
) c; b1 g7 c6 s) x下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。' Z2 M- M0 D4 j3 t
+ |; {2 u5 V$ e! m1 p
80.4.1 第1步,制作前重要提示  C) |" r) z! @5 |% K
这两点非常重要:& G# E5 |2 ?) y0 b' W

  A! A3 [- b+ A) X2 a/ q4 t8 ^  程序里面不要开启任何中断,全部查询方式。. f! d* H9 I" a4 J  |
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。- y' y% R8 D+ J8 h: z/ H5 k8 ?

1 q$ }& L0 }0 E) ?; R( s80.4.2 第2步,准备一个工程模板: G; F& z9 P$ w8 d4 {+ {
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
6 B% F' k8 o6 `6 Y  ^
. q' I# Y; t8 p3 y
a91aaf84a25d730b77273624d2b502f6.png
3 ?5 K6 @6 A/ v9 N* D7 b9 b: n' R
/ }3 O: C* i) R2 M, k: q5 v. v
80.4.3 第3步,修改HAL库/ H, R3 e" p# Y2 `
这一步比较重要,主要修改了以下三个文件:% }1 h- D% I/ d5 S+ Z7 F
! E* T/ J8 Q+ L  o$ S
4471afac6bbc201866d0d9a8ea7d876b.png
+ _- c: \$ Q( A& t) ^
  @+ y2 e& H7 M6 T
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
2 T  D8 ]4 E6 y( d* ]& N
6 A# ?# b( D5 e! G7 Q0 Z. K80.4.4 第4步,时钟初始化
: D/ G6 @& B8 ?* J我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:5 h9 z1 O5 Z( k: S

6 {5 N: `1 y4 n
  1. /*$ D% K. z0 d8 ?3 U+ R3 _4 G; |
  2. *********************************************************************************************************" d% X, `, f: G0 w
  3. *    函 数 名: HAL_InitTick3 e- u( v5 F: ^$ h6 X8 d
  4. *    功能说明: 重定向,不使用
    3 {& T" R3 D7 y- `8 n2 {
  5. *    形    参: TickPriority
    - b2 m7 D) o# l: z
  6. *    返 回 值: 无1 b% v* V1 ?  a6 `$ l
  7. *********************************************************************************************************
    % z0 ~6 e7 C9 S4 `# \+ X% h6 a( s7 I
  8. */) _5 P2 N/ C6 o# l  ~: z/ S
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)4 c4 ^# E# e1 U, M1 d
  10. {) c3 e* b9 o2 ]& b) [. O
  11.     return HAL_OK;
    * {; r% O+ {7 ~9 u3 S5 ]. d
  12. }
复制代码
3 p9 u6 d7 t; `7 T. L
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:3 X9 ?- f: s) _

" C4 X, t# A( t# _8 T
  1. #if !defined  (HSE_VALUE)
    & D$ x. g, I5 ?2 ]- S; I8 V4 k! u' h
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */  h9 V/ w; t3 O' c- q
  3. #endif /* HSE_VALUE */
复制代码

/ y5 n" g' Q  J4 A. H$ a  i* a' W4 l最后修改PLL:
2 K8 k, P  c4 ]1 G4 k7 l, d8 {% i8 }  {, \+ c
  1. /*! j  R% i1 v6 ?* s) `/ f
  2. *********************************************************************************************************
    " `, J" G% N" s2 L0 |1 k) E
  3. *    函 数 名: SystemClock_Config
    : R+ W$ D  r% ]/ j% o+ T0 z% \
  4. *    功能说明: 初始化系统时钟
    2 m. A: V  ~# i3 x
  5. *                System Clock source            = PLL (HSE)$ M' X2 Z# E1 X3 O! J
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)  Y" f  e/ a& K6 ^2 w8 g* W9 ?
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
      o) X5 m$ x7 `9 {
  8. *                AHB Prescaler                  = 2
    3 W  M' z. ?$ x4 S
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)! W  N0 ~6 [; O. ~, y
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)1 w+ |9 J) k  ?9 x/ v* }) Z0 m
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    0 l8 S0 Y, k# R$ j% _% ~7 ~8 Z* x
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)! h2 a# U4 c9 C. J7 `3 V/ w
  13. *                HSE Frequency(Hz)              = 25000000
    / s, F5 l3 D9 f2 W/ u+ `  l7 b0 s
  14. *               PLL_M                          = 5; s1 s& X+ Z" ^* a
  15. *                PLL_N                          = 160
    4 l+ H/ ]" {% `; z
  16. *                PLL_P                          = 2
    2 k; `% t8 T5 _/ O; R1 h! N
  17. *                PLL_Q                          = 44 y5 x9 ~) p- M& k! y
  18. *                PLL_R                          = 24 v1 d+ m3 q7 x1 N5 [# \  K
  19. *                VDD(V)                         = 3.3
    . F( M+ A  |  }8 Z( J
  20. *                Flash Latency(WS)              = 4
    4 b/ F7 n7 V6 W: D! A3 q
  21. *    形    参: 无
    - P% a7 ~: q5 W/ Y& O: F
  22. *    返 回 值: 1 表示失败,0 表示成功
    & d2 Q( ~4 m( _& e4 z8 Y. t
  23. *********************************************************************************************************% y: @$ M1 d% w. u
  24. */
    1 z9 F, g! o+ c# Q/ c  Z5 t
  25. int SystemClock_Config(void)
    & E7 w& W( }  Y' ]+ D5 h, d
  26. {
    ; G6 U" _8 ]  v+ m
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};$ D1 Z  G% t/ f* x
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};1 M- d% h1 I' b9 K, v* t. d* K" ?
  29.     HAL_StatusTypeDef ret = HAL_OK;6 \7 r; I; x0 r* B

  30. ; g1 d1 ?. {! D  u' W8 I
  31.     /* 锁住SCU(Supply configuration update) */
    8 @! g9 n4 S' `; @- q% U! E
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    & k1 @# v6 M3 m) v

  33. $ q- d  o8 {$ n5 M
  34.     /* : F3 b7 M1 I$ K0 s5 Z' I
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    * S5 R2 a1 ]# E- G+ C$ y( d: A7 }
  36.          详情看参考手册的Table 12的表格。
    , C7 A2 |2 v, `6 y1 Z
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。, j6 \3 s0 F6 h; o8 R" S
  38.     */* L/ c! E! b" f: v* @% r
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);9 E2 c7 p. a! {! Y9 [% v
  40. % J& V  u  ~" \) P% p5 W/ w
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    6 d7 C* \1 w* d6 m
  42. & e9 @2 N5 M4 X3 B+ ^6 z
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */. c# k, t% W) s
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    7 W% E. _3 C+ a( I# {/ T  k
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;* a" a$ h- s9 |! k! g9 S+ c& X. \
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    0 R4 s, D$ W5 A  L
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    7 }9 J5 v+ r3 M8 E$ N! `
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    ! b! b& @+ Y9 R/ U  H
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;3 p" s' M% {5 u

  50. + L  Q( v1 O- E) J- n- `* k
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    * e% Y: |) |" |) T8 O
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    7 G7 @. V& K- g6 f) Q3 x
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    4 O. l# T4 n* }
  54.     RCC_OscInitStruct.PLL.PLLR = 2;  b2 W* I- F+ p- d" v3 t! L
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        * }6 O) t4 P* n6 x$ J
  56. # G0 J1 b: a/ E4 J- e
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;! E6 d; s% W3 a* c. r! ~2 G
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    % H  t) m! N& \6 ]' \
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    % m3 X% q1 ?; ?# f& a. Z- `4 o( L7 q
  60.     if(ret != HAL_OK)1 d* l+ b8 D$ [8 G2 Q! o, U* ~& G
  61.     {* U$ y8 h3 {$ T0 Z0 O8 s" C; ?2 ~# F
  62.         return 1;        ( M3 J& i; O( d' r
  63.     }
    ) v9 K/ S$ R- ]* @% C: \3 H
  64. 7 N) R+ r* v3 d" m$ w" @
  65.     /* ; {# M# ]% ]; a" q8 ^3 M4 Z
  66.        选择PLL的输出作为系统时钟
    ' y3 U2 y0 X' _
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    $ N4 u0 b- X, Y8 X! _
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线% A8 G, I' S; i! y0 J% U. N
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线3 I0 k6 Y* s* O9 z' ?2 S
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线' c& r  m4 T7 x+ p5 \# `0 O! m! I
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    ( [* C& K! `9 G. p
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    4 U- q  Z  F" k9 r
  73.     */; O' t4 V, Z6 ^7 r% v( d% E
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    * J: z4 _& J+ l/ Y, l5 v5 v7 ?
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);) l! b4 Z  |) Q0 W8 b* U4 q6 f* i
  76. & O& P# H, \  p( t% f( i" H+ ~
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    : w. J) {+ p$ [3 M6 b  t2 m3 p
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    0 u. d9 p1 t/ }  P
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;) G  O/ H% k6 e2 o: q9 I
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  # g3 Q. H* i/ p' `/ G3 s
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    4 M# _" ]) x6 ^% @8 E2 w7 x
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    : {  p# ~" \4 q4 ~1 g
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    1 p! |: F# i/ z. l! m
  84. 5 }5 Q; y% d, N* ?: u; F
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */; r5 L% K. {1 H* \% Y
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);. p0 ?8 a& S4 _7 Q6 w7 y
  87.     if(ret != HAL_OK)  ^: X6 {- \8 ]! U
  88.     {
    4 @+ o( f' I" K2 R- e0 [  n, x  X
  89.         return 1;
    ) v+ Z( V: {# B- }2 }' s
  90.     }
    $ w" X: m( O- z

  91. 1 F+ q" K4 n# O0 S& L
  92.     /*4 z, s: K4 p1 U9 s2 Z# T; Y) n6 e6 ^
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    " G. l  H7 R( g, l# q) E; \
  94.       (1)使能CSI clock
    : m0 p4 }2 l/ M" \/ G: ?
  95.       (2)使能SYSCFG clock
    9 N9 k: V+ M- @4 @# [; Z
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
      f- r+ ?  b9 D- ]5 o9 b
  97.     */
    * h# Q- _! P; Q
  98.     __HAL_RCC_CSI_ENABLE() ;
    & u; I; ^& m$ K8 u" \

  99. * n; [3 F6 ]! Y5 ?( J
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;' y9 I+ N) X0 _6 C, R1 p! D
  101. . W' T9 |8 v7 o; A; j7 m+ @
  102.     HAL_EnableCompensationCell();9 z9 x0 c" m8 l. x: A( z
  103. ! P3 p' s0 C& o
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();) |% C) ]; `) ]$ Z# O1 N7 o$ a
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    - p. a/ o# Z( }" T/ N* E
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    0 F1 |7 ]# i/ d
  107. 6 t  x/ f. I4 S& l4 _- Z) L
  108.     return 0;3 `9 S9 Z; T# d; F  Z# H- C4 s
  109. }
复制代码
& `5 D& a$ X0 e
80.4.5 第5步,配置文件FlashDev.c的实现, l6 v( \% G: `/ h% B9 k5 w
配置如下:8 q; t* E8 @/ G1 ~) a  _3 M9 m

/ a/ Y7 O/ n( B; T, R- ~
  1. struct FlashDevice const FlashDevice  =  {
    + N% |5 F- B- D! ]) h0 k
  2.     FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */7 Y6 _; W! e' C% @4 Y
  3.     "ARMFLY_STM32H7x_QSPI_W25Q256",   /* 算法名,添加算法到MDK安装目录会显示此名字 */
    ) s9 i8 [/ K+ k6 b8 _8 o
  4.     EXTSPI,                           /* 设备类型 */, J1 g; t* d9 F' s+ r$ X& }
  5.     0x90000000,                       /* Flash起始地址 */
    , F5 g" p; r) h% N
  6.     32 * 1024 * 1024,                 /* Flash大小,32MB */1 w, Z4 t" R: K+ b
  7.     4 * 1024,                         /* 编程页大小 */
    / {; M; a  Q: I4 q0 p  {  t
  8.     0,                                /* 保留,必须为0 */1 c' a; g% P$ a9 f7 b
  9.     0xFF,                             /* 擦除后的数值 */
    7 `0 K7 U: S& d  T+ c  D+ p: u
  10.     1000,                             /* 页编程等待时间 */
    2 x! T* E# o+ d
  11.     6000,                             /* 扇区擦除等待时间 */' |4 R. z8 f9 {
  12.     64 * 1024, 0x000000,              /* 扇区大小,扇区地址 */
    1 g* E' J0 [/ {* M) ?
  13.     SECTOR_END   
    , L, i2 g2 G6 b7 ]
  14. };
复制代码

  b5 {- w0 @6 s( D+ I8 `注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:
3 F8 f: L. Q4 B( z  ^" a* T) P+ G# ^* r, H& c
ffed7a581be4c5da388ee29fb67249b4.png
# t; ^7 ^" g+ G' D  Z
+ l5 C/ N* g0 c' |
80.4.6 第6步,编程文件FlashPrg.c的实现: g' o* A9 G& @
下面将文件中实现的几个函数为大家做个说明:; J0 |0 Y' x4 w4 Y" J

7 o$ J8 k- R+ g) R$ O  初始化函数Init
- {5 _; S' n; a0 i! H! h* _* b
  1. /*1 V- ~  l" A; V! M4 F+ f
  2. *********************************************************************************************************
    5 e1 M! y; G; `
  3. *    函 数 名: Init! z7 r& C; g( n+ l* O) G' s
  4. *    功能说明: Flash编程初始化
    + M$ X4 U7 a7 w" F3 G
  5. *    形    参: adr Flash基地址,芯片首地址。' C+ j  `$ J+ [2 }$ _5 Z
  6. *             clk 时钟频率
    - |0 y  t3 I: J
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    5 E/ l( \7 Y3 s5 ]3 ^
  8. *    返 回 值: 0 表示成功, 1表示失败
    : Q9 y, G8 ~9 j3 T
  9. *********************************************************************************************************& }  d  L" I* v5 ~% Y2 B1 y& G
  10. */" p/ _0 ?. _! ?( ]5 i7 O' R
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
    + G8 s' K' m5 p1 _
  12. {. X- u" v+ V; K7 v
  13.     int result = 0;
    # ]" X, @! k! @. M

  14. 9 F# o$ k; g3 x' _9 X" v
  15.     /* 系统初始化 */. F$ l+ k9 x/ y$ d
  16.     SystemInit();
    9 K0 u4 C) W' s% T+ ~8 _

  17. * M& A7 R! j( z) P7 j  u
  18.     /* 时钟初始化 */
      z( @4 ~9 g% d* w
  19.     result = SystemClock_Config();
    / A0 v5 U7 p" L% D# B  |
  20.     if (result  != 0)1 I- S7 g+ _9 _' L5 p
  21.     {7 E( W& m5 A$ ]4 V5 [4 k
  22.         return 1;        
    ( h- J1 T& m* G9 ^  w- l9 f
  23.     }
    4 }2 N* N. i6 u" ?- B+ x# Z1 o

  24. " J+ X; `$ X! ~5 S& [) J
  25.     /* W25Q256初始化 */" d* h; [$ H8 f* x! d6 N
  26.     result = bsp_InitQSPI_W25Q256();
    9 m' P0 j- g1 w* @
  27.     if (result != 0)$ M2 h4 G# d  s, H+ c3 t
  28.     {5 ]  r! L' z7 j, @  a
  29.         return 1;9 N0 Z. ]: Q' J+ }2 w8 C! {* m& `: ^
  30.     }- b9 C, O! R/ u! [
  31. 6 n+ j5 V4 K" z0 s( {; d
  32.     /* 内存映射 */   
    7 X* U. U, R* @& N9 W% o0 }) p
  33.     result = QSPI_MemoryMapped();
    , i5 n' X( J+ u& A9 w: r, P
  34.     if (result != 0)4 [* H8 d- K  C. d" o1 X
  35.     {* a8 L, k- j  B( I0 p
  36.         return 1;6 N) l! V" k2 w5 T6 \  ^4 ^8 }
  37.     }
    $ f- x. J$ G2 D$ z* t  q
  38. / q" U9 v, r2 ^" Q  E7 m
  39.     return 0;
    ! r) I, U5 ?# |. E
  40. }
复制代码
) d6 h5 Z! e- _1 W
初始化完毕后将其设置为内存映射模式。0 t% y: t5 x; H; F  {
; h, C. X# H  C
  复位初始化函数Uinit$ \# f5 G9 Q! ?% ?/ a
擦除,编程和校验函数后都会调用此函数。) D' p8 v% J& Y

: Q$ Z+ j# x0 b* L$ s
  1. /*/ u8 b' E0 N- c" ?9 D8 O( {$ A
  2. *********************************************************************************************************
    ) [! A  i( r  o2 ]0 C! h
  3. *    函 数 名: UnInit/ S$ F% Y1 k  C$ T! ]8 K; N9 b
  4. *    功能说明: 复位初始化! |; a) v4 o$ x4 L4 P* P6 Y- \6 D
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    , ~# z  w! t# @; Q
  6. *    返 回 值: 0 表示成功, 1表示失败
    9 q# F9 Y, a2 |5 H$ C  M
  7. *********************************************************************************************************2 z2 Z: s; m8 O. _
  8. */8 P* L+ {! \1 z7 h6 v$ S; g
  9. int UnInit (unsigned long fnc)
    0 @2 f. [& d8 m( N) p, F
  10. { ( d7 L( E$ x( f( ~
  11.     int result = 0;; [9 l8 D- ?( v' d" t" i$ Z
  12. 4 @$ S( }$ x# `" U
  13.     /* W25Q256初始化 */
    % l; L' _# v  s# |" e2 M" m% h  j
  14.     result = bsp_InitQSPI_W25Q256();, ~8 l# u& I, E8 u. Z6 u
  15.     if (result != 0)
    * ~! i: k& E( H7 b# _0 O
  16.     {
    # {6 {8 L2 r% ~2 z$ Z
  17.         return 1;
    . V- I+ }0 s' S0 q# u
  18.     }
    , b: K& Z; ?  D1 D: t

  19.   }; w( v# e( n9 f* e
  20.     /* 内存映射 */    + p" W1 c+ x. O( O( a! D
  21.     result = QSPI_MemoryMapped(); " }( V. {* @: V7 t* _4 q
  22.     if (result != 0). Q8 Q' K8 ~  y$ R, H' J  P
  23.     {
    1 B9 a" p6 s) K0 V5 I
  24.         return 1;/ F( J* X5 E) B
  25.     }" {' B! t) A0 M/ J" d3 o- s0 }
  26. ! @8 J7 T/ F6 C+ u" u4 X8 B
  27.     return (0);
    5 V' x, {$ x- O! G
  28. }
复制代码

; Y( u% A+ s, u2 W) N, R复位初始化这里,直接将其设置为内存映射模式。# g) J: p# W- u0 _
: ^: Q" _9 R" g" }! i
  整个芯片擦除函数EraseChip( a6 U0 a% u3 |: A" O5 M5 i7 l  h; c! F
如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:
' ^2 }1 s8 Y  x. E8 O# [* k3 {. P
6 U. ^* e; I  P! x& e" @. W
f623e662bf09233ea594f820f4c7952a.png

1 R: s. q5 k& a" |1 {5 |2 E# m' }7 s0 m% b; b
实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间,比如32MB QSPI Flash整个芯片擦除需要300秒左右。
$ o' Q6 D, W5 f
4 x7 m$ J# J( b9 y- M4 f5 a另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。( T* R2 V9 q! N! T  C
( G7 O' e" t$ k1 L3 X
  1. /*
    0 e" o# g  l$ K/ O9 v! ]
  2. *********************************************************************************************************3 Y1 O# T! ~  ?- B% m
  3. *    函 数 名: UnInit
    1 x5 _0 H  |" r9 w
  4. *    功能说明: 复位初始化  E, K4 C7 T0 E' v9 B2 f
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify) @' i1 X" b4 Q& n( i$ f) ^
  6. *    返 回 值: 0 表示成功, 1表示失败
    # Y3 d5 U* [& Q6 b6 o
  7. *********************************************************************************************************
    : y# P6 S. X8 x- E
  8. */
    ; F/ N( l, y- m6 Y- w
  9. int UnInit (unsigned long fnc)
    0 H8 H9 k0 A' H
  10. { ! E  Z5 ^. E: C0 V( e( {" u# u# O3 k
  11.     int result = 0;/ x2 w% o8 c' T& c
  12. $ K. h( }- h! e. o+ u, _
  13.     /* W25Q256初始化 */
    $ u6 f- M4 _6 X
  14.     result = bsp_InitQSPI_W25Q256();* ~1 ^  A0 x/ T9 K/ [4 L7 z8 _0 Y
  15.     if (result != 0)( z7 n$ W9 y0 W, u# R' V4 t. u$ _/ h
  16.     {
    + f: I9 ~! v+ R2 [
  17.         return 1;/ m+ g8 p" g" L) P& ^( i
  18.     }
    % m* c9 ^' b( G& f

  19. 1 Y% @0 H: Q( r! Z: J# [3 ]# j
  20.     /* 内存映射 */    " q4 K; Z* j' h+ _% {" k
  21.     result = QSPI_MemoryMapped(); 4 F; m- x2 k4 [# B; j6 R: z
  22.     if (result != 0)
    2 B+ s. e" g8 k/ h% W# B. E' T
  23.     {
    / E" u' J( O0 F' v8 Y" k$ G7 J
  24.         return 1;# c7 Q3 Z. o* }
  25.     }
    0 C# r" _0 q4 X! z# O1 j
  26. : r5 o1 E8 Z- \( W
  27.     return (0);
    + Y9 {6 t! X6 Z! [4 C
  28. }
复制代码

2 I: _0 l1 E0 y# v& Q  扇区擦除函数EraseSector' Q/ c7 W# x; s
如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:
$ @. t- }+ a+ p+ `1 g& W  l- Z  e# `/ `  ~
733d5af26119f64bff0261515013d3a6.png
. H  v4 P4 [. I$ q2 a; h8 K
' E0 q, E# R# o3 i6 x" o  x
  1. /*
    8 @0 ~# d' [+ q  K4 q# F
  2. *********************************************************************************************************! }1 o+ |+ A. l0 {) w- i( l* C
  3. *    函 数 名: EraseSector5 S% b$ |$ K1 v  F: h4 a6 x
  4. *    功能说明: 扇区擦除1 k* c1 w0 f+ y3 x6 y: ^5 j+ N
  5. *    形    参: adr 擦除地址2 Z! N+ D( M9 [
  6. *    返 回 值: 无# E( a, g0 R! q3 V4 Z
  7. *********************************************************************************************************
    0 K8 K% B4 c: B5 _) }6 y
  8. */
    ( H, ?  j$ @2 _  b, g
  9. int EraseSector (unsigned long adr)
    ) r2 q. E" W2 |! W0 V
  10. {   
    , j( A$ N/ K2 H1 w3 [& p; }7 ^
  11.     int result = 0;
    3 Q; V; F9 c$ w1 U. }% R! Q

  12. 4 y* g/ M3 @+ e/ p5 x. h8 L* S
  13.     /* 地址要在操作的芯片范围内 */
    1 H: I, E3 T  L$ G7 R5 P
  14.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    7 N* V4 W# i( W3 E
  15.     {
    ) E9 W. _  ?/ w: c2 A6 |
  16.         return 1;1 {, q8 N4 u# T# S3 `6 h
  17.     }
    * h" U$ l6 ]( K/ r

  18. 0 l+ |7 ^5 Z- `/ P2 _; H0 k; s
  19.     adr -= QSPI_FLASH_MEM_ADDR;
    5 _" t. B2 }. |7 m8 D7 ^2 e
  20.   B6 b; Z$ J. }1 B
  21.     /* W25Q256初始化 */! E+ b! J3 T" ]+ {% d8 U/ j7 a
  22.     result = bsp_InitQSPI_W25Q256();
    : Y$ ^3 B# }( _+ R) q7 ~+ u3 ~% ?
  23.     if (result != 0)* X# b) @8 a; E" F0 f
  24.     {
    , P- B% z/ s0 f) Z0 F* V' S9 Z
  25.         return 1;
    6 U  E( Q6 z' M/ W5 f% ]7 V1 K6 e
  26.     }. h' i2 r* d& H+ ]
  27. 9 y4 y* x; N* \* \, J2 Q
  28.     /* 扇区擦除 */
    % _! ]/ J  e6 A6 l' h
  29.     result = QSPI_EraseSector(adr);  # d6 |* t+ O3 C' z4 o  ]
  30.     if (result != 0)& n2 J9 l+ z& E' H0 [6 h
  31.     {
    $ W7 i. @5 ^/ A( T) t& p& s
  32.         return 1;
    , j/ Y0 A  G7 E0 d  h
  33.     }   
    : {* j* @* a+ @. o+ ?' E
  34. : k4 i$ s8 j$ w5 f9 Q9 L, M% s' z
  35.     /* 内存映射 */   
    4 I( m9 W# u2 o5 d7 _0 \
  36.     result = QSPI_MemoryMapped();
    , A8 p3 n$ `0 B4 e: L
  37.     if (result != 0)
    0 _1 M3 r% W9 C! ?* a7 E
  38.     {
    - L3 x' D0 S( [- E0 b. l
  39.         return 1;% s+ V( u5 }% O& K& R4 o
  40.     }
    3 M: ~! O5 e0 h* ?

  41. + l5 A) ^+ W$ @+ R% N
  42.     return 0;   
    ) Q( g& A" C, l( q6 }
  43. }
复制代码

. u8 T1 e$ t/ V# z这里要注意两点:
0 c$ ^; S7 {( k7 Z' F) c8 E% m" q; E
(1)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
- w9 E: L! i* b' K2 Q- Y: L2 W0 {- e7 e. f
(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。0 G% i- f( b' r# |
5 A( A/ ?8 B8 q0 x
  页编程函数ProgramPage
# k& Z( W6 X0 j# Q) M6 x0 W
页编程函数实现如下:' Y* \' q3 u+ r+ h6 N4 v
9 `' c9 K8 a+ {3 f% o
  1. /*1 y2 R+ E* y4 ]3 q# W9 G
  2. *********************************************************************************************************
    # V$ X/ D* I: w+ K8 Y: U$ I: x5 l
  3. *    函 数 名: ProgramPage
    " d4 e! K0 Z. V9 f+ H) N" g
  4. *    功能说明: 页编程
    0 g# c; g: A5 m; F: g
  5. *    形    参: adr 页起始地址, ]8 h, U% V) d; I$ b
  6. *             sz  页大小
    2 b9 X0 m0 X, g/ |7 k
  7. *             buf 要写入的数据地址
    ! Z4 R, }1 M' Y* i  Q9 c
  8. *    返 回 值: 无8 ^0 Q: u  H$ u4 I0 m6 n, Q
  9. *********************************************************************************************************( A: }+ |4 q, ]+ O0 c/ n
  10. */" t5 q# l$ s7 i) G5 f6 a8 H7 V. S
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) ( s! n, Q# T7 }7 V
  12. {- f: Z6 ^% U; y$ M8 _
  13.     int size;, M0 c7 k5 }/ ]6 q. {" `
  14.     int result = 0;
    ' t# P& \% ?; l+ {
  15. * V0 p8 M! m" y, j# q
  16.     /* 地址要在操作的芯片范围内 */   
    9 }1 w9 l% P' b/ r( W3 |0 S) j7 j" X5 J
  17.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)5 U7 k; O' x  P  q: x
  18.     {
    9 S& D, H9 i2 {# [$ w
  19.         return 1;
    6 q; Y) T, ^8 N5 _
  20.     }
    + {2 u* d- b* _/ T( Y' q4 Y

  21.   ~( q, [5 I6 c, t( E- @6 ?
  22.     /* W25Q256初始化 */6 M6 q5 R- Q# Z* q2 K1 D
  23.     result = bsp_InitQSPI_W25Q256();' `' M, c! R9 ?$ }8 l7 y
  24.     if (result != 0)" }) r9 U. J8 c. z- e* s
  25.     {
    + s. _' Y3 ^( V9 S2 V
  26.         return 1;3 |# V( \- \: e6 t
  27.     }( D: w' Q$ K, T0 o( d

  28. ' o, }7 q" x0 g7 R( l, Y
  29.     adr -= QSPI_FLASH_MEM_ADDR;9 L: I) F+ l" L7 j
  30.     size =  sz;
    * _# B+ }. {- u2 v) w

  31. " O: Q  L8 I& S
  32.     /* 页编程 */2 r# `/ W! Q# [  f* C  z
  33.     while(size > 0)
    0 r4 _+ t# S3 n: E' z0 z
  34.     {
    ( G$ [3 R; W. y* O+ V$ _2 i/ U
  35.         if (QSPI_WriteBuffer(buf, adr, 256) == 1)
    6 B  b- w/ P  g. K/ z
  36.         {9 G& v! J1 ?; B
  37.             QSPI_MemoryMapped(); ( P+ k2 I9 e  k9 d9 ]0 |) D

  38.   O! G1 U; q: M. g
  39.             return 1;   
    " ~5 H# y; d7 I4 [; L" m
  40.         }* P8 K$ G  c/ {% T7 A* U
  41.         size -= 256;& Z8 \. U0 a( ~0 y
  42.         adr += 256;. A0 g5 g% @# J% R( P& r
  43.         buf += 256;7 ~& I" T6 U7 P3 }0 w6 C* \
  44.     }5 [, B! k# X9 p; N
  45. ) l6 e8 m, T  T) A, k
  46.     /* 内存映射 */    2 ]2 @+ Y0 y- g9 e* {5 D
  47.     result = QSPI_MemoryMapped(); , v8 q& a* S8 Z5 D* ~5 r* F
  48.     if (result != 0)
    - W6 A' Q5 N* [3 {
  49.     {) |- l/ U# a8 \
  50.         return 1;
    7 t& |5 h* ?% l) A
  51.     }; m" B. M  v3 m1 y3 W
  52. - z+ I$ s4 H$ N4 e# H( A
  53.     return (0);                      3 v: j* E" \) f* m4 W" [8 g
  54. }
复制代码
/ r, P+ E4 r$ i- j0 B
这里注意两点:
! S) o6 j; Z9 Z  `8 Z" p2 ~( K# K$ Q  U9 h
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。1 W3 {3 `& u% y" |
% f8 h& t, E# F
(2)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。* h: ~# @# r9 e& c
4 _  `9 p% z2 ]
  读取和校验函数
+ _' H% L3 j" K# D- _1 H我们程序中未做读取和校验函数。
( i' j+ \3 }0 C
% f  |: l& z4 N" s(1)     如果程序中未做读取函数,那么MDK会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。4 J- W  |" b2 ?/ I
% z" u2 m& W) |# I6 h
(2)     如果程序中未做校验函数,那么MDK会读取数据做CRC校验。
2 J' _5 W+ z; Z8 n. C' Q4 X$ |. ?
- p, }: c& G- U% W80.4.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)

  p* ?, S% q, T; z6 [' A最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
. y  v( j7 P& C1 b) c0 h, y' U- m9 n6 S; S5 y7 l7 t9 G/ a
  1. /* # t$ l3 C3 B1 A% A/ h( l$ Q
  2.     STM32-V7开发板接线
    4 L; L" \1 V; N2 B

  3. # U& n. a" I  X0 ]
  4.     PG6/QUADSPI_BK1_NCS     AF10
    * q# \6 ~% ?7 ]4 G; Q3 p' E
  5.     PF10/QUADSPI_CLK        AF9* f( P( P$ ^, i' C
  6.     PF8/QUADSPI_BK1_IO0     AF10
    0 e  s. ~; p9 f, j; M
  7.     PF9/QUADSPI_BK1_IO1     AF105 R8 A3 D# m, j8 R6 o1 q# L! m9 `
  8.     PF7/QUADSPI_BK1_IO2     AF93 ~" O8 h8 {( u7 J
  9.     PF6/QUADSPI_BK1_IO3     AF96 S( x+ R5 V3 P) B+ n9 ^
  10. ( }3 {  n) `0 m
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
    2 V# ]9 J( z7 o
  12. ( ]& D8 W+ }$ `7 v
  13.     H7-TOOL开发板接线* E& m0 s; u' ?

  14. - x0 h7 Q$ E& M) O6 Y5 X$ {- }  c
  15.     PG6/QUADSPI_BK1_NCS     AF10  _2 x/ a' m7 a/ J
  16.     PB2/QUADSPI_CLK         AF97 R$ r3 p8 s. z$ X) B# _& y
  17.     PD11/QUADSPI_BK1_IO0    AF10( q+ Y/ }: L- S) {! u" S8 @, @; o; x! i
  18.     PD12/QUADSPI_BK1_IO1    AF10
    7 q7 ^6 I' Z7 N0 |1 p/ x; F! u
  19.     PF7/QUADSPI_BK1_IO2     AF9
    $ r( }$ p) a) U, A  s
  20.     PD13/QUADSPI_BK1_IO3    AF9
    ) X1 Y0 D6 x9 i( J
  21. */
    : l- S; a9 f0 \0 ^
  22. $ }* B5 ?! ^( }' e
  23. /* QSPI引脚和时钟相关配置宏定义 */
    # \1 r7 `* p" [+ c
  24. #if 0, f8 x8 W# Y+ M
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    % b( M3 `& j& b, P5 s
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()- K) q2 \. M$ a4 u# V
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    7 U' ?* i1 G/ c9 T' u* J* G
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()1 H% F; J- U$ y  Y" c
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    ; {# @' M  |; H. z! E4 u: Z
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()9 v. k* o/ e0 f
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    0 e& n& w; M8 s0 z3 V9 B  S
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()9 t5 z( J: [2 U3 p! u! u
  33. 6 Y/ n4 e' a* N3 O. O
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()5 K& S; d  ~* _! A1 T' d2 M
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    " Y; _: Q8 V" x3 `" [2 \# m
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    7 H. G% \0 W5 Z& e: V

  37. 3 h+ H, B! }" G, ?2 C
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    : B4 ~. e5 Y) ?/ R* N) O5 ~$ a
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    9 o: I% C0 d, [4 x) B9 V3 U
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    2 C% y0 Q0 G* _; @0 G  T8 I
  41. ! ?) d$ M- {' J  S4 b
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2! v8 U- D7 Y- O: N
  43. #define QSPI_CLK_GPIO_PORT              GPIOB$ L: K, l! o. v! W( d" D5 N$ F0 t
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI$ |; w1 x! o0 i

  45. $ d( l1 ]) g- F/ y
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_119 i7 U, |+ {) A2 n0 Y1 }! M- t
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD* q! V2 T' M. `! ^3 c! T  a
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI
    ( j; ^# B  T2 U( }0 T$ \

  49. ( h  U1 H& ^7 b5 v3 w, L9 V
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12; v, z! X& t2 g" n
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD
    1 k9 _8 `$ o  M/ s( E
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI0 F% l  s/ [2 b- _  C7 j& H' K
  53. " L" X1 f7 R+ a
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
      d( k2 P! q* F# v9 ]0 e, g, I
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF4 y4 O) t6 |! x& T1 L
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    % C0 W4 d. G  c( Y, o

  57.   X$ N* \  S% \* @
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13' }: G! f/ Z+ W2 d! H8 S7 V
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD  e1 ]+ D# \( F; Z* _$ [
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI6 {+ ]; \/ D% x. d- s' {  r
  61. #else
    " O$ r3 ?/ ~" x. D* ^/ Q2 e; o
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()3 I$ W+ g' ^/ G& ~' w. d1 r
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE(): B# k6 D0 S! E' L7 Y- l' \
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    - V+ X' j9 u2 }  }
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    , m& M. \% e; \/ f0 m( K0 I
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()2 q# P& v4 E+ `, X" P# l. T3 R
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    . w3 {$ Y# r) a& K( ]" C2 S
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()4 x3 w* S# x4 H
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    ; y0 I: V7 [! c0 ~* t5 I2 f
  70. + Y1 o3 w( y. L/ q; u
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()9 [# o" v1 D: b* i3 S
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    8 c& h% V7 a& w/ m7 f1 Q. N
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()2 x. i* V) b4 L. o/ C# T! Y& r" a
  74. - w# [4 `' O. U) X) k
  75. #define QSPI_CS_PIN                     GPIO_PIN_69 Z) J$ m; `2 d& r
  76. #define QSPI_CS_GPIO_PORT               GPIOG) W0 Z0 B; y& X. E
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    # ?# x  I( b# L
  78. : Z  o6 n# I& K1 u/ W, \* I2 E: l
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10; D9 _- j0 `. B' ~" [; e
  80. #define QSPI_CLK_GPIO_PORT              GPIOF, ]: X) Q  z3 v0 @8 r$ u
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI5 |! u: Q1 U: d$ _3 y  ?

  82. + \7 c9 U- J" B0 \* l
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8
    5 {9 U# o# B9 V* \' `9 o
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF
    ( r! D1 x2 H# s/ p2 i& k
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI8 f& f/ s7 h6 I4 P' X3 e7 V: D

  86. 0 v2 S3 @+ z- Z4 w' [, N
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    * D: |  [- |2 x2 \% }1 q5 K
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF/ N2 ]" i  S- K! `) y
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI/ F. j; i# z3 ?& E3 Z
  90. - u) i6 ~, f4 i/ k* b' m
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_79 \8 W5 S, _6 P$ e# t0 {; Y
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    ! ~3 j, E* D" q
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI  }& H0 x5 x6 A# P+ \! f0 z5 ?: J
  94. ' b  @8 O0 O$ O& v
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    6 K  P) I# g& V! q5 ?, }6 O9 D
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF
    ( i# _% f! K3 _% w: z
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI2 {" E5 n. ?, X, r3 t( }
  98. #endif
复制代码

( b% S( r5 T) m3 ~. E) D硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:" X$ N1 K, s  C% Y( V' a

, K0 e9 V9 D2 `8 n主要是下面这几个:
( Q! K# [5 w, g7 h+ i" B6 S' i' E3 i& {! C$ l" W1 Z
  1. #define QSPI_FLASH_MEM_ADDR         0x900000005 E( M) U5 ^* C) ?( H

  2. 9 G. u) {3 G$ a, T  r
  3. /* W25Q256JV基本信息 */( H0 i- y% e( q  ?- @
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/
    : r6 M5 ?6 ~* h; y
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */
    ' V* K+ a, k8 Q
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 *// M' w% o8 d9 w- S
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */* {- W# W2 N) K" y- v+ |
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    $ ]! M, w8 p8 q  N+ Y' X5 m$ g
  9. , H. A/ K. ^8 `: Z% `& r
  10. /* W25Q256JV相关命令 */
    % u( M% W4 C8 p
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */# g5 [  K/ A3 H! B3 S1 B
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */
    ' ~/ n& Q2 R% s# v7 m
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */. R* F0 R/ q4 x
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    % ^5 N! V. t( B; }6 v0 z: b- g
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */
    # I( p% K0 m0 `( ]7 ?/ Z% ]
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */
    2 j2 F; g$ }4 O0 o5 i
  17. 3 e6 `, N* Z/ u
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    ! j0 U, Q* M1 A  V; E# I
  19. ! U* u  t8 w2 u$ d# D* R
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

0 Q4 M$ @9 R1 ^8 _, {/ K80.5 QSPI Flash的MDK下载算法使用方法
5 j% h0 ^7 n$ E3 k9 z& l' g3 G编译本章教程配套的例子,生成的算法文件位于此路径下:5 B- U, e! k% S" m3 }

* K6 a/ |# L# `# U( ]6 |/ {5 P$ s- f
ed1f9465c60f92522e906639b76b29f3.png

! G; n' D+ g" l% Y4 z8 i3 h6 g# O* P/ A) X  h$ [
80.5.1 下载算法存放位置
5 g  o2 r) A. x! ~生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:
/ W$ r) ^/ M+ P4 J) d! B4 w; G/ q6 J; A: X6 ~$ j
  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。# o1 b( p! y! P! ~) H/ m
  第2种:MDK的安装目录 \ARM\Flash里面。
& Z& y% p# l( L- E8 g+ D! O& c0 n) O2 a, H" [
9d5d65cb90fb28ad36464cb1b8368759.png

" N& R) o/ d/ Y, W" i7 D7 j
1 r4 }& t6 C1 |80.5.2 下载配置
& h* b; [& b6 S$ P$ m" ~) X+ {注意这里一定要够大,否则会提示算法文件无法加载:
+ B1 q& E$ \' o& @: d0 a5 T* R, \2 z& g- Z
8eefa57c2d7f128449000c2fb4e788d9.png

( M# E; q8 m5 }
: A1 \) z7 E3 \. s我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。% ?' X" k& c" G9 h7 z& O

/ C4 {" Y4 y$ A如果要下载程序到QSPI Flash里面,需要做如下配置:
/ j) N0 k) X: i; h8 _1 o
0 I- {: M5 i- @& I5 S
caa01b15ccf9a9700690d2ad1bdedecd.png
6 J9 G# ?9 S6 A4 w

* O7 e; v8 f' ]: S- j" j1 \80.5.3 调试配置
6 q+ {# ?  V) V$ {. U
注意这里一定要够大,否则会提示算法文件无法加载:
! q6 m+ Y( i8 F5 P+ |4 H: Q  [- [( w+ B! s* c3 Z
f23244d646498fc30aa5f1301a327430.png

8 @3 o8 e! v5 g- E+ P+ m7 |+ |& E2 B1 V
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。
& T3 l7 B3 `. u( _. o* {; E9 h' B3 ^% W
如果要做调试下载,需要做如下配置:
' {" M3 N  A# ]. a' |4 B8 \
# ?3 {  Q5 h" C6 J! W. b& Q; n
84ee4945e5faabd776fedd6f8f7d96d2.png
/ C0 u! Z7 T/ s/ {- g
$ S) H+ _4 k( D
80.5.4 验证算法文件是否可以正常使用2 A. f! U9 m+ I+ X/ Y
为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。
. S, V2 ?/ _2 ?$ e% n: f2 I3 [$ n9 P4 ~" c' _" W! X* [
80.6 实验例程说明

# M5 p) B; l; C6 V6 [1 }2 n8 g本章配套例子:V7-060_QSPI Flash的MDK下载算法制作。
# B+ b# Y3 q- G3 @3 l
& U, Y9 C3 V& [9 ?: @* Z- b- ]! P+ V编译后,算法文件会存到此路径下:; X* S0 D' k. ~0 w1 H4 b8 N

1 k  s: e( d2 F5 [

4 G  K' K3 c; @, O. |* {" S, L1 i( r
9 I! B  y+ u( y" W% m80.7 总结$ g9 g5 i8 C1 Q4 T% j1 }3 |
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。  C3 |5 [: P7 e& y$ L
% X: ^4 [3 S3 [  E  }1 d& L# |, H
3 k0 I$ }& F% ]! `+ R
4 s9 e/ I1 a1 p+ }6 w  J

) F& x7 R: I) c3 Y; D: ^0 P5 ^
  e& K0 s/ \0 |( V; Z/ i1 M( U
b69a199f1b4a12d880e64c7ba134070c.png
赞 收藏 评论0 发布时间:2021-12-19 16:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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