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

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

[复制链接]
STMCU小助手 发布时间:2021-11-4 22:11
80.1 初学者重要提示# b" Z0 ]" H8 Z# l# X
  QSPI Flash的相关知识点可以看第78章和79章。
. M2 K; u; c0 r$ B; s9 v% O2 @  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。" P* i. a  V# z3 O  j9 h
80.2 MDK下载算法基础知识; W4 s& i# {3 }4 p# R( j
Flash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。" o$ h/ t" m8 R: C
' y1 W8 N! l4 Y7 R
80.2.1 程序能够通过下载算法下载到芯片的核心思想
3 p6 [- {1 P7 @9 t3 P认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。& m. x& D- f; w
) c- s. N/ ^* ?- S2 J) S# N
80.2.2 算法程序中擦除操作执行流程- b/ I% f+ o% c% T5 t2 P- W
擦除操作大致流程:
* k8 \7 U; d  X. y2 u5 p; |* g3 ~, l# S
d4b4af6b23f5f1d5ee3f3387ccb8fbdc.png

* f8 L# @: A2 Q. b2 b& ?# q3 W' |. z3 Z0 E
  加载算法到芯片RAM。: i+ S" s0 u$ t
  执行初始化函数Init。( N6 _0 a; _7 d1 ]5 y) U! ^, N: m
  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。  O- n8 c+ ~% _/ l% s
  执行Uinit函数。
  e$ R/ w% N7 o+ i  操作完毕。- l" T% r% n0 p) D. C" E, N, c) {
80.2.3 算法程序中编程操作执行流程
. B. q: W. a! x; n) b: w编程操作大致流程:
. Y9 T/ V+ ]3 _+ G3 O# c/ v' t
" n' T& v. _% Z7 {7 `8 i
d3f601364bb033d42e275ad686c03726.png
+ S# f' Y! k& Q4 X; w4 W( Y0 t( v+ U
/ r7 ^& R( w( z+ W0 P
  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。
% D& w9 x/ q" @$ i0 t0 D7 D, S  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
6 o- H5 e- c* v+ E* N4 m  加载算法到RAM。- C- j- O. B% }1 @
  执行Init函数。
3 q" U# L" T, j/ N+ ~0 ]# |  加载用户到RAM缓冲。
% R: J* A) w0 b& c0 V* a1 T  执行Program Page页编程函数。3 f0 M7 ~) l( I- _; D
  执行Uninit函数。
( b- w% T: t; G/ n  操作完毕。+ K$ \8 _( [' G$ q7 N
80.2.4 算法程序中校验操作执行流程
5 z, ?5 \- D5 `2 r校验操作大致流程:6 S' }0 G' j" j- Z+ C* I

9 p; u  K( x; |) O8 K* V, W
c7b2f373d946bc7d166ab1c1ba9b67e4.png
# M* |& D) @5 M" p. d/ |* d

6 _* o' W- d; R8 G/ f: K1 N  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。0 @5 l, U/ K9 `8 h1 F  n
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:: u5 K6 q! r7 W
  加载算法到RAM。+ T" y& E9 R7 x9 L: d0 n
  执行Init函数。& G$ H7 ^; d0 T2 |; i9 y0 K; V& N
  查看校验算法是否存在$ n& w$ B* P$ }: ], f
  如果有,加载应用程序到RAM并执行校验。
# O5 P5 M9 [! [6 K/ m9 {+ m5 C( R, o  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。
6 d, T0 N$ k$ |% F  执行Uninit函数。
% M& E2 y5 B! A& x3 J- f, E  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
% r4 }* y- h9 g. Y5 E" j3 H; y, G  执行RecoverySupportStop,恢复支持停止。
. |" r( i6 Y* x  r4 I  执行DebugCoreStop,调试内核停止。
$ h  x8 g4 T8 k  运行应用:
; Y# o8 M2 L2 I: L* |/ ]  执行失败。8 \1 j# g& P4 ^
  执行成功,再执行硬件复位。
/ F: x0 `2 c  w5 M  操作完毕,停止调试端口。
* K. o& L3 [% s2 _  K80.3 创建MDK下载算法通用流程' Q$ f& b- ?- u' ]- x
下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
1 N* Q3 L: `: e; X% n1 P( M4 H$ m5 }/ {& Z& R, s- f6 y2 c
80.3.1 第1步,使用MDK提供好的程序模板
5 n. m1 K2 [3 q位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。
& l2 p$ g% N: h3 E, p
! H7 W% P1 b/ V' U效果如下:+ a$ o$ J  c* x5 L, N
2 r3 E. X6 a% j7 K- F
8e08d948d34b38592e3699136725ade1.png
8 I9 S0 ]+ A/ y# }7 y
; Y) {# p) t$ q+ z; N. G& I3 {8 G8 W
80.3.2 第2步,修改工程名
! |; I; r& ?' h, @MDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。
  d8 n; I$ [8 Q# K( a  P4 P- k/ x1 v( c6 Z9 Q3 [
80.3.3 第3步,修改使用的器件6 x# q1 h; X% _% [7 m; U, n, n
在MDK的Option选项里面设置使用的器件。
# S9 j1 J$ ^3 n& X7 K0 u& U3 A" h* u  x* [- ?$ S# v
347d6def457f6e101f1c2555f9156cda.png

: d5 [1 x" e2 V. @9 c! I, b
, ~4 \  P& T+ W0 x6 x3 o) ]1 d% s80.3.4 第4步,修改输出算法文件的名字
) ^' h& w1 w7 h6 N5 z/ s这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。
6 h2 ^; Y5 W4 H6 S/ M6 ^
, R! c' h/ S4 E5 X% }
07d754db49256bd0b73f4538a328f3cd.png

9 P0 r9 H- J8 x9 }- D5 r% ?! T& R- ^, O: j
注:MDK这里设置的名字与下面位置识别出来的算法名无关:0 n& D' T( _& y7 E, k

9 J2 q8 [3 }' b* s* u+ s
a00b0ff99a0a691f19dfc2565fde811a.png

! M8 {* ]' F" v3 Z: r3 ~! X! c+ I3 X
这个名字是在FlashDev.c里面定义的。7 o& l+ b! l: X

