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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:35
85.1 初学者重要提示0 _0 y# c# a* p" \" T
  QSPI Flash的相关知识点可以看第78章和79章。6 E9 ?$ O+ c) b5 F1 ~! @
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。* J1 k' B! U6 j4 D, F
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样9 H+ @# ]& ]7 C
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。1 o6 K! p3 c- j; w  L& k8 v" ]
85.2 STM32CubeProg简介
) K2 U$ p( I4 _2 ~* JSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
2 c6 q& C! Y0 S5 Q* S. U# ^8 A! F3 J5 q  n* X1 R% U$ N' \; Q6 D8 C
d80578d217eb2a83520c6f3d797218f4.png
4 J+ n+ @4 g0 [
% }) J+ B4 h! f6 Q  u# H- X
85.3 STM32CubeProg下载算法基础知识$ g/ O6 O- p! E9 F% T
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
4 C9 {) A  M+ c" ?- g. G$ N7 l8 P7 l6 f: s' [' W1 u
85.3.1 程序能够通过下载算法下载到芯片的核心思想
) U$ ^/ `, |3 b# a: \3 P2 R认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。8 p) [2 G; ~9 \
9 b8 k# Y0 W% I; ]! \) D
85.3.2 算法程序中擦除操作执行流程
9 G  I: T1 C: x* X9 T2 }注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
2 x* y% I6 q7 E% I
" x- v* X# v$ n# U擦除操作大致流程:' {. \7 n$ e1 E  X" S

) W' h  E9 `  s+ V
d75670256363bb50203bed0bbe4f9fd2.png

; T) z% H/ f3 t! h6 K" p% I; v! I' ?8 l1 t3 s
  加载算法到芯片RAM。
  t  n, |& K' v# Z1 s  执行初始化函数Init。% k9 X6 _; g# i/ c# Y
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
6 H8 k0 b. X/ e: O; S- g  执行Uinit函数。
/ [4 y7 L, A3 G4 U) B4 m8 N% q% K7 p  操作完毕。6 s8 ?% Y" J7 u6 c/ f. K" T( I- Y4 t
85.3.3 算法程序中编程操作执行流程
; @* W: ^2 M# j注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
% P# z1 ]( K2 \$ D
: a' k7 ~6 S$ F8 B编程操作大致流程:) `- R8 e( D+ e7 q

6 f6 |4 a+ s9 p% z
4fa96e9e33d700081f2cdbd312afb31e.png
0 D# u  e$ `" W+ N6 s* \6 H
8 ^9 v9 W9 R$ A) u& I
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。1 G) M/ }! s  ~. H5 k& U$ t9 [/ P& |
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:4 Z1 {4 `/ h6 e5 ^8 X6 m! C# p
  加载算法到RAM。+ [! q4 P1 c! [& C2 e# z# N
  执行Init函数。
" U9 X' g+ s" Y+ [  加载用户到RAM缓冲。9 ^" Z5 M& y3 p, Y# V7 b
  执行Program Page页编程函数。# L, o3 \: l. ^: P. V% }
  执行Uninit函数。
& K1 K5 f) Z# r+ W3 I) v, U  操作完毕。
& t. b5 I4 L5 M% P: |$ E* C; ]85.3.4 算法程序中校验操作执行流程
8 h0 h* G0 \' B, p8 ^注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
0 `. o! c1 y, g/ j! J9 p: X
6 ^" Y$ F9 A7 S7 d校验操作大致流程:
) `( z& @) S2 i- M: d& @2 V+ e; g( `; T# [; D
7deeae4b650bd82c457492af14fc26b7.png
% i2 R0 F4 C1 X2 @) G7 Q9 ?

5 j8 g! v" t5 k) e  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
* v4 l- ^" \8 ?; m! O  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:+ J, e; b! U: Y; g' B- h
加载算法到RAM。. Y  R4 |2 u6 d  `; }
执行Init函数。  F7 l0 N  |- P* i
查看校验算法是否存在; c* Z& H& Y  e, w# r; r2 w7 j& ^
  如果有,加载应用程序到RAM并执行校验。
6 C4 M9 c; {' Y  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。  _* ?* K( I/ o: q- x
  执行Uninit函数。3 C8 }8 b* b; b/ x& A7 N4 f
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
, k, U/ Y: D4 N6 F) O  执行RecoverySupportStop,回复支持停止。* U9 K9 m4 c* t- u, r
  执行DebugCoreStop,调试内核停止。, q$ y2 {1 ]! b7 e
  运行应用:
3 H' x% [, t5 O1 U  执行失败
, i( Y( y3 m0 y- n  执行成功,再执行硬件复位。: g" K! [$ {* @- |0 y0 H$ {5 E4 j- Y, p
  操作完毕,停止调试端口。1 j- C: x9 d/ J, O+ [) ~, @
85.4 创建STM32CubeProg下载算法通用流程/ h( v& H. E$ x
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
8 t& T  d4 y8 Z/ F+ B) D3 w( N$ v1 ~! |% f/ j, e2 g4 E
85.4.1 第1步,使用STM32CubeProg提供好的程序模板, H( g  M& P0 R" C" Y; f
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
8 C2 n( A+ a2 D4 b" B5 G: Q
7 d0 @8 g! F4 t- z3 I& `6 k+ b
97f385e3b62a055bac9b5c0214b877e5.png
6 W7 Y" v9 p9 a# d5 U
  x5 J) q- z1 t% t2 O5 ], K6 P
以M25P64为例(注,其余步骤就以这个为例子进行说明):
+ v; V. U) e6 h6 C, b4 ^" d) @) t' r: m3 |9 d9 R& C5 S6 Y
43b298f996937fe35e89697fc6479cb9.png

