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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示# _3 K& h) ?% {  b6 W
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。" i# ^: R" ?( e& I. N
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
5 B; o/ z" F, E  l. [) ^# {& s  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
& P0 `, n, m$ k! z: v85.2 STM32CubeProg简介
7 ]# x) O4 W0 \; U; d. _( s4 v" cSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。4 }+ N, t3 [: M' R. x1 @! \6 R
0 b" a0 u8 i, r2 _0 ]* U
d80578d217eb2a83520c6f3d797218f4.png
5 r. R  S& Y( G9 J

' f3 K7 f( |9 l2 m( q85.3 STM32CubeProg下载算法基础知识
$ B& j, I, S$ u: v5 m7 l
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
, g+ e/ i, d2 s2 Z  X/ x" B5 u+ m* ^( X
85.3.1 程序能够通过下载算法下载到芯片的核心思想& Z% Y- I$ a  U, Z8 e
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。( Z2 |; i$ {2 g- b0 j1 E

* B4 {' [) t6 g) i85.3.2 算法程序中擦除操作执行流程
9 K1 d3 X0 ?1 m  J5 ]  D注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
; f/ q. a6 r" O  V3 _6 [$ N2 W
擦除操作大致流程:
7 S, E# I( r0 b0 j  e, q+ X
) D. J) }" ?' f; U  z* W% N
d75670256363bb50203bed0bbe4f9fd2.png

! p# a( r& i1 V1 q; O2 F" B' }  N
! |0 W3 E5 \% E" U. ~* Z  加载算法到芯片RAM。+ Z4 M$ y4 p  ?9 |6 S( X! X, h* z
  执行初始化函数Init。+ A9 k* U2 F5 P9 H' t/ u$ }
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
  n7 z9 N  ~- ~2 N& ~  执行Uinit函数。
+ I8 E  a2 d- H. _  操作完毕。
- D" H2 ~6 m5 ]0 T  d: L! w
* x$ |* ?; B: z, l) z85.3.3 算法程序中编程操作执行流程7 Q9 v# I* ?9 P7 w* _
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
7 c1 j5 r; P) G% x$ B
' c- y6 T; O3 m5 O$ A1 V; j编程操作大致流程:
/ ~* w! x: b7 D
5 q( L; w) K& r/ D
4fa96e9e33d700081f2cdbd312afb31e.png

% F. ^$ M/ n% ]/ f2 d9 V
1 G' Y1 B) X% j  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。- t) E9 a( M2 D) F  `& Q: H  h
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
9 L% ?! p6 ~+ i) k- T# L  加载算法到RAM。
9 _1 w! N% a+ r8 z2 X) ]+ B  执行Init函数。6 T0 O$ d8 C* t& O0 k
  加载用户到RAM缓冲。+ {6 U1 \# P8 B) J2 A
  执行Program Page页编程函数。
  u' K+ p( S4 h7 X- d  执行Uninit函数。
2 H; c7 b0 r, O& C* b  操作完毕。
1 l* ]( v: A) E# X5 ~. }- e4 |( x- l, S) E0 V5 b" s, o: ^  V
85.3.4 算法程序中校验操作执行流程
7 S  G; I3 E3 ~3 G3 e注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。2 X3 e0 U6 R, K. Z% H, ~

8 {( M# C" R  M$ O4 K校验操作大致流程:5 J( M& d' G- R( a& e2 P6 o

* O* s" a8 O- h% O1 f; r$ ?
7deeae4b650bd82c457492af14fc26b7.png

3 L; s" s8 Q/ ], @4 n) s/ F
' c# ]; R6 B7 E% L5 f4 P  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
2 T) d- _/ g6 V! W  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:: H" G) n" f% i7 p% Z% V* n, {
加载算法到RAM。
5 h. i8 v- q6 S0 x) N5 h! ^ 执行Init函数。
, b/ k5 D! C5 D0 P1 `) e 查看校验算法是否存在' F4 y0 y) D- k3 w) D8 M
  如果有,加载应用程序到RAM并执行校验。
$ S6 j+ w3 U9 m/ z$ |- t; T3 H" b  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。& p9 w) x) e8 }" T, w) a5 k6 I
  执行Uninit函数。
9 ?# ~, e/ U' h  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
* S2 `' y, J5 [. K4 w6 \  执行RecoverySupportStop,回复支持停止。, o. j8 j! y: J, M4 g# ^2 f8 w
  执行DebugCoreStop,调试内核停止。
' m9 Z: [( a8 Z0 j9 X  V  运行应用:- z% N: K" d8 c& S* b# T
  执行失败0 j' E) o( Q7 K3 x9 K
  执行成功,再执行硬件复位。
: y& v$ x8 H4 u9 I- k' K, O  操作完毕,停止调试端口。0 ]' [' F% Q; R5 ^0 j0 _

& ]# G& c- C6 M. m" K85.4 创建STM32CubeProg下载算法通用流程
( g% W$ E* n5 \  O下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
& i  k; k* E; ~9 e& Y( L; N1 l" J5 K; U! W& S
85.4.1 第1步,使用STM32CubeProg提供好的程序模板5 D* G9 d  ^1 H. u3 ?  ]% Q
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader# l+ K4 Y, ~/ {' s

5 k" C0 l1 ^* _2 y' {" o2 ?& N4 F5 _
97f385e3b62a055bac9b5c0214b877e5.png

  f. a: I, y4 ]- k' u# V
& z) o; w% q4 K7 R, A4 g以M25P64为例(注,其余步骤就以这个为例子进行说明):' v: s; n; q$ ]; O/ l* V

6 w& J  b+ Y+ ?3 d5 b* e
43b298f996937fe35e89697fc6479cb9.png
0 O$ t0 B) L; O+ A/ u8 m

1 x2 ^8 E' X7 b5 q  }; S85.4.2 第2步,修改工程名* }4 C" D' x0 ?9 c
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
" o% ~: w7 y6 }7 O) u, w: y3 s) F, P) v
85.4.3 第3步,修改使用的器件