6 W8 B# \+ S3 q+ Q80.3.5 第5步,修改编程算法文件FlashPrg.c
7 }8 {8 H7 u- n0 R% w模板工程里面仅提供了接口函数,内容需要用户自己填。+ @5 n- e2 T$ {, q% I- p0 r0 ~% d. a
1 P& E& q: h+ W2 M
  1. /* $ D8 A4 c2 H+ O5 T7 P( M/ N4 `
  2.    Mandatory Flash Programming Functions (Called by FlashOS):
    1 n; n$ s' x  `( D
  3.                 int Init        (unsigned long adr,   // Initialize Flash
    - s* w: K1 O# k
  4.                                  unsigned long clk,8 z5 T6 N  V0 P
  5.                                  unsigned long fnc);0 a, d* ]& u# k5 n
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash/ k7 U8 M8 Q4 P  J. \8 v( B
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function
    / k8 y# h" {4 g9 t( U
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function
    8 @  J: N  p$ Z% a2 O
  9.                                  unsigned long sz,6 Z& n" [- V2 k8 N
  10.                                  unsigned char *buf);
    0 m" f9 i! K) i  u1 P' i2 K! o

  11. 3 {8 O+ ]/ L7 o+ `6 G8 p# i; X" @
  12.    Optional  Flash Programming Functions (Called by FlashOS):
    / I$ X0 ?- F6 n+ j; p5 E+ L
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check1 L, I$ p8 V# X  ^9 K4 |5 I* U+ p7 U
  14.                                  unsigned long sz,4 P- M+ h+ q6 q, ^. Z  B/ `7 z" N
  15.                                  unsigned char pat);
    " V5 C& n0 z6 R: G/ F
  16.                 int EraseChip   (void);               // Erase complete Device
    0 V! ]4 l' ^. c8 Y& l- [9 M
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function1 ~" N! C% Y* I
  18.                                  unsigned long sz,8 j) x! s9 Z2 O2 \/ U/ c3 v+ G
  19.                                  unsigned char *buf);3 d! e1 I9 R# {2 r4 H. Q( z; S  ]

  20. 0 H3 ?8 ^9 O$ F  u4 a1 T9 S
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space
    1 V6 A: R0 K( ]: N+ u* |
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space
    6 @& ?% A7 }6 b6 N* ?
  23.        - if EraseChip is not provided than EraseSector for all sectors is called7 z" V4 {: h' D
  24. */  V4 ?( O; [9 i; L9 Y/ [

  25. + K8 B0 p% z5 s  F- k' i8 @" v6 M9 ?
  26. /*
      p1 r6 W5 w0 C1 q
  27. *  Initialize Flash Programming Functions
    , E3 ?, ~/ V$ J$ F: m! h
  28. *    Parameter:      adr:  Device Base Address
    6 J! I/ @2 d- x! _5 _
  29. *                    clk:  Clock Frequency (Hz)
    ( P: E2 v- c. c& A; D, {- L
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    ( J# c: \7 C$ S9 }- F/ F3 }
  31. *    Return Value:   0 - OK,  1 - Failed: k3 P7 @, `% |: Q- n
  32. */  Q9 [* [4 b$ f$ `
  33. 9 f/ E/ p3 I2 [9 A" X. P: S
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {5 H+ w6 @6 L  h% ]$ o( K# p

  35. ) C* g3 B6 q' @, k2 q$ J/ l
  36.   /* Add your Code */
    & i# E& E+ T& f; M& i
  37.   return (0);                                  // Finished without Errors( |4 L: r" k# K0 x" e
  38. }5 {4 [1 t' z6 f! ^0 q
  39. ! k9 z. O& J4 N0 I# ~

  40. . P7 m+ I) d- a6 I% d9 Z; |
  41. /*
    / n1 {0 v9 S% e1 F
  42. *  De-Initialize Flash Programming Functions  H+ a, Q0 E- B3 \* ^. P% v) u/ S, ]# Y
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    9 |  b' t- D7 _
  44. *    Return Value:   0 - OK,  1 - Failed/ ]; d7 f% E1 x4 l
  45. */
    $ M$ ]4 \" s) O; \7 d

  46. 6 V6 s: m8 u* {8 f  g8 z
  47. int UnInit (unsigned long fnc) {
    ; [6 g1 @7 I. d% T6 q
  48. 1 r" E6 L! R7 s3 x+ O
  49.   /* Add your Code */9 q) h% z. M; i1 m- P. ^* L
  50.   return (0);                                  // Finished without Errors. E9 l9 s  b1 g2 E% c
  51. }
    9 a3 R) G4 A! U- ~
  52. - A! W5 P8 Y; L2 P7 R) M
  53. 9 B2 O3 u+ @! z& U# W! N( \
  54. /*! C* v' @$ |* `7 }" A" q
  55. *  Erase complete Flash Memory
    5 J% v( J1 q9 k
  56. *    Return Value:   0 - OK,  1 - Failed. \/ z% e6 c. ~
  57. */
    % e! R; ?/ a5 L: c5 C) [% g
  58. ; D/ u" p# C5 s/ k4 [9 j7 {$ F
  59. int EraseChip (void) {7 d5 M8 ^$ m; M0 ^" ?8 [! g
  60. # x9 v4 s( E# v3 [  D' C, v9 z
  61.   /* Add your Code */
    ( m  x) g3 o( `" [% t
  62.   return (0);                                  // Finished without Errors
    " k" v9 Z- D3 C7 ^6 [3 g& k  u/ W
  63. }
    % ?- D" z' y. X2 D5 V5 X9 f
  64. ; Z* h- v* |. C3 ?3 ~- Y" p

  65.   K/ x! T. Y4 C: Q9 v
  66. /*) g( n# ~9 x1 [
  67. *  Erase Sector in Flash Memory
    * q& k1 R* p5 T+ \" _6 l
  68. *    Parameter:      adr:  Sector Address; R' d4 q$ C. V$ K0 g
  69. *    Return Value:   0 - OK,  1 - Failed1 l  u$ q- C8 S6 _/ Z! I  _
  70. */# l  ?/ v* K% J
  71. % S( H' b$ n! z: [1 v
  72. int EraseSector (unsigned long adr) {
    4 V4 y3 R4 I, \4 ~# t

  73. 8 H3 G: T0 b- S, m. c2 U
  74.   /* Add your Code */
    8 f* y9 }7 ?3 e9 k
  75.   return (0);                                  // Finished without Errors
    , f6 C9 ~6 l- R, J$ _
  76. }/ Y+ W/ g3 }2 l% i8 [
  77. / y6 n- Z; L, F, v. v) ?5 @

  78. - b8 Q7 a' S, G; d1 n) J1 T
  79. /*
    % I- d" N; T) |# \% _/ }
  80. *  Program Page in Flash Memory5 [* x# K% H' c* T0 h$ b$ R3 u  j
  81. *    Parameter:      adr:  Page Start Address
    : M. E9 w) k( w, q/ G
  82. *                    sz:   Page Size
      S3 v4 k3 ^7 b  M* P* w5 I, ^
  83. *                    buf:  Page Data
    ( e$ E, x3 H/ ^$ x
  84. *    Return Value:   0 - OK,  1 - Failed; C: D2 V2 E4 W: \
  85. */; h$ \: o( J  r  z
  86. + @9 X& T( V# w& u  r0 f, R
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
    : K& Y) d; k6 x# S: ?/ j

  88. 2 @" W' H+ u! f6 U
  89.   /* Add your Code */' x) R' J9 D- C: `2 J, P9 z4 i
  90.   return (0);                                  // Finished without Errors
    / G. x1 F+ b! L, v
  91. }
复制代码
* `3 R2 C2 W) A! P* f8 A* x/ j
80.3.6 第6步,修改配置文件FlashDev.c; ~0 k" \  m- O" K! g$ n
模板工程里面提供简单的配置说明:
: `3 R- a+ ^$ c# Y6 o% A2 j5 m/ X* U( ]' O" e! {
  1. struct FlashDevice const FlashDevice  =  {% P3 G. G: i3 {! w
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!
    " j) ~8 Z1 l& @2 n) ^
  3.    "New Device 256kB Flash",   // Device Name
    - ?6 [3 a; ~9 `4 _) R- A5 D
  4.    ONCHIP,                     // Device Type
    6 G9 x; o, t* p; h- ~! H
  5.    0x00000000,                 // Device Start Address" p; f# C) L: J6 m9 g
  6.    0x00040000,                 // Device Size in Bytes (256kB)
    9 c; g8 }5 T9 W0 [) X- I
  7.    1024,                       // Programming Page Size
    " \" R3 V7 L: k9 j& ?
  8.    0,                          // Reserved, must be 0: i8 z0 H5 l+ V  D+ O! L% r
  9.    0xFF,                       // Initial Content of Erased Memory
    * A/ e% C5 i" V7 Q
  10.    100,                        // Program Page Timeout 100 mSec8 t. Z0 P0 o! A, g, U1 k. c! `6 Z6 e' k! U
  11.    3000,                       // Erase Sector Timeout 3000 mSec5 x( U& B7 T" J7 [, o+ _* R

  12. 1 x( H5 o  T6 J$ H$ \( F& F2 D
  13. // Specify Size and Address of Sectors
    ' t2 I3 e- a7 `0 Q! d
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)0 J) G% b+ k3 Y; G  q: b* o% X$ _
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors) - a, k/ k7 m1 V5 z& C
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)/ j2 B+ @' N( x/ f( x
  17.    SECTOR_END+ a" M$ t* E. v) W, F+ C
  18. };
复制代码

5 W6 r# {, ]4 w7 w* [1 l注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。& D! D# r+ C9 Y) _! J
% [/ O( G) h# A5 E! Z; G, s
80.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关3 Z: L8 ^, B# y0 @8 O' v
C和汇编的配置都勾选上:
3 |4 ]4 O9 {4 @# h
1 B% n3 D" r6 D! V7 h: k0 y
1d27a7314fc6c472937ad43852722b6b.png
* h& Q& T) j: s3 s' }% v% k' F5 c

# K! Z+ k' k1 S7 \' d, B汇编:& t6 [; x9 I; x5 N& k2 @
9 y. G  h8 U+ l8 M1 C5 G
bbea604970c4ef63c77f10e553e28ea6.png

1 i0 x' a3 b6 {8 d3 [
* B4 O" [3 D7 O$ j% [1 [, h. C6 n* X如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:+ {- v4 ]3 B9 f1 m* o. S( D& c

4 i' E' g5 Z" I8 s4 q(1)加载以响应运行事件。9 ]4 x4 y6 J- C+ U
9 v1 Q5 q9 w2 i! m! V, F
(2)在不同情况下使用其他例程的不同组合加载到内存中。
7 a# U3 U* x9 P7 S5 M2 R1 t. R/ k) g# J; l  x! l& O
(3)在执行期间映射到不同的地址。3 y1 t1 r0 P# G" L( ^: I
! n9 d  O0 X+ x
使用Read-Write position independence同理,表示的可读可写数据段。
& C& S+ @# T: S% x" W/ @3 c) L0 s. r2 n8 g/ {
80.3.8 第8步,将程序可执行文件axf修改为flm格式
9 g/ K% g5 |: Y3 z通过下面的命令就可以将生成的axf可执行文件修改为flm。) a) }5 k9 E- v8 M
  d- X' K6 x" |: t9 f
6b504444979e9fa3a3477c6284abca8b.png
% q3 B/ z2 g3 f! z2 R

3 I+ h( K; l$ n80.3.9 第9步,分散加载设置6 O4 v3 \, Y3 \* `
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。
3 {" z9 a5 H& Z
& n: w2 C5 M' J
e8e79854b204b618bbda32af92f28bf2.png
( W; }/ g2 N# q

& n* q% b% L3 o% z分散加载文件中的内容如下:
2 s9 E1 R4 @4 f" u$ D4 x- {) h) O7 h' o) i: x
  1. ; Linker Control File (scatter-loading)5 _1 i( b' i# u( H8 P5 y) d6 V& _
  2. ;( [' r: [0 a! r: z9 r5 c3 x* H
  3. , p6 w" x- |, ]" W2 e  ^' l
  4. PRG 0 PI               ; Programming Functions+ B+ e0 V6 l  r% ~" G  _% q* C5 U
  5. {- _$ Z+ ~! F+ H2 t% P
  6.   PrgCode +0           ; Code- X; D4 C1 p# u/ M. X/ e2 D0 b) V. K
  7.   {
    + i. e4 L# m3 ~) T
  8.     * (+RO)
    1 K/ G! a3 ~) E& z6 m8 A% Y
  9.   }
    6 a5 d0 a9 ~; E! v" k
  10.   PrgData +0           ; Data
    ; D% n% r$ H  J1 p
  11.   {2 a, {  c2 T( U) a; \
  12.     * (+RW,+ZI)( H: E( q6 I: ^" [6 E" _! \
  13.   }
    ( [4 Q. [3 N7 _! [1 g* [
  14. }
    - l+ k% c1 m( `. x/ Q
  15. 3 J2 _" Z- q; r, d7 G
  16. DSCR +0                ; Device Description9 Q" ?6 ?9 o( ^, o1 K
  17. {' l% r+ K9 e, [" c9 ~& Y
  18.   DevDscr +0
    - u# [0 }( x8 K! l" T0 h" \
  19.   {
    , v" P) O& O: j  a4 z
  20.     FlashDev.o
    ' U' `6 ~( y+ d
  21.   }. R! s5 w8 N4 \+ J
  22. }
复制代码
7 \7 E' b8 y- }/ K2 L6 {! @9 ^
--diag_suppress L6305用于屏蔽L6503类型警告信息。2 m+ t* d4 Z, @. w; W& S

! B$ b6 l  x; [" K特别注意,设置了分散加载后,此处的配置就不再起作用了:
4 N8 h- p& V/ H4 J0 ~! i' A  Q1 X
+ q- v; e/ a* {
b249eb40e5562b04ac3eb05ec0219c1d.png

2 b8 H6 H# h! K& L2 G% w" m) @1 O- z7 R  @- k' s* E3 B
80.4 QSPI Flash的MDK下载算法制作
) H. ^, x0 i& x" G+ a3 X% h下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。: n, P$ j* Y4 C& s: ]! h
1 z1 O% y5 T* Y- z1 \2 M
80.4.1 第1步,制作前重要提示) l6 m* a" I" b3 y& U3 k
这两点非常重要:" ]! {, u* O2 Z9 t# W8 D
" e5 J. D+ S" J" X- t- j+ O
  程序里面不要开启任何中断,全部查询方式。
4 r! \. n: O) D6 c/ \: `  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
# r! c- l) g% `5 K) s7 f: i80.4.2 第2步,准备一个工程模板
* ?7 ^' u0 `# s 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。' [' h# S5 A4 |7 H) P' w1 _: u

; [; r2 |& j; d
a91aaf84a25d730b77273624d2b502f6.png
% Q4 z& G, ~$ L7 o* p) \8 ~

+ y* |7 i  S) _80.4.3 第3步,修改HAL库
$ h* p, |, Q2 o这一步比较重要,主要修改了以下三个文件:1 O" _' P- s1 Y$ `( l
; O+ _2 u- U/ v0 J& B" }
4471afac6bbc201866d0d9a8ea7d876b.png
% F* I: [$ p3 ^& w3 x1 N

4 P+ q" s9 P+ b0 j主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。& A+ ^% k' h" l6 S( I

- u) q) J) \" R( ~& Y80.4.4 第4步,时钟初始化
, ~% i" w5 |5 T: o7 R我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
" I, _' g* F- i0 y+ l6 e/ V" _: o7 L4 e2 s* h. O; l. c3 N0 w* S9 w
  1. /*
    : l; V" C+ e2 f6 t/ N8 z# j4 R
  2. *********************************************************************************************************6 ]& o& ]5 u+ w9 b* c# [
  3. *    函 数 名: HAL_InitTick. L. `5 X& F' n9 K% i
  4. *    功能说明: 重定向,不使用5 X& }) Y' x6 \( ]
  5. *    形    参: TickPriority- {" ~& m+ ]9 b. [3 G
  6. *    返 回 值: 无
    ) }: a( a$ J3 |0 b- L
  7. *********************************************************************************************************( u1 }1 I1 R1 K( _6 X5 J! N
  8. */
    * D9 `# |( f! E% x3 B
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    1 Z0 N! i" b2 ?
  10. {
    2 ]+ a, d7 ]% k* W* o7 L' `
  11.     return HAL_OK;- m& G- I; D) p9 z, m2 {
  12. }
复制代码
2 V4 ~& ^$ H' p5 F
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
- G& H+ T" e  c5 L; y, A# b& C. b, i/ A6 H. R3 o* [$ Z8 p
  1. #if !defined  (HSE_VALUE)
    % k  A6 {/ j1 w' L
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */2 b/ t8 ~) V( [- _/ v& N( r0 n
  3. #endif /* HSE_VALUE */
复制代码

: V$ Z  _4 E# a/ L最后修改PLL:
% H) E. L, w# f+ m6 w3 V0 F, e6 I
2 f* m3 j6 `3 g; d6 ]
  1. /*
      w' M+ F* g8 k1 [, X5 z
  2. *********************************************************************************************************! |- i; H& n/ \2 O
  3. *    函 数 名: SystemClock_Config
    / ?9 a6 `' L0 B: d. a5 O- P
  4. *    功能说明: 初始化系统时钟' H6 v8 z+ K* k0 G
  5. *                System Clock source            = PLL (HSE)
    & R2 k. q5 R( T; D
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    2 H8 L% y7 O  b; U0 g
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    ( c3 u) f6 w: G, V4 K+ i2 r, s; Q
  8. *                AHB Prescaler                  = 2
    * e& t& D. O6 o
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    5 e' A) s5 y1 S& w
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)- q9 I/ v- f8 ^7 t% B6 D
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)1 u7 _: |7 U  Q4 ^9 n/ Q; G8 {
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)4 I$ {4 F( y$ d) V! `
  13. *                HSE Frequency(Hz)              = 25000000: y9 _- a& b- g! C
  14. *               PLL_M                          = 55 E2 k4 v0 e* w% B9 Y4 c2 a" ^, N
  15. *                PLL_N                          = 1600 l, D) D2 `2 X$ f
  16. *                PLL_P                          = 2
    5 r! U0 }/ f; ~+ K1 p, z; h
  17. *                PLL_Q                          = 4
    + j2 g5 S( f, V1 `1 k! w
  18. *                PLL_R                          = 2$ E2 H4 n0 J' V- k4 i) h
  19. *                VDD(V)                         = 3.3
    9 ~+ C7 N/ a* B
  20. *                Flash Latency(WS)              = 4
    & W3 ^' n; p; ], }" P/ V
  21. *    形    参: 无/ x: y" m' a5 ?. c! B
  22. *    返 回 值: 1 表示失败,0 表示成功
    $ {/ {1 U# A! v- A
  23. *********************************************************************************************************% H& g' d9 J1 ~2 ?: {- O" r
  24. */
    * `5 @4 j: D" |: ?
  25. int SystemClock_Config(void)) ]9 K6 q( [( z0 _
  26. {2 I; M( c+ A$ k0 w5 K, s  Q
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    ; c5 e9 J1 r3 {- t
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};- i8 o! P! \  A7 s
  29.     HAL_StatusTypeDef ret = HAL_OK;) d& W6 r6 ~5 W

  30. ' u7 Y: ^3 D7 w- ~3 b+ v
  31.     /* 锁住SCU(Supply configuration update) */4 q9 l* e1 A; o: D$ U0 h
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);3 n; f' Z7 R) `1 g" K
  33. : z! t6 M+ E4 S6 y4 |; N
  34.     /* 2 L2 U4 g7 r/ V  [1 |5 c
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    0 _( H" x9 A( U7 x( e
  36.          详情看参考手册的Table 12的表格。2 s  _4 H/ ]6 v
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    : r. \6 r7 x' [" O3 a
  38.     */7 K6 a6 E7 ~4 ]* i: [$ m
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);' R' Q8 L; n3 E: h5 }

  40. ! a; }, y; r0 W, |7 I4 E
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    ) l. U9 f$ z* Q/ n+ Z& J! b
  42. ) e; E# G) o! g. C- U  [- |: ]" t  Y  l
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    2 }8 Q* q6 Z5 g# R
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    9 h* I( b; p  l# y* n
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    ; s: ~1 T" T0 q8 b% _
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;5 K! H0 `+ A2 q
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    ; R2 r. C: n9 N8 F6 ~, o$ I
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    * K6 W* |8 T0 \. I* t% Y
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    5 b9 D2 }$ H( T" c7 }

  50. & v) q8 T+ z6 q" T- ^* Y& s
  51.     RCC_OscInitStruct.PLL.PLLM = 5;& a7 V: G8 z- J! P$ y- _5 K
  52.     RCC_OscInitStruct.PLL.PLLN = 160;8 l! |- L$ O8 M
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    2 Y2 s( u- {) U  V/ P" l
  54.     RCC_OscInitStruct.PLL.PLLR = 2;0 _8 x$ ]+ u9 B: k# L1 T# S$ |
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    " }+ e, q! c- ?; J2 ?4 j& a8 J
  56. , W" q3 Q) g  A
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;0 v2 g- V+ M, N/ E
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    6 Y9 ?& e* y" V8 C4 q# V% a( @6 @
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    + o9 z2 q0 W4 m! N8 x! n
  60.     if(ret != HAL_OK)4 O: N7 {  h! f- @+ o6 Z
  61.     {; C! S- C+ c$ l9 h0 T
  62.         return 1;        , K7 X. T: T7 d; f4 R  M* D
  63.     }
    ; ^" r7 y2 ]3 z& W$ a) Q6 e4 j

  64. 3 ~1 U; q. D( k, @  C. ]
  65.     /* 7 N" F9 p8 X5 H
  66.        选择PLL的输出作为系统时钟/ P. _9 @. e9 F0 f
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟% S4 }, M1 T! a, c" j
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    1 ~% w* E( H5 k
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线. y0 [4 d, f6 R( d# @7 W; w
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    ) _( Q0 f* z, v3 r% [5 b
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    . }- [9 g4 l' q1 d# G' g- r
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     , x/ Z% ]/ m9 z+ t
  73.     */7 v+ ^. F0 `7 I  p6 v# k, M, R
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \7 ?% g, ~- n7 e9 ^
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    . b; f+ \% H- D& F4 V# j3 V

  76. 0 `/ Y! o" K! z4 Q, E
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    $ d3 V6 O* ]4 P5 c
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;9 j9 n1 E+ Q  u" b6 b
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;; ?3 q' j2 E' U. A' Q
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  / t4 n8 P+ }/ V3 @
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    % r' `% b' Q, L, d
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    1 k, M' r! s" z+ k. z
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; ; P1 a" Y  e# j2 }

  84. # |: P2 v# s. }! O/ m' V9 i
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */$ }) @% X/ V! j( z
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);! h4 Q% y/ L) v. B6 _: \
  87.     if(ret != HAL_OK)
    0 B8 G8 M; ?# D. R( Y4 @) b
  88.     {" R& Z/ |! W9 a* M- Z* O* T
  89.         return 1;3 r  M' {% S  f% L  H- ?) f6 H* K
  90.     }5 j! J( ]6 _/ d6 D4 {' z' Z# _& }

  91. * n- |/ e. b+ Z3 u) [# _" |2 s6 [2 y
  92.     /*
    . G! e* n7 i8 R( e- X
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 . ^& s8 c* d' A9 A! o
  94.       (1)使能CSI clock
    0 j9 C. o! b+ n( ~: i
  95.       (2)使能SYSCFG clock
    # g  y' x% s  C1 j# Z" N  \8 Y2 f/ y. P
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0" c" ^. b6 t3 i
  97.     */: U- g8 A* E( q. F
  98.     __HAL_RCC_CSI_ENABLE() ;
    % h/ f( d7 Y2 z& L- O" d. a

  99. ' |3 _* H) |5 y: h
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;& F( s  q) n& z+ l/ e
  101. * g! w; v) a" d+ b7 f' M& `  @% K
  102.     HAL_EnableCompensationCell();
      H8 X0 U5 x; K2 s4 A
  103. 9 }. I" o) Z+ b! C, r3 N
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    % B; T; V/ H5 b! w5 S
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();+ P1 N4 l+ P1 ]9 {1 {) E
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();0 i  }6 i) _% k8 p4 |: U

  107. 4 n( ^% Z' J6 E/ D: |. R, l
  108.     return 0;' u; B! @& U9 Z9 z% P, I
  109. }