3 N: C' n& n# C/ _# p2 c+ [) l+ s+ u+ s$ D  L
85.4.2 第2步,修改工程名; J5 V" X& @! y- D
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。* P( i3 O+ Y6 g% r; e) r( C
' B$ t# a/ X; ~" U/ Q9 t" o, G
85.4.3 第3步,修改使用的器件
* r6 `7 O  G* R在MDK的Option选项里面设置使用的器件。
  d5 D7 V5 m8 {* {  q) ^7 d( M, m2 m" h, K% p/ W
36026b9a6d60249610d0246eb725b246.png
/ O6 N5 a. J* D: f& \  W9 r% E
; f& Y1 P4 x1 K
85.4.4 第4步,修改输出算法文件的名字
3 c# A$ ~" U0 o4 B& s. L1 {这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。  Q9 O! ^: @* Y4 V  Z
' l* \) f$ S( c* _! s. r
52c72b3993f28a366a22eac56f8fd043.png

. a9 `8 ?: l- s- m% `. `
. L: W& K9 a0 o, U注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
" N  b2 w$ T5 X2 s" f1 m6 V8 i4 ?3 x3 c1 G; A  K
7b48153027a44de33926c2da008716a0.png

5 H: ?4 S/ x& {! p4 c8 K  X; a1 J" l0 ~& {, b5 Q! A
85.4.5 第5步,修改使用的库文件和头文件路径
7 p8 P& d$ M& \( z( V+ j根据大家使用的主控芯片,添加相应的库文件和头文件路径。$ N  F3 V0 z' Q" G( D
* p$ Z3 T- Q! E3 @
85.4.6 第5步,修改编程算法文件Loader_Src.c
9 M& m  n: U  Q7 t( O) g/ Y模板工程里面提供的是M25P64,部分代码如下:
% K- X9 J+ Y( a' k8 U1 n: j* z7 ^) F
  1. /**
    1 s' `! U0 X$ U
  2.   * Description :$ d; e0 @$ w7 s+ T; i' M4 S
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    8 {! F' ~! d8 H8 S6 s# Q
  4.   * device and initilize the FSMC with the chosen configuration ' e# j- p8 S' k1 Z! @
  5.   * Inputs    :
    1 A6 g8 h# H2 f/ p8 R! T
  6.   *      None
    3 C$ v: V* R' E6 _3 q
  7.   * outputs   :
    & E4 p# y. _  w, a2 N2 V, x" }
  8.   *      R0             : "1"             : Operation succeeded+ \1 F$ P5 H& R' u8 N& d8 E
  9.   *               "0"             : Operation failure2 u8 \* A+ _) y& s( Y6 ~
  10.   * Note: Mandatory for all types of device
    - b7 t- ~0 F4 H9 C+ z
  11.   */+ w8 s% h- {1 s9 C
  12. int Init (void)# _, y; Q6 L% W  |+ }
  13. {  
    - G& h3 M) j& [: H! `
  14.   SystemInit();; _0 m6 p: C3 v' [* s: e
  15.   sFLASH_Init();
    . J% W$ @" u8 G
  16.   return 1;
    $ A2 r2 G7 b/ X: R9 V0 t
  17. }( T5 a0 P- N& g

  18. + K4 y3 J5 @# j; X/ z

  19. : j- m3 z: c4 Z6 ], o' ?
  20. /**
    7 i8 E6 Q% `7 c( E8 d7 M' V  U
  21.   * Description :
    1 e8 q" K, M( Z
  22.   * Read data from the device
    5 |0 }) `+ o# [
  23.   * Inputs    :
    : q7 ]* i! R1 v+ T9 R( Q
  24.   *      Address       : Write location8 O) a4 D( L5 X
  25.   *      Size          : Length in bytes  - j: s8 F& W$ G) T* n) K* o. @
  26.   *      buffer        : Address where to get the data to write6 G& y! E9 g8 A8 [. u! s" e% u
  27.   * outputs   :9 B2 s8 V& D8 @0 T! w
  28.   *      R0             : "1"             : Operation succeeded$ Z6 p0 R8 _5 W- `, h# A/ f
  29.   *               "0"             : Operation failure2 t- N) m# j* B& d9 H; P
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    7 _; k# z8 @9 l$ E2 u+ b
  31.   */3 O' x0 u# ?2 D* _4 c; ]
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer); C6 ~1 R# F% O3 a
  33. { , _; R) J  S% S
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    + j6 E4 ~+ ]7 ^" C& }
  35.   return 1;' H5 q' P/ k: X1 ]; H+ C
  36. } 4 i' J. V# t. \8 G

  37. / G- w# y9 z& E- ^7 X/ z5 N2 [
  38. 7 N8 e" q) @! K8 ]" y" a6 c
  39. /**
    / r: V% k6 M+ ~: X  L9 n
  40.   * Description :
    + L) H) l: U. w
  41.   * Write data from the device : e0 {) j. A- y" j8 s
  42.   * Inputs    :
    + T- _) l4 d, V! d- b; e
  43.   *      Address       : Write location
    % @' A; e+ o3 J2 R0 C: Z
  44.   *      Size          : Length in bytes  
    & O  \1 D; d; W' j
  45.   *      buffer        : Address where to get the data to write
    / u& i9 Y& D4 p$ @5 r/ g
  46.   * outputs   :; [; G/ D% U0 i4 a$ u  w
  47.   *      R0           : "1"             : Operation succeeded3 E, h# e2 j% S- j' ]
  48.   *                     "0"             : Operation failure
    ; H4 C! K) R* W  C6 U. a% m. M  I
  49.   * Note: Mandatory for all types except SRAM and PSRAM    1 L+ W. Y& r$ G) r5 m, Q* r. a
  50.   */) ]. [8 ?" v5 o0 p" z& c5 U
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    + b$ l1 x7 @( @2 B$ i
  52. {
    4 v6 g7 g3 y4 Y5 z
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    / m& S( j$ `/ P6 w+ y  a. v% s
  54.   return 1;
    3 X9 [- l4 s2 S  p' a  B& _% H: c
  55. } 6 e7 i2 Z, P, R2 l
  56. 6 W$ D9 c: `  u. ?! M$ j1 d- i
  57. * U: w# _1 V6 g* B$ K
  58. /**
    1 J: g6 e  V( Z, _) j
  59.   * Description :  K! @: U! o7 l3 B6 |/ [: D
  60.   * Erase a full sector in the device
    4 r- P* n4 _, R& }  b! e
  61.   * Inputs    :
    9 g8 B5 W: {+ `
  62.   *     None
    * e# H2 d5 N" v# U3 Z0 `
  63.   * outputs   :
    ) h$ z2 e! t" V: r5 z6 r
  64.   *     R0             : "1" : Operation succeeded) F# ~$ G  H2 P5 a/ {% S" _
  65.   *              "0" : Operation failure6 v( E! w0 I3 I# m  X
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH, l3 h! _9 {0 _. M/ C. j- O
  67.   */4 |( I& |0 J4 L3 K2 ~/ m0 D# R
  68. int MassErase (void)3 G# I. v8 [9 F
  69. {  & A7 }6 G) q1 o
  70.   sFLASH_EraseBulk();4 \- Z* K; G7 h, O( j
  71.   return 1;    ) ]( p( y5 H) N0 Z3 n
  72. }
复制代码

; p3 i- k" H/ ?85.4.7 第6步,修改配置文件Dev_Inf.c. |$ L% E0 X& \  u1 D+ L
模板工程里面提供简单的配置说明:
/ W# V& U" v  E: ?6 D& n0 _1 Y( i5 L1 A
  1. #if defined (__ICCARM__)3 S$ z- i( e& W) i( a
  2. __root struct StorageInfo const StorageInfo  =  {) K8 Q9 @$ G6 C( s1 f# |
  3. #else
    8 b) J  c" N1 v8 R7 y6 _$ r4 b% ?! u
  4. struct StorageInfo const StorageInfo  =  {7 ~" t& T: R! w4 O0 u
  5. #endif
    - b$ m2 D: X; N5 ?5 L, d' y" S
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    # d; I$ W0 V: f6 @9 l* p7 H
  7.    SPI_FLASH,                       // Device Type
    % G( H# _4 \7 h& x# J
  8.    0x00000000,                     // Device Start Address9 \! F' q+ A/ Q6 [7 Q3 A' j# X) l
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)* J$ _( R, c0 U$ |
  10.    0x00000100,                      // Programming Page Size 16Bytes
    + Z$ P, P. c, K( \/ M
  11.    0xFF,                            // Initial Content of Erased Memory
    " C. G$ ]3 x( ?  ^- b
  12. // Specify Size and Address of Sectors (view example below)
    ; j/ a- u3 N- r4 p' h4 T
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes - H6 V: H; x/ a/ B! j# t& y  l
  14.    0x00000000, 0x00000000,$ g5 z( W) O, {/ q8 j$ k1 p* Z
  15. };
复制代码

3 n0 `9 [( F/ [4 J% e2 c注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。1 {5 ?  w+ z3 J$ [$ J; i+ _! Z

2 l# n" y* n1 P- N' Y85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
( h$ M) M$ d+ z/ iC和汇编的配置都勾选上:# I5 W: M6 p7 h, _& e8 R
! ?. Y0 g8 v' ^4 l( a; L% b1 d4 k
afddd6e839292aa2b94b43e79544d630.png

3 C3 S) M/ D  x. x5 L; O
' z# a! G! n+ i汇编:
5 p) c5 D$ v, p3 Y. o" I; m& L0 H) X6 a7 W
f639ae5b0d3c2e0e0190ec509fbc8a4d.png

; v8 U+ {7 Y3 r, V0 _) M9 U
3 ~) i8 O' i3 s' s" @0 n/ f如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
# ]: n% u( u+ K1 I4 O
2 J1 r; X! _# t+ J" ~  l8 w* y(1)加载以响应运行事件。
: ~# x) f' k0 b5 I9 a+ m, L8 @" A& a3 @# U; _( c
(2)在不同情况下使用其他例程的不同组合加载到内存中。
- e: e: L. _/ a8 T8 C# r
& I  b. S* z2 b: u6 }(3)在执行期间映射到不同的地址。
) g& t4 b5 E7 A. c, O" V% q* A/ P7 q3 U- G+ ?( f
使用Read-Write position independence同理,表示的可读可写数据段。' ?$ u$ k9 v( |4 i0 M

9 j( N3 U1 l) y4 T' q85.4.9 第8步,将程序可执行文件axf修改为stldr格式
- s* O8 p+ l2 W3 `通过下面的命令就可以将生成的axf可执行文件修改为stldr。- \, P$ b4 V% f
" a- _  a! e: u  Y3 {" s; j/ [
c683673d2481d8dce75b6fa01d624600.png

6 t* G+ P8 |7 O& d( O' z3 q: R4 G) x( H) [' M) ?+ }+ Y
85.4.10   第9步,分散加载设置
0 X$ E! f* ]7 J, A8 n我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。8 o" `5 k6 j1 c: g8 Q. F# ?

& O& i0 V# V1 c1 \5 z% h) J# D5 d b96835a7506516319002e6b2e8db9371.png
$ l' Y" P6 T' z$ L% j  Z
( |* j) y; w, N6 W: V7 l) D- M分散加载文件中的内容如下:
( e1 B  t- C& z' [! B8 ~" H+ s9 E% j7 R7 w( z4 m) ?# g
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    2 {2 Q, Y; ]0 z7 G1 f4 ^
  2. {
    : i8 J+ P6 H9 t$ `6 E5 T
  3.   PrgCode +0           ; Code
    & m; T& t5 z# i/ O7 v& p
  4.   {8 J6 `1 `7 E5 [5 i4 C( W* s2 w
  5.     * (+RO)6 @. h& I1 C' u! P* B
  6.   }
    % L; P/ A2 L! Q+ q5 t+ _
  7.   PrgData +0           ; Data
    ; ^( ^! y" S( m9 F
  8.   {, _+ i' S0 }" j6 _7 j9 q" q( q
  9.     * (+RW,+ZI)
    / E$ o% s: g& g1 B% K& z
  10.   }
    # E  {* Y3 f) ~4 m$ v# c, B
  11. }
    ) s( `; T9 Z: H( D7 ?7 C

  12. - `' |' m/ [2 |; h
  13. DEVICE_INFO +0               ; Device Info7 d6 c5 Z7 w0 {/ m! ]; D) O
  14. {8 b  v- o/ D, C* t7 e3 O
  15.   DevInfo +0           ; Info structure3 b) C/ B4 {. G9 @. X3 N! u' w: }
  16.   {
    & n7 J0 u' N7 q" X8 O$ a; k' e
  17.     dev_inf.o
    # Q; S, @" ~- }
  18.   }, G8 }8 D- b  J. c: [5 Z9 @
  19. }
复制代码
* K/ m# K* m( O! d
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
) G0 X6 s! p  g7 p  z* `+ Y* \7 C8 U/ t2 y) x
--diag_suppress L6305用于屏蔽L6503类型警告信息。8 ^- B. L; j3 @, h. Y% L5 N% E

4 R4 |9 J' e. Z0 [) u5 I特别注意,设置了分散加载后,此处的配置就不再起作用了:
6 n( E" L# z/ K/ e$ V- U+ t% i! c2 C0 W
  i( L; D# V. \$ H, D  g4 P
641395c374a3af02f1a92d3ff9ee4199.png
( ~* j6 r8 J; P8 m) K) B& H

: A  K% R: p* R& x  W, _85.5 SPI Flash的STM32CubeProg下载算法制作/ p5 n/ @2 Y! }0 k, \8 X& A4 `
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。1 e" S* j( o  t# r( O! [( [) V+ Q' ]2 o

3 r, v- B( u* [: y85.5.1 第1步,制作前重要提示, F* b/ _, S) d2 M7 w- C* y7 B4 d
这两点非常重要:
' u4 w' `& ?6 Y5 q/ e
/ f7 o1 P& Y3 ?8 R$ i8 L  程序里面不要开启任何中断,全部查询方式。9 K( g$ t0 H* h  _! e  ~$ j% o
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
  T+ b( {% T3 I85.5.2 第2步,准备一个工程模板/ I2 c7 H! w: j/ M) o
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
" p5 ~! ~# F+ a3 y7 F0 D
# X, z0 ^' R  I" {$ K0 X
80ad5e3abdd6a993c8621230401a1313.png
) o' V1 X" T7 r# D: _1 C. b# Z; a

5 S# J  n% U! P5 l85.5.3 第3步,修改HAL库* P$ z: P( x/ m7 B1 }$ R  D
这一步比较重要,主要修改了以下三个文件:
6 g7 X, u3 x6 |2 x3 P4 A/ F" a, J* E* `. l- n* s
33bf89f25487547d4008f43b7cf10e03.png

" B6 D, H8 L5 b( v& u3 y0 b1 H7 O1 P& i; F
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。' u: U( x" J; [

0 W- U; C5 U+ x7 S. X$ `+ t85.5.4 第4步,时钟初始化; l! _  q, u( ^
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
9 W: T* Y) Q5 G  H- m: ~
/ x( [4 e/ r6 R) P+ {5 C& |4 N5 O
  1. /*
    , J% n1 ~, ?. F/ c9 `
  2. *********************************************************************************************************
    ( W0 v8 Y: A% J. \3 s6 A
  3. *    函 数 名: HAL_InitTick
    7 u$ _- Q* D& G0 g/ p
  4. *    功能说明: 重定向,不使用$ Z/ ?  u+ I) c2 D- m# g
  5. *    形    参: TickPriority
    6 W9 }$ k5 g4 o$ O: c& X. d) P' g
  6. *    返 回 值: 无* R3 k0 z3 }/ B8 I4 t
  7. *********************************************************************************************************+ Q+ v2 o, q! I7 k) R
  8. */* F4 w- m* O- j0 S( t9 R
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    7 @7 J1 x# Z7 I, H6 n" F& N8 Z/ ?
  10. {1 r0 b4 i6 K9 E( ]/ P2 L6 u
  11.     return HAL_OK;
    ; ^2 s: k9 E2 J" F
  12. }
复制代码

8 H7 ?2 J" H% b: ~' L) K" ~然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
/ X. W, x7 G0 W& Z4 |+ d) ^; i/ {! t% c$ ^2 A; c
  1. #if !defined  (HSE_VALUE)
    7 g1 |6 b  l6 j! C; [
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */5 h% M) E; V/ k$ ^$ G* K1 J) _  E3 @
  3. #endif /* HSE_VALUE */
复制代码

+ w3 T* c7 q" l: b# W最后修改PLL:
+ q& }& D  E. [& {3 r
* G  L" T$ P3 o; G* N( s9 D' \
  1. /*
    - J3 y3 ?# `5 H0 Y
  2. *********************************************************************************************************
    * l7 M4 h# u* F1 V
  3. *    函 数 名: SystemClock_Config
    - |7 j1 c# F  e0 a& R& o- |4 S
  4. *    功能说明: 初始化系统时钟
    , d& Z+ u' e% _4 j! |$ N, D0 l
  5. *                System Clock source            = PLL (HSE)
    2 v- S) G# W7 ~1 E+ H+ V
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock), H* I" F* ]% q2 _# t' f  U
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    ( l7 T4 Y0 `, o  g
  8. *                AHB Prescaler                  = 2
    ' m3 B; i' e1 m/ J/ B8 Y' a
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)& @% ~! n6 n/ q' v  i
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    7 J7 {" j% X5 k3 _; V
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)! H, n9 G8 t4 n: F7 G3 H2 N
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    & s5 [1 j* O" g! b$ K* k9 T
  13. *                HSE Frequency(Hz)              = 25000000
    # q. k7 @$ A7 \6 D, x- m' j6 Q. h
  14. *               PLL_M                          = 5
    5 Y0 ~* `# ?2 e, X
  15. *                PLL_N                          = 160
    ' r- p! T6 |! c  S0 [7 X% r- |' Z
  16. *                PLL_P                          = 23 G& f. Q2 s8 G$ u4 Z
  17. *                PLL_Q                          = 4
    # x* T) c1 n; x
  18. *                PLL_R                          = 2* c( G% C2 J( j% D
  19. *                VDD(V)                         = 3.3
    7 @9 C; ~1 s* {6 B
  20. *                Flash Latency(WS)              = 4! N6 @0 y  _& ~3 p" L3 [
  21. *    形    参: 无
    % x2 n5 J" E5 s! Y: H5 n* {
  22. *    返 回 值: 1 表示失败,0 表示成功
    # M( D4 A) T' K/ g8 {
  23. *********************************************************************************************************! _2 U/ x7 V+ }" k* U
  24. */1 \+ n9 Q8 B; C( |2 `
  25. int SystemClock_Config(void)! B6 `  d! w# f" S% v# u! d/ S% t
  26. {  U1 `9 D, H2 v( B* }  |+ {
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};+ G1 R$ V) b$ M6 [3 l6 T; `
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    % q% D( t, `% l% L9 O6 ?
  29.     HAL_StatusTypeDef ret = HAL_OK;
    , @; G% t3 \+ d! b" U! p( s" ~3 s

  30. 4 w2 O, S9 d9 Y) p  X# Y
  31.     /* 锁住SCU(Supply configuration update) */. N: _' a6 m2 u. A2 c8 Z
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);$ R3 r! W, ?# k8 A2 e8 d; R" t

  33. ' n' i) r1 o6 V
  34.     /*
    ' ~' G. @6 Q( O/ j6 e% ^; n" |- j# \
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,7 o/ z7 ]+ P* R
  36.          详情看参考手册的Table 12的表格。# K4 X3 a  V. l/ N1 `9 `) S6 y
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。3 k7 n1 B0 a+ R( `! z# q
  38.     */( u2 R( @0 @# X2 u$ h
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);* s) j' m0 z* Z# n
  40. 7 H( r: `9 k! W% ]! {! A) ~+ K
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}; C# ]. r& }( s! h+ i

  42. / K4 F# a3 b# `  v4 W2 x  q% R
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */' ?; N% s9 z& X! Q3 p
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;' k/ x' ?- }5 M/ D& ~, s
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;1 O& B9 _" f- I* L3 e
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;$ p1 r2 ^) s: K2 }$ V
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;* C, B3 z3 w! W
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;5 [9 b: }8 |) m* Z" D4 }, N
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    * M$ T6 S# t" L% n

  50. ( [2 {2 d# h: ?- d( F5 n3 ?; V
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    # Z1 N( N; o+ P
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    , P9 }( p. }' `* ?% ?0 \" ^$ j4 L
  53.     RCC_OscInitStruct.PLL.PLLP = 2;2 E% b6 r: {: h" k3 N: p
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    ) d/ T1 r6 M. s) v1 R
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    + h4 V7 X- r- k6 A

  56. 9 u7 n' q' S; _& I( }" l
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;# c/ ]; U% c5 K, |0 B/ Q
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    # ?1 ]# e, J2 C( G0 b
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    , R" O/ B8 r, c9 g  J
  60.     if(ret != HAL_OK)8 D7 S7 T) y& B6 h1 h
  61.     {* j2 n# x  z0 l; t2 N9 o
  62.         return 1;        
    / `4 T! Q# f! Z5 e
  63.     }
    $ Z* B  u3 r1 A3 J
  64. 6 M( H% h# X* `/ Z
  65.     /* ; e* h% X, c/ ]
  66.        选择PLL的输出作为系统时钟* I3 m* @+ J& T$ g" Y2 Y. o- r
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟5 U8 n4 z8 p- Q) ?! u
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    + }0 F. s; n, f( C8 U9 K" Q, F
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    6 {* ?# b4 P! \1 N
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线/ z2 ~) Y& C4 m" c4 V& G
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    # i$ \" V( n+ f% S2 A
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     # `0 i8 {! |" Z& b/ ~4 u1 S6 d$ {
  73.     */
    9 o5 o' M* I4 }' F8 a
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    ! J3 ^0 |- @+ D0 p8 ~
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    ) Y8 _0 [- `2 {5 |# M" t

  76. + g& }/ K/ j' @& N: b! b4 g
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;- _0 R' h3 K& W/ h9 B; j6 ~
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    0 l. U0 q* p8 v9 F5 c- X) n
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    " w/ x" G- t. n6 v! N% H1 K
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  4 K% \6 I9 V" t0 B, ~
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; ( q" D# `4 q, F* T
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; + v2 y  ^& T3 ~# a
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    & t4 H4 @/ P$ u1 N4 x' l0 K

  84. 0 e: J# H" h/ n
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */( X3 `9 z) I) |1 [% ^8 e
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);" p4 o7 e0 e5 M
  87.     if(ret != HAL_OK)+ W' a0 x* t0 o7 o# p( U' m
  88.     {
    / `2 b# M+ h5 a+ ?  F3 ^& l9 B
  89.         return 1;  K, ~, C' h" G( l5 `  N
  90.     }
    4 D# F, E: {3 S0 {
  91. ; |) a* t& _  @7 A9 w5 @6 j2 m: U. e
  92.     /*
    6 o  I2 A8 E3 Z/ p5 U9 D
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 , Q+ B0 h* J( v& d& F
  94.       (1)使能CSI clock4 B0 l9 i! U/ ]" z: Q8 \9 e
  95.       (2)使能SYSCFG clock
    ' Y. v2 v1 [5 r& O6 {
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    6 ]2 C8 r5 L! A( R; Z, }  ]
  97.     */: P; q/ o# u9 {9 f. y
  98.     __HAL_RCC_CSI_ENABLE() ;! D' Z; G, I, ^+ T/ U' U) S

  99. / `' w2 I$ `0 }( I8 G* N
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;7 ~  d- c' }1 k5 l

  101. % {9 J2 B& w/ Z& ~" n. I4 l
  102.     HAL_EnableCompensationCell();  _7 X& M, H' h% u0 k1 b5 b" Q7 l

  103. : q# L$ T' V2 G  k' x0 u# e
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    9 h' U/ |$ R/ P; l9 m  W, g8 v0 w0 T
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    ; o8 C1 H- n5 t6 B6 v
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();- R0 V- O1 [% \5 r( ?* D$ `/ E" b

  107. 1 O. z  s1 t- _9 o* Z9 E5 D% [5 r
  108.     return 0;' g! X0 j7 r2 ~( K  R
  109. }
复制代码
: [1 [+ ]# {& n# c
85.5.5 第5步,配置文件Dev_Inf.c的实现
& x! g5 J1 S( Y% B配置如下:) ~* r+ q" C6 W8 v% H; v

& W. Q$ k: j) \* k
  1. #if defined (__ICCARM__)
    1 q0 W+ Z$ D+ ^, }
  2. __root struct StorageInfo const StorageInfo  =  {
    & q3 r* {7 y3 k! @4 o( E; i
  3. #else) I; I! ~; s( [
  4. struct StorageInfo const StorageInfo =  {
    " F  L! [9 S* Y& f
  5. #endif9 N3 S2 D1 t( X* N
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */9 ]; L2 l! n! X: \# v6 n) p
  7.     NOR_FLASH,                      /* 设备类型 */
    & F" A! r* N9 l! Q) K4 G
  8.     0xC0000000,                     /* Flash起始地址 */7 A4 C. Z6 f& R/ \2 L0 R
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
    - P9 ^( l2 r& N9 Z- o: I$ j
  10.     4096,                           /* 编程页大小 */
    & ]6 V2 q( S$ ]- |+ r+ |8 q
  11.     0xFF,                           /* 擦除后的数值 */
    & Q6 {- n) N! i
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */5 p( ^6 [; u1 B* ~  K' K6 p0 C
  13.     0x00000000, 0x00000000,6 f: a' h0 D4 a: O! m! M
  14. };
复制代码
+ l7 U. I8 b) d6 T& `8 L1 {
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
5 }. ?* ?4 `# E( Z' N# U2 B5 I2 B) K+ L( s4 h# G
51f220556177a36712977b8d140b627f.png
  n7 K( a+ e6 _8 C! R' S
% `  @- N0 L2 V5 q6 o
85.5.6 第6步,编程文件Loader_Src.c的实现
1 u2 U6 _) A% M# b1 U+ I下面将变成文件中实现的几个函数为大家做个说明:
3 Y( Q3 y! g( Z; ?# m( `9 Z$ {& p
# h; [2 i: ]" ~8 d  w5 ?  初始化函数Init8 t- l. @. m2 u3 }6 Y) x" L
  1. /** V+ ?0 G3 M/ ^& d
  2. *********************************************************************************************************2 j$ p9 C7 e& h
  3. *    函 数 名: Init- _  G% w& b$ Y& a
  4. *    功能说明: Flash编程初始化
    : P- p8 o( D8 ^! p7 i
  5. *    形    参: 无) B* T: h. s) _! s& F
  6. *    返 回 值: 0 表示失败, 1表示成功
    + G) E2 P5 p3 p8 j8 I' i
  7. *********************************************************************************************************
    & |7 D  Y& ?5 \1 s" x* s
  8. *// [0 S3 T& Z! {+ M
  9. int Init(void)+ F' H  x# s! c
  10. {   . _8 C0 E% q( v
  11.     int result = 0;
    ( E: v3 t2 G) l' h
  12. 6 ?  t7 X1 o$ r
  13.     /* 系统初始化 */
    1 C' \& Y! `1 Y4 q: y6 L3 @
  14.     SystemInit();
    : M0 g2 e3 v5 u; c* n* p, ]

  15. 6 W: O; g, g7 N6 v& {$ b3 j
  16.     /* 时钟初始化 */
    " u: o% O6 H& v5 z7 T0 e8 i
  17.     result = SystemClock_Config();
    ; U) D* |$ w, F8 \* P
  18.     if (result == 1)  ~6 w- C' _1 s) c
  19.     {
    ' c) P. n0 K% U* f! f2 O' s
  20.         return 0;
    6 {% V/ {; O& T8 [! E- ?2 r
  21.     }
    . U+ x$ y$ D2 Y0 n% c! \! s. _

  22. % Y, d& J' f) [) l: F$ |+ P% N
  23.     /* SPI Flash初始化 */
    - _8 ~& }, f( i( ]- u6 H( S
  24.     bsp_InitSPIBus();- s# N# t* d7 N5 o) ~6 `6 D
  25.     bsp_InitSFlash();# l- j6 P- `# a: {% U/ o  V& x

  26. 2 X, q% E8 [5 S& G
  27.     return 1;
    . _! z3 C% T9 u" a! S
  28. }
复制代码
+ |% Y2 D0 P7 q; C
  整个芯片擦除函数MassErase
$ Z7 R/ J1 D3 [0 O, ?$ S/ _整个芯片的擦除实现如下:$ R, @/ N: U3 P# k; G% s# A* s
$ w: `+ A+ Z7 R) o
  1. /*
    2 F  `7 Z/ m9 ]
  2. *********************************************************************************************************) S/ _! O) d' J' u3 e* K
  3. *    函 数 名: MassErase& d9 ^  R! u( I0 }6 O' ~: [/ t
  4. *    功能说明: 整个芯片擦除
    , o+ z1 r$ v' S2 M
  5. *    形    参: 无
    7 R7 G$ e) u/ ?/ Y' v& n5 L
  6. *    返 回 值: 1 表示成功,0表示失败
    4 `2 i9 l# d, U; Z: I$ Y6 K0 a3 k
  7. *********************************************************************************************************
    - y) Y' a3 c5 Z; A$ C
  8. */
    8 Z/ w: `) @# U8 {5 U& n, p' ~4 f5 G
  9. int MassErase(void)  j7 ?3 ?7 |1 n
  10. {, ?4 d8 @- G8 G4 D- a) c
  11.     sf_EraseChip();
    & c( X2 }  _2 a- k

  12. ' b8 I; T$ e8 U
  13.     return 1;   2 g0 T; t, o+ ?6 {8 H
  14. }* h$ J# y6 q1 D) ?4 I
  15.   扇区擦除函数SectorErase
    3 L9 ~) h# ?  D4 ?; A, F
  16. /*
    6 i4 D! T. `% K5 V  a4 g
  17. *********************************************************************************************************
    - G7 `$ P6 T8 ~9 H" O) l
  18. *    函 数 名: SectorErase
    ) B2 Z' m6 Q0 q, O8 M6 ~8 {
  19. *    功能说明: EraseStartAddress 擦除起始地址
    ( e* k, \5 q6 z  e3 O  g9 M
  20. *             EraseEndAddress   擦除结束地址
    * f9 A+ E7 M9 L
  21. *    形    参: adr 擦除地址6 ?. S$ y) q! V/ C
  22. *    返 回 值: 1 表示成功,0表示失败. v3 f6 f3 p. r/ E9 S/ y$ s& j6 p
  23. *********************************************************************************************************
    - L0 Q, u! D, D6 P. o! V$ W5 n
  24. */
    " F2 E* E' {- Y# y1 g1 l4 ]
  25. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)/ x. f; @& ~0 {; [/ e. P$ j% d- U
  26. {
    2 C/ P9 c# _" H7 G
  27.     uint32_t BlockAddr;
    2 M/ r. v4 g  t. y2 u9 j  Y
  28. , @/ ^9 E- r! Z7 |+ M! [
  29.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
    8 l& s: p& P$ z2 M: i4 A0 i. ^) P
  30.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;* m1 G) @  U' w8 T
  31.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    0 e: f/ g- f( E( T) X" G$ S0 ]
  32. - x) ?  J4 c' P# t# C: T: l0 O
  33.     while (EraseEndAddress >= EraseStartAddress)' N' @- m* E, z) `
  34.     {
    - J, b0 u6 ~, v" B  L) x
  35.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    ! b; S6 U+ y; g2 T
  36. : C: G0 W! b4 |. b9 q
  37.         sf_EraseSector(BlockAddr);   
    ) _! e0 a# |6 e( H% n: b* z

  38. ' `8 e* \3 v: s2 [
  39.         EraseStartAddress += 0x1000;
    # W5 Y" ^% ~- Z3 o( E
  40.     }
    4 t+ ], `% O2 M9 B; g9 M7 S+ ^* E

  41. 1 ~. O* n# R6 x( s/ F( v5 b+ d
  42.     return 1;
    5 k0 y% [+ C6 K( r" D- @, p
  43. }
复制代码
) C- C8 r* R3 Q; _+ _( E- H6 \
这里要注意两点:
% K& F: e. Z% ^. }4 U. g9 f
" m# r, R! {; X(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。% o  W! }9 X# k

! a7 v- a" X- C6 B0 i5 q# |(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。" A- f5 M  _# U  E. Q2 F

; ~2 H3 N' @1 i3 C1 ]* e  页编程函数Write
" w7 g/ }: K1 R# C& f9 M页编程函数实现如下:5 ^- |1 o1 b! p* g" \
( z, @- I6 @, R+ }0 c6 h# s1 X7 q
  1. /*
    ; c1 \3 f! d  V/ w2 ^
  2. *********************************************************************************************************7 N8 p) ]1 p: G3 p( `( V
  3. *    函 数 名: Write6 s8 A( m' w7 h# [
  4. *    功能说明: 写数据到Device
    ' P# s( m6 d) m; L6 C* q
  5. *    形    参: Address 写入地址
    2 c* m5 g# l& r( [* U9 X5 e- F
  6. *             Size   写入大小,单位字节9 I" `4 [0 ^) z& F2 @
  7. *             buffer 要写入的数据地址
    - @1 p6 L! h5 C5 @5 ^0 }) d- i% V
  8. *    返 回 值: 1 表示成功,0表示失败9 o& L6 o5 l( [! H# I3 p
  9. *********************************************************************************************************
    * h5 _0 |( ]7 P( ]
  10. */
    ; Q) @  p: S3 _* C
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)! y5 s0 J; [$ C5 |" z2 [- V
  12. {  
    + G5 ?% s. a6 M, s% z5 q
  13.     Address -= SPI_FLASH_MEM_ADDR;# u. F( s  Q; ]* p4 O9 P
  14. & R. B$ c* k* ]6 o. N, G$ M
  15.     sf_WriteBuffer(buffer, Address, Size);/ r5 n) d7 w. ^5 S' \' |

  16. ! R$ O" f( c9 A
  17.     return 1;
    ) L3 G8 q: [# o
  18. }
复制代码

4 H  J7 d% n, T5 Z* y8 \/ {: P这里注意两点:
" r. Z# g/ ^, W* ?4 x
$ X/ {# v, R- |(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。
5 t! r8 X6 H. u# u* y# {! N% M- D( J. e& S& T/ ~# L
(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。) o! g! z# b; p2 U$ |3 X3 c4 }: e
) e. F) s; ~2 ]6 e
  读取函数
7 k: f/ P( p8 e" M8 h
  1. /*
    % x$ J* c$ @) W( z: \
  2. *********************************************************************************************************
    5 F8 z' \- o' H( Y/ h  E
  3. *    函 数 名: Read* q, S8 c" e7 u; p5 Z) P( M( G
  4. *    功能说明: 从SPI Flash读取数据
    4 w; v' ~) L* f4 Z
  5. *    形    参: Address 读取地址/ H1 k$ Y( U0 P/ ~1 {
  6. *             Size   读取大小,单位字节2 x; Y3 b5 ~1 _0 C
  7. *             buffer 读取存放的数据缓冲
    ( S8 O7 u8 |& n/ [0 a
  8. *    返 回 值: 1 表示成功,0表示失败
    7 D3 n9 g+ b3 J3 q# l2 f0 R0 X
  9. *********************************************************************************************************
    " @2 f1 I# M& P3 x( `) ~. W
  10. */
    / s) q: b. R$ l
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)1 v( j) |2 m( w; h% E
  12. { . f! K" h: ]- X4 X  L) b. y( {
  13.     Address -= SPI_FLASH_MEM_ADDR;
    0 E; z% s9 [# c; b
  14.     sf_ReadBuffer(Buffer, Address, Size);
    % T( t8 s+ T  ]3 J
  15. " G/ X: R/ J2 u) _4 }
  16.     return 1;! ]/ A( D! l6 Q% ?. L& G1 n* M) v
  17. }
复制代码
6 z* A8 V- y2 b6 w2 D: z/ G
  读取和校验函数
; `" `! {- L  W* ^+ D我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。5 `, k5 E: {4 j7 R* s% j

: d( j0 |% z$ z85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
( V! N( U7 {+ X9 @( z8 L  o+ B最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:5 N% q/ a2 Z9 ~7 h

+ v( K+ F, k2 \2 Z
  1. /*9 s, B! j$ X- \; [9 v+ j1 A$ Z; n9 _
  2. *********************************************************************************************************
    / s0 q8 D+ D/ F  H3 ], j& T
  3. *                                时钟,引脚,DMA,中断等宏定义: c7 p# ^: [; C. L  R3 n  U
  4. *********************************************************************************************************$ z4 R* L3 Q4 U5 s+ E& `
  5. */, Z9 k" d' ~$ u1 V4 ?$ z
  6. #define SPIx                            SPI1
    # C5 U4 \5 w1 K8 n
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()5 |9 `2 V- T  o/ e
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    $ u. O$ ?# F# U0 V
  9. 7 M8 J. p4 B5 \! ^/ E! `4 @
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    $ |6 f& }* i0 F, Z, g0 N" D
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    9 S- Z# ]5 R; [, i0 _. k

  12. : e5 L' I% C: G6 j% L( t1 N( C
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    1 G. w: Y6 ?9 l6 L
  14. #define SPIx_SCK_GPIO                    GPIOB% C) R) X5 t, J: `/ F! w8 H2 E
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    : I8 Z* i" _' F
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    6 q* b3 K$ G4 j: W: S+ ], f9 B

  17. " h+ y! b: [4 m5 S
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE(); F( z) l6 _( a. l- K- ^
  19. #define SPIx_MISO_GPIO                    GPIOB
    5 j% t: }$ D6 _
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4
    2 D3 g0 @3 g2 i& y9 J
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    8 d) ~8 P$ I& d) g8 J
  22. $ |1 k0 {  N3 ~7 h8 c% s- j( D  S
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE(), J; ]( L8 T: q. I
  24. #define SPIx_MOSI_GPIO                    GPIOB( Q) K* m% D. l
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5  r/ I4 |9 M; G7 ^( \
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
    1 k) |$ o- z" l
  27. 硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
复制代码

( k7 l# G) P6 K9 ?8 E. @- c8 K' v" G" R, U9 ^& |
主要是下面这几个:
0 p5 s2 V) p' f0 m1 L/ }/ M8 ~5 b$ H, ?7 n
  1. /* 串行Flash的片选GPIO端口, PD13  */, X) X& ~/ M' t2 n2 @
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()
    ' r* Y* R! b$ n7 E
  3. #define SF_CS_GPIO                    GPIOD  Y4 q" U9 @- k  f' w
  4. #define SF_CS_PIN                    GPIO_PIN_13! b% p6 U$ N6 x+ o1 F
  5. - n! S/ _4 x: Y% d$ f
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) + a9 R3 C/ \" q) d2 G" k$ y( T
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN0 X8 h4 e1 t* p1 j( M$ R
  8. ) g! ?4 \; _# O1 J
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */, q% @2 o, o! t  e5 k
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */- L$ S; c$ i! Q( o; d/ w
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    3 B& P. n( c' B) l  G
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    4 @" M4 \" B# j* K+ {
  13. #define CMD_WREN      0x06        /* 写使能命令 */9 q& M5 U: p6 N  W/ l! g0 `& `
  14. #define CMD_READ      0x03      /* 读数据区命令 */* f. F5 v7 u6 [; l
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */  |4 G* U8 t' h+ f' P/ `' T9 x  E
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */0 y# K& m! f9 T8 _5 ?! Q/ q8 D
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */$ S- R# k9 ]( o% M( N, f
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    0 |- W, E' B  L6 K, ?: i1 D
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */  W- c) ]* }7 D# P! x' K

  20. 7 o# E& q9 {& U; O& K$ l& N
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码
6 z( {0 u* H) r: j; @
85.6 QSPI Flash的STM32CubeProg下载算法使用方法
- h: m5 A  c- R- P: Y# a6 c' k编译本章教程配套的例子,生成的算法文件位于此路径下:9 o' u1 v- |0 u6 L  R& Z! }6 ?
' ?6 u; `/ R8 s" y
5ec6d402a7646232a55f4834cd00ac0c.png

7 c* u( L8 ?# D/ `6 @2 `, ^: z7 N+ c' c9 t# I8 l; x" E% I
85.6.1 下载算法存放位置+ \7 @3 m* x2 r5 S* H! K, O3 W! {
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:" H3 N5 |# X7 L  p6 x

' g: m  O/ W, T5 d! P6 e\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
6 ]0 e8 a( C( Z. V$ J; \2 p+ T- ]* \
2609073778af5845a869911ab36624f4.png
+ `0 J( p7 D4 D0 @; s9 y+ P

# w7 a0 c, q6 z+ v85.6.2 STM32CubeProg下载配置+ }* J, k, n2 n7 q/ _, \6 v- H
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。' t* }& N; Y, k

$ z8 I$ n4 T" k3 A: c, `6 d8 Y
27a88335ceb606bd021c3ebc65d88e21.png

# `. a4 C: {6 ]6 R
! N+ a/ M% X, a+ x6 e点击Connect后效果如下:& @( R* Z4 ^$ i' H; V
* O+ t+ {6 M- W: D4 J# D
d4e256633eafdbf4bcd8cce6e3b5909f.png
% D1 |6 z: R% S( N
* a  }* n: o! X1 E; }" d- v/ s
在这里选择我们制作的下载算法:
) z! I; _! n4 }% R3 _
( L, M1 w' |8 v* l+ x
d4fa28f64be2de98e04f3188eb2fe09e.png
1 }5 R' P, R0 J% |3 z
6 x" G$ o4 t. e) n
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming1 Z' M0 K, b7 V4 Q( g" S
8 ~8 P4 y* `3 ?: l! N( e; v
842518a7cc1dc569598d372132b2ee58.png

  L8 ?, I# q. M: v3 u
( k( j$ w. a1 s& f1 U* G下载完成后的效果如下:
) w1 C9 f( W5 ^. V1 \# @) ?3 N( a! b0 I2 J
68a92f267829ca77f125db982d6c0479.png

! |* X' N, [7 G- D
& c% I+ R% l" M85.6.3 验证算法文件是否可以正常使用
3 n& A6 Y7 I1 e为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
2 C8 _1 h; p2 e; e9 e( l$ C( j6 K2 h5 |- r) z! g7 i4 _' W1 b& ^3 A
566d2fba421c710da70e5656c6934466.png
+ w  g3 e1 p3 I& U' q- l* e5 S2 k

* G7 F1 M; M( F8 J- E2 @85.7 实验例程说明  J5 m, u4 G8 g- \7 R/ Z" A/ V
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。+ p2 S/ V& l9 N; ~  a6 C1 w

3 n" Y$ }( n, ?, n0 C8 e编译本章教程配套的例子,生成的算法文件位于此路径下:
: u& P: P$ S4 I9 p' b  l
/ J2 R3 M  A6 g! n5 g3 t
6108d1ff7e349f2aa268ee31804c57ca.png
3 p- Z" c9 R# x# b6 |7 Z6 U

) X4 E) y  t; l9 j- W. f$ n% v85.8 总结* T0 a3 e* Q7 U% O' C0 I. E
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
9 _; s& y* R5 V& @1 E; a- y/ L  F1 j1 a% j3 B7 m4 F
) x0 g1 E7 h/ W5 {8 u4 T; }5 ^2 Y
收藏 评论0 发布时间:2021-11-6 23:35

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版