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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:05
84.1 初学者重要提示
+ c6 ~5 T* d. k( z1 l" q  SPI Flash的相关知识点可以看第78章和79章。
5 n4 {# x' M: E# l/ w  SPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
* y+ g1 ^7 n: U4 Z' d- h84.2 MDK下载算法基础知识
- c4 s0 `/ [; Z" XFlash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。( N0 b; G. a2 f: H% j
  `4 v: U/ C9 ?# ^3 I4 T
84.2.1 程序能够通过下载算法下载到芯片的核心思想
8 M* S& a8 w- \, N; U* N! o; Q认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。
# g% [2 }$ f( n; U* f8 q* Q7 L: Z$ `% _: L/ i  G
84.2.2 算法程序中擦除操作执行流程
# O+ R( R8 i2 _3 W  ]擦除操作大致流程:
. c/ O! X9 m2 @( |3 b9 v
& K5 h! u$ k( O( I- K+ O% g) V3 K2 b
0d071a0e889826f72cb93768e721eace.png

( v' X! C8 `! q" j8 `/ G7 [  i7 K/ M: u9 K9 {
  加载算法到芯片RAM。' c& T; ?3 G5 \2 i7 o$ x; S
  执行初始化函数Init。+ e8 K: ?; q7 o7 k/ x2 ~! V5 O
  执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。8 i; }7 e5 H) H; C& }
  执行Uinit函数。  r4 ^1 _2 P; E6 h9 \3 l/ I
  操作完毕。
1 t, X3 W6 i6 k% U, F84.2.3 算法程序中编程操作执行流程
! R, E9 W( @/ h8 L/ R; `! c编程操作大致流程:
! O. J* S& Z( o0 o; N, a) ?* k* o8 N6 F7 t7 [! n
201339135816bc603e60da45668f991d.png
; {4 G* `+ V* \" V6 m' Q
/ u: ^$ f* O% M" Z/ X1 [. y
  针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。
+ s& T* z9 W. F  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
0 d3 ^+ r- I& w+ B# _# o: H  加载算法到RAM。
3 E* z! s: j/ M5 T& l8 U% w( ?  执行Init函数。$ d" O5 k0 T# A1 m4 n4 d' O: H
  加载用户到RAM缓冲。
# ^  Z# R' h3 k  执行Program Page页编程函数。) T9 d* q/ Y7 v  n
  执行Uninit函数。
4 q" x- @3 c1 f  操作完毕。  C' e5 e8 D( N0 H3 r; q: Y# Q/ {
84.2.4 算法程序中校验操作执行流程
+ ^- F5 \; S/ w" U. {# ~1 \校验操作大致流程:# L9 K6 t% z$ ]8 b& R0 U

+ y8 \' I& o- m
a4a5c9e717fac7516ca272976cec4a7b.png

) }9 |9 ^' Y" ?
- ?5 X( s- p7 h7 g) I/ s  校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。
6 W+ S. d0 A/ e7 v( i  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
& i5 J* s  B" _; U, J  加载算法到RAM。
6 z! t2 p* T2 L' Q0 t) T4 I  执行Init函数。
3 z& t$ r! y1 P0 {  查看校验算法是否存在7 S$ V3 Y! i1 y2 J4 I
  如果有,加载应用程序到RAM并执行校验。: r. f/ ?( f; v$ ^
  如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。( q' ^  q$ Z) l$ n, D- h5 i. v
  执行Uninit函数。
6 R% @: B6 H: ~  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。9 d$ r( E. _6 d0 i, c6 l* B
执行RecoverySupportStop,恢复支持停止。
0 F' i. y, s( H$ ~: E  执行DebugCoreStop,调试内核停止。
0 g3 O6 Y8 c# m9 B% c/ A  运行应用:! _" w! l" `8 I' q/ j, n
  执行失败。. J8 a1 ?* p+ u4 P2 o
执行成功,再执行硬件复位。
5 Z. O5 v$ F( o& Z# ^; U  操作完毕,停止调试端口。. H! B. d; P- d% o" H( i
84.3 创建MDK下载算法通用流程
/ U" W8 k( x+ ^' y& I$ W! z下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
7 e( I) H0 a2 |
  o3 l8 A; T! D2 X84.3.1 第1步,使用MDK提供好的程序模板/ I. y4 ~& D# I9 D- z- r
位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。
0 B3 b1 u* ]) k7 x. w5 y' _
1 Q( \/ l% O  W* X0 D效果如下:
/ {! g& E4 T6 M2 o
2 R" h& C' R: V0 q% K5 b
92bb82bfce2261d3ecab5865e30cd939.png
2 Y  U; w: A0 N0 ]
# w. \  B+ z) d7 K
84.3.2 第2步,修改工程名
$ [1 Y5 U" S; z% T) CMDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。, L6 L  e5 H4 ]

: C  ?6 ?5 ~9 d4 S2 j2 w! G84.3.3 第3步,修改使用的器件
; P1 ?2 i' X1 p! @" S在MDK的Option选项里面设置使用的器件。- v7 w" }& x$ i8 u2 ~3 a$ H
  n  d7 |, A0 B9 q$ j, u
fbb792f4aa614c8d634f6846a97a399a.png
1 [3 D! X0 k) f7 V$ N

, P" b1 A. e6 k/ a* {  H  y$ ~84.3.4 第4步,修改输出算法文件的名字+ X. R  a/ }+ S: k  }. {+ G
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。
9 U+ [3 e3 h& r, `& n+ O3 O1 {5 m- ^8 ~5 \4 `- S3 Y3 ~
73ede509936b0b438a5ba540fbc21a7c.png

; H, m' n! C) w3 ]; A/ y+ M% m! Q
: \2 S/ o: f+ L8 }$ y/ _注:MDK这里设置的名字与下面位置识别出来的算法名无关:
# e. @; E* I/ e  |4 ^+ a  _( U* N
7 {& F  _- A3 \$ O
eb32aec305e29b4627294d2d4228d4ed.png
9 _6 e0 `! E+ G: ~5 Y  ]# v6 E
1 J  r/ G6 s" r: X# G, Y8 Q
这个名字是在FlashDev.c里面定义的。
; G) X9 J8 {7 P4 `. B+ L+ E/ f8 o( Z- z1 w4 H6 h1 Z- e) C. d7 m
84.3.5 第5步,修改编程算法文件FlashPrg.c- f3 e* v" R: ~/ f
模板工程里面仅提供了接口函数,内容需要用户自己填。
  D, L: f6 w8 z0 J$ d2 J0 n  r$ b
  1. /* " Y  V9 N+ o; t2 d) L8 c) V
  2.    Mandatory Flash Programming Functions (Called by FlashOS):
      l$ u) K+ k" m' A% N
  3.                 int Init        (unsigned long adr,   // Initialize Flash
      G& E3 J+ q1 S8 o8 _
  4.                                  unsigned long clk,4 F3 k% O6 |  L% h6 ]
  5.                                  unsigned long fnc);: d: X" N# B$ x  I% m
  6.                 int UnInit      (unsigned long fnc);  // De-initialize Flash7 r8 d" ]. O+ u6 J5 s) a; b
  7.                 int EraseSector (unsigned long adr);  // Erase Sector Function
    , q; l! e* G! P5 r
  8.                 int ProgramPage (unsigned long adr,   // Program Page Function
    + w, _$ B" A9 Q/ U" ?0 U: e. U
  9.                                  unsigned long sz,( p, H1 e9 x' A# R- t4 D
  10.                                  unsigned char *buf);- K# e$ R" c9 B" C

  11. / ?$ f* S9 Y9 p
  12.    Optional  Flash Programming Functions (Called by FlashOS):% [1 S7 s% v& H6 X4 y; j
  13.                 int BlankCheck  (unsigned long adr,   // Blank Check
    ; N9 {* {) A$ q* _2 _
  14.                                  unsigned long sz,
    / o! i6 ?' l; b' p1 X8 \3 {7 Q8 |
  15.                                  unsigned char pat);
    6 U+ p5 u/ z2 M
  16.                 int EraseChip   (void);               // Erase complete Device
    8 O# G1 }+ W) f% Z
  17.       unsigned long Verify      (unsigned long adr,   // Verify Function
    % j8 @5 P( p5 ~5 `' B% w6 `2 N8 @* X
  18.                                  unsigned long sz,
    ' p! l) u: a: K  _: r3 y  x
  19.                                  unsigned char *buf);5 U* `7 S# h$ x  r% P( b. s, A" U5 k

  20. 5 d; }+ I6 J/ G3 c
  21.        - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space
    : m4 Y! q6 I! @% S# r: k' u9 {
  22.        - Verify       is necessary if Flash space is not mapped into CPU memory space! T! E/ ~3 Q' z' I
  23.        - if EraseChip is not provided than EraseSector for all sectors is called* v' K+ R2 z' l
  24. */
      M2 s( x/ w# ?8 P
  25. # G8 Y3 c9 u: C! n+ ~$ p& q/ v, Z7 r
  26. /*
    , g( S2 G$ w. T) T# U" m
  27. *  Initialize Flash Programming Functions
    0 g5 D$ G2 u. b) ^9 {5 Y8 e; J9 i7 i
  28. *    Parameter:      adr:  Device Base Address
    ! p, |4 B6 b1 l7 X4 g5 y+ M
  29. *                    clk:  Clock Frequency (Hz)/ \8 S# v5 F2 a6 l
  30. *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    1 e0 O/ Z* o) n; [, U- e' v& y
  31. *    Return Value:   0 - OK,  1 - Failed6 ^. e. {8 w5 E, n
  32. */
    # R5 p* K. R+ Z6 p4 ^
  33. ' f# W. D3 g" m( O+ p/ V% E
  34. int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
    ) I! k6 N( s) a) Q* [/ O
  35. + q; _1 ^: |% D
  36.   /* Add your Code */
    5 d: ^8 G1 F( J* D2 ^
  37.   return (0);                                  // Finished without Errors" Z" f5 A. Z( Q0 Q$ W" z
  38. }) y0 B* o( ]5 A& ?+ t3 n, x
  39. 4 f9 ]! c- A0 Z
  40. 3 I! @( V- D( H9 A5 c+ E
  41. /*( Z( f# z3 y( c
  42. *  De-Initialize Flash Programming Functions1 t# z! i7 H% }: y, _, e
  43. *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
    * K- P5 }( v2 ?% H3 \
  44. *    Return Value:   0 - OK,  1 - Failed
    5 }0 u, Y( A( ?& o0 ?
  45. */1 l2 W& u$ m4 Q. U1 B3 g) F2 f
  46. : e! R- h, [9 x7 U/ E' p3 l
  47. int UnInit (unsigned long fnc) {" ]+ y3 L2 s0 {& K) _
  48. ' }% }) k& F3 }( c8 G! E
  49.   /* Add your Code */( D9 \  `  g" r
  50.   return (0);                                  // Finished without Errors
      i3 g8 v1 b) Y. T& t' y5 G
  51. }3 h, o+ J' C& B; \

  52. " T, N& `: t' q* l( s* l/ z6 t$ E. q

  53. . C# ^+ X/ L# h5 Y: s6 [0 t  Z4 C" |
  54. /*  Y* f( B0 v& u& D5 }( U/ A! b8 t% y
  55. *  Erase complete Flash Memory
    ' V! W1 n0 O' I$ X- a
  56. *    Return Value:   0 - OK,  1 - Failed+ b! U- [9 Y2 [' s% {
  57. */
    6 R3 G! V4 D' O4 ?0 P1 @
  58. 7 j& h. g$ m3 g3 u& ]7 @1 e  P9 r  O* M
  59. int EraseChip (void) {
    - J' i8 ~( Z8 T2 W3 g

  60. 7 _3 L8 t$ e1 D! f0 W) G) e% ^. j
  61.   /* Add your Code */
    2 Q( V% l5 \6 D# t, p8 y4 _1 l4 D  g
  62.   return (0);                                  // Finished without Errors+ y- C3 d" C+ W* K2 d+ t* h8 d
  63. }. t1 C# C4 w5 I% I- U0 r
  64. " T* B( Z8 c$ [+ Y( A$ D1 X$ i% V. Y
  65. & J5 I8 `* b# g5 ~( {
  66. /** T! S9 D3 e1 m( Z' {
  67. *  Erase Sector in Flash Memory- \& y8 T( G! g1 y- N# @
  68. *    Parameter:      adr:  Sector Address
    3 {, @5 o$ H, E% w" e
  69. *    Return Value:   0 - OK,  1 - Failed' z: Y2 O8 s6 n% o* v6 H- C; w0 q
  70. */5 _  d+ [3 F, p5 H& W8 O8 I- J
  71. ( _$ M5 [7 o0 o. z* u2 |. s
  72. int EraseSector (unsigned long adr) {
    + @5 C& s* c- z' R4 h1 ?$ G' z
  73. ! l  c$ ]5 `7 }7 ^; E
  74.   /* Add your Code */; C( D7 a, V) |# @7 Q& S- b
  75.   return (0);                                  // Finished without Errors
    3 q* V5 B* Y5 M* u8 ~0 l
  76. }/ l( t1 Q4 v5 H$ ~9 U! t

  77. ; Y  c+ u7 l' H& C" b
  78. 3 ~) e9 V8 V- c* G* F
  79. /*2 S+ W2 v# `  u' Z
  80. *  Program Page in Flash Memory
    5 q# h# u1 u( D% R
  81. *    Parameter:      adr:  Page Start Address
    5 X9 t+ w% A. @& Z( ]
  82. *                    sz:   Page Size
    9 X3 n8 V" `; G9 N. {
  83. *                    buf:  Page Data$ f6 B3 A: I& A2 n. C. S1 |
  84. *    Return Value:   0 - OK,  1 - Failed
    / x* ]( I0 S/ ]
  85. */
    , T' \8 j4 |; n
  86. 9 K. |0 G9 O) `. y) S2 \# O9 W0 z4 y
  87. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {% X& u/ q9 U; l# b( |

  88. 6 {' r- n2 M$ b5 R9 B5 f/ I
  89.   /* Add your Code */6 y( y+ {5 q4 L  @  F. K
  90.   return (0);                                  // Finished without Errors$ c: r. X  ^: p  `
  91. }
复制代码

3 U7 s- @$ F3 }: x5 }84.3.6 第6步,修改配置文件FlashDev.c
! f% C- Q/ B7 D3 `! o模板工程里面提供简单的配置说明:! o$ X7 o6 i' F4 Y9 n

$ D" M* J+ @7 F& A) |; a- @5 |2 Q
  1. struct FlashDevice const FlashDevice  =  {
    * {1 |. T  M; ^
  2.    FLASH_DRV_VERS,             // Driver Version, do not modify!
    3 U1 t- h4 Z) P$ @
  3.    "New Device 256kB Flash",   // Device Name 6 t9 z2 }0 j4 `; P/ ~& }8 Z
  4.    ONCHIP,                     // Device Type+ m( f* m  b& f: `- q
  5.    0x00000000,                 // Device Start Address  Z: L' F9 b% {+ |6 ?
  6.    0x00040000,                 // Device Size in Bytes (256kB)) o1 S4 t1 p0 }7 S: m' n: b
  7.    1024,                       // Programming Page Size1 F/ |9 F* ^7 r/ ^2 l6 f3 a% W& r
  8.    0,                          // Reserved, must be 0
    + p# n6 I  ]1 U
  9.    0xFF,                       // Initial Content of Erased Memory% P/ x; Y3 T; G& u: n0 M4 d7 I2 V- G
  10.    100,                        // Program Page Timeout 100 mSec0 D; U& t( d7 [
  11.    3000,                       // Erase Sector Timeout 3000 mSec2 G5 @5 T2 J. X. U. d

  12. - ]( }7 ?' i* \) S; q! ]# i" p- S
  13. // Specify Size and Address of Sectors; [' u/ o) g, D9 H+ C% O
  14.    0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)
    : H5 F/ G# E7 P" S5 U
  15.    0x010000, 0x010000,         // Sector Size 64kB (2 Sectors)
    ; z1 `) d" S' ?  {# W
  16.    0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)- x- e! V* c2 b. ~9 K% |: Q
  17.    SECTOR_END
    ! N% u+ V* C" {; |7 L! M$ ]
  18. };
复制代码
$ z6 m9 _& G4 i5 v: v  _
注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。
- G. M  w+ a' j- a! b4 N& ^( N1 \8 L: M# d% l
84.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关& A( @5 Y& y- c( u! `% T+ [  i1 j4 H
C和汇编的配置都勾选上:
; @) d% J2 U6 L' _% ^' ]8 z0 l  |$ \7 k
/ f4 p9 N  e1 j; X: ?9 J; v5 M7 L# [
81745e69eaae3963023d8d62d8d854db.png