复制代码

6 J( c+ n) X- X' M' K80.4.5 第5步,配置文件FlashDev.c的实现+ w6 i0 z1 W% `4 r5 ]: R! N( k
配置如下:
' m5 Q: p5 ~& r+ R" i7 [2 g
& e8 E4 A  y7 q  s% {
  1. struct FlashDevice const FlashDevice  =  {
    & Z" k2 ^0 ~) H* _* C
  2.     FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */% U& q3 Q1 Y& P+ y$ q' q! l
  3.     "ARMFLY_STM32H7x_QSPI_W25Q256",   /* 算法名,添加算法到MDK安装目录会显示此名字 */
    ; Z# W2 v3 ~8 b( S" {$ w+ s
  4.     EXTSPI,                           /* 设备类型 */" v$ z0 o( C/ o+ e" k0 A
  5.     0x90000000,                       /* Flash起始地址 */
    7 j9 _7 \6 Q0 b! q, N
  6.     32 * 1024 * 1024,                 /* Flash大小,32MB */5 r3 t, O4 H# G
  7.     4 * 1024,                         /* 编程页大小 */
    / c- G# X. l  |" R" q% y
  8.     0,                                /* 保留,必须为0 */
    2 Q8 I: G: ]; m! ~5 ]3 H
  9.     0xFF,                             /* 擦除后的数值 */
    & _3 O7 s  i1 w- j/ s
  10.     1000,                             /* 页编程等待时间 */
    ( ?) R2 ~2 M4 v2 i
  11.     6000,                             /* 扇区擦除等待时间 */) r& p- `, f" r
  12.     64 * 1024, 0x000000,              /* 扇区大小,扇区地址 */# _* |+ }/ @" Y1 l! A
  13.     SECTOR_END   
    & p2 f/ T5 r1 U5 @6 j. U5 l* f
  14. };
