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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示
/ m! p  U2 t7 z% O( Q: J  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
! ^$ [0 m$ o; I+ z  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
+ ^# m) w4 L) ^. D9 H  h9 a  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
2 D4 ?, Y! a# A! [/ p2 y$ a85.2 STM32CubeProg简介
- F5 c2 W" G6 l" q" ^! JSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
0 s: M' r' P, x9 K. ?, \! i+ ?* h% o5 @
d80578d217eb2a83520c6f3d797218f4.png
) p( l& {: x9 H% ?5 a
1 t7 z3 f- ?4 l5 f, P
85.3 STM32CubeProg下载算法基础知识
! K1 r, _! G- }  D5 u& h
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。+ \" I" b, `0 _6 a/ `* E
- m: a, j; a. e, i; ?' l
85.3.1 程序能够通过下载算法下载到芯片的核心思想
/ F8 m% p. P$ K( t认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。8 G6 j' t+ z. y9 o5 t
4 v0 \/ K: j9 a5 z7 K6 u
85.3.2 算法程序中擦除操作执行流程- g, L" r( `  [1 [! R  `
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
# o: U! Y0 b2 D; @$ ^4 l( @; K6 F2 V' Y3 l/ |2 F3 @- H
擦除操作大致流程:, v5 f- R) o: w/ W# B6 d& }

  E/ K/ h* }; h
d75670256363bb50203bed0bbe4f9fd2.png
8 a1 `) A6 Y+ @1 C8 L! N; r, U

  I9 g2 v1 c8 e0 z' y+ I- X  加载算法到芯片RAM。
  T6 f: O/ Z- g( T  执行初始化函数Init。  F& d6 H8 G9 V3 ?6 C& o& p
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
7 Y% f' s& Q  M1 t  执行Uinit函数。# S! w; C1 V- ^/ C3 u  n* B* d/ Y
  操作完毕。
7 G1 K8 x5 u8 l$ M6 d0 Y5 G8 V0 i" V$ F; M) g& K9 k4 s
85.3.3 算法程序中编程操作执行流程5 w& N* e& F& F# L% l9 k4 C" J& Q
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
/ Q. R8 b* g: B1 q( `# K1 }; f/ ]. K% l/ F0 l
编程操作大致流程:+ J2 [& \: U  ?- e# X% O; o5 m8 m# M
7 t) B* g4 d, Z( m& j6 M" R
4fa96e9e33d700081f2cdbd312afb31e.png

$ S1 r4 I6 i' h& X& E+ _. s' f: M( q+ J# f# d
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
0 {3 o+ j  B1 u* p% x6 O/ P  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
7 z1 G2 E. {- \; F  加载算法到RAM。9 G1 v" ~5 j$ C. K4 N
  执行Init函数。
4 v; A# ?+ i9 h( {3 M: S0 c  加载用户到RAM缓冲。
1 w  Q( V% h9 g  执行Program Page页编程函数。, f  y; }# Y2 P- T
  执行Uninit函数。  Y9 R$ @  I! _- L' h: m  h0 o
  操作完毕。  D  n' E: q7 z3 A
* X* {7 Q: W1 }, G. m$ r8 F, e! @/ b
85.3.4 算法程序中校验操作执行流程9 T" T& k* Q8 y( G3 h
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。. @4 v/ ~% r3 o9 K' U
3 K' J1 I4 T6 G; l* `! ]$ i
校验操作大致流程:7 [, z+ N) D0 e5 C& G

2 a" E3 o; Z( V5 f
7deeae4b650bd82c457492af14fc26b7.png

; R' `+ l7 Y( c# R  c; g* f) o
, I" ?+ z& C0 g& f2 v% k# z  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。3 P! T# ]! J" R( Y, K8 \  T" q; s3 t
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:3 T- u( ^1 W2 X
加载算法到RAM。
2 S& T: Q0 w: A/ z: Y- ]0 O% s 执行Init函数。
5 S; r" s% ?- Z4 r! v 查看校验算法是否存在& p8 p( E. [# Y* ~8 w5 F2 V
  如果有,加载应用程序到RAM并执行校验。
6 @: u' H3 x  I0 f* S4 f  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
, R$ ?- m" g# @  |  执行Uninit函数。7 D/ U. `' Z/ `1 }- ~; B
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
) e4 T' f! ?' }" ]  执行RecoverySupportStop,回复支持停止。; g( D5 Q; K! @: O3 [
  执行DebugCoreStop,调试内核停止。
+ k  I: z0 D1 j8 {7 s% P  S6 l) ?  运行应用:/ g& ]7 ~+ i/ R( m4 Z7 e
  执行失败
' S9 a  a/ F/ O) G  执行成功,再执行硬件复位。! \! ?  S% K: R  u6 j! [
  操作完毕,停止调试端口。4 g& o/ f% d2 o9 ]

$ ~  [" w0 K2 ^+ ]: c7 q) x85.4 创建STM32CubeProg下载算法通用流程
1 }/ L6 v4 ^0 P" X下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。' m" b2 k: X% D+ y, T/ `. @

# Q8 O% O) R8 s: M, y85.4.1 第1步,使用STM32CubeProg提供好的程序模板. O. |/ {$ E4 j  t
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
) q9 I* x: r0 y% B; d7 w2 a2 f. }+ U: `6 X. w0 ]
97f385e3b62a055bac9b5c0214b877e5.png
+ R  D5 m. X" Z' P- R$ g+ c2 O2 P2 D5 S

0 B( h# C4 v1 w以M25P64为例(注,其余步骤就以这个为例子进行说明):3 C5 U6 O: s# e7 m/ c+ F
% C' g. f4 _: z$ E& A$ Z, @
43b298f996937fe35e89697fc6479cb9.png
0 z) W7 o; r  u) G& H% E3 a) S
9 k5 p! H( [4 @1 m8 v
85.4.2 第2步,修改工程名5 u3 H  Y: E& \$ I
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
7 U+ q( K8 s. R4 E$ }) ?) t
* \# u+ N3 ]' M85.4.3 第3步,修改使用的器件
- I$ Z7 u& v. q& k7 @
在MDK的Option选项里面设置使用的器件。8 B4 R. o6 ?) F" C2 U% q