! X9 O) S" I( r* q6 [  n" Z
* a! H; q0 Q! ?% f6 a' |汇编:
, n9 X( T. o: h- G. N% V3 _6 |5 J
1fce5c0df84635c2b704a595d853a5d0.png
0 o! o  x; {! t' }

0 y6 S1 {+ @* s( h2 `4 n如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:7 ?- h0 x% U$ p% U3 A
2 Y: Q* h" H7 b) X  ~. E: I
(1)加载以响应运行事件。
0 h- r0 l9 f0 G) z8 ]8 A* {: l1 o$ o" m" q3 n  u2 @5 @* I: w& q
(2)在不同情况下使用其他例程的不同组合加载到内存中。
( }4 c2 d9 S; X& i6 {3 _: g9 G
1 ~; z2 ~% k. F1 X5 S  x* i( k( g. K% ?1 c(3)在执行期间映射到不同的地址。; s) I2 I. P5 D6 V8 s5 @
7 O  V5 l3 ?- Z  s. r
使用Read-Write position independence同理,表示的可读可写数据段。, p- M, l$ A1 r/ _% d! b6 I
3 J1 s5 b4 \3 n' v' f0 e4 Z
84.3.8 第8步,将程序可执行文件axf修改为flm格式
0 U2 M! a" ^+ _( K& _* E; r通过下面的命令就可以将生成的axf可执行文件修改为flm。
' Y8 H( b" d% g- X4 w- d! m; v0 u/ P
3 U& R5 G0 `1 O
0f1f6d27190f470484633137076dbe3c.png
! B  s. w9 e0 f
6 o6 f' t" U; _
84.3.9 第9步,分散加载设置
& L9 T- z( r  p, w5 m我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。8 h! C, ?7 _- f( n, P
6 {* ^- Y) U1 T) T
c54ffaac8b74e54b1bac0ff78d6bb18a.png

! t! D8 d0 Q) b4 I2 o0 r, _( c3 P" D/ M+ T$ U
分散加载文件中的内容如下:
$ e) A% j2 p$ a9 z2 y. |
1 ?& f) ]& D# ~" E7 z) f' r
  1. ; Linker Control File (scatter-loading)) l( [* B8 n4 J; _8 u; c1 a
  2. ;9 h- E; g2 s$ D& D9 J

  3. ; [$ _# ~8 U( Z* x
  4. PRG 0 PI               ; Programming Functions8 m; h- U, ^/ N
  5. {% F4 a, `. L& z/ X1 P2 @  s
  6.   PrgCode +0           ; Code
    : t+ q% ?$ ^  `% ~+ j2 ~2 E2 @$ R
  7.   {: L. W( A3 t! U7 r$ R) K9 ~
  8.     * (+RO): ^$ ?! M3 [4 x% S( o0 d! r+ ~
  9.   }
    7 p7 C: e* k! m' C& g- v" \
  10.   PrgData +0           ; Data, S5 y5 o% I7 p- i4 Y9 \3 h
  11.   {
    / w& s9 {, i% N! h1 s8 x; `8 f
  12.     * (+RW,+ZI)
    , G  g* p* d! g8 `8 [4 a! e
  13.   }
    ( ~, R5 A; X8 o( H) }! E  r
  14. }
    " V) r* R% \: p( _, m- V' l, w
  15. 4 L/ u' c4 h: Y  B9 k/ g1 m( l
  16. DSCR +0                ; Device Description. l: t) T3 b1 [' ~. ]
  17. {
    . D2 ^  U- f: ?1 H: |& w# T
  18.   DevDscr +0
    ! g: D' c$ r* g3 W
  19.   {* i- Z* `  u7 O% q6 f* m
  20.     FlashDev.o
    * ]( ], B, r5 U) K& `- w
  21.   }( [2 r" \' @+ S' r! j! L1 f9 N
  22. }
复制代码

1 m. z4 U0 j, H! b* W$ E9 G--diag_suppress L6305用于屏蔽L6503类型警告信息。
1 ^$ V, C* L! A. m. j, `, z4 q  N+ h, \' M% M
特别注意,设置了分散加载后,此处的配置就不再起作用了:/ c$ I8 j- n% F% J: B' H0 u. N6 P
2 \, _1 `3 T8 I. p2 B
b4e157ab29667b0d404715604c9786cd.png
1 w! J* Z/ c$ ~8 J( P1 I; I
3 C; V$ X8 ?" x6 P* j
84.4 SPI Flash的MDK下载算法制作
8 {2 h$ s, L8 c# g$ l下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。' ]; R2 b; ]1 ?$ I8 S5 V1 A

5 Z& ~& J5 S- \' k8 ^9 Q/ H" }84.4.1 第1步,制作前重要提示" L' \5 Q% x2 A; t
这两点非常重要:) T8 e; l& c; a: }6 [9 n3 d

9 m2 ^; q$ ^3 \$ c& K/ H. @) T  程序里面不要开启任何中断,全部查询方式。3 G2 a# {& p. v9 G
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
5 n( a8 c" H8 a84.4.2 第2步,准备一个工程模板: \, N8 f! `+ v5 e
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
. s* f% }* P. B  A6 q1 \0 _2 {* x; v
2 q- ^0 B( B: u
b0cfb040027000e7e6c6dac47bc3f960.png

- _/ p! b' W% K1 [
9 n2 f1 e+ G1 U2 W% e9 Y5 ]* l84.4.3 第3步,修改HAL库
0 t6 D$ ?8 ?; u  W+ y; H这一步比较重要,主要修改了以下三个文件:
$ H) x& B) I, K) ]$ S4 I( {0 }! c6 z! m
75fdbf8e42073683e67cb1828f2f66ff.png
3 H# ?8 R: I3 t1 L# ^; b/ j

5 U2 m6 w2 H. i- c8 g主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
/ K3 {8 v6 a. E; {, C6 c+ x. I' K: N! C# V( T8 Z9 Q
84.4.4 第4步,时钟初始化
& U' J$ [. n! R! Y- k, P0 B/ }我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
: F6 ^! E& ]. c+ A# j) L. z/ p: `4 ~( n# k  O$ w; Q+ q
  1. /*( {9 M, b; I2 N- ^: ]
  2. *********************************************************************************************************
    1 T# O, D+ O! x! c: f
  3. *    函 数 名: HAL_InitTick0 B; U' B/ ^$ k; E
  4. *    功能说明: 重定向,不使用& m: ^. z; S+ t8 Z
  5. *    形    参: TickPriority# ]9 q$ t8 J2 w4 v$ A- n" q
  6. *    返 回 值: 无2 J: T6 v' ^1 S. I  a) c2 q* B6 `
  7. *********************************************************************************************************
    . E; {9 A& P: F4 l6 p! r
  8. */
      d8 N) l0 z/ m. U9 _6 L9 ]# U9 g5 R
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    ; T: j. N# Y7 v1 b" n: j- g
  10. {
    ( h9 P" a! P& }# Z( h; [6 F4 i" Z
  11.     return HAL_OK;
    ) z: l2 A3 n( k) M3 o! T6 |  r, p
  12. }
    ) D' R2 e6 X. N0 f6 n1 Q+ B
  13. 然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:7 t& y' S9 l5 B/ u6 {
  14. 9 z, l* E; ^% Y7 Z/ z
  15. #if !defined  (HSE_VALUE)
    5 F, j* {0 E, `# Z1 x* g
  16. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */3 W: O  T1 a. U5 z0 U' a
  17. #endif /* HSE_VALUE */
    ; a  H+ b0 Q& w7 Z, F
  18. 最后修改PLL:
    7 n4 x% j. T+ w
  19. " s% N+ q  v2 f
  20. /*
    - u2 h7 P' w: H
  21. *********************************************************************************************************) f5 [1 ^# Q. U4 X1 p, d
  22. *    函 数 名: SystemClock_Config# ?% X. S  _4 g
  23. *    功能说明: 初始化系统时钟2 W; G* Q: u+ ]
  24. *                System Clock source            = PLL (HSE)7 q8 _+ @" y' D2 @0 f2 `
  25. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)8 F/ h- D! P; t% G- U7 r  _9 C" G/ f
  26. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    6 q7 y" y* @9 `0 H# M: y( e
  27. *                AHB Prescaler                  = 29 {& e  D+ q3 |4 L3 S6 o# |
  28. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz), _2 w- K  T* l6 M9 s8 z8 h) D
  29. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)- {3 P+ K# i1 [1 m% ^! c  i
  30. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)' H) v! d+ F, Q
  31. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)2 h  S) p! o3 K
  32. *                HSE Frequency(Hz)              = 25000000
    6 {# }+ d! M. U! ]
  33. *               PLL_M                          = 5
    6 R2 L: h7 y2 _5 ]- H; c
  34. *                PLL_N                          = 160  y6 a, I+ N6 ?6 h
  35. *                PLL_P                          = 2
    & h* `4 O; Z6 L: E0 q+ ]
  36. *                PLL_Q                          = 4
    9 }+ s8 s! Q& z8 J: S
  37. *                PLL_R                          = 2
    5 i9 y4 x! g9 u' d( ]$ s% ^. l
  38. *                VDD(V)                         = 3.3# z% U7 c' t% F2 J8 ]
  39. *                Flash Latency(WS)              = 4
    , h  H! v5 W$ k+ z. |
  40. *    形    参: 无
    1 n5 ~; v1 @/ t6 G+ R
  41. *    返 回 值: 1 表示失败,0 表示成功/ L3 y( s8 b2 s9 M
  42. *********************************************************************************************************. O7 h! M0 [$ Z# `) O. j( w
  43. */- ~- G$ t3 v6 b. N
  44. int SystemClock_Config(void)4 |  _' t$ Y2 r5 J9 y* o  x0 ?
  45. {
    % z. ~2 G2 X' G* }
  46.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    5 n2 G( k+ a, v  h* V0 q
  47.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    : o" h: y$ ?) C* D0 w% s  G
  48.     HAL_StatusTypeDef ret = HAL_OK;% h( Y, g+ C6 ?
  49. 5 V9 ]0 p- O  `2 J
  50.     /* 锁住SCU(Supply configuration update) */5 L; f7 u# Q+ p( o% ~- @4 t
  51.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);8 M' |  R" k- ]' u; g

  52. * M8 T1 z% A5 |, I' I' J
  53.     /*
    & `6 J+ U0 [9 I( v, m. h. A
  54.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,1 l$ _7 o/ e) a  T
  55.          详情看参考手册的Table 12的表格。
    ' B* T8 r# w+ h( a3 q  |9 h
  56.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。; |) T5 [% O6 J* t: a, B8 N; i
  57.     */( j- @+ Y5 y0 t# e
  58.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
      m# [; K8 Z  A- R9 {: r
  59. $ z! H1 ~1 c$ K
  60.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}7 {% D% g) a$ O1 k0 o) J

  61. ' C+ C2 h. U$ n, g
  62.     /* 使能HSE,并选择HSE作为PLL时钟源 */+ `/ K; x& x" l! n- U  O
  63.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;2 b% i- R* Y! r7 {; X
  64.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    6 \9 ~9 \0 z% E* j$ C
  65.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;* P' f3 E' \) m& l# c7 Q. _' S* N
  66.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;' @4 {& i" Y' h2 Y9 s" H; m
  67.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    7 O* H8 p  Y3 p! y3 ~/ S
  68.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;9 A& S/ n2 U# {, s

  69. + k6 o4 P8 l& \* c
  70.     RCC_OscInitStruct.PLL.PLLM = 5;
    7 N) c3 {# ?: X9 G
  71.     RCC_OscInitStruct.PLL.PLLN = 160;( k2 }, v+ f3 ^  \# q$ s
  72.     RCC_OscInitStruct.PLL.PLLP = 2;
    ( G# g+ I* x. v0 _5 `
  73.     RCC_OscInitStruct.PLL.PLLR = 2;
    ' a  [( I" Q' Y4 f
  74.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    8 ]2 w+ W1 G: L& X0 l) f
  75.   ~+ L" W2 Z2 ~  f) ?# V
  76.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    7 K  ^, w3 k0 W' {( G3 k
  77.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    $ x& A0 \& c1 n* b% t/ Y7 f
  78.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    ' x/ D2 P% }4 K' `: k7 D
  79.     if(ret != HAL_OK)2 _# L5 U1 x- v- L6 K
  80.     {. q7 a; U$ L6 i+ g+ I1 a4 N
  81.         return 1;        
    % f2 q+ z6 R: R6 I- N. w0 V
  82.     }8 t  ^6 Q% U% n4 `6 D4 `8 c
  83. # w  g7 }6 X" e- E
  84.     /*
    8 p9 x) ~( f$ h- Y/ S3 t
  85.        选择PLL的输出作为系统时钟
    $ ?/ a1 S/ e- E+ t: d; [
  86.        配置RCC_CLOCKTYPE_SYSCLK系统时钟( F9 ^8 j: h, [
  87.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线0 X9 v! {: @/ g! p; ?/ V! W, i4 f( l
  88.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线% j; g( t2 [/ `
  89.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    % u+ ^+ u" D9 m; q' d4 y
  90.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    0 r: }4 ], o5 K9 l: }" c4 H
  91.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    , A5 T0 }0 [1 y! G% }/ u
  92.     */
    . _2 N  Y9 G; X$ L. U& O
  93.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    ' Z) `6 _7 |' N, d3 _9 n# y
  94.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);& p- }% A2 w0 K0 P

  95. ' l8 X3 h# F6 ?% G
  96.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;$ z4 `+ k$ z! S# k3 B8 I1 l4 J
  97.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;9 R" M# E8 ]# z5 H7 g* ?% {) m5 C
  98.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;* {0 ~: l$ H) J1 @: q+ U+ z
  99.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  ) p" c# v( `; d+ t
  100.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; 9 V1 D2 s- x6 a* ~$ w; H  E0 w- \
  101.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    4 w& r% _9 H0 O3 t
  102.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; 5 \3 ]! |6 N' P
  103. 9 A# F* Q: v3 Q/ m
  104.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    . v! E! b5 x) \1 w% Z3 }
  105.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    : x8 z' B* h2 E4 B$ T1 D- o( v! h
  106.     if(ret != HAL_OK)
    5 z4 |1 u; O2 f4 `& a8 G$ G$ y
  107.     {/ n% W, [9 @) [0 s$ i, P% p& L$ S
  108.         return 1;
    $ q. ~7 u+ u* p$ L# W8 k+ o
  109.     }: H7 T; t' b) |& r
  110. ; {- o. N, B* f+ ^: l) l( U2 U5 p
  111.     /*( w0 }( X( a  R9 m6 ^; l+ X
  112.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 ! ^" }1 L* d% h; C, G# L" c
  113.       (1)使能CSI clock
    ; D8 R" R& W6 _* r6 C
  114.       (2)使能SYSCFG clock
    5 U" ?- R3 \( B' x% g: u# h
  115.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0, q/ {  s, B. ^6 O" l  q( p* k
  116.     */' Z6 O5 z* t- H' P
  117.     __HAL_RCC_CSI_ENABLE() ;7 z, W" w9 F; N" w$ T. C

  118. 2 ?! E- ^; L; Y; p0 {& M. V" m0 r+ T
  119.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;: G8 P! h0 V( c) T; C8 o( \
  120. , L' ]3 G9 y, V  Q, y! g
  121.     HAL_EnableCompensationCell();
    8 o  }4 D- S; r

  122.   E- F" a. }# M% O' `2 a5 b* k* p
  123.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    * M. e  ^( z& z* e
  124.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    , z& g, I) K7 Z4 n5 w; b- X: V
  125.     __HAL_RCC_D2SRAM3_CLK_ENABLE();! j  V8 j7 }# r& D( e
  126. 7 `4 d, z# R5 F6 U4 u2 T/ v
  127.     return 0;6 V, r  T, X/ r" W: f6 b$ h$ R3 I
  128. }
复制代码
6 O6 S, V. N% [" t- g6 n; }2 s
84.4.5 第5步,配置文件FlashDev.c的实现
- R# F* l7 M. r; p配置如下:9 \# P) D3 J6 p* p

6 s+ T2 D" u% u2 r( G# N
  1. struct FlashDevice const FlashDevice  =  {. Q5 P* c% u5 f! }$ R( t. O' h
  2.     FLASH_DRV_VERS,               /* 驱动版本,勿修改,这个是MDK定的 */  y! I) R1 R0 f  d; _) Z3 q0 R
  3.     "ARMFLY_STM32H743_SPI_25Q64", /* 算法名,添加算法到MDK安装目录会显示此名字 */9 k' }# G0 z! q# k: G
  4.     EXTSPI,                       /* 设备类型 */) u6 F' E6 ^) B8 z5 G$ f
  5.     0xC0000000,                   /* Flash起始地址 */1 ]& Z+ s3 r6 w- R& U2 i
  6.     8 * 1024 * 1024,              /* Flash大小,8MB */' Z" U+ L, g: P( N, W, e' ^
  7.     4096,                         /* 编程页大小 */! D. L( I: I$ M# X3 M
  8.     0,                            /* 保留,必须为0 */
    $ H& m* H5 j8 G; v; P
  9.     0xFF,                         /* 擦除后的数值 */
    & l) \* c; K$ o* w& H
  10.     6000,                         /* 页编程等待时间 */; @( y& i4 C* K! g7 X
  11.     6000,                         /* 扇区擦除等待时间 */
    ) M( l; D7 n  G) T6 w2 v. e
  12.     4 * 1024, 0x000000,          /* 扇区大小,扇区地址 */* m! c0 e! Z: L/ P: I7 D- j: j5 [
  13.     SECTOR_END   
    ( }! l  w  Y* |
  14. };
复制代码

0 W' s# `# V1 c3 ^7 k# P注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:' v% O/ `7 W8 Z$ q* _+ O( O/ G