复制代码
2 D: J3 r) a: h7 @: I) _( @
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:
* x8 L* e$ |; D2 i' V2 a; ?6 A! G/ w3 y7 T# H3 a
ffed7a581be4c5da388ee29fb67249b4.png
7 ^1 i7 |; v, ?- G. E2 H
8 V0 z% A. Z) Y  V
80.4.6 第6步,编程文件FlashPrg.c的实现; b! x, [" i, M+ O" O  w
下面将文件中实现的几个函数为大家做个说明:9 Z- H3 w, T7 B

- a5 o7 d3 a! G2 {  初始化函数Init
' B6 d. H* q; q: N  ]2 U; B3 h" u' Q
  1. /*2 o, s/ |$ P. M8 f
  2. *********************************************************************************************************
    4 B0 U, V' [# z/ Q3 ?" f% W" |
  3. *    函 数 名: Init
    8 N7 J9 u8 H" _) L
  4. *    功能说明: Flash编程初始化
    % I8 K5 \7 R1 u
  5. *    形    参: adr Flash基地址,芯片首地址。
    & S6 \( P8 i% [' A# x: k4 h% f
  6. *             clk 时钟频率
    5 ~- X  j+ D- V5 d6 {
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify: N8 p% ?7 ^$ t; B5 m& @4 K
  8. *    返 回 值: 0 表示成功, 1表示失败2 T' _9 ?( v/ q
  9. *********************************************************************************************************
    : e$ J/ h4 ~+ k3 U; m
  10. */
    & D% I0 g- N3 u1 d" ?4 l! j
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) , @; b9 i0 u7 F9 L* ?& d
  12. {, ?5 `0 `8 {8 b6 `
  13.     int result = 0;0 w5 R9 a4 K( d. A, q
  14. - x" @) z0 a0 q0 A) N9 Y6 o
  15.     /* 系统初始化 */
    % L! X; U1 V2 R
  16.     SystemInit();
    ) h! E. o8 G9 ]+ z% N# i4 d
  17. * T* `: J: A# J' {' a) b
  18.     /* 时钟初始化 */& y" R1 b+ F5 R
  19.     result = SystemClock_Config();$ s/ W' }" U' t) L3 O3 ^" S
  20.     if (result  != 0)
    6 L4 u/ v0 x+ ~4 i2 g
  21.     {& C  v! W4 q+ E; W
  22.         return 1;        
    ' t0 N: n2 {+ w1 S, O
  23.     }% r5 d7 `5 h. g* v9 s" S2 u
  24. ( i4 f5 V' _* v6 Y5 A. F
  25.     /* W25Q256初始化 */4 S/ m0 x4 _+ A1 f) q: N
  26.     result = bsp_InitQSPI_W25Q256();
    - N9 u1 F: M4 D# o, }/ z/ i( T" k
  27.     if (result != 0)
    ) F$ X: x  i9 E, f
  28.     {
    " c  K' w7 a( w# ?% I* h
  29.         return 1;
    * R0 B6 d7 b! V: i5 m0 i
  30.     }# x1 n" M) ~2 C2 U, ^

  31. 0 |, l$ m/ f5 l. C9 Q  y  p
  32.     /* 内存映射 */    ' L+ i7 C# C! F+ _
  33.     result = QSPI_MemoryMapped(); " K+ r7 n* s' B& _/ U4 g$ b) T2 I9 @
  34.     if (result != 0)
    ! ]+ }% m# D1 R* A5 U2 g" V/ R
  35.     {. s5 K. c: `/ M4 g, v4 @
  36.         return 1;
    & l7 E7 x9 c5 d' t" s
  37.     }
    9 r1 r4 C! h! O, r
  38.   @" x8 f3 K6 P( u
  39.     return 0;# Y6 Y1 @  @3 x: U, i" ~
  40. }