5 [% a6 e. E4 r/ P, G" c& Z" A* e7 i
36026b9a6d60249610d0246eb725b246.png

  t( @& T5 M1 v2 \3 j( L# [" I' b0 g5 w
85.4.4 第4步,修改输出算法文件的名字$ K( `3 i$ x! {' c9 l
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
5 [  U4 Y4 G7 A$ W; L: w1 x
  o+ b& ]  m% Z0 U. T+ v
52c72b3993f28a366a22eac56f8fd043.png
8 u/ b2 x) [& f. c5 v

3 C$ O3 ~+ j! b4 N  e3 B2 n注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
" x3 Y5 `2 U- e% ?* G' l& k( O
7 p/ H8 ?: Y) b/ F3 O2 O  X
7b48153027a44de33926c2da008716a0.png

& M* Q0 G9 z# T' S# L! t8 R; c
0 H, O) ^& `% H) h6 T85.4.5 第5步,修改使用的库文件和头文件路径- R+ s' U  b; l  G
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
# b8 M+ [& a( K1 _! J
- V8 p2 Z. e' `4 R7 \! m/ H85.4.6 第5步,修改编程算法文件Loader_Src.c
/ N1 F( o. Z- l8 U2 }模板工程里面提供的是M25P64,部分代码如下:1 Y, \. R" M7 U+ n, d

, c/ o( V5 q+ `! k0 X6 U
  1. /**
    $ J" T  @! I2 ~5 ?$ \% u
  2.   * Description :
    / t- M+ {$ [7 w. K
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the" F. q* @  L/ p
  4.   * device and initilize the FSMC with the chosen configuration 7 J) O6 ~& P* T# \2 u, g
  5.   * Inputs    :
      s* }( F3 J0 g6 u
  6.   *      None9 |6 ^9 b4 u; ?, |
  7.   * outputs   :3 N- n7 {8 i( [8 T  {
  8.   *      R0             : "1"             : Operation succeeded4 k( t& m, v5 W9 T- S8 q
  9.   *               "0"             : Operation failure# }* f0 v3 I+ J, {# C4 h
  10.   * Note: Mandatory for all types of device : X/ ?( ?* R- u
  11.   */
    # K. D$ x: \+ I+ h8 L+ {8 ]7 t' f( L
  12. int Init (void)7 g+ _; [% J; \" ^) U- l  @
  13. {  $ |- n: U* B" p7 W7 o& U
  14.   SystemInit();( c/ s4 V& {) T
  15.   sFLASH_Init();' `+ b; R1 h/ Q; {/ a' e2 N
  16.   return 1;
    , R& Z2 F9 v: z. k$ h+ V& A, O
  17. }/ @, l- @: y) w4 c4 K4 Y! k8 x

  18. 5 q5 z3 i0 Q* W, [8 W# u7 p( }& q( {
  19. ; u, Q6 Q$ c. u0 N* J
  20. /*** |( p' e8 h1 j$ _6 I; J- f9 _, m
  21.   * Description :+ ]/ Q% I% S) [# [5 O  a
  22.   * Read data from the device ! C$ h, N/ h2 \' w
  23.   * Inputs    :7 `) {8 Z$ T4 `, R4 F
  24.   *      Address       : Write location0 y5 d; ]4 }& f7 W2 F
  25.   *      Size          : Length in bytes  ( w7 `/ W* G0 L1 B! n  k
  26.   *      buffer        : Address where to get the data to write
    : K' b" J2 |4 x. ^7 ?3 a" P! a
  27.   * outputs   :
    6 V+ M# \8 K+ ]& J: k) R
  28.   *      R0             : "1"             : Operation succeeded% k3 q& d# t5 V+ R
  29.   *               "0"             : Operation failure
    5 p2 [: P- d* [4 ?' h' x7 B
  30.   * Note: Mandatory for all types except SRAM and PSRAM    % |. G& G1 D2 x* g8 O/ b& Y& @
  31.   */
    . L3 w" z1 G5 e' Q" V9 C7 \
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    9 a$ P8 d1 w' r+ }! [2 l  l/ X" C
  33. { 3 i& o" P2 {+ g: p0 h/ R
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    . j0 v( f! ^- Y9 d7 \5 p
  35.   return 1;; k) m8 r3 O5 {. M7 U
  36. }
    , i" B- t/ u* ?
  37. 6 Y8 R! ?3 F6 J6 C) X
  38. ( V0 A1 a2 B' q, Q; h. f
  39. /**
    % Z$ h. }. s9 f5 Y
  40.   * Description :; W. s; f* s% {7 N: x+ J
  41.   * Write data from the device
    $ U# T8 n& f- f2 N8 u
  42.   * Inputs    :
    & l, w, x# f' M! t
  43.   *      Address       : Write location
    2 v# @6 k; ]' S5 \% \) O; W" V: i; Z, D
  44.   *      Size          : Length in bytes  
    2 B8 k* f, h" h" J" U
  45.   *      buffer        : Address where to get the data to write( N( g  U: @; D/ Z6 Q
  46.   * outputs   :7 w6 }1 |  t" |9 n% y7 d" p
  47.   *      R0           : "1"             : Operation succeeded
    . N, G' Z% ~; v2 ^, Z
  48.   *                     "0"             : Operation failure
    + ~' ^& v- h; a7 @# U; v! G% ?
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    6 \2 j* ]3 J7 B$ _" d, n
  50.   */
    % f& {  ]5 o' c7 D9 V& w
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)4 O; A/ g% n( I5 ~
  52. {0 C+ [0 u4 E, X9 O* B- u6 e8 h1 f
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    7 u/ U9 w7 w- i$ F5 S% A6 t
  54.   return 1;
    : D; _# K7 |$ U7 d( V/ X3 `
  55. } ; E8 ^8 e& D+ W& H3 I

  56. ! G5 P# H4 n# ]' D: B4 o; y* x7 ~
  57. $ D  \( M* u+ s9 y
  58. /**% M+ e9 ^3 J! t3 |% V. e
  59.   * Description :
    + ]3 Z9 }2 y* E/ h+ B' N- @
  60.   * Erase a full sector in the device
    ! Z) K1 Y4 _- h6 E* }
  61.   * Inputs    :
    , a" o0 S4 y' `) s. ?* }2 A
  62.   *     None, _) X0 p* d' ~$ K- ~
  63.   * outputs   :- E. z4 d; \2 ?  p: H
  64.   *     R0             : "1" : Operation succeeded  O1 d4 l/ S; d+ `
  65.   *              "0" : Operation failure
    % u4 V/ H" F) H3 x  N+ y
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH: F  l, E' M" |% F
  67.   */, \4 ~+ v; o2 ?' e- U* E" O- l0 _
  68. int MassErase (void)# f3 Y! G9 L# `3 Q" E' |
  69. {  
    + z) r' p4 P4 r! O
  70.   sFLASH_EraseBulk();
    ( k. o/ a# I) k6 x" Q9 R* B
  71.   return 1;      T' m  }( H+ c& Z. G
  72. }
复制代码
/ U% `: X3 J$ K0 [
85.4.7 第6步,修改配置文件Dev_Inf.c0 W- ]; K% O( ]* p, W
模板工程里面提供简单的配置说明:
$ Z5 M( S4 e- r1 `6 r0 X' x/ v' S, o; o9 v7 `% b6 b- c
  1. #if defined (__ICCARM__), f. M& z5 B; L: P) F3 k
  2. __root struct StorageInfo const StorageInfo  =  {
    ! Y1 M1 J1 x0 |1 b# n  e
  3. #else! ~+ x" l. \, Y
  4. struct StorageInfo const StorageInfo  =  {
    # l: f, U+ x0 `5 W' i: h( n
  5. #endif* I7 T2 z2 {7 f! W" |
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number  m; ]# T4 |3 v* m& y
  7.    SPI_FLASH,                       // Device Type& b# K! P4 |- Y
  8.    0x00000000,                     // Device Start Address
    - J7 }, E% G# t3 f  K
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    6 s: Z# R8 y, @; F6 x/ ~" E
  10.    0x00000100,                      // Programming Page Size 16Bytes
    5 l% A6 H# j0 |- F/ x" S$ D9 |
  11.    0xFF,                            // Initial Content of Erased Memory, J- {; W3 T" X3 l) L
  12. // Specify Size and Address of Sectors (view example below)0 x* i( g  U- c" r8 L" u
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes 2 l. T) o! |- T
  14.    0x00000000, 0x00000000,
    7 B$ \$ G* P, |8 u$ c" T
  15. };
复制代码
. b0 l6 y* v8 H" _* D4 k, @# x
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。6 M8 w8 ]' a! s( `

! r% ]: u/ P, C4 L, a8 N85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
6 y  y- {: q  F
C和汇编的配置都勾选上:/ C- B, p( v9 W7 x' ~2 p( W
. v) A5 n4 i9 k- P& |5 z4 Y, Z
afddd6e839292aa2b94b43e79544d630.png

5 N  r1 W" B, }- t" G
9 t: s8 i7 ?0 a7 p2 E) r汇编:
# D) `* w$ _$ W( X" W& ^" o$ o$ |
3 M1 x+ \1 Q( t' L6 v% u* n9 [' {
f639ae5b0d3c2e0e0190ec509fbc8a4d.png

/ L5 A, D" \3 Q: p" e$ o7 q" ^; I: m3 }$ h  v# h
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
% z5 l5 l% l- H; G0 u5 q2 T  S! o& A/ G( L- y* a; f( W
(1)加载以响应运行事件。  `+ q3 @0 X) z# y! I+ V
" c0 o: R, \; V2 R
(2)在不同情况下使用其他例程的不同组合加载到内存中。6 L: A  E  \; Z
+ m9 Y/ F$ F9 U# |! h" h
(3)在执行期间映射到不同的地址。
- n' L% E* v; ?# Q
; l# h7 k5 L+ ?使用Read-Write position independence同理,表示的可读可写数据段。
' V' f# {; {9 I0 ^5 `9 O, t7 q8 w+ y  G. @& w: [
85.4.9 第8步,将程序可执行文件axf修改为stldr格式
: _* u4 c, O% e* j! e通过下面的命令就可以将生成的axf可执行文件修改为stldr。
) `- U  l+ ], y1 O' e' L9 k5 M9 \
c683673d2481d8dce75b6fa01d624600.png

# F" a3 g( o, @, D# K$ x0 X. q# V" k" P* p' J" o
85.4.10   第9步,分散加载设置
, b' `' ^7 O" |2 ~( k, E7 S) G我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
' S4 X+ L4 z  R9 X* @5 q- @
/ M0 L8 p0 V8 A9 a
b96835a7506516319002e6b2e8db9371.png

7 r. z- u; b0 z9 {9 Q* D/ y) X# l4 y) A9 E
分散加载文件中的内容如下:! p/ K7 v% ~: y0 i* I& {$ s

8 }! Z* ^( X+ Q
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions1 u1 O4 T. v; z6 _
  2. {
    9 g. v) H* k% h4 r* W
  3.   PrgCode +0           ; Code! P# @) c2 q, C# f
  4.   {
    ; ~/ m9 F- M: A$ a# |$ }
  5.     * (+RO)2 T5 H0 X  M& U: t% H6 |  c% ?
  6.   }
    1 Q1 t/ ^, [; w
  7.   PrgData +0           ; Data- @3 ]8 ?# m; L5 E& \0 |/ ^. ?
  8.   {
    3 T9 ~8 U) G5 Q) }& o
  9.     * (+RW,+ZI)
    ) o3 d+ ]4 T/ w# [* ]: @' X/ U0 s4 L
  10.   }% W; U4 E8 b9 D: p: j
  11. }1 z  z+ I& |  [' s0 d

  12. * M# \3 h2 b" E0 C" C* P& D
  13. DEVICE_INFO +0               ; Device Info6 n' B( z9 Y9 w  \- Y' `& s
  14. {: J! L0 i. h8 T6 |2 d
  15.   DevInfo +0           ; Info structure
    9 E* {$ i6 l& W
  16.   {
    / t6 O; Y2 ]% \0 J7 b
  17.     dev_inf.o  X7 R+ v/ ]# J3 u/ o9 U4 m7 [
  18.   }
    2 ^9 Q  |  t. [" `6 ~3 c# T' r
  19. }