8 f( ~" x$ S1 M% y
5e80b34f327decc1acee5eaa274ff154.png

1 h1 U8 H. G3 g1 |! |
% W5 M* A4 U' c' I' a. P: I8 A. x/ i84.4.6 第6步,编程文件FlashPrg.c的实现: H+ M1 n2 P" y
下面将文件中实现的几个函数为大家做个说明:
: _- J) m  L, e7 m
( e4 F! ?( C) c# j% V/ U. G) P9 J  初始化函数Init
/ d9 m1 g6 I" v* i5 s. \0 \- b1 V5 |
  1. /*
    : R4 K1 R: e4 ^+ ]5 o9 a
  2. *********************************************************************************************************9 Y+ G6 [' t+ Q2 p6 z
  3. *    函 数 名: Init) Y- a3 O/ \! t5 J4 r: K9 Y
  4. *    功能说明: Flash编程初始化' B5 T$ S9 H: L4 K" `
  5. *    形    参: adr Flash基地址,芯片首地址。+ D9 M6 P( G4 Y" X5 R+ }+ F
  6. *             clk 时钟频率" E* A& }" d  \4 ~+ B
  7. *             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    & F- @# G9 V2 V0 q  d- Q
  8. *    返 回 值: 0 表示成功, 1表示失败4 K" D# W. }1 s: l7 Z
  9. *********************************************************************************************************/ b- X% n9 l2 Q  y
  10. */7 o# l( B& A% D& H
  11. int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
    % G- ]$ i) w4 T6 C, W( J$ k2 h
  12. {& y8 X% V+ L* h( L0 U
  13.     int result = 0;5 s- ^; m3 ]. a! p+ i7 ]
  14. , o/ J3 m* ?7 k
  15.     /* 系统初始化 */
    9 r. J( K7 ]/ }; a8 I
  16.     SystemInit(); ! B0 {" ?% D; \/ t* p6 h' o) L
  17. 4 I8 [2 R3 C; A
  18.     /* 时钟初始化 */0 @6 [4 V* a! I6 a2 i
  19.     result = SystemClock_Config();
    1 n+ p# }* T- \. y5 t6 Z% H2 |1 f
  20.     if (result  != 0)
    - Q% ]+ z6 A8 \8 [& M
  21.     {( U0 Y6 p9 u; [
  22.         return 1;        ( c2 M4 R8 t, d2 o
  23.     }1 h# Y: F) [& {

  24. : i% X0 x1 F( @7 J- d
  25.     bsp_InitSPIBus();: s, w4 }1 ~: N/ |6 E, F
  26.     bsp_InitSFlash();1 l+ D0 {" [3 e

  27. ; c2 y7 l8 G4 B- e; `
  28.     return 0;; k" z3 o% f# d! f. K' W
  29. }
复制代码
+ q  i8 A# U  i8 F# f/ I
  复位初始化函数Uinit
! |7 z0 S6 i& j/ \擦除,编程和校验函数后都会调用此函数,我们这里未使用。. @( N& I/ d( X" K( r$ G! l! p
, B, _' a* _% N5 N9 K
  1. /*3 _/ D; X8 p+ w4 m8 |1 ]
  2. *********************************************************************************************************. `) |! b8 S. k- V2 }7 f
  3. *    函 数 名: UnInit  L: v2 `3 U8 l, D# f
  4. *    功能说明: 复位初始化8 g2 ?+ X4 X4 ^
  5. *    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
    5 N+ X- I5 Z/ [# m# x
  6. *    返 回 值: 0 表示成功, 1表示失败
    1 M' z- j  }) f8 l* C
  7. *********************************************************************************************************  b# U# U; E3 {/ `9 d
  8. */3 D$ d* T3 B% k+ ?, N% k3 v
  9. int UnInit (unsigned long fnc)
    ' A6 d: Q% J- C/ n8 i+ B4 Q# E0 T
  10. {
    0 k  Q0 Q7 p% T) {! n0 ^
  11.     return (0);
    5 ^; {$ v2 B/ P8 C7 Y
  12. }
复制代码
) [# r/ m/ R* T. ]
复位初始化这里,直接将其设置为内存映射模式。! _0 c% r% E1 |
& R7 C( ?9 `- p1 d
  整个芯片擦除函数EraseChip$ Q4 m& ~" i/ w6 t4 q- K
如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:- O$ @& v  f8 O) t, A, }8 c

8 |2 p- t7 S8 ^8 G. ?- g! v
23b**ee7f8052bf8a02d0531285d14.png

" \1 `, F  u; n8 y& `1 q+ _1 z2 e6 q/ d
实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间。' c4 L) g( P- s9 j2 T% \& K  Q
) {8 e. v1 r$ I6 G/ ~+ c: u
另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。
0 h1 K/ b2 l1 R& W6 S
" F/ E. a% b: y1 s
  1. /*$ Q  C0 j1 [0 N$ \
  2. *********************************************************************************************************/ R3 x' g+ H6 c6 o
  3. *    函 数 名: EraseChip
    : I+ t+ |( Q5 ~( V2 v
  4. *    功能说明: 整个芯片擦除
    ! J; L* I# |; m" I
  5. *    形    参: 无
      m+ o# B. o5 L# `
  6. *    返 回 值: 0 表示成功, 1表示失败; B% E0 q& a; _4 h* t  ^# j
  7. *********************************************************************************************************) {" y  u$ w7 ^6 U& @
  8. */. E) K! Y' h5 _4 W
  9. int EraseChip (void) 1 S  B( B# `+ n9 p
  10. {    : z* x- t4 x/ x4 Z
  11.     sf_EraseChip();8 ~8 {; W8 A* F4 \
  12.     return 0;      ! k- D& V! F2 T" I3 Q
  13. }
复制代码

6 Y. _& u* ]  [  扇区擦除函数EraseSector$ t/ p0 K& h9 t$ U7 P" v
如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:
# v+ t* h5 ]. ^3 K+ Y  N
' e  O5 G/ |! }4 `- i

- V" N/ @5 n2 D, [
1 F2 i. L+ H, D# h
  1. /*3 r0 C3 Z  ^3 w/ b
  2. *********************************************************************************************************
    5 i; T% o* x' J! u3 q" U
  3. *    函 数 名: EraseSector( a4 O. Y! ]: P& k5 d
  4. *    功能说明: 扇区擦除+ h% b+ \9 N4 D" s% S
  5. *    形    参: adr 擦除地址
    ; k$ C; M7 M9 P$ I. ~" ^! X
  6. *    返 回 值: 无1 z& f8 I+ a5 ~: A# h1 o* V
  7. *********************************************************************************************************: B6 p" J' c9 E  W8 X2 Y1 K
  8. */
    3 A1 J& F8 d9 i; |  r& H- b
  9. int EraseSector (unsigned long adr) # m: s/ P. A% W, u3 ~( g% }
  10. {   
    / \5 \$ W. l8 D" s" q, U
  11.     adr -= SPI_FLASH_MEM_ADDR;
    - E7 z  c* I& a' L0 b4 p* v; D8 L

  12. ; z  l: k8 r3 a; M+ S, p
  13.     sf_EraseSector(adr);
    ) n5 ?/ n. j  i9 u5 b" q5 ?7 v
  14. : D! m/ P& g9 p  a9 v6 `& a
  15.     return 0;     ) m3 o/ i/ \: X( o
  16. }
复制代码

. U4 z# R. V/ c% o* |这里要注意两点:
  O. j  n! l% q4 f5 S) J' g& {# z: |) j9 \
(1)     程序里面的操作adr -= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。特别注意,我们这里的0xC0000000是随意设置的,因为STM32H7的标准SPI外设并不支持内存映射。* u1 @4 U1 D$ Z9 {
8 l1 e4 e) v9 d5 y) O$ L
(2)     这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。4 g  p; j; W  s- e: N

' h8 Q3 O2 Y2 E7 {" Z6 L  页编程函数ProgramPage! Y$ m7 I2 L1 a: B) ?$ j4 H. e
页编程函数实现如下:; M% i6 D0 h, S6 ^0 i$ W1 e
" J$ M& X9 n: \: E( `
  1. /*! Q0 ^  V  F4 E2 d  j
  2. *********************************************************************************************************
    ) t( T8 ]7 R$ m1 n0 N
  3. *    函 数 名: ProgramPage4 A% \; P$ ~7 c, O6 Q* s
  4. *    功能说明: 页编程
    ; c' l& Y1 K' U, U% H. b5 M
  5. *    形    参: adr 页起始地址! m1 G! F/ O* @$ c
  6. *             sz  页大小* c& M% N( \6 f
  7. *             buf 要写入的数据地址
    / r, c5 [7 D3 c, S
  8. *    返 回 值: 无
      F( B5 c$ k- \# C  l
  9. *********************************************************************************************************6 [6 D" R+ I3 y( F" G8 l9 ?! r
  10. */
    : P( }9 `- a8 Z7 {
  11. int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
    3 X$ L5 x/ U+ G' D( T
  12. {3 M5 h% b5 ?$ k+ w4 c
  13.     adr -= SPI_FLASH_MEM_ADDR;
    + ^: G" x" k- M

  14. ) o! T+ k) U% t: ?0 d- \2 O& a
  15.     sf_WriteBuffer(buf, adr, sz);
    . x' E5 |  x! l. q
  16. / S$ K) c) J5 Z, _
  17.     return (0);                     
    8 s7 W- c* {8 e8 M1 `1 N" T
  18. }
复制代码

# h( `5 u% R: M: a! R, }: i4 w这里注意两点:
9 R% J6 @8 Q+ [, y* I) o7 b1 h! H4 S" [+ C# K% O$ H$ [! ~, G- {, Z
(1)     W25Q64的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。/ C7 v  e/ W( \0 V6 m! _! W- F
4 {7 W& Y3 M; }+ f/ K+ t
(2)     程序里面的操作adr -= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。9 D& d7 B4 Y) R
3 g3 u7 P4 D' w
  读取和校验函数; @0 t- f% E+ n4 l
校验函数实现如下:
; H$ y3 B& Q! x/ Q+ v
, r0 N7 W7 |. R4 h  @% Y
  1. /*; D' X+ V! M3 G# c! b8 R) _
  2. *********************************************************************************************************' ]+ b$ ]4 ?$ t9 d2 |4 G
  3. *    函 数 名: Verify4 X4 ~0 _8 A& r/ n" Q& j- b
  4. *    功能说明: 校验" c+ ~8 |' E! L( X' C( i4 l2 u  s  }
  5. *    形    参: adr 起始地址
    8 y5 y6 m: u8 v/ |2 b
  6. *             sz  数据大小* d6 k8 H3 ~+ K
  7. *             buf 要校验的数据缓冲地址
    7 x- t" X$ @' O( D
  8. *    返 回 值: -
    6 _1 ~' n/ f$ e6 l* q
  9. *********************************************************************************************************
    ; f" m: z& R+ j2 G& R1 {' s1 T
  10. */0 a* T7 k) |% }: p. D# U: V2 Q, A
  11. unsigned char aux_buf[4096];6 [7 O) B  c2 M
  12. unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)5 k" h5 e/ A+ c: N; m$ V) K$ q
  13. {  K. s0 s5 Y% ^- r# r
  14.     int i;9 |. ~. O/ d4 |* b! B

  15. 9 A1 m- f0 f1 K* B" Y- b; y1 \( o
  16.     adr -= SPI_FLASH_MEM_ADDR;
    6 V8 `7 P" f+ |( j
  17. ( ~( ?; a7 {6 T& w0 [! _
  18.     sf_ReadBuffer(aux_buf, adr, sz);3 D* c  Q9 B* n5 k; }" Q

  19.   Y2 v+ `9 {' ^, \
  20.     for (i = 0; i< sz; i++)
    , F3 k" U0 C2 [* s" T$ i7 K
  21.     {
    : K! O' J9 m0 R2 L1 H
  22.         if (aux_buf<i style="font-style: italic;"> != buf) 0 j/ K9 y7 C. ?7 @; F( P# {
  23.         </i>return (adr+i);              /* 校验失败 */     - }; @/ x+ Y+ z* R3 ?
  24.     }
    ) l7 D* x& ~9 a" ^1 |* J
  25. 3 W! b1 W6 v) w" r
  26.     adr += SPI_FLASH_MEM_ADDR;
      s8 B( m* ^. ~/ V/ E, g
  27.     return (adr+sz);                 /* 校验成功 */   
    , o/ U- ?7 v/ h- {" d5 R: F
  28. }
复制代码

% N1 Y6 P! i* u( G84.4.7 第7步,修改SPI Flash驱动文件(引脚,命令等)) p% \, S0 `- |" q
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:* D. a7 }# n% I' F5 w

/ s) r6 u4 G) k
  1. /*
    6 e, w) K( G- Z) a4 e
  2. *********************************************************************************************************
    - U! e" `3 T+ o7 A0 q; G
  3. *                                时钟,引脚,DMA,中断等宏定义
    . K- `1 \* t) E/ v
  4. *********************************************************************************************************
    / Z/ Q4 x- A' D, T
  5. */9 D8 n; o. Z( Q5 Y, W7 w" v0 b
  6. #define SPIx                            SPI1
    2 D# r1 I' l( O6 x
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()0 D- }6 s6 G7 Y/ I% Q
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()' ^" K/ R, N! Y9 f0 Y
  9. ) s+ ~5 [: Z+ z. W7 r! D0 ]
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    $ j1 H' ?5 C# o4 p! {# o9 p
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()- Q3 W* ?2 c: Y( _: e; J

  12. . \9 U: H9 g( A7 m! J. `  C4 k
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()# B6 h1 Z# X- j: l! y/ S
  14. #define SPIx_SCK_GPIO                    GPIOB
    ! {% G# W; f$ x
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    7 y& L; p5 y2 A
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI16 Y( M, B. `  }$ Q8 L: _. B

  17. 1 s& ?* z) ~- ]' L( I0 S0 W8 [( H
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()) s  J, K+ P; o" ]" ?. H; @
  19. #define SPIx_MISO_GPIO                    GPIOB
    9 [8 @- W% ?, ~, b
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4; s6 o7 m2 ]3 C2 d7 A: {5 P
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    # O5 M# B2 P2 l

  22. $ p/ H& w- p; b! h( Q( I
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    0 h3 H6 i6 G  e2 G8 z: S3 T" N
  24. #define SPIx_MOSI_GPIO                    GPIOB8 G6 s7 _1 _. W, q* g% V$ t
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    7 `3 _: U5 n7 A! l7 ~
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码

5 C' v, P% o+ z- I. m; L7 {硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
2 @# f' }& ^' V$ R7 r5 p( H: {
, Y. R  H0 x" c主要是下面这几个:
% U3 g5 D7 m% L8 D. g
' e$ g/ U5 ?+ g8 R2 {
  1. /* 串行Flash的片选GPIO端口, PD13  */) U: n& a8 m$ {
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()# T7 G6 R7 w' Z% q3 F
  3. #define SF_CS_GPIO                    GPIOD% Q$ \" h" {4 a! H, M- B
  4. #define SF_CS_PIN                    GPIO_PIN_13
    3 R3 f1 h  t3 W7 t9 K' Q+ O7 O
  5. ( m: r2 B$ a& \8 |- P: v
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) 9 S/ \: A* W: H" H
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN2 @+ F- _) y, |8 w! A' z& F
  8. ! L! W! b+ G* y4 ^* U$ K
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */
    : E- F& ^1 n' T+ K
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */2 ~4 x1 ~' z/ ~& O# K+ }) S1 @
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */' A2 F$ C; `9 [. y5 r# P  ?
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    $ Y5 y& y& ~4 u2 y5 V
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    8 L( N, A3 V0 O3 B1 ^, `: ]0 b
  14. #define CMD_READ      0x03      /* 读数据区命令 */6 m- X" W( l- l$ r* L. ]. \' v
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */1 ~& A4 ]8 d( |) B+ I; _
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */: P) P8 U1 Y) |- C
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    ) p5 A. w9 ]  G8 U+ P- a3 T
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */+ ?9 B: h; r$ O  ]) a+ E9 N
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */
    4 q% M3 E1 S. {, X4 E' B( g

  20. 8 x5 z( X$ @3 m8 s! O
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

/ j0 D4 h, u6 Q8 j84.5 SPI Flash的MDK下载算法使用方法
& D1 S$ \/ j9 }3 y5 X6 e& |编译本章教程配套的例子,生成的算法文件位于此路径下:1 z+ P1 @2 N: x

4 y. `2 k1 A3 N! f
15d26ccebf6ffb3480f4ca6cecf15906.png

8 g3 S! Q6 c6 x7 c; j  E
5 V9 @: l9 ?6 J. c1 J84.5.1 下载算法存放位置
, i( l* |& Q. f/ J* ~, D) D生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:
1 O; S% l/ Y/ R
; u: j1 O! k/ Y/ @' q2 q1 Z$ v1 \+ J  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。
' p* d7 X' C! p! ]7 k& g  第2种:MDK的安装目录 \ARM\Flash里面。+ u- n% w) D5 p* Y
9 U2 Z' y1 P6 U# \/ O/ i

6 o- j3 ^' E9 J& {  O84.5.2 下载配置7 a/ g1 O, F4 c9 @4 E
注意这里一定要够大,否则会提示算法文件无法加载:6 }1 k+ v# c( w! X' [4 z! d

/ G! `+ {6 V- }
f6e80114a68806de27b08975ee670898.png
  E8 ]% V7 v9 D
5 G: ^; X9 M1 }' _; }9 t
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。3 d5 U  d% Y+ W

" e/ p2 Z1 \. R0 @! Y1 N' A$ X如果要下载程序到QSPI Flash里面,需要做如下配置:5 n6 M* X1 Y6 z4 ]) r3 |

5 d& B' Y8 G% e. n
efe1ac1d0fcdd711fb010157b7e1d3a8.png

% m( E$ s) @, b' U& {4 F
+ ?. \: N. v! i84.5.3 验证算法文件是否可以正常使用; X, u( G1 p" b$ l5 Q- S5 X9 E
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。
1 J8 E5 n: {1 K9 e2 _
& @2 Z" q; b+ T84.6 实验例程说明
1 s) n; u7 f( O; E- {* r本章配套例子:V7-065_SPI Flash的MDK下载算法制作8 D9 e* O- W- P* V$ A0 v/ o

( u& ]+ B& J2 q3 [编译后,算法文件会存到此路径下:- f, C$ B( k, _9 w: ^

$ U. Z8 S! c; N: ^" x2 s
d96f01a2f53788079a9c6504bdae69e5.png

1 ^8 {2 E* ]. U' {9 C  k4 m5 C# J
6 a! u9 l: ]8 J2 Y% p5 @84.7 总结
" |" y! ]% J, H! \本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
0 A0 m1 H! v0 A' u
) d! j$ `+ F2 f, v( U9 A$ z+ Q9 ^9 A. P2 A8 i
419d43f9b4e68e2849437c14e5a20fcf.png
f0f8d92a80ae444a604f30fa68d200e7.png
赞 收藏 评论0 发布时间:2021-11-6 23:05

举报

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