" v7 R$ r; S$ q1 g* y! V, P在MDK的Option选项里面设置使用的器件。. y: E/ O" \+ b3 c  K
% @, }) j/ H+ `- t$ U" j
36026b9a6d60249610d0246eb725b246.png
5 C/ v- j2 z9 C3 {( T

3 e& K, z0 {$ O0 g! U85.4.4 第4步,修改输出算法文件的名字: z& d) _% }# v
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
. }" T/ Y2 t7 a, V5 z0 r# \# ^: B! {
- Y9 |* c& \5 l1 u: Q
52c72b3993f28a366a22eac56f8fd043.png

4 D4 `; @. D6 v9 k+ S" j& }
. B- ^; d; \$ K* X7 Y6 A注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:! [1 ]  F' [: H; A

* P: F) g6 O. \
7b48153027a44de33926c2da008716a0.png
: u1 C1 ^$ L  n( b9 s$ S- W+ e2 ?
4 k/ g$ H/ t) A9 i) W
85.4.5 第5步,修改使用的库文件和头文件路径* _" s3 J3 S5 Z9 C
根据大家使用的主控芯片,添加相应的库文件和头文件路径。  y! H, ?0 O6 R0 j2 X
2 E8 c! ]1 S0 j! I+ D( K
85.4.6 第5步,修改编程算法文件Loader_Src.c
& {! a0 Y$ ^& U! C( v1 Y* |模板工程里面提供的是M25P64,部分代码如下:1 ?2 n& x! P$ X! `! L" \
1 i# c+ B% a/ y1 R" h+ W( M  o
  1. /**
    . R1 z# y' X- ~$ d9 r# w* B3 M
  2.   * Description :! N( T- C; q! }; h4 O3 [& y1 A
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
      z6 e+ k5 U0 d, _# |* q: J
  4.   * device and initilize the FSMC with the chosen configuration 5 E5 W3 x! m8 x) p
  5.   * Inputs    :5 q/ _" g- @5 e; {; Q) T: a
  6.   *      None; t% m/ a+ Y- Q. N  M4 d# b
  7.   * outputs   :# l% E# c; T0 Y, N' N
  8.   *      R0             : "1"             : Operation succeeded
    : Q3 j" Q6 y3 c' }! o7 @1 F8 V
  9.   *               "0"             : Operation failure
    3 `4 N: f: A$ s) K8 j7 g- {
  10.   * Note: Mandatory for all types of device 0 R; [7 o- b4 J2 H+ G/ M
  11.   */& Z: V, a" y/ X' u' I* R) }1 L
  12. int Init (void)
    : D; {0 v* y% j/ m0 `, R  Z) @
  13. {  
    % M2 D! r" \3 X
  14.   SystemInit();
    , p! V2 e; X' l2 f
  15.   sFLASH_Init();& X8 H0 j6 ]& B2 y3 \0 K' x
  16.   return 1;% j% O) \! u. Y* K/ z7 {/ ]2 B
  17. }1 r( h9 N$ {* r) h, ]" C1 Q, s
  18.   b- j% U* w3 {. u6 x
  19. / F$ C2 A0 A6 M+ @& D
  20. /**
    . \( u6 H$ e0 {& `* Q
  21.   * Description :
    ) ^6 W6 w3 x5 E
  22.   * Read data from the device " z7 `1 l! k& F. h8 D0 {0 d
  23.   * Inputs    :, f4 R/ {4 c5 X1 b# F; S
  24.   *      Address       : Write location$ U5 i: s/ X, K2 ?' ?
  25.   *      Size          : Length in bytes  0 g  W& _8 |7 K- S; x" ]9 Q0 a; e
  26.   *      buffer        : Address where to get the data to write
    ) X, Y& F% b2 R# `) ]: G* H
  27.   * outputs   :: h+ z$ z, y$ N! \) a4 B
  28.   *      R0             : "1"             : Operation succeeded
    3 @& E. ^9 s# O& E% P0 Y' x
  29.   *               "0"             : Operation failure- @. X8 b" ~1 K) k& N
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    . ~0 i+ s* d) p
  31.   */
    2 w, J5 Z7 S$ b1 u! C2 @
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)% V+ D; X$ w" E
  33. {
    $ n$ o& x- j+ V* G
  34.   sFLASH_ReadBuffer(buffer, Address, Size);4 n1 K4 r0 a8 N+ ?# U
  35.   return 1;+ I, }2 E5 |& J! V- W
  36. } - I0 W. w; p1 i0 a3 u: U5 o: L7 T* y
  37. ' d- i% X# v' g1 ^7 n
  38. ; s% ^2 Q1 H" n3 G. ?8 M- H
  39. /**
    0 I$ C. u* c+ y3 `
  40.   * Description :' T0 u' x0 a8 b8 R" I
  41.   * Write data from the device 2 Z0 Z7 o3 g( B, R/ c5 X' L* x
  42.   * Inputs    :
    * f& B! i; I2 _
  43.   *      Address       : Write location4 l  X' T# h* i- B
  44.   *      Size          : Length in bytes  6 R; b2 _+ t6 e4 y/ m1 I$ M* L- T
  45.   *      buffer        : Address where to get the data to write
    ! T' o4 H" X* w: H
  46.   * outputs   :6 l2 {$ B. Y6 C/ a
  47.   *      R0           : "1"             : Operation succeeded/ A- h& z3 M) i$ M( j' {/ s
  48.   *                     "0"             : Operation failure6 y, J2 n5 p. ^% Q5 y+ c$ \' r
  49.   * Note: Mandatory for all types except SRAM and PSRAM    ( C% p7 C$ O1 v9 ?
  50.   */
    ( R( B/ ?' ^9 q! Q4 i4 n% j
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)1 A2 z' V# @) X# ~
  52. {
    4 K4 W# h: Y5 N5 K
  53.   sFLASH_WriteBuffer(buffer, Address, Size);) E% O$ g, [( Q
  54.   return 1;  f) ?$ `3 k" c+ t' d; v+ P
  55. } . [# V% F! n9 `* G" ?' [# D

  56. 7 R0 Q6 J& J3 e5 o* X# Z$ ?
  57. ) [' h( M! Q* C# q
  58. /**) Z! t2 \9 S5 Y9 C' @( N4 m
  59.   * Description :
    2 D! v% e2 ~, i7 s1 u& a/ T
  60.   * Erase a full sector in the device
    7 V( c6 U* _! o
  61.   * Inputs    :
    3 r4 _6 x, B# C) n! g2 I+ G8 A- \
  62.   *     None7 E" E4 k. S" H
  63.   * outputs   :6 h0 O  i; {. \" I' x
  64.   *     R0             : "1" : Operation succeeded
    & b+ |5 m% p: V% J) m4 Y
  65.   *              "0" : Operation failure
    % D8 I5 w' _- L. Y, E
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH! l; G) M' U3 t8 J
  67.   */
    1 n3 \( ]( R4 r' h* C
  68. int MassErase (void)5 z% \7 K" F- w' e  V! z9 H
  69. {  
    * c* a& O% t2 C! J0 V
  70.   sFLASH_EraseBulk();4 H; G, P: ~7 L! i$ W
  71.   return 1;   
    0 ^6 ~1 v$ p; ?, ]$ k( J4 {0 }
  72. }
复制代码

. }3 e, e9 D7 p, |& R85.4.7 第6步,修改配置文件Dev_Inf.c; c. P7 v$ b# t$ @
模板工程里面提供简单的配置说明:* l) r& X- Z( ^% A8 |* I
1 i9 O, l3 ]7 v/ X4 d- S/ n# a
  1. #if defined (__ICCARM__)
    ' q* X& y6 @3 e" R+ ?+ u
  2. __root struct StorageInfo const StorageInfo  =  {* ^% E1 m& \5 v9 c% Z5 q  ~
  3. #else% W+ b5 R' t/ c! @. e& C7 p6 e  I
  4. struct StorageInfo const StorageInfo  =  {9 G6 O- S0 P& E1 U
  5. #endif
    9 x- O& R) |6 g8 H  i2 W" D
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    # u* D. }4 J2 a
  7.    SPI_FLASH,                       // Device Type
    # }2 m5 f1 B0 L) r/ l( R6 q6 U0 m0 ?
  8.    0x00000000,                     // Device Start Address4 V' w4 @: U( d4 K. N& m
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    0 W4 }7 Q  b9 v: {3 a- E; V
  10.    0x00000100,                      // Programming Page Size 16Bytes# j4 L0 r; C, O  e
  11.    0xFF,                            // Initial Content of Erased Memory  `2 m3 h4 V$ L; U% y# ]
  12. // Specify Size and Address of Sectors (view example below)
    7 }* I: Z  ?5 M1 U0 e7 V  D5 A
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes ; C  q' _9 o* P7 t
  14.    0x00000000, 0x00000000,
    0 f  v! R" v/ [% v/ r
  15. };