复制代码
1 W3 P/ K2 E& R- V; s
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。4 O6 Q4 }7 S# ^' |% X
  [: J2 y9 |# t) L5 M6 P
--diag_suppress L6305用于屏蔽L6503类型警告信息。
! G8 o" l3 l7 y( x- Q' g- K/ Q
5 c+ @1 J3 d/ U2 d特别注意,设置了分散加载后,此处的配置就不再起作用了:7 c1 Z; l% B# N3 _9 J- I

, {" @8 G0 _7 p9 e' Q$ m
641395c374a3af02f1a92d3ff9ee4199.png

6 F, o# j, J9 F
' X( p7 t: I. \/ _2 o' C2 e85.5 SPI Flash的STM32CubeProg下载算法制作
1 I& C. W* J" O% o% v下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。/ [. d8 k7 N7 F& p% J
) d9 M- H7 D/ k/ Y1 x0 a
85.5.1 第1步,制作前重要提示
% U5 B/ h- D8 V
这两点非常重要:. Z: A: ]* A/ W( u
0 |; M/ i) ]! g) J4 W/ R
  程序里面不要开启任何中断,全部查询方式。& W) T, f8 T" y6 O' ^
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。$ c8 l$ ~( w; p: U, {+ t

+ t+ i- N* ~0 i' T85.5.2 第2步,准备一个工程模板- ^3 D! R( q$ V, Y* d3 s, w2 g
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。. v+ ]  Y/ _* {) |: q
0 K- g1 ]0 g3 X' _- V
80ad5e3abdd6a993c8621230401a1313.png
* b6 d9 P  K, B2 }# R
0 O4 f% _: o# H/ O+ |  [
85.5.3 第3步,修改HAL库
) \( Z' ?* a2 b9 l这一步比较重要,主要修改了以下三个文件:1 y" d- L) Y1 O* T. I

- o+ ?5 o9 @7 `. T( J
33bf89f25487547d4008f43b7cf10e03.png

5 ~" e& @0 p4 l
* l8 Y8 n5 |7 e- \+ u! l1 `- W主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
/ G7 @& d. v. p5 L0 X" o. C3 X
4 D) `7 C6 i+ ], G; u; r) Q85.5.4 第4步,时钟初始化* I7 w' ]$ |9 {% `% X( I) G
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:. V( s! H* Y% X! w
6 i( k9 ?( T( l+ N# q
  1. /*
    + o4 ]! b4 E$ {9 Q( @
  2. *********************************************************************************************************
    , G- x6 ^  s1 j' G9 ^
  3. *    函 数 名: HAL_InitTick
    & M$ K3 U8 z! d3 B' ~6 `0 K
  4. *    功能说明: 重定向,不使用
    : H$ Y2 }. L4 B: m0 e9 y4 l
  5. *    形    参: TickPriority
    1 S  ^; v. `' \' D
  6. *    返 回 值: 无
    / J; j! C0 \2 K1 e! N4 @2 @  W
  7. *********************************************************************************************************1 A" G8 E8 F; v* w3 W' I( E7 @5 A
  8. */) s. c3 L$ s2 y+ M# X
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)2 Y" K& W2 k$ q
  10. {8 V) ^% `/ o- i' l2 I
  11.     return HAL_OK;
    ' g' {3 x- ^% S- b- |! I! d
  12. }
复制代码

$ L1 r4 f4 M, N$ ?* u( q然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
5 u* T9 W/ e2 e4 C( Q! q9 ^. g- s, S9 o  l6 E
  1. #if !defined  (HSE_VALUE) 2 W' O3 D- G# ^% C) P7 S' O
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */9 z3 q7 A! i: _! W
  3. #endif /* HSE_VALUE */
复制代码

( _) W% L! U( L7 V4 n9 ]最后修改PLL:; D* G" O2 j. Z% {" P. N5 u
9 E" s; N. W' ?: t/ K5 n/ V) n
  1. /*
    9 L2 @" B$ X0 P% d$ K
  2. *********************************************************************************************************+ k$ [0 {% E: G  M! S
  3. *    函 数 名: SystemClock_Config
    7 c4 }7 e. z  l' Z( u: `7 X% L6 p  P
  4. *    功能说明: 初始化系统时钟
    6 v! @/ a3 _4 ~! A6 [
  5. *                System Clock source            = PLL (HSE)
    0 @: \: V# D/ H. S( B
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)% l: q0 z8 g2 g$ L' r
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    5 Z0 b1 O1 f" H& J; S
  8. *                AHB Prescaler                  = 2% S  }/ {7 }* W5 u3 _4 Y
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)8 M! _! Y) S3 [* M  T- S
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)$ {3 V0 x7 e/ j) ?  P
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)" o" a$ J6 h7 P
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    ! }, W/ R$ W' @$ u4 m3 d
  13. *                HSE Frequency(Hz)              = 25000000$ g1 Z: r4 B" c: w1 v
  14. *               PLL_M                          = 5
    : W3 ]3 u) {* ]9 k% u/ U
  15. *                PLL_N                          = 160. {" l7 R4 k6 h2 [" [3 t
  16. *                PLL_P                          = 2
    ' s- W/ U' u3 p. P
  17. *                PLL_Q                          = 4  \; }3 @8 D0 b; J8 R* f
  18. *                PLL_R                          = 2( E* {  a8 P) C/ T0 x/ _
  19. *                VDD(V)                         = 3.3$ k8 ^" R, `2 t' Z( f' @% K
  20. *                Flash Latency(WS)              = 46 N  h8 K& T3 ~: ^8 Z
  21. *    形    参: 无
    : z7 Y$ q1 `7 g2 r( i; S
  22. *    返 回 值: 1 表示失败,0 表示成功
    0 G) K0 `7 ?6 e/ R% {9 r1 @! K$ d3 J
  23. *********************************************************************************************************
    6 _$ |/ X& o6 {- T8 p5 A
  24. */
    ( R" j/ c! H4 W; D7 R6 d% ~
  25. int SystemClock_Config(void)
    8 s3 C) w9 ~# f0 h
  26. {. b3 K# B$ @7 U( x( {  @
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};. a5 V8 Z3 o) ^$ H1 m" f- u) I& I
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};! }) n3 y9 ^; Y8 D/ r
  29.     HAL_StatusTypeDef ret = HAL_OK;
    9 o6 {, K. e# ~

  30. / ~  U* B( d% \1 d5 E
  31.     /* 锁住SCU(Supply configuration update) */
    5 [8 j4 b  j% G$ D( M4 a
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
      y# V% p5 f$ o9 T4 P+ ~% U

  33. 5 s  S( a  V8 k* A
  34.     /* : G  c& G, u# ?/ j/ ?2 D; j9 c
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    $ [. n3 H$ T) n" x. [% W
  36.          详情看参考手册的Table 12的表格。# h- M0 \: e5 F3 H; k! q6 v
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。# o7 j/ h. e  H$ ]
  38.     */
    ) i- t6 f6 o9 l& \& L5 X! n! ?, n7 I
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    5 r8 R1 \9 y8 s4 }
  40. 4 _& h* Y$ A- V  N4 X5 L
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}( k( ]* b& b1 G/ m
  42. 6 x: B/ M7 u* [" C+ f9 _
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */+ ?. [1 \5 G" E! s: t3 K
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;9 y5 ^3 a1 d! r, E9 v
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    / b5 _  w' o/ G, ~+ d; X4 v
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;/ G& N4 r& E( b5 V( R$ l
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;9 x, c. R8 E" i! r' W
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;9 R$ g* G6 y$ U1 {: K
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    ; C4 }6 `9 k; k# Z) B
  50. 4 I  [+ `, ^4 l1 e$ }$ b# \
  51.     RCC_OscInitStruct.PLL.PLLM = 5;/ A: T  M3 W8 p0 @
  52.     RCC_OscInitStruct.PLL.PLLN = 160;8 J! S) p5 R  ^5 Q, L- V- j2 l& C8 u
  53.     RCC_OscInitStruct.PLL.PLLP = 2;5 ?* w& z2 Q; z
  54.     RCC_OscInitStruct.PLL.PLLR = 2;+ {' F1 D* C8 Z$ O1 s/ i; v
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    3 Q% ^4 \& l! T. B

  56. & W5 N( |8 H0 h& d+ O2 C  G. A. b
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    : K9 {8 ]  _' w) ]
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    : G; F# y8 d4 d. D' e9 A' K* y
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);8 E/ X' w/ Z# M3 @
  60.     if(ret != HAL_OK)* K' y& O# k' Q) J
  61.     {5 n' M& m& v, k& i* P6 j# @
  62.         return 1;        - @3 ~2 L/ @3 W" a2 }; y/ Y5 n
  63.     }# b4 p7 P4 t' V0 }8 @  P
  64. # U7 h1 o* Z2 H( q+ q# _+ Z( E1 @, f
  65.     /* ) R9 K& s2 P( T8 z+ r3 c
  66.        选择PLL的输出作为系统时钟( m. n' s$ B! @7 l
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    " r8 Q0 W! R" C$ k: z7 I
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线& [: r/ A6 I- m2 N8 b) x6 T
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线6 P5 p$ V. X8 {% G  L+ i* }# v
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    ( Q. P  [( z; t* X4 ^
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    7 H. Q) B* v0 C5 q4 a% g/ i9 y. }
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     , S  o2 @5 T& d! l$ d' R; Q% e
  73.     */" h( T, S; ^5 J5 m
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \" q* l/ V, T. ]
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    " v, @  e1 f! [
  76. # O# @) X( V8 i0 u' w
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;/ {- ?, e7 a, ?6 f
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    4 l2 o6 [3 \: X: z8 r
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    ) {1 _8 \  p5 d# d) o  D; x' V
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  ( a) T& [& i1 B* L$ f; o2 v4 _6 {# \
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; ' @1 z% }! V* O/ K
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; . t  m0 S+ t& W! R; u5 P( s5 D
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    ! c, O0 J; l6 D& H9 @
  84. * _  b2 ~4 P9 T7 a# w% w5 d
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    8 s4 v( a: j% m) Z/ A
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);4 j, ]; H/ h; J5 C& H2 L9 S8 U
  87.     if(ret != HAL_OK)' ]7 f% W7 j6 l. J, s+ z+ y8 p  T
  88.     {: X& m, l5 q1 b: a: D
  89.         return 1;$ J. F$ U% N) D% E4 ~/ u* e" L
  90.     }! w0 Y* \5 S6 Z0 c: w# _: M( Y
  91. 3 L/ D4 ~1 K8 k& u
  92.     /*
    + v: a9 o6 C" m& O9 m- p
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 4 _+ x: O& d  A' q& Y) G2 _2 n/ q0 p& @
  94.       (1)使能CSI clock" D6 Y) l9 [5 G% k
  95.       (2)使能SYSCFG clock
    2 ]/ M: \. q7 ^
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit07 e& Q$ U: N. I5 _! B( a
  97.     */
    + M7 R7 e6 D) c' N' {8 k7 p
  98.     __HAL_RCC_CSI_ENABLE() ;  V8 v) L  D1 P0 H9 p/ r7 g5 c' o
  99. ( y, `. |6 m/ F
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;9 ~2 x) S/ n2 g* D( @) p' g) x

  101. - i4 ~) b4 I, |
  102.     HAL_EnableCompensationCell();
    + [# _, Z* K% A) {

  103. " [& v. u- y; s$ u. C
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();: J% f: S# K" Z
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    , n: d, W! f8 h  y3 q" a
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();3 y  n( l% Y% j2 H5 |4 a" r+ |

  107. 8 [7 \4 o/ W" N5 }
  108.     return 0;
    9 n* w  O9 D) @
  109. }
复制代码

: `; {; S( L  T# M85.5.5 第5步,配置文件Dev_Inf.c的实现* ~. V* C: Y  y, ]9 g% m
配置如下:/ m4 Q4 a- \9 a9 s6 f

6 {& |' k5 ]$ w/ u: K3 t
  1. #if defined (__ICCARM__)
    8 f6 o% }3 K9 w" V4 D2 F
  2. __root struct StorageInfo const StorageInfo  =  {7 s8 T" x! l. R+ a) s2 Y) u$ X/ u
  3. #else1 j) O# H1 L8 i
  4. struct StorageInfo const StorageInfo =  {
    / ]$ a1 N1 b/ ^" @% m" z2 s$ S
  5. #endif3 q. }" m6 t, F4 x" T/ v+ L% H
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    # e+ G4 q# P0 F, V; F
  7.     NOR_FLASH,                      /* 设备类型 */8 z1 p* M2 Q( X! }
  8.     0xC0000000,                     /* Flash起始地址 */
    & g. i6 c2 O+ Y6 N( |5 S) S" |6 }8 }
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */& Q& |) g3 m# A% _4 w  w' o* A
  10.     4096,                           /* 编程页大小 */' E0 [$ ^# g6 ~6 q- F9 t9 \3 I
  11.     0xFF,                           /* 擦除后的数值 */
    ) }0 I4 Z+ Y" g! Z
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    1 `6 ^# V2 y  f/ R2 r' K: Q5 k" `
  13.     0x00000000, 0x00000000,! M# P- ^* c2 `2 T7 l( L
  14. };
复制代码
4 h2 b* y9 `( _: C3 P9 X
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:$ P* ]& d( L1 z2 A0 j! u
5 {  @3 l- q+ j' Q, F( P
/ j# y6 E' `8 Q+ f
% m8 N: _5 u1 [
85.5.6 第6步,编程文件Loader_Src.c的实现3 j$ y6 t: H6 T7 V
下面将变成文件中实现的几个函数为大家做个说明:
6 b# u- J7 o! O& Q: a6 e9 b
/ B; P; E0 g* H! T5 a! L; ]  初始化函数Init& B7 J) a6 k1 X* i; i* O
  1. /*/ r" y7 V* M, c* N* Y
  2. *********************************************************************************************************; h8 s- Z5 B4 R- w
  3. *    函 数 名: Init: _8 [8 ?" D+ D$ @
  4. *    功能说明: Flash编程初始化6 P0 U2 \, M: E. L( G
  5. *    形    参: 无
    6 {: p% ~) E9 {! M; C: [# p$ O( ^
  6. *    返 回 值: 0 表示失败, 1表示成功
    4 h$ z' q' }9 r6 y
  7. *********************************************************************************************************! J  D  P( l( k) Z) A1 ?: L' K
  8. */( Y8 y4 e; `. t" m: [
  9. int Init(void)7 @3 g' r* }& X" K; o
  10. {   
    & H7 ?5 w, T5 L7 L
  11.     int result = 0;% i* @! l( A- W1 y
  12. " e. V  D6 z8 A0 w3 K' O) ^7 [
  13.     /* 系统初始化 */1 w& L) K: }, U! B& [
  14.     SystemInit(); $ z- H- K# ]; E  q' R% _
  15. 8 p$ F- H' M4 D
  16.     /* 时钟初始化 */1 f: U. f( h/ F
  17.     result = SystemClock_Config();
    2 o# j3 v  \6 Q6 t
  18.     if (result == 1)
    4 w0 P' L9 @( G1 J
  19.     {  r7 g, ]8 W5 y4 P4 B% ^$ Y
  20.         return 0;
    9 d' k5 V4 o1 A- `+ }  |
  21.     }
    ' ?6 U- h( k% z, j% o/ M
  22. 1 o/ t! Z, j& k/ r2 s
  23.     /* SPI Flash初始化 */
    ( D  r: ~% B1 _" b0 k! `' _
  24.     bsp_InitSPIBus();
    / d, Q* {. `) S6 M9 |
  25.     bsp_InitSFlash();* q+ Y! E' @. ?4 m* V. c" H) |

  26. 2 x7 _: |5 I5 E* D. i% L
  27.     return 1;
    7 j, F2 B# s7 P) X5 D' A2 G
  28. }
复制代码

; \7 V/ M1 V! m  X: `, r2 ~  整个芯片擦除函数MassErase; G) M& J8 t" C* @( w& Z3 ?' ]
整个芯片的擦除实现如下:: H# i( v) o5 ]4 W, Q' t
$ q( W' S( Q) L, t
  1. /*1 c4 S9 `/ o3 R5 K  l7 z% F
  2. *********************************************************************************************************5 u! Q: _; S8 p0 ~) c( p% L; u
  3. *    函 数 名: MassErase1 i7 Y! l9 O' Y/ ^, K
  4. *    功能说明: 整个芯片擦除5 a, S1 H! ]4 r4 M- w! L7 @
  5. *    形    参: 无7 u. J) M+ \% R# c; W0 b: }& B9 ^
  6. *    返 回 值: 1 表示成功,0表示失败
    , V% x( w# d4 |& q; }
  7. *********************************************************************************************************
    , F3 W0 }) p' O: f% Q
  8. */
    ! K& l3 ~0 @1 d- y
  9. int MassErase(void)
    3 [5 j( }# L( ~' J6 \" X+ }0 m
  10. {
    1 k$ U$ p* y. P( N/ A  |
  11.     sf_EraseChip();) H& i1 [5 w6 {$ F6 Y3 G

  12. 5 g9 o; T( v1 Z" P
  13.     return 1;   
    ' p( ^% X8 m0 l1 X* i
  14. }
复制代码
% _0 [7 ~+ B( ?, Q3 `/ U
  扇区擦除函数SectorErase( L* m& y: m3 r* j  J/ W
  1. /*
    2 P0 H, \7 _8 e! g0 A/ ~
  2. *********************************************************************************************************
    " ~& z9 `2 T2 L/ B$ u3 b' M
  3. *    函 数 名: SectorErase
    ' H& c! D0 A# q3 O% U6 J3 Z' R8 s
  4. *    功能说明: EraseStartAddress 擦除起始地址7 g& }1 m1 x7 K0 b* a1 w4 W' p
  5. *             EraseEndAddress   擦除结束地址
    2 H+ c7 Y: J3 U, O- C
  6. *    形    参: adr 擦除地址: K: m6 G' H9 `2 @# \: M, k% h2 X
  7. *    返 回 值: 1 表示成功,0表示失败% Y$ h; @. ^7 R" H
  8. *********************************************************************************************************  I+ {$ j% X7 T7 Q0 ^$ {9 m2 a
  9. */
    * C* J3 K) i4 R4 \3 ~  {
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)# H: a7 t3 e" }* a6 p
  11. {
      T6 l0 W- Q2 Y+ c5 v  h
  12.     uint32_t BlockAddr;
    * k* h1 p% D  e0 Z; \
  13. 3 V1 a7 C. P+ Z& x9 E5 j8 B5 T
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;5 W. h9 A# F  u0 u, e' ~5 S
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;3 m4 z; |/ e6 L
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */9 U6 J; N5 T. _9 V& ^/ y. c3 Q

  17. / Y8 {: Z  X5 k" J& }
  18.     while (EraseEndAddress >= EraseStartAddress)
    6 ^6 K- g: c8 e1 b# u0 f
  19.     {& ^" h3 k' [4 D9 B  \/ p
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;  K: ~" h, O) {. n" U6 e- M* u

  21.   N0 N8 w, R1 ^# z, D% Q
  22.         sf_EraseSector(BlockAddr);    ' ~% }1 s+ p; o  h. g4 J: n

  23. 0 o) }) Q0 M! z& c' R! j# R
  24.         EraseStartAddress += 0x1000;
    " M5 c+ A7 J, f
  25.     }
    0 N' ?2 J5 r' q  ?, T

  26. * X& H# w8 [. D
  27.     return 1;
    - t5 H4 L# l$ j8 j0 Q, g
  28. }
复制代码

; I7 Q, J* X' [! G: N0 P这里要注意两点:
( x# r) m+ }3 S2 [' T9 K: P' n9 S) I* O2 c: E7 B% V
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
- T4 M0 P" M. D& n5 X* J1 p) n& ?. {6 |1 P4 K0 Q$ G( ~( w
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。2 [; M1 T7 N& \) I+ k0 B

* R7 U5 K5 }! U5 }6 F9 i  页编程函数Write; B7 ~& |; l, M# y# m9 C9 N
页编程函数实现如下:
: d9 m- ^' f! G. b7 o* R- z3 e8 @* f- Z9 R' B4 R$ {
  1. /*
    / o/ W7 i  X0 _8 b
  2. *********************************************************************************************************2 O( U5 U# e! k0 H( _
  3. *    函 数 名: Write' f; s! L, q/ ^( Y
  4. *    功能说明: 写数据到Device
    4 l9 ]) Y: ~- \9 U7 H6 ~) |6 p
  5. *    形    参: Address 写入地址
    # }5 M2 c9 ~1 [; K
  6. *             Size   写入大小,单位字节
    3 @! |" v" W" r0 F, b' Z
  7. *             buffer 要写入的数据地址
    # E! @( E& w* O8 a
  8. *    返 回 值: 1 表示成功,0表示失败
    8 D+ M, U% N+ M: @& H# o  s
  9. *********************************************************************************************************0 \8 g! q% m, u% X% t4 V* T3 E: A7 y" D
  10. */2 p+ K; y& \' s6 ~5 d/ [$ p
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)) K: E+ E6 W3 ]
  12. {  
    # [9 K. V! x# w( ]
  13.     Address -= SPI_FLASH_MEM_ADDR;/ g* z: r, Z. m9 M3 H
  14. ( R) G  }! ]7 S" Z
  15.     sf_WriteBuffer(buffer, Address, Size);$ s  U  i* d, ?. D9 p
  16. 7 Q! w; n  O# w. P
  17.     return 1; 8 S0 n6 _! D! Q! t0 ~4 [; O
  18. }
复制代码

- h( e: m  ?1 m# }; ]1 A! E7 B这里注意两点:
9 c4 r# g6 A) @- l  e, j( M  s6 J' m) h' w5 U- Q1 u
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。3 a+ }# F- e5 w1 l; X

" _7 H  d9 S4 m8 S+ `7 b, f$ {(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
* H2 l) u5 ]! x9 p" E. ?: d# r& E! D: N5 F/ r
  读取函数
5 `! O+ s0 u( f
  1. /*( q1 t8 s" _9 t- Q( w
  2. *********************************************************************************************************) b/ v. S- D0 H! ^) q8 F
  3. *    函 数 名: Read( V& A, a- h3 U- v3 d* O2 I: X
  4. *    功能说明: 从SPI Flash读取数据
    9 J  [% Q6 P3 b$ c. V6 T
  5. *    形    参: Address 读取地址7 c7 g5 C* e+ Q% X# m
  6. *             Size   读取大小,单位字节
    ! m. b  P! R+ o: J! W# y( R
  7. *             buffer 读取存放的数据缓冲7 T. j# X% I4 m# j+ ?9 p1 c
  8. *    返 回 值: 1 表示成功,0表示失败6 \% C4 P- K/ R0 D( T+ M1 N
  9. *********************************************************************************************************
    : j# g1 q7 M) a0 K+ T2 D! G
  10. */7 d7 e3 X& n! T
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)& A- P5 s0 W: G; X1 r/ ?; r: A
  12. {   n" D* V( b8 Z/ S/ _' y. j; _
  13.     Address -= SPI_FLASH_MEM_ADDR;' a* s; w: {4 `! z- j* _. E
  14.     sf_ReadBuffer(Buffer, Address, Size);0 N) ~7 A' \+ a* O; g5 I

  15. ( r- E) P- S. o
  16.     return 1;
    $ c; k( N5 g7 ]8 V9 D
  17. }
复制代码

5 a1 ~, K$ i! N9 _# c. o  读取和校验函数8 b2 r2 n2 l5 g& P+ w% N
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。' Y- @4 j1 P. U1 h3 P
' E7 }% A9 J3 [8 F4 B' ]/ V; V
85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)$ s1 W/ {  b7 f" _
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
5 b$ z7 Z! S# h2 j$ P% T2 x9 o: V7 B. ?# [2 s: j5 P
  1. /*
    1 H, W" I& b6 Q* y# h4 S8 ~) h5 O
  2. *********************************************************************************************************
    5 s7 n9 h/ M# Y, X
  3. *                                时钟,引脚,DMA,中断等宏定义9 k9 p8 V$ d2 f7 U5 r( e$ ~2 C' P
  4. *********************************************************************************************************
    3 ~! `& G2 E7 W: p5 ~
  5. */$ g- w) U7 ?2 H+ G6 d. J; ?) Z
  6. #define SPIx                            SPI1( O# {7 ]  s; d9 w4 B
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    : R( M8 d* p% v/ J, e
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    3 S0 ^! ~( H- @  S
  9. : m# `0 g5 j7 l/ Z9 n
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()( \# O* {: i/ y7 z" |0 c4 h
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    0 |1 Y$ J4 V# X2 r
  12. % H6 s' d+ @8 [- b6 k
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()0 @5 k6 V7 N( e9 u! f
  14. #define SPIx_SCK_GPIO                    GPIOB2 q+ ?7 v2 B8 b1 k3 j
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    , L5 F3 f' b! _
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1, u) o8 W1 i4 T* n& X6 s* g) E* _
  17. 8 ]  R: ^. |3 k" W- d/ b1 q
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    ( l' Q9 r* ^. E2 C! \, Y# |5 u0 Z9 u
  19. #define SPIx_MISO_GPIO                    GPIOB9 c: z  X" \/ V' \4 A
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4
    . H5 _5 ^5 L7 |, w) t
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI16 b+ o' J# {: V

  22. ( D8 X' U" k$ A' k' u& Z
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()$ e$ C, n: H  j1 b" J/ ?  x/ \
  24. #define SPIx_MOSI_GPIO                    GPIOB
    7 g7 F& W, R" G+ I0 J8 P, M
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5% \" D. u% x. P8 N
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码
- N3 A  U+ }# V3 y. Z. e
硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:% p2 u: t5 q* V: p5 ~+ E0 M

& n8 m9 Z; `, b, T5 s主要是下面这几个:, L% ]$ E/ I) j$ }

9 l2 X: J! x& n2 B4 C$ K& J' Q
  1. /* 串行Flash的片选GPIO端口, PD13  */
    ; f+ j3 s# Q3 ~/ I! p
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()
    & c5 N/ c0 g9 a- B/ S
  3. #define SF_CS_GPIO                    GPIOD: `. Y  ~: o8 O+ a% h$ Y% w
  4. #define SF_CS_PIN                    GPIO_PIN_13
      o: _& H7 I- h: r* o( R- Y7 P" Q
  5. $ N1 O( r0 K* D) |7 l! t6 G
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    $ y( F5 {' A& \. R, J5 U
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN7 ]6 U: _7 B1 k& H. p; Z1 c6 t
  8. / m5 R4 s  B8 N8 k
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */
    7 e2 n+ {  `5 J8 g9 R
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
    9 V/ |+ r" [* g" q1 l
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    2 l  N' _) F7 J
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */0 @) @4 k" |: ?- v2 i3 E. E
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    & H$ {' x  d6 }2 U
  14. #define CMD_READ      0x03      /* 读数据区命令 */
    0 D/ t$ m6 n" i" D( D6 o
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
    2 B4 D1 t/ z$ K2 P: i8 t0 s
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */
    7 |" }7 G& }% t3 P3 P2 S3 j
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    ! x$ `0 G/ {. I% i6 q/ @/ S
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    $ R( R% ?( N2 F4 e
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */( I- c, {! T. j. ~* s
  20. / X2 ?& D3 e" X" u
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

( V9 b% E( H! x7 S85.6 QSPI Flash的STM32CubeProg下载算法使用方法2 _' l! U5 c! [* I- B4 Q) Q9 c$ i
编译本章教程配套的例子,生成的算法文件位于此路径下:/ j+ B" Q' [# l: l! J( j9 c
- D0 {' Z. O. j8 x8 {, x/ V# L" ~& ]* R
5ec6d402a7646232a55f4834cd00ac0c.png
8 f) D9 m4 N+ s* n8 T

+ y0 q3 E; L( F7 l; Z, P* K* A: P2 y85.6.1 下载算法存放位置: l& f, b7 `- ^" i  ]: q! P- r
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:- J5 G: K1 m8 ~4 A) z. o1 ?
7 V( S% F' a" k# U6 ~
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader) O: I0 |4 |& `8 T! o

" X. `& H0 [# d
2609073778af5845a869911ab36624f4.png
* }) x# F3 h' H% Q: n
( R0 i! m; t! j0 Z8 M8 x
85.6.2 STM32CubeProg下载配置
- e1 K5 `' B$ m0 H$ {* z  V我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。& R, E0 g" p# w4 c8 T9 a

; F) i# d( k. Q" Q1 B* L4 O7 d# c
d4e256633eafdbf4bcd8cce6e3b5909f.png
8 [( j5 Q' M$ L
7 G+ c- b5 |! f& ]* ^% u
点击Connect后效果如下:
6 y! H5 g8 X7 O6 q' L
  c* d" u" w" n( M

, u/ T! y: Z- O, y( W. Y* P! U1 ^
# J* Q2 n8 ?& X在这里选择我们制作的下载算法:
/ a7 E5 Q% i. y" @
! P  [% d( Q! u: Z$ o5 R  k9 @
d4fa28f64be2de98e04f3188eb2fe09e.png
# `4 V+ q* A* g3 p* M& K6 Z
9 {  Q; d, t" G- j. y( E5 w' D
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming' Y' L# h# ^! \- ]  T

; I" G0 E: J5 T6 ]: C* w
842518a7cc1dc569598d372132b2ee58.png

3 W! ~6 t7 ~; T) I6 F6 t1 G- N: l, C  M
下载完成后的效果如下:& U5 X7 r& k3 N- w4 S( W6 ]

* U6 T( s. H4 F0 K% J
68a92f267829ca77f125db982d6c0479.png
) w4 w+ h4 p0 ]3 l

: L3 A1 k3 r5 f9 J$ F: d: k0 j# F85.6.3 验证算法文件是否可以正常使用, @9 N/ \1 H$ Y) E6 t! q6 S+ ~
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
3 x: Q6 d6 c3 n( n% L
0 t& \* @+ C' Q
566d2fba421c710da70e5656c6934466.png
; p' t) H2 C) Y' _3 v

9 P; n1 Z. w* x- X. T' S85.7 实验例程说明
8 {2 _* Q- ]" ~) B
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。6 W/ N* y. z: T5 A+ Q$ Z

# c+ l, N; H% H, p9 P编译本章教程配套的例子,生成的算法文件位于此路径下:  J9 x3 Q: n; Y5 p

/ B  Z4 p# f7 {; E! K8 B( i1 S5 z
6108d1ff7e349f2aa268ee31804c57ca.png
# F5 i! E0 V7 D, S9 k% g

; B; |. @: k' c3 c5 y( E85.8 总结
: T+ M3 ^, S' O8 _, g  j4 V6 ]& s本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
1 E" f9 Q) T1 V+ v, Y; S3 ~
; N) K" s" a6 o" |0 V- {! k% d0 l- L; {) f
! z& C, W% Q/ x) Q' t& o3 I, M
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
收藏 评论0 发布时间:2021-12-19 15:00

举报

0个回答

所属标签

相似分享

官网相关资源

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