复制代码
# Y( H5 ~8 T$ m
初始化完毕后将其设置为内存映射模式。& e8 o7 w- M. k
4 x3 J0 |# X. R
  复位初始化函数Uinit& b$ w: x4 z% I! t( q
擦除,编程和校验函数后都会调用此函数。8 H, p( W$ i# k4 D! |* Y
/ b" n( f" x6 x/ z6 `  Z- X- n
  1. /*  ~" W' D( P! }, L7 Z3 F
  2. *********************************************************************************************************
    * ?% Z- K9 B: U4 R
  3. *    函 数 名: UnInit  ?( j! I; T7 l4 K
  4. *    功能说明: 复位初始化$ {; j+ V1 n; y2 @2 i/ Y
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    2 e% A) z9 B/ y) }) j% t
  6. *    返 回 值: 0 表示成功, 1表示失败8 D2 ^6 z% D* ?. d1 c' c: Y
  7. *********************************************************************************************************- O2 E8 a# W* h8 f& l
  8. */7 D5 A: b7 v# g  b3 K: R! S  p
  9. int UnInit (unsigned long fnc)
    * @) ?4 A$ d9 t1 s* c- R
  10. {
    ' s  Y# u! u+ g1 j( R" V) x
  11.     int result = 0;
    3 x1 `9 V# `2 H) m: k% j6 o6 b9 J- s
  12. 9 p7 V  T1 j. G( ?; B  a
  13.     /* W25Q256初始化 */
    ( G$ |* J1 N& K* S% r& p+ L
  14.     result = bsp_InitQSPI_W25Q256();
    ) k- g2 j: q- C4 m% V- S# y
  15.     if (result != 0)( P% Q5 K  {$ v  L  R3 Y
  16.     {
    7 J6 }5 }) K8 F$ ^: }
  17.         return 1;
    2 t$ {2 [/ {, B3 y- A$ Q6 B
  18.     }4 r4 L/ v/ f. p4 S0 N- N
  19. + T7 s! q/ ]$ v( @, f) l% t
  20.     /* 内存映射 */   
    8 W9 g* V0 b+ Y6 g; |# I
  21.     result = QSPI_MemoryMapped(); 8 t6 R" n1 I( q; |# ?' x$ }1 U* E
  22.     if (result != 0)
    ) f4 v% x3 q; P  Z5 \
  23.     {
    ! |$ D6 `7 u; l# C% {/ W
  24.         return 1;! u( }1 L6 P% r" Z- g
  25.     }: b# ?& w; L. U' ?9 n: ~, Q7 C7 d- a

  26. ; T. c6 c! R, }, s- l: X
  27.     return (0);
    # Q0 W( _; o5 K) n
  28. }
复制代码
1 ]: w' g9 S% `
复位初始化这里,直接将其设置为内存映射模式。
/ n0 i+ K3 g% p( o0 n1 w8 ^$ P" r( d$ d+ g& ^2 l  O
  整个芯片擦除函数EraseChip
4 P" p. Z$ |- K% g如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:
. r& C, n; S) f& j  k  `+ B' {& L
8 E+ R" |3 p7 C4 |
733d5af26119f64bff0261515013d3a6.png
# M; \6 z' S9 Z- `+ J6 g
8 {5 J9 q* s2 h) P* ~- ]2 o' t
实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间,比如32MB QSPI Flash整个芯片擦除需要300秒左右。
* M5 Y7 w- H& ]+ R9 w5 s/ D; [$ T9 l: Q  k' t
另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。
: D2 N$ j$ P8 z0 b) P5 R$ N# r  T5 e% E6 C: P# }
  1. /*) K- U2 [2 k: h  S9 q2 G
  2. *********************************************************************************************************; F" n9 s& Q1 g0 V' O
  3. *    函 数 名: UnInit3 ~! `" [5 B6 ~
  4. *    功能说明: 复位初始化, B3 D2 f# k) e/ y* l
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify- b- Q& l+ a! Q% P" @6 S
  6. *    返 回 值: 0 表示成功, 1表示失败3 w/ P7 H$ R. A7 Y# M' f8 n( e
  7. *********************************************************************************************************
    ( ?7 B0 x3 y: G( E' P
  8. */
    9 N! P* z! G" o9 n* g+ x
  9. int UnInit (unsigned long fnc) " B3 b$ p/ O" o' F) ~" [1 c: z
  10. {
    + Z/ ?  W7 D' D/ I% f
  11.     int result = 0;
    4 K4 A1 D! {! G' J
  12. 5 k/ U) B( F% j
  13.     /* W25Q256初始化 */9 P3 f6 r7 y. c9 M  r" }( J
  14.     result = bsp_InitQSPI_W25Q256();
    6 \  e; q) s8 W4 N0 h0 m, u  u: |
  15.     if (result != 0)/ Y  R. s1 s0 \; v& D- w- R
  16.     {
    # f# O$ ]- W) `" R& H$ V
  17.         return 1;, ^/ I/ p3 {6 p. j+ @5 e
  18.     }
    0 v5 ]- Y  K$ V

  19. 9 q2 I! {8 j1 u
  20.     /* 内存映射 */   
    9 e. u6 }. d: K9 c& o/ O. }5 E
  21.     result = QSPI_MemoryMapped(); 1 Z% @3 C6 r& j  i/ \9 f. u8 P$ W
  22.     if (result != 0)
    ' k: L' {1 W2 n- L9 j
  23.     {
    8 X. x# E* j' ~1 o
  24.         return 1;
    & q# s6 d- i) U" u/ M
  25.     }
    ( ~! e- C. \; S2 d5 ?% E; Y
  26. % R- }7 h/ G! Q5 O
  27.     return (0);: Z& l* [8 s$ ?2 k
  28. }
复制代码
- S5 C2 e9 W9 U  r5 W
  扇区擦除函数EraseSector
* l7 c2 A; v* M/ ]如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:
; C& s% Z- R" y- |& |' `9 j/ M. u8 ]" c
f623e662bf09233ea594f820f4c7952a.png
' `  ?+ |# j, E# c+ ^( D& G2 I
4 O4 ?  _# u2 Q5 `
  1. /*
    ; o  d& K+ d% j
  2. *********************************************************************************************************
    , s7 E6 E% k5 B# Z3 [; v$ R  q% l& f
  3. *    函 数 名: EraseSector
    : H0 c2 G  ]1 a. Q7 V- B& p
  4. *    功能说明: 扇区擦除$ _& {" w" a) P/ z7 W
  5. *    形    参: adr 擦除地址0 _. C. z7 p, W7 P
  6. *    返 回 值: 无
    ' \9 r' U# R, u6 K7 O9 c
  7. *********************************************************************************************************8 b3 \  |; f# \% z7 a; i0 _1 z9 J5 ]
  8. */! N; Y. E" O  ?
  9. int EraseSector (unsigned long adr)
    2 x3 K! C, z$ ?# N2 L2 Q8 O
  10. {   
    ) A% q0 t: ]$ n3 ^' \
  11.     int result = 0;
    ; }- I6 d6 j* b- k/ S
  12. % Z- _! _9 q4 f, n0 w: ~
  13.     /* 地址要在操作的芯片范围内 */
    $ x- M' N2 H  y3 D$ ]1 C, I$ {' X- n
  14.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
    $ L- z' d. @& s1 ]# x
  15.     {4 F% j% t8 Q$ K) Y
  16.         return 1;
    ) \$ l! L6 C! F
  17.     }
    3 b' d' O. B) H% H$ V+ \& z
  18. ( i; ?! u0 \9 E! r
  19.     adr -= QSPI_FLASH_MEM_ADDR;
    9 o; {# e& N4 K

  20. " Y9 n8 W" A; @0 A5 X
  21.     /* W25Q256初始化 */0 T0 j; `3 h/ l8 L- x; E& M
  22.     result = bsp_InitQSPI_W25Q256();4 W5 w7 O/ |5 i6 M
  23.     if (result != 0)
    - T2 F# j: {. v! }) N, x, Z
  24.     {9 s* K1 F; Q% m. B8 r
  25.         return 1;1 b# ?7 N1 K4 ^; I; F7 E
  26.     }7 X4 ?) R9 T$ X  o$ A2 j

  27. $ H2 f* I4 J' W8 @
  28.     /* 扇区擦除 */0 n5 U2 B. m. L. G0 y
  29.     result = QSPI_EraseSector(adr);  , T, L2 ~# [2 {4 I  T6 {% b9 _2 d
  30.     if (result != 0)$ c* U1 p9 O9 ~+ L; M7 y
  31.     {
    % P7 _/ V3 B5 e& T* E' ]0 s
  32.         return 1;
    0 B0 r8 p$ |) Y. o4 H, E
  33.     }    4 U% P7 y5 L* h2 M" G, `0 G
  34.   E$ w5 b6 x+ G; a
  35.     /* 内存映射 */    5 p) {# J5 P' K4 n2 g
  36.     result = QSPI_MemoryMapped();
    5 h  r9 v' x3 w6 `# T* h
  37.     if (result != 0)
    8 U: B# g. e* u$ U5 q! ~
  38.     {6 p0 W( L$ U7 W! |+ Z6 p: k
  39.         return 1;
    # |) i( x( d1 O: r# |2 L
  40.     }
    & p* a, t8 d7 d7 C7 U
  41.   o: l! y+ q/ g
  42.     return 0;   9 j6 {# u/ x' ^3 V0 a. e
  43. }
复制代码
# {0 m3 Q* h+ I4 S, x, E
这里要注意两点:) x/ H7 R- M+ B  p  G* ^- H! i! Z
3 r' a7 C# b+ f8 F& x& t" J$ A& W
(1)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
- k( O) b0 [4 ?1 X* `
8 }, N) ^+ j, Y& m(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。& w5 F5 D9 j8 K& Y+ f: Z' z. D% U
- X" I9 Y9 I# F8 V
  页编程函数ProgramPage0 b+ n% h. R5 l$ N" F' ]1 a
页编程函数实现如下:; _/ _( O7 I1 i# w- }! O5 H$ @- |7 I
" I$ ]5 V8 K$ M1 U7 z
  1. /*! A5 c) U+ f# l, A
  2. *********************************************************************************************************
    ' v/ k5 c/ f& q8 }0 F6 n
  3. *    函 数 名: ProgramPage3 g; v- U  D4 n8 H. F
  4. *    功能说明: 页编程
      e; S  d2 I8 Q3 _1 X( _3 }
  5. *    形    参: adr 页起始地址2 o- h/ I# Y3 Q# Z
  6. *             sz  页大小4 z- x4 {' n% x
  7. *             buf 要写入的数据地址
    3 P- q$ C; N1 h$ Z4 p
  8. *    返 回 值: 无
    0 r% T' j/ a  |/ X. _
  9. *********************************************************************************************************
    & g, X$ ?' z3 D5 D# g# E
  10. */
    " N- _/ a' F. E6 }8 R
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
    # p1 c& @5 m. K
  12. {" f5 W9 K; p& A" Q, ]
  13.     int size;
    9 B" V- P0 P9 X
  14.     int result = 0;
    $ _, b  H1 f1 L/ ?; |3 z; X! e/ X

  15. . J/ x3 u8 J" o% x
  16.     /* 地址要在操作的芯片范围内 */    ' Z4 l# `# p/ y. I2 \
  17.     if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)0 a/ a8 w! w! k( t0 X* U
  18.     {
    ( |' E( J8 z6 o/ g
  19.         return 1;
    # K+ k5 ?; m$ p9 U/ G% d
  20.     }
    ' \- S$ x4 S+ s! _( ~7 Y/ g6 }- @
  21. * X0 n4 _& g- R7 ?7 E" |  y
  22.     /* W25Q256初始化 */3 r( b  k1 Z" Q: R0 h
  23.     result = bsp_InitQSPI_W25Q256();
    6 I3 t7 \, A3 U0 }
  24.     if (result != 0)
    / @" a& d1 }* t' ~: E: P8 P
  25.     {4 p" \8 w6 L' s+ M$ E2 s# k& \% G
  26.         return 1;+ t) i, d1 w/ y
  27.     }
    8 W4 m* Z9 D& \1 U! t# W+ j9 Y3 I
  28. ( _& D: m* p( o0 P
  29.     adr -= QSPI_FLASH_MEM_ADDR;
    + e( s& d* O: g; C+ Q! m; L9 g* j
  30.     size =  sz;
    7 |5 X- z; @: C! w& u' w, M

  31. 3 G0 U  Y4 t1 W& y: W
  32.     /* 页编程 */+ n3 `) H! E3 o* @9 K
  33.     while(size > 0)( p& [( ^  W% p
  34.     {
    , E. P4 T5 i: D9 b1 m
  35.         if (QSPI_WriteBuffer(buf, adr, 256) == 1)
    / O) A$ {8 y% o2 ]3 ~* u4 {
  36.         {
    ) n0 o( N5 a% q  C: T: @9 f2 q
  37.             QSPI_MemoryMapped(); " f, u" H# Z: r; J# I  h$ w, T

  38. # p5 T% U+ X1 {/ R
  39.             return 1;   
    * h) C0 V0 d2 `7 J$ ~: \3 K; K
  40.         }" P6 @- i) z9 A* G
  41.         size -= 256;
    6 K1 z. I8 N2 o
  42.         adr += 256;7 d$ i/ ^* e6 Y; o+ h3 y
  43.         buf += 256;$ y7 D, S- e# }/ i7 B9 `. Z
  44.     }
    # g$ n/ f; H& M2 B* \
  45. 6 a# @4 [5 e& K9 L
  46.     /* 内存映射 */   
    - ]9 t; L2 N! R( i$ m
  47.     result = QSPI_MemoryMapped();
    ; h6 J" {& G! G  [
  48.     if (result != 0)
    , @' d! F( k- N- |+ Y* ~- |9 u
  49.     {
    , l$ s+ K2 ?" n$ S
  50.         return 1;1 F* i5 @! N7 j! s  j8 M2 v
  51.     }6 T6 E1 g( V* \1 t

  52. & z- A( U; `" z$ _0 w2 v9 L8 X
  53.     return (0);                     
    6 M& n$ N8 s2 b" Y) K. O
  54. }
复制代码
' a2 s# M) B1 T, m' V
这里注意两点:
+ X1 b7 e' v, l: E6 G8 \- z
, {& U, X  b; h7 A% y* G$ F% f(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。" M& z! {  M0 S: K
5 T' a, i4 q& A7 f8 b% t# M
(2)     程序里面的操作adr -= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。$ n  u/ L8 X' T9 C' I

# B4 @3 M) p2 V  读取和校验函数. V! h: ~( J& y! ^: E( y) }
我们程序中未做读取和校验函数。6 g% l/ d  X2 G, J
) Q; ~3 I7 J$ k
(1)     如果程序中未做读取函数,那么MDK会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
5 _/ D! G9 u. P' @" ], C) B7 v: b; E( Q
(2)     如果程序中未做校验函数,那么MDK会读取数据做CRC校验。
' h& E7 s6 S/ O* @" x3 O
7 ^+ l% c, l# H. r5 |& A/ m80.4.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
) }( h2 P& V8 p% I* h: [& W最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
6 |1 c. X/ T# f
; n4 ~7 f2 A, ]
  1. /*
    . e6 r2 w  e" ?8 W2 Y; f
  2.     STM32-V7开发板接线
    : Y' h! A2 O6 b: \8 S

  3.   B; L2 O# C% Y
  4.     PG6/QUADSPI_BK1_NCS     AF10
    * c2 E1 h, J: l% D: P* f; h
  5.     PF10/QUADSPI_CLK        AF9
    ( c, I1 p% ?# Y2 w
  6.     PF8/QUADSPI_BK1_IO0     AF10# e" i) Y' v% z
  7.     PF9/QUADSPI_BK1_IO1     AF107 B6 `. J: [# T/ Y+ q- _
  8.     PF7/QUADSPI_BK1_IO2     AF9- q2 U! w& |4 i" u
  9.     PF6/QUADSPI_BK1_IO3     AF9$ H$ v- v, i7 x% `2 J4 g- o4 [
  10. / E1 ?& c8 o! N" Z
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB3 n: X& y# S1 i7 l: r
  12. 1 S+ l' ^/ m; ]0 C+ e* x5 p
  13.     H7-TOOL开发板接线
    ; R$ ?. ?2 x. u" |( ^# i

  14. " G- h3 V/ u# E5 x2 D2 Z
  15.     PG6/QUADSPI_BK1_NCS     AF106 s: M2 X/ b0 h: t
  16.     PB2/QUADSPI_CLK         AF97 W4 |$ L! L1 T4 K  Y; V8 M
  17.     PD11/QUADSPI_BK1_IO0    AF104 k! r, S3 @1 [8 D( z" X6 i
  18.     PD12/QUADSPI_BK1_IO1    AF10- A! I. X) k/ I) m/ y
  19.     PF7/QUADSPI_BK1_IO2     AF9; R3 A: _" V1 l2 u' I8 @. D  _
  20.     PD13/QUADSPI_BK1_IO3    AF9/ E& ?- ^9 h4 d3 I& i0 u  V
  21. */9 _% ^( f! }6 n' O5 {) _
  22. - G6 I" A- M0 J1 F% R
  23. /* QSPI引脚和时钟相关配置宏定义 */9 U" q' U  B* H2 V( ~! x0 z
  24. #if 05 R" k( M1 [# l
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()
    / C: N4 [. ~: R
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()$ Y2 ]( O' t6 q9 n% b1 r2 y5 D( J( T
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()" O: J+ q& @$ q1 w! }: d( e$ D
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
    ; i+ v& s$ q; [# F9 f
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    , |: \- ~! F! `; n9 {4 |
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()# [! h. G& t6 M* |: L8 M
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    & l8 D2 Q9 ]# \2 a( j
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    7 `1 P/ ?/ A8 a' J$ c8 G5 ~) f) W

  33. , O4 d+ N/ K2 a
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()( ?! r5 J8 d& O" B% Y/ p
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    * g& f8 t) G7 m' v
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()  |, T/ M; u$ x5 h) Z; `/ _% b, h, u
  37. * ~1 r6 W, e+ U
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    9 L. T9 s0 c7 T$ E/ J3 J
  39. #define QSPI_CS_GPIO_PORT               GPIOG
    3 {1 R1 H! H, r3 `$ t
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI8 }7 N. x) J5 ]  w

  41. 3 ?! t5 D( e! x5 b2 p/ `
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2  m. B9 I* y" W* d- F
  43. #define QSPI_CLK_GPIO_PORT              GPIOB5 ?5 U. a0 Z/ T* ]9 A4 T2 j7 U7 x8 D
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI6 d, g8 }' X0 R3 r

  45. ) n- K6 A; s( O: a7 r1 H' R' [
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_111 @- I$ Q( A; k$ A2 @4 S
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD
    * K8 V  j1 Q% B
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI1 b+ W4 [% H$ r- C+ l

  49. , l! [$ [2 T3 Y
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12
    * x5 F* P! z; H% a6 F1 H& V
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD
    7 |) d: p% a; l* g
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI
    5 ?% W7 M* i. r) w  k  d. d
  53. . g7 y/ F1 v6 a0 p. D
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    7 T6 s/ p) {! M) N. A( C1 |1 J
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF9 j4 c, y" I( F+ \; f, D
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI& [# \! V( M9 e1 K' B

  57. * j, B* J) f; i
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_138 a! ]& l+ S& `3 u1 N" @5 X
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD
    6 I/ q  w) }5 T. M
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI3 t" Y, y- U' }
  61. #else
    6 H( I. w  ?4 j) L* {& m9 @) L
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()9 l! a  i7 a' }2 B
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()7 g% i- v$ H4 r6 D" G
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    - Q1 e, g  E* g. `$ F& g) N
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()
    ) R. A! u+ M7 A3 H  a8 f+ X
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    + W/ M! w3 a# ]' V' o
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    . c9 T7 {+ O* M% o3 J1 J7 N+ ^
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()5 d. B9 z( E  N$ u2 }6 ?+ u
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    ( r% Z- c. j4 {$ w/ H3 g
  70. & C) S/ _2 o3 R/ k! t
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()# Y6 }$ o8 D9 U! ^; k5 [
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    2 e5 P8 J, @( ?- i  j6 _
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()
    & D0 i" M% a: s8 Q

  74. & q& h' Q/ @7 ?4 N" q4 l9 T1 P
  75. #define QSPI_CS_PIN                     GPIO_PIN_6# U) [% J1 x: Q8 E( L
  76. #define QSPI_CS_GPIO_PORT               GPIOG
    ! B( a0 i& J) P$ j6 i* L
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI1 F% t( j$ r" v7 e% m; _
  78. 1 c: B, |( D# h
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    - }4 G8 K* A( e7 m, c7 g0 L
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
    " ^' G2 }! X" Y
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    2 V! U! v7 l+ b4 m$ u4 x
  82. 8 \  F* ~( j% k& X" g6 r
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_8( R2 X: S4 w/ X0 t* _' T
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF6 o' A: C) W/ ~) J" Y
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI2 s) I5 o. J+ T/ M+ j& _
  86. " _# Y  }' T8 r9 n
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9) P8 A& L7 d+ N) T% m, }
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF
    ; L$ Y. _/ k! ]+ I6 |
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI
    + T( ?: g/ c6 y+ B) d) o
  90. 3 t- s7 F0 p9 L# W0 w2 j: n9 J: `, |
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7; K$ o0 O: p( |2 A6 g$ Z
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF4 i6 }6 n3 y6 M0 V( Y
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
      J. M8 u' g) Y/ H4 P$ w) l: m+ ^

  94. % d9 \- j' H8 H8 v/ n, ?1 v
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6
    " e- u; f0 D3 E1 z. n/ l
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF
    ; b9 D* x! y0 U. _8 @9 R
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI. O6 E* d) q1 Z' t
  98. #endif
复制代码
" O1 p' E( \$ S9 Z
硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
& v+ G% `4 A) Y, t, R* y' ?) ^+ |! v8 q, S2 G
主要是下面这几个:
7 n% k8 [( D: B* K
) b! d& x9 H; ]/ D8 l
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000
    . m7 ]0 e6 x7 p+ V8 U

  2. % y* }4 [- b9 W5 M9 y2 c4 X
  3. /* W25Q256JV基本信息 */
    ' I0 |% D  L8 M4 u/ ^% W9 ~" Z& r
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/
    6 d1 F9 _9 z% h9 E$ `
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */
    2 V$ ^. w/ F3 u  x! C: x
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */, Y% ~( m" Q5 y) E2 J7 p
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    # g+ v6 V. `# P; s+ {5 O
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    + [1 S# }& ?; e- Y3 i

  9. + E; j! e" q+ {6 l0 s! }0 W
  10. /* W25Q256JV相关命令 */) f# p: J- `* ~! v, G" C! u
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */! G& ^% T: i( c; X
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */
    ' w" ~0 c3 p8 c2 b  i8 s( [0 W
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */
    0 A& A* V8 m, `
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */& H' M# G  E# R: F0 I! T  t
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */4 S( ?* ~5 `$ E9 T0 E
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */
    . x. h. z1 Y6 P# D9 d, E" n# z

  17. ) U; }$ m. M# E; t+ A8 L* R
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */
    4 V( U; o* O# p' M% j; p# E

  19. ' D- u2 ?4 V/ S; W8 J' X) V
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

# V, F1 P6 \9 _& R# ^3 E80.5 QSPI Flash的MDK下载算法使用方法4 ^! D" `9 I- r% v
编译本章教程配套的例子,生成的算法文件位于此路径下:
/ }# x% y3 _, `; \! D4 D6 a
% a8 K# T( t1 f; g$ B
b69a199f1b4a12d880e64c7ba134070c.png
* W6 F: P2 Z. }( ^8 }4 B/ m5 Z
1 {: y5 ?0 D/ C1 ]5 N
80.5.1 下载算法存放位置2 J2 t( O" u# s3 Q. i
生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:6 [$ [! R1 ]2 \' |- U( @
, D) f4 X/ F3 S
  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。
  B. `% ^! R+ n+ |( x, v- Z) `5 G( ~  第2种:MDK的安装目录 \ARM\Flash里面。4 A: P$ F, C; O! d
) ?5 }* [( U& N6 p; k

; ^! d/ X( T  h5 }2 b/ `80.5.2 下载配置
# p3 Q! J1 o% o3 G5 p注意这里一定要够大,否则会提示算法文件无法加载:' ]9 `& v4 |$ ?7 A' u6 X

5 f  l# D$ J! Y: \% W0 ?
9d5d65cb90fb28ad36464cb1b8368759.png

! W" o% \0 ?+ ^. t$ k! s! a; l' h
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。/ u( a8 K8 C( U' A1 U4 C
- Q: N6 J0 U  j; t( b: L
如果要下载程序到QSPI Flash里面,需要做如下配置:
: E6 t6 d' x9 B$ P3 ]2 n3 Y. \6 R4 q5 K- Q. M# \
f23244d646498fc30aa5f1301a327430.png

& q; e- K7 j1 M' w! u7 ~
# }6 P- b' c9 {& F4 X( |80.5.3 调试配置
1 M% ]# T" ?9 R; v* ^  k! Y注意这里一定要够大,否则会提示算法文件无法加载:
- p  P5 x5 [, E3 |7 u
7 v; R7 D3 w& @2 J. ]0 h. |
caa01b15ccf9a9700690d2ad1bdedecd.png
  [, J5 W5 v6 L  D- H" Y

5 M7 H2 ~$ v( }# ~我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。1 `+ o2 L6 ?" s4 D6 c" e

, M! ~2 L; M+ |; J" V% v; N4 m% x9 t如果要做调试下载,需要做如下配置:5 _: a/ \! x' U1 J( G3 K$ S

% o/ w) u3 C1 G3 |4 ~" h' t
8eefa57c2d7f128449000c2fb4e788d9.png

# S- {5 O2 Z5 \, j) s- h4 p
$ z! p7 ^/ p! o& u8 v80.5.4 验证算法文件是否可以正常使用
' W  q$ z: k. U% p1 k9 c为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。9 I7 N' a' [; I
% F& p/ R5 F. `/ Y
80.6 实验例程说明
" Z8 {# l0 h9 h1 ]本章配套例子:V7-060_QSPI Flash的MDK下载算法制作。- t) b. }1 f! x
( P1 x! S. t- P7 F
编译后,算法文件会存到此路径下:
$ Q" R- g+ @0 D$ E, ?
8 o3 }9 O. T; e* @' J  C. c
84ee4945e5faabd776fedd6f8f7d96d2.png

% J  n0 i, b% P9 o$ [
3 k' k- v5 e4 Y: F& e80.7 总结
8 G4 @2 U, \4 X. P+ p& `0 s本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
5 c) ~/ y" C& ^- F8 t& d; U& c+ t7 b" o: d: {) n
7 Z& p" m8 T  H; z/ P1 d9 n
ed1f9465c60f92522e906639b76b29f3.png
赞 收藏 评论0 发布时间:2021-11-4 22:11

举报

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