复制代码
. q0 v) k0 [3 ?4 f6 O9 P: G
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
7 p4 O* x4 \" w4 [; B/ [8 X
4 ?5 C0 _$ u+ v85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。

1 H6 h( D. D: H$ w6 uC和汇编的配置都勾选上:* N8 `3 |4 a( y, r
* S" ^* F4 t7 j
afddd6e839292aa2b94b43e79544d630.png
$ K& o+ S2 s6 Q# W, N- E& ^
1 |- H3 s4 W$ s  I5 E+ h' m
汇编:
) R9 ]/ i: Q; C3 J1 J- h* H+ K/ v3 {4 o7 I0 E* ?
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
3 \! V3 H+ f$ `/ z2 r! p

5 r4 @; p* R9 u# ]: F4 f" f如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:- [8 b0 a3 D- k* `) E3 w2 @; }
0 q: J* _* `/ t
(1)加载以响应运行事件。
, W' Q% q8 e& R% ~& Y0 |! k; I5 r: E  g; T5 i  s# ~/ y: \
(2)在不同情况下使用其他例程的不同组合加载到内存中。, l* |# t1 Y4 L
0 {; a3 u& h" n8 g
(3)在执行期间映射到不同的地址。
5 s8 V0 e. o. D2 Z" b7 l$ l3 \
使用Read-Write position independence同理,表示的可读可写数据段。
3 \0 f1 T- p& k+ B& r/ R
5 A7 @0 X4 s+ }2 S0 G( h85.4.9 第8步,将程序可执行文件axf修改为stldr格式
8 {7 q5 K) C  F7 j通过下面的命令就可以将生成的axf可执行文件修改为stldr。
9 t: |9 c' @: G! k9 x- i
2 R" J  y: h: \7 r) y. P
c683673d2481d8dce75b6fa01d624600.png

& ^( C) R6 T$ z6 |1 T% v- N4 E& J1 I+ j
85.4.10   第9步,分散加载设置% z4 K8 |/ b9 u5 @( o* ]
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。- Y* F3 G0 I7 {* h, U

0 A# v' q& O4 b9 J" n1 p
b96835a7506516319002e6b2e8db9371.png

* I$ a2 E2 j- r: _% s* b8 Q; m
7 y. S' s3 ?9 J. [分散加载文件中的内容如下:, i$ a9 _5 p* l! M2 u

5 r6 |: A1 }+ w2 {! [* e% [
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    1 t4 S2 J( Z% `0 _+ V
  2. {
    : q. y" u% q" H. u( i
  3.   PrgCode +0           ; Code& _1 S+ X- [# ^" l1 E
  4.   {
    0 L. v* y8 c8 H/ S# i; J
  5.     * (+RO); g% L& C# t. K
  6.   }4 b% s8 r& b9 b2 H
  7.   PrgData +0           ; Data
    0 Y0 s: \( f0 q7 C1 @6 P! q
  8.   {
    2 F4 ]( K6 i/ h( _
  9.     * (+RW,+ZI)3 X. m  F0 d. E5 X& z) N& \0 F
  10.   }% `4 h2 W. _  W! [! R6 a
  11. }
    ' L' s7 U! Y; ?% y8 U# P

  12. * M9 Q2 m1 N! l# _* Q% ], I. k3 i
  13. DEVICE_INFO +0               ; Device Info
    ! w- o, l8 S2 ?  U9 a/ g
  14. {
    , d- z4 i" O& G1 G1 P
  15.   DevInfo +0           ; Info structure1 ^* z2 V4 _1 O2 H9 x
  16.   {
    4 q. E0 u9 W" H/ X% a  {
  17.     dev_inf.o
    ; ^* z" G: ?$ t1 P+ C' Y0 B
  18.   }
    # X$ i" @; M( V* ]" S$ ^6 c1 o
  19. }
复制代码
* h3 L" D' X( v/ W, {0 V
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。* E0 t; E0 W0 w$ j0 ^
2 p" \; N( y3 i, A5 Z: n- U
--diag_suppress L6305用于屏蔽L6503类型警告信息。9 L9 f) u* T3 J0 V( p

% ~2 T; r7 G1 z9 u- x特别注意,设置了分散加载后,此处的配置就不再起作用了:. m, u) e9 D& c. l/ `+ A0 @0 t
3 ^. ]# I' x" g2 p4 B
641395c374a3af02f1a92d3ff9ee4199.png

7 A& ~; h9 ~( g  M4 V$ O! o+ a- k+ r9 c- _/ a2 C
85.5 SPI Flash的STM32CubeProg下载算法制作6 z7 C( E8 J! E9 F* x$ X
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
# ^$ d# k- Y( w" \
5 P! F9 p% _( e0 ?  x2 z. e85.5.1 第1步,制作前重要提示

4 V  @$ H" f# j: {- s1 x这两点非常重要:7 [6 M0 F; S( L* S

  j5 r8 t. U8 U' I( e) D3 Z  程序里面不要开启任何中断,全部查询方式。
/ L3 Z. e" l. L/ g9 x6 j% \$ c7 y8 E$ p  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
& w* ]: N- z% z8 Y) e/ ~0 P' v# _  ]( Y9 o8 N5 p
85.5.2 第2步,准备一个工程模板
+ M5 k1 G, U# s& }- p8 D) M 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。8 ^- f; T( ?% d+ M. t; n; T  Q& A# ^

% g9 u2 @8 G# [, ^3 n' {9 U
80ad5e3abdd6a993c8621230401a1313.png

- M2 C+ ^, D* c, X1 G! M& t# h6 u
& |  I0 q) [9 J8 @  D& z85.5.3 第3步,修改HAL库
% K$ t+ v& u* F! q1 W4 U- c这一步比较重要,主要修改了以下三个文件:' A: m9 G; S$ Z, d$ v
5 t& h" e' M" A! N: G# r
33bf89f25487547d4008f43b7cf10e03.png

, l0 u# c0 z7 Q+ b
1 |- S7 ^1 {% I% w  c主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。7 [1 ]) X' Q- J0 \% z

4 P$ d! c2 ?2 S3 s85.5.4 第4步,时钟初始化2 |; Z  d1 U; y9 O  c$ T% ~
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:# z" u. D9 M) D7 M% d  S

* v- x% a" r8 }! z1 L
  1. /*; F) ^+ p+ R: ~: [
  2. *********************************************************************************************************- {* Q3 c/ I3 b; m
  3. *    函 数 名: HAL_InitTick3 y' T8 a  s$ ?& i* n% \
  4. *    功能说明: 重定向,不使用5 r. T" G) W2 ^( R1 v
  5. *    形    参: TickPriority
    ) h5 T5 J' _* j- `& r# T/ [2 M) `
  6. *    返 回 值: 无% h1 q, O8 p: [6 y
  7. *********************************************************************************************************
    ) J8 k1 ]5 d$ |! o
  8. */
    9 P5 G0 _7 J+ f1 k- P! g
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    8 h( v* V! t# m" ]
  10. {5 Y& b; }  Z2 S! n. U$ t
  11.     return HAL_OK;
    ' {, E* V$ v& U% b* d* v/ n0 l6 Y
  12. }
复制代码

! c7 N1 j. s- m/ K然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
5 j. f: s# e9 s$ W! K; x2 y/ b" G, @' i' G; w; x- ?
  1. #if !defined  (HSE_VALUE)
    # q" `' |; U8 E) w5 W% q+ s
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    2 T( R4 E* z1 a2 Z: o0 j' C* G+ ^
  3. #endif /* HSE_VALUE */
复制代码
9 M5 k. A( f: `
最后修改PLL:
+ H- P9 @+ m7 c5 J' I; o9 ?) I8 A: K) G- F
  1. /*
    ! {: S, X! i- [0 z  @
  2. *********************************************************************************************************
    " |9 ^3 F3 H; j' l: _6 Y& v
  3. *    函 数 名: SystemClock_Config
    * u9 e& o& c! j+ ~
  4. *    功能说明: 初始化系统时钟& y! z0 [# B9 _) S- Z4 K, m
  5. *                System Clock source            = PLL (HSE)# ?9 d% Q3 F/ M" D5 E$ B
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)$ W3 x# [8 j! P- e+ ^1 J0 ^! }
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    # V4 N, J: X+ H1 ^
  8. *                AHB Prescaler                  = 2
    * q  S( X, f% D6 J
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)* k8 J' Q: o$ {
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    - P0 D) w7 Y# _. v% K2 }4 N/ G
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)1 `8 \# @5 m8 z
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz): _! w) f  S8 e1 J6 E3 j
  13. *                HSE Frequency(Hz)              = 25000000, x* O& [9 k  s2 t* m! l* v- S/ `
  14. *               PLL_M                          = 54 i, D) T9 ]& e9 n
  15. *                PLL_N                          = 160
    * b& v0 D0 T4 R- n
  16. *                PLL_P                          = 2/ n6 N" U" d0 ]6 T! K
  17. *                PLL_Q                          = 4
    7 Y, d& \: L6 [& \" Z, l
  18. *                PLL_R                          = 2
    5 z  G8 n; b9 k1 k( c
  19. *                VDD(V)                         = 3.3  D, R8 F/ [4 p# ]3 Z+ `' p
  20. *                Flash Latency(WS)              = 4, s4 K7 M) T6 V* f+ C7 K
  21. *    形    参: 无
    ; e( F& x4 V- H8 h, n0 ]
  22. *    返 回 值: 1 表示失败,0 表示成功! I: `0 w6 B5 D/ |
  23. *********************************************************************************************************: s/ o+ g( x; h8 Z8 H+ U' q+ R) I
  24. */
    . {" ?6 i. R7 Z# p6 N7 c1 m* ?! q2 f
  25. int SystemClock_Config(void)2 E( x5 g$ }- ^" E* |
  26. {) S1 ^+ ^6 W+ |  a! d' v. g" Z9 O; b8 B
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};8 c0 E: @! ~* ?7 J
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};0 {, m( p$ ~6 n; d$ ^9 N0 \
  29.     HAL_StatusTypeDef ret = HAL_OK;0 o/ S& |) k9 l7 t4 a: e* c$ I

  30. + l' Q/ G# {0 j+ C+ H: x
  31.     /* 锁住SCU(Supply configuration update) */
    , o& n2 J: p% v* v
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
      K0 \' H9 ?5 V6 t

  33. % l# u$ x, Z+ z3 V
  34.     /*
    ' _/ S* @8 {* [, d3 V- ?
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,' y1 j# r# M4 ?# n
  36.          详情看参考手册的Table 12的表格。5 Y) P/ j4 f6 P( N4 ^. i
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    * W0 o3 W" s. T6 t, G
  38.     */5 {& x5 p; z$ c/ K
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    , E8 a' S0 e4 P4 o
  40. 3 ]  X( f3 Q! M' ?
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}) t* S. M/ ^: N4 v
  42. 6 |- N0 X9 I8 {$ I; D; ^
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */4 ~" B( B1 r* E) E5 w  ^/ H6 y' o
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    4 ~" p# n3 Q# z9 L9 }# V& }
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    6 j: P& x$ G6 l- `( }4 P9 ]9 l
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;! m5 r& d7 S8 L+ s9 f# g0 A
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    " m/ q1 M  u3 D4 i2 B; E) G0 O# _
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    ! D0 k5 }/ d# z9 c
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    / Z/ q: G* p  i* E

  50. # ^. ]' P; l0 T$ g% T
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    9 P6 @+ M" h/ q. I/ X, I4 N, W5 ^8 s7 N
  52.     RCC_OscInitStruct.PLL.PLLN = 160;2 |2 @* C( r  K* c! `  Q
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    + V7 e. ~& \! k
  54.     RCC_OscInitStruct.PLL.PLLR = 2;% B6 k7 P6 l, Z+ R8 U; h
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        : h7 K5 F1 G  C+ ^
  56. * q% ]' k/ V) u( x! o. ?; {
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    4 A# l$ L. Z4 g% D' Z: X
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    1 p6 g5 j  A) C! J
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    7 `9 s: _: h9 Y9 @8 w8 V. K
  60.     if(ret != HAL_OK)4 ?% J9 R! k1 c
  61.     {9 S  m( A( U# k3 {0 T2 w1 w1 \
  62.         return 1;        + F7 @* q* \9 Q$ J, K7 B. Q
  63.     }5 E. `3 K8 b8 d

  64. ; n" |) L$ I0 }: t
  65.     /*
    6 B0 Y1 x2 i. B2 v, p
  66.        选择PLL的输出作为系统时钟
    : H0 w& w: t2 w+ |$ |- Q( o9 T6 P6 x3 _
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟& _  s7 r- Y' k$ g* t  |! p2 ^
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线- I1 |7 [( G/ |) K9 \7 w5 G: ~: a. _' `* c
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线2 _) W# I8 U' m6 A; i0 }
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    - x' K: X9 d+ P" I8 r
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线5 M6 j  T6 q5 u% B' D. P- f
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     ( X+ u. u9 r3 P- _
  73.     */6 _4 L! \+ H/ `' ?
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    7 f# }0 G5 r' ^* }& P1 M: V4 P
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);1 R# m" A& i- d9 R& o

  76. - k* z. \) E" O
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;& E; L/ t6 }9 `( H
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;: [9 c6 g: x1 K2 G: m- g# W$ _' m
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;5 u, l% [' h2 i% `6 d$ y6 N
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    ! N+ c( I. ?0 v# e
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; / a$ m4 J( l3 I8 Y+ T/ b. c6 C3 g' I
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    8 W. M7 S7 r% Q+ ^& V& j8 s  ]
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; ! N6 {1 n  }4 b: R" N( c9 v- t; N# A
  84. ) I& Q. q) _2 b# v
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */: E% G# a2 ~: I9 L5 `; K3 U9 H: w
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    4 V( m$ o1 ^# B% g9 F
  87.     if(ret != HAL_OK)
    1 A5 b6 I( l1 g$ {
  88.     {
      D; ^4 A, X8 {5 @+ E; v3 A
  89.         return 1;
    6 a* v" R5 ~9 q: I5 O- D
  90.     }
    & D9 ~; q0 H2 N

  91. 3 N, a/ @0 h! t' c$ G
  92.     /*8 s3 D" P4 Y: p1 \
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    0 X( J$ N/ P' @
  94.       (1)使能CSI clock
    0 W" ?7 R' p# ~0 z/ T3 [: T4 m
  95.       (2)使能SYSCFG clock. H# g, d8 c: d
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    7 Z7 }4 z( C# C$ C/ F$ [
  97.     */- q: T- |9 O6 j( k6 h
  98.     __HAL_RCC_CSI_ENABLE() ;
    * \( y$ C5 ^6 I% t, W& t
  99. 6 i! O1 [5 b( a* a% }( X+ f, N
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;1 a' N- Z5 a" T# k

  101. 5 F1 N& b- C4 Q7 V* X8 T3 \3 f
  102.     HAL_EnableCompensationCell();7 o# r- [$ I; M& x0 K* D7 J+ U

  103. 7 y# |5 g! [) M7 b
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();: {/ }5 o5 I$ J0 j5 }
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    & ?" l; X: }& ?: u
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();# _& }8 ]5 M: w' H" T
  107. 2 N6 B; r# n3 g
  108.     return 0;
    . B4 I- X8 x$ B; s
  109. }
复制代码
+ Y5 Z8 p9 r7 C) {
85.5.5 第5步,配置文件Dev_Inf.c的实现) `6 O6 p& Z- U& H0 B$ P
配置如下:
9 U* Y$ f9 [1 W: \; F% B  [3 z
; Z3 t% O0 s# l1 |
  1. #if defined (__ICCARM__)
    4 z4 y% j# G/ e* I0 b( K8 p7 Q% y
  2. __root struct StorageInfo const StorageInfo  =  {; Z: U  G; ~$ Z$ m* p% C0 D/ `
  3. #else1 G$ N( [( G% R  b1 v! u- A4 f
  4. struct StorageInfo const StorageInfo =  {" g( X' L) k+ o4 ]9 z: p$ E
  5. #endif
    6 \+ F- C* w8 _; p8 K, q0 ]
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    5 x7 S0 S/ f8 b9 Y* p
  7.     NOR_FLASH,                      /* 设备类型 */
    % O& h' l) O- H: n, V
  8.     0xC0000000,                     /* Flash起始地址 */+ L6 c5 `" Q9 Z  r! o; r' N& t
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
    7 u3 V+ U9 Z4 n' s' ]& W
  10.     4096,                           /* 编程页大小 */
    " O9 h- f# t9 e* @8 `
  11.     0xFF,                           /* 擦除后的数值 */( j! A, F0 U9 |6 D& K* @6 T  d
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    ) N& w7 Y, O. r! A2 R
  13.     0x00000000, 0x00000000,2 s. e0 ~5 A2 J, h' G
  14. };
复制代码

" K& F+ m$ W  i) Z4 M+ ]7 R8 N注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
2 v* k2 l. |, n3 t6 Q7 B
2 V- d0 r7 X: B1 I/ u" u; z/ e2 D
4 Z2 m5 j- O' s  n

& o4 H. u7 {/ r85.5.6 第6步,编程文件Loader_Src.c的实现3 H- k) G; f8 p9 [' ~9 l& [
下面将变成文件中实现的几个函数为大家做个说明:" f# k" Y: p' D
  y6 ?5 A% R9 R# s) B& d- u
  初始化函数Init  z4 y3 [* Y& W  s5 G$ o2 L
  1. /*, _- n! w* g9 ~/ g3 a& C. i
  2. *********************************************************************************************************
    " o9 C. l9 J  f* @$ t
  3. *    函 数 名: Init/ S$ {8 @. v$ M+ P6 O( S5 M$ x
  4. *    功能说明: Flash编程初始化- c/ v2 b! M( V2 f8 Y
  5. *    形    参: 无) K5 k: ~, Z3 y6 j8 j2 L# R
  6. *    返 回 值: 0 表示失败, 1表示成功: s. P* c! G) p: |* }5 Z# F0 A5 G
  7. *********************************************************************************************************
    - w8 C; m, Q9 n) W" [  F  t9 B5 U
  8. */. C/ y. q$ e+ q" a$ C5 o* ~; }
  9. int Init(void)
    * v# t" V  y2 A0 |! p' Z% s. F
  10. {     a. Z* z" p( f6 ?; M6 Z* \
  11.     int result = 0;$ u  s4 C5 Y8 r" a, y# @
  12. 7 Z. u1 y9 T+ o; J3 u$ m
  13.     /* 系统初始化 */5 T& H; Z" O* j+ h$ Y  l
  14.     SystemInit();
    / v# i, K7 {1 d; E9 C

  15. : z; p! J* d9 G8 J4 i& U/ R
  16.     /* 时钟初始化 */
    # w& ^& q8 e7 b# V. F. _
  17.     result = SystemClock_Config();) ~+ i% h) M/ P: y4 Z
  18.     if (result == 1)
    / n* {9 O# J& C( ]; `! V
  19.     {4 y; B8 M" t/ Q$ r5 o/ n( Q
  20.         return 0;. s7 A2 T1 s! m
  21.     }
    1 \: F2 v) J5 F: Q5 A
  22. $ t4 U& J5 x& i5 m/ v/ \. `1 \
  23.     /* SPI Flash初始化 */
    / ^& ]- B" ?* e9 }- l2 }4 n
  24.     bsp_InitSPIBus();% d  S; y# r* I& B* ]  Z
  25.     bsp_InitSFlash();
    0 j+ ~9 `% N% o) u/ q
  26. . `, E& f4 g3 e; j; ]  Z
  27.     return 1;7 @; z3 H. d# v3 u* [: \
  28. }
复制代码
& H! L  O9 e7 @' c& V+ n( f9 _/ U9 H
  整个芯片擦除函数MassErase* i5 M0 R/ |$ G4 ]9 ?6 r5 ?+ |$ y
整个芯片的擦除实现如下:7 s( E; m5 e3 _4 b, V* ?% S2 e7 G
  D( [5 Y, B. I. p2 P- w0 H7 k, P# X
  1. /** n& u  z0 F& ^
  2. *********************************************************************************************************  n, l3 r# E* L3 f, ]
  3. *    函 数 名: MassErase
    , o, g) G; D( l5 |; e! ^
  4. *    功能说明: 整个芯片擦除
      D1 {' b9 h+ I- R4 [
  5. *    形    参: 无
    * W7 P* A% Y* R' [1 E
  6. *    返 回 值: 1 表示成功,0表示失败
    ! @( Z6 U8 ~, T9 v. P& ^8 i. K
  7. *********************************************************************************************************  w; V7 }. G0 @" |# F  f/ S
  8. */
    * X1 I, Z7 j7 }% E7 x# W( d
  9. int MassErase(void)
    ) W) D1 @, N, Y4 R; ]2 ]& A
  10. {
    * Q- G: Y$ i* T% ?- g* Q
  11.     sf_EraseChip();9 V1 Y- e* w5 U0 v
  12. 1 _( }  {# F+ }% n% p  z
  13.     return 1;   ) ?$ g8 r. T4 J7 [: i3 {
  14. }
复制代码

+ |2 \7 h3 H* g: \' }  扇区擦除函数SectorErase
% J/ [$ {1 ]( x" Q7 k$ N/ ~
  1. /*
    1 [, j8 A) G) H. E6 n" G
  2. *********************************************************************************************************9 q) @8 Z- j0 F& ]9 N
  3. *    函 数 名: SectorErase
    ! [3 ?) `2 U. v5 i; Q
  4. *    功能说明: EraseStartAddress 擦除起始地址
    ) F( y* Q5 a4 s, o# Z9 I
  5. *             EraseEndAddress   擦除结束地址7 @' A6 G) U' R% J5 M9 S
  6. *    形    参: adr 擦除地址2 j) i* E; r6 ^8 ]8 D
  7. *    返 回 值: 1 表示成功,0表示失败
      L" y+ `' s0 @  U' F/ `* c+ b8 Y
  8. *********************************************************************************************************) q2 z2 S% }& F3 W
  9. */
    9 F+ u0 D+ D' Z
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    , j7 F/ y' E- N% W% w7 `" E2 E
  11. {
    / `+ s, }( g( x+ {
  12.     uint32_t BlockAddr;' l7 ]- A6 B- E1 p* @8 [

  13. 7 r, s  t; G: Z, |6 s% E
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;0 m' U% Y) s3 V% N9 h$ l
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;
      T* X( `9 p9 R; W; Q+ b, s3 T' d4 ]
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */. K9 [% G$ j& g1 T; b
  17. / s, Y( K& v8 ?4 i5 O) t7 s' |) W
  18.     while (EraseEndAddress >= EraseStartAddress)1 b- Z1 z9 Y& ?6 R5 c7 V2 ^
  19.     {
    . C7 x' w# _# r
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    - O! ~5 n. _% @% c! r

  21. . M- G" A6 @- B0 U7 M  j
  22.         sf_EraseSector(BlockAddr);   
    # |0 I' U/ D* u- m7 G) @

  23. 2 i7 y' q. R) n- m6 ?5 q
  24.         EraseStartAddress += 0x1000;
    ' t! D# |* s0 q% z8 U8 s& z$ A
  25.     }
    $ z+ s3 {" }4 s$ j- }8 n
  26. ; d3 |8 a* @1 L
  27.     return 1;2 e! ^& o& r( k
  28. }
复制代码
3 d% Y5 q3 z+ j6 h
这里要注意两点:( l9 i" I. v' G0 B
  O/ H9 Y( K" S$ ~$ _5 \
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
9 P2 S% ~4 E' |( A7 A, Q5 V9 ^7 Z  z
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。3 @# t4 G: e: {
. H& v5 _9 s$ h) T' A* H/ _' o
  页编程函数Write
$ y5 p  d2 W# d' R# Q2 d: b  L页编程函数实现如下:
( Y2 e- Y6 t$ N, [! L" B/ P8 q  M7 D! k5 A& K5 B  u# E
  1. /*
    * L8 H* h$ c8 B  T3 q
  2. *********************************************************************************************************
    3 A: l5 I) C; f0 `
  3. *    函 数 名: Write
    : ?7 g. O. s( |  ]( P0 @& {9 f
  4. *    功能说明: 写数据到Device
    ) n# f% ^9 x0 K) M6 h0 m
  5. *    形    参: Address 写入地址- R" n: h9 Q4 j0 ^* w- H
  6. *             Size   写入大小,单位字节: ?3 v/ _6 N& n+ j' w
  7. *             buffer 要写入的数据地址
    1 p) W: H1 ?) f* a9 z1 c* S
  8. *    返 回 值: 1 表示成功,0表示失败
    9 J7 m4 V; D8 A6 i0 C3 T& X2 l
  9. *********************************************************************************************************
    ' ]2 M" w1 m4 l% @% C+ n7 o
  10. */# I( H) J5 }! ?$ q
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)/ o- [* @0 ~4 W3 X3 a! @( J
  12. {  
    % Z; P! v( s+ |, K
  13.     Address -= SPI_FLASH_MEM_ADDR;
    - _! }. x$ F# d2 B6 r/ M1 W/ A

  14. 4 D8 L1 Q- H  a5 y( s$ A  @
  15.     sf_WriteBuffer(buffer, Address, Size);
    6 j+ G# d5 R1 h& Q! T3 T+ _

  16. 2 E. C8 f$ e. i* u
  17.     return 1; ) C2 p* `# S% y9 D, Y) E/ Y  B" Y! D
  18. }
复制代码
$ u$ h9 ^% B9 V' V0 Z5 C
这里注意两点:
1 I; v- q/ ?, ?0 O' ?2 U
# g2 l# n. e+ E+ x- v) _, {(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。
3 H# F3 l- S- z# q. p8 C* u  k8 E) a
( r5 l' f9 y* f0 d" s(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
. ]: E0 P0 k6 I2 |; B* b! ~% @1 S5 c! H' [
  读取函数
7 R! |" h( @, H3 K
  1. /*
    ' ^4 b' F9 F3 r& {/ {
  2. *********************************************************************************************************
    * e' \4 h* x$ W0 @3 e6 w
  3. *    函 数 名: Read! r. C  Z' k( C  S
  4. *    功能说明: 从SPI Flash读取数据
    ) \7 Z8 {9 N: P  y$ d' B) U
  5. *    形    参: Address 读取地址" p% F  O4 z( E: d) j
  6. *             Size   读取大小,单位字节
    & x' R# Q: f7 \3 A5 v
  7. *             buffer 读取存放的数据缓冲
    ; N" B- ]: _, L4 M0 f2 b
  8. *    返 回 值: 1 表示成功,0表示失败. @3 ]" F6 j: y$ d& c' ~' m
  9. *********************************************************************************************************; c1 v# p/ m% M
  10. *// ?- Q$ P  n" |! p# b: q2 `
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    9 X& J) W# Q7 Y1 G, g8 U, G; }
  12. { & p/ [3 v2 @4 B0 A
  13.     Address -= SPI_FLASH_MEM_ADDR;
    & H3 O7 ~5 l$ ^- O7 x
  14.     sf_ReadBuffer(Buffer, Address, Size);
    ( V. [  @- z) y; I" C- |$ X
  15. ; X% F: y8 F  i, m* j/ G
  16.     return 1;% ]6 Q# d3 T& x0 F
  17. }
复制代码

+ U( w) Y- J6 Y5 F  读取和校验函数* G- Q9 w; p! G  T: u4 V9 w
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
3 A2 X5 A4 x0 y' w; D0 {
0 h/ ~1 }, A) J8 r0 L0 A! U. S85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
1 u# Z4 G, n0 A" k最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:, x4 i5 _1 @8 C; D3 v1 ~+ J! `

+ [/ S3 g! f" N! r  Y1 E: r
  1. /*
    ( [* p+ Z3 ?7 j6 h" J" _
  2. *********************************************************************************************************
    ) m! o' z) I! L" U; y/ e
  3. *                                时钟,引脚,DMA,中断等宏定义$ w6 @) e, l" Q# L0 }
  4. *********************************************************************************************************
    ( o9 W8 Q+ u# m# ?7 G! E
  5. */
    ; S1 }: a0 b* K8 {  O
  6. #define SPIx                            SPI1
    ; ~8 [( Z( M0 \# s3 w7 c
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()9 n. ?8 ~7 |) X+ ^
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
      q3 k' o% |" Y% j" D! r8 T
  9. % C+ L$ `3 |5 l% K1 E+ W, A  s
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    ; t. i% @9 c, t8 \5 e' z  ]
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    % r0 m5 V0 Z3 t; J' R2 ~1 j1 X, Q$ C
  12. 6 Z, M& j  V0 m& t, {7 G" ?
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    # d! V& h; {9 `
  14. #define SPIx_SCK_GPIO                    GPIOB
    % J% z9 [3 w# d4 i
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3! m' @9 X, u( t2 _
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1- Z- i- ]9 D( V9 A% }
  17. * o' o' `) i4 l
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    / H; s+ L, r8 c5 U
  19. #define SPIx_MISO_GPIO                    GPIOB
    6 h. q3 y3 v7 z
  20. #define SPIx_MISO_PIN                     GPIO_PIN_48 w+ \+ o, l+ [" H
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    5 S! v; \  Y: T5 ^' b
  22. 9 O* r3 m2 w, Q: q! n8 P6 p! I
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    ( y) m7 p6 Q4 K% S! D& S
  24. #define SPIx_MOSI_GPIO                    GPIOB
    & q  ]8 t1 v: V  ^: L7 l
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5$ `' a4 W; C9 _9 H5 O2 M
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码
. \+ p+ @( t' _7 J
硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:4 e0 e# `( }8 w  T$ n' l5 G& F
; v0 f0 b1 U; l' v2 V% |2 X
主要是下面这几个:
+ y' a8 v7 U6 y" T5 ?3 g/ ^0 P6 `: y3 Q; |1 i4 y* @, G, @8 U
  1. /* 串行Flash的片选GPIO端口, PD13  */; r2 e" T- K3 e( Z0 z
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()
    - ]' O# T+ n. `) B
  3. #define SF_CS_GPIO                    GPIOD
    * z/ a) H! k5 o
  4. #define SF_CS_PIN                    GPIO_PIN_13
    * p% s* C. O7 w

  5. ' z! A) b' ~7 M: w. @2 O& V
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
      {# O" X! d8 h( ]5 B
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN( O9 @  K, x5 I9 j
  8. 6 r6 Y! n5 E2 s) P) G
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */- U6 y6 Y  D# y9 q* f! t- q9 |" }0 @
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */" N  k5 f  M  E( K
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    - @0 H9 `5 z4 a0 ^
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */+ C, V$ I4 ^# Q' Y- C: W
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    3 Y% p$ O5 ?+ B0 ?' q! W# }
  14. #define CMD_READ      0x03      /* 读数据区命令 */+ o1 o7 ?6 T2 _( j4 t+ d* s+ y
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
    # k6 @! G' j& ~
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 *// o4 |% L7 p) E- D, N
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    : V8 q8 r' r3 q0 \) I  u
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */. {9 [1 `, X$ v7 d
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */0 H! L1 z! t6 [" W5 R

  20. - J0 E. t1 Q1 q) e6 K+ \$ l
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码
' ^& r, J* r2 O
85.6 QSPI Flash的STM32CubeProg下载算法使用方法% x$ z# H' J% q/ ]5 ?$ G
编译本章教程配套的例子,生成的算法文件位于此路径下:
3 P3 d: @3 z5 S6 {# j! A  U% a5 X. m9 l" x, [+ G/ m
5ec6d402a7646232a55f4834cd00ac0c.png

$ {' R3 g: ^1 s8 }; D) G/ H6 v+ ]8 ~, M, j
85.6.1 下载算法存放位置4 a$ U% }% V' S- ]! G' n6 P
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
: R# x% J. |9 Q% `6 E( O( U+ @
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
7 m' T& n4 Z; |* A! \  X( J8 t
) q, ^5 H5 A; w+ V$ |
2609073778af5845a869911ab36624f4.png

3 N+ T5 M9 p7 `. Y( e0 l0 \" B
* {8 d' C& O: [# y3 _85.6.2 STM32CubeProg下载配置$ c7 q+ p" P% W2 J7 Q  K
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。& S0 ^! H+ y/ H4 x
, t2 x: U: I- M7 K! k% c
d4e256633eafdbf4bcd8cce6e3b5909f.png

1 b. c  R) |! g" I4 r; R  A5 r0 H/ b  ~- F, }* N5 x+ N) A
点击Connect后效果如下:
* ~3 f  r% F% u3 k8 }% @' G; R" y, D* |3 a4 k

' R  v4 ]' v8 d+ j" L7 F% c' a# A# z, N7 x; n
在这里选择我们制作的下载算法:% C. D! m: v  z) `! P
$ T& z1 U4 j  w& `
d4fa28f64be2de98e04f3188eb2fe09e.png
5 K2 o3 \/ l0 B; F6 D* N

; B8 D  O: Y) w+ w- ^; k9 H任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming- X% N# ~4 t5 m6 Z- V
* i% p+ N3 T7 H, s# u
842518a7cc1dc569598d372132b2ee58.png

& P  M. }: V6 H- s( W
# @0 {" d0 Z2 z9 M8 o下载完成后的效果如下:+ q, ^7 g7 F) R
$ E$ P# c( d1 r# K$ t) k
68a92f267829ca77f125db982d6c0479.png
, H! |- {& x6 {9 ^+ f  f
* q  M" h3 [7 w7 x# y5 D
85.6.3 验证算法文件是否可以正常使用
! V! H2 m: r2 _5 E4 r为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
; N  {$ [# O% {6 j& h  O4 E6 v0 k% C5 Q. x6 j( H
566d2fba421c710da70e5656c6934466.png

, P- n: {% h6 A! Y( b6 x$ Z
& z) b8 p! Y  N$ ^( {# h7 n1 C85.7 实验例程说明

; j7 |7 t$ K$ a+ V本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。$ s1 ]# f7 I2 S" L

; Y$ n! t; S0 \编译本章教程配套的例子,生成的算法文件位于此路径下:
) \: }' `+ E9 f& J! z" K  m6 z7 T  d3 `
6108d1ff7e349f2aa268ee31804c57ca.png
7 d$ T. g1 F  F+ S5 r

# G8 _/ j% ^& ?4 u7 X0 `85.8 总结
. B' ^" e  @* m6 V1 q2 p8 H/ R本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。( ?# k+ R" a1 E7 `# U0 \- }6 n1 B0 J! O
: U% M. g3 b5 R$ }6 C- a
2 t6 f: D2 z$ D  R! E' ?

8 ~& L7 _0 m8 V# c$ t, y" Z
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
赞 收藏 评论0 发布时间:2021-12-19 15:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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