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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:35
85.1 初学者重要提示2 h& }, ^! J' {6 Q6 k
  QSPI Flash的相关知识点可以看第78章和79章。
+ A  \: X- G4 z% L% a7 }  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
5 E( g- ]$ `+ a' l- `9 @: L  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样, ~8 w" o" _/ p( t5 T* e
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
+ o! k  R7 u) }7 f  Q$ |# T85.2 STM32CubeProg简介
: `. H" W( H$ G' A& o! r$ ASTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。, Q9 E4 b- j  _0 L. b) Z

1 Q' w! j( M+ A+ L, ]
d80578d217eb2a83520c6f3d797218f4.png

6 t+ {  b/ j: T" z
/ n4 b; d% U3 v9 N85.3 STM32CubeProg下载算法基础知识
! K. ^' h; Q6 O. w5 ~7 c% [+ Q( lSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。+ C% l) o' P" X3 Z0 h

) i* P- O7 }3 o2 C. b, o& J7 U' D1 F85.3.1 程序能够通过下载算法下载到芯片的核心思想# E1 W, h8 w$ N' H. k
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。& f' Y0 t$ \3 O1 Q' n
3 l- |- x1 X( s" F* X# ^+ W. B; D
85.3.2 算法程序中擦除操作执行流程0 E( p' y% n" f6 O4 v# }, n
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。) ]1 G! D' F5 O8 X3 c4 w
7 Z) ]% s' g4 T6 N2 p& o- v, _
擦除操作大致流程:
0 t4 Y( c8 d, B0 h4 q. _1 U, D, i
$ U" v8 i( z' L* }
d75670256363bb50203bed0bbe4f9fd2.png
* {. f6 W5 x0 m2 N
2 l4 {7 ~+ \% M( c# k9 f
  加载算法到芯片RAM。
5 C4 P% [6 @- q. k5 \6 O2 h  执行初始化函数Init。
+ t: {4 E3 f: f/ \3 H  X  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
4 \: _* k) m9 c; @0 e3 J  执行Uinit函数。4 M& {, [2 B2 ^3 }3 z3 w4 R5 v6 _' m
  操作完毕。/ T2 j' ]: S' i) A7 O
85.3.3 算法程序中编程操作执行流程
( p9 B, ~" N3 n注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。, W7 G! B' `+ U3 }1 \

2 F! H# U# j" k! v) Q/ K编程操作大致流程:
0 V' O5 U# l$ k, x8 f) r# y0 o' _' M/ ~/ `7 A* O/ }& x
4fa96e9e33d700081f2cdbd312afb31e.png
+ m" P0 N' Q8 k
. W3 h! k  N& Q0 w9 z
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
) w: p% F8 a3 a. r  a  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:1 d3 k. o, z% M( j( O7 @0 j! `
  加载算法到RAM。
/ u: L8 m1 }, z7 v7 b: s  执行Init函数。! \( T5 ^( H% @) ]1 h
  加载用户到RAM缓冲。
5 n$ q, F; ^  l! W0 P  执行Program Page页编程函数。
/ ~) A1 S$ L& a" [. Z: V0 Q, J0 Z  执行Uninit函数。( {. j* L  ^; B" ?# C
  操作完毕。
7 ~2 S  G: [, e+ E9 \85.3.4 算法程序中校验操作执行流程
) A) W) u* W0 v; w* O注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。* }- T) S3 T' E# y/ k- [
& |1 C( e4 Q, G1 d; h5 Y
校验操作大致流程:* w) q( i& p" w" c5 r* A1 L
) f7 @1 \- Q2 ~6 q
7deeae4b650bd82c457492af14fc26b7.png
% ?' o/ I, z; C- I' ~/ N

* W- l$ ?# N! v& b6 ]. L  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。- v# ^2 c4 A  w+ j1 n+ c2 p
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:7 v' ]6 e* s+ e: h# }
加载算法到RAM。
" l. `8 {5 Z) D6 k. g6 u% z# D 执行Init函数。
( D% C; P( A. }, i 查看校验算法是否存在& K1 L! F* T1 f. w5 v
  如果有,加载应用程序到RAM并执行校验。4 I8 l& ?; P$ m0 @4 x
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。+ u* w% @5 z0 D& {" W. d5 X1 O: [* C% S8 g
  执行Uninit函数。
) j% L  [* P; L' @$ t+ p+ b  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
3 N" O: \0 p5 E  执行RecoverySupportStop,回复支持停止。& g& n- O+ e/ D
  执行DebugCoreStop,调试内核停止。
3 O+ N* ~. y4 s- s3 i  运行应用:9 x' ?& _3 B% y3 ?, m6 k
  执行失败
8 i  |5 ^/ m" ]4 o  Y6 s" t  执行成功,再执行硬件复位。
. k# Q9 v9 \- j" j  操作完毕,停止调试端口。8 h) U" d; ?' q, s" H$ d
85.4 创建STM32CubeProg下载算法通用流程- N" ]8 O: R+ K! o* x7 [" N
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
5 B$ h" g4 R, K/ i; ?) p) Y9 h" Z- \
85.4.1 第1步,使用STM32CubeProg提供好的程序模板
5 n( t- s) I, I# ]. }: q位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
3 \: P, x: H4 W4 @3 T' x  \/ ?' v5 ^) e
97f385e3b62a055bac9b5c0214b877e5.png

0 A. u6 a; s5 Z
0 L: z7 y& q9 T1 P- y, F以M25P64为例(注,其余步骤就以这个为例子进行说明):# R1 O8 N/ ~2 Q4 r' j* X
$ Q' K' }2 P7 @. b7 r$ j; `: Q
43b298f996937fe35e89697fc6479cb9.png

1 y- u( o3 S. _6 d0 @4 u
2 R# A; F6 B: [2 x) o  g# F85.4.2 第2步,修改工程名
4 ]/ K# ?' t0 W3 V7 q4 }STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。! u4 Q$ d. k2 Z0 G2 `
) l7 s  n6 v8 b' w' O) z( i
85.4.3 第3步,修改使用的器件
! `7 h" w" I8 v# k( t- q7 h" C0 [# o在MDK的Option选项里面设置使用的器件。
  a/ K$ }1 P6 l2 d) l/ ^' r, I
  d( M1 G2 t# j, d
36026b9a6d60249610d0246eb725b246.png
. n2 Q9 V, u& m8 V) A
$ s) U; i; ^" B' R
85.4.4 第4步,修改输出算法文件的名字  }& s' p! _2 S+ [; F
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
7 q, X3 S6 s  H9 r6 H6 A% e" B! G/ p6 r3 n4 x
52c72b3993f28a366a22eac56f8fd043.png
' [; L# F; p8 \; Z1 X/ D9 @8 j

& A6 l0 j, z0 ]+ b. ]0 v注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
+ Z$ l1 h% a0 T/ [! ~0 Q* V( i* b, i% i" t' p
7b48153027a44de33926c2da008716a0.png

( Z2 n: c3 m- c5 ?) c
; o( `" K; t0 \1 r85.4.5 第5步,修改使用的库文件和头文件路径/ W# s2 G  K, J1 L! ]( y4 s0 i
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
6 h- m8 x1 |# M3 w' u% `" e5 }5 w* N9 P* q5 a- E& M
85.4.6 第5步,修改编程算法文件Loader_Src.c
( a3 A8 @4 g4 D+ G8 P" d( _4 f6 F: K模板工程里面提供的是M25P64,部分代码如下:
* o- r8 Q, f/ F$ s$ d! g
2 i' J' E2 h0 y0 Q
  1. /**
    / L/ g1 F1 L& y3 ?7 G* J
  2.   * Description :
    * _1 c9 k; v: C' }$ N6 q
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
      N% i- @+ [$ ?3 ~; w2 F
  4.   * device and initilize the FSMC with the chosen configuration
    4 q7 _* ~! w6 u+ C
  5.   * Inputs    :8 n  I0 L+ y6 D, Q
  6.   *      None
      a; T( N% m6 A9 p0 M" ~' h# f& g8 o
  7.   * outputs   :
    " E8 h+ s4 f/ c$ l- k
  8.   *      R0             : "1"             : Operation succeeded
    : y3 Y$ C7 R- c0 n0 _0 P- I9 u7 y
  9.   *               "0"             : Operation failure+ i' {0 y) W5 K( Z. V: r% I( I
  10.   * Note: Mandatory for all types of device
    " k3 ]! f' Q& c& ~; q1 N: q
  11.   */
    & S  Q& P  p- l" m6 w( a
  12. int Init (void)9 l$ O5 u( ]2 Q7 Y9 U' b/ C
  13. {  4 b5 z$ W  I9 V7 I2 v1 z
  14.   SystemInit();
    * \4 ?; Z" @( H' ]
  15.   sFLASH_Init();
    + o; t$ i0 a( D. O: v  Q8 E3 Y0 {
  16.   return 1;
    * m. M; K1 K, g0 x
  17. }+ T+ d6 H$ t7 Z' `

  18. * B9 K4 Q7 z6 b9 p% z( ]' e

  19. 5 Q6 S: H/ O3 u, R
  20. /**: p9 @' E' g  b# Z( o" M& p9 U3 h
  21.   * Description :* x! h9 {+ n* R8 W" \
  22.   * Read data from the device
    / B1 z! n$ ^/ ~9 I
  23.   * Inputs    :
    9 M7 C& t+ ^. `% w6 `. p6 H3 j
  24.   *      Address       : Write location( w% [+ M3 _. P2 F' }" w
  25.   *      Size          : Length in bytes  
    / m3 M# U( J" v
  26.   *      buffer        : Address where to get the data to write
    8 h& G" K6 {. k# N: u: X+ P, i
  27.   * outputs   :! b6 r) t! T; p; Q
  28.   *      R0             : "1"             : Operation succeeded! l4 h  T0 O% W* J8 H0 `+ }
  29.   *               "0"             : Operation failure. ?$ I1 g$ x; x4 U
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    ) |$ i' p+ z! G3 `9 @" _3 B
  31.   */. h8 g( u: R# H! Q2 i
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    ; ^+ s2 [. w7 g6 S0 J: a
  33. {
    ) v4 q8 G" ?8 h3 t) _$ v) m3 q
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    - l2 Y  O. M3 F4 U0 z* ~' n
  35.   return 1;
    - Y( n6 l' n7 V7 `. v* q. u
  36. } 3 Z" q1 z2 _/ ^/ j! B3 k

  37. / j$ X* G/ Q' W; @2 A% f
  38. 2 y' a( V1 ^" O9 i- H
  39. /**
    * P. y# l# G1 ^7 s7 r, n2 M6 Y
  40.   * Description :4 [8 o1 X( r& P( h$ k! f
  41.   * Write data from the device ; k! F8 J: r2 B- G, X
  42.   * Inputs    :
    & q% v7 D9 n6 S5 ^: ^9 V6 x
  43.   *      Address       : Write location
    : }7 F: w- `+ p7 Q9 o
  44.   *      Size          : Length in bytes  ! M" z! S! K  x( W
  45.   *      buffer        : Address where to get the data to write
    ! E. v, z  Y8 `! l7 m
  46.   * outputs   :
    6 a6 h. b! d" C' _
  47.   *      R0           : "1"             : Operation succeeded
    6 S# `2 h7 q+ M# [7 N
  48.   *                     "0"             : Operation failure3 O" V$ E0 f, o! ]
  49.   * Note: Mandatory for all types except SRAM and PSRAM    8 _- z' N! k8 q  e( E5 [  |3 @
  50.   */
    9 i7 ]' Y- e1 i
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    # t; o: ~5 k4 ~
  52. {
    5 ~& F9 {8 a: g" R$ U, ^4 [1 x0 F* Z
  53.   sFLASH_WriteBuffer(buffer, Address, Size);4 {! x! f6 g: o- a
  54.   return 1;
    & k& a8 C7 z0 v* `' O# `
  55. }
    # u3 F# A  u9 f3 [- ?( S
  56. / i$ Y# f) z2 M" W1 I
  57. + T" M5 H7 q$ U& W' }# {
  58. /**
    & B. \& L; a; h: u
  59.   * Description :
    5 F+ R! v; y4 b/ m
  60.   * Erase a full sector in the device7 d+ [' n* m4 A% ?- Y  R+ \
  61.   * Inputs    :& s9 ]9 R7 [3 S' v$ g
  62.   *     None
    7 x( \: P! o" }' u' ~# ]; @2 _
  63.   * outputs   :7 ]6 d. E0 {3 {9 [: [, @2 i
  64.   *     R0             : "1" : Operation succeeded  d$ J( P- @5 r  v
  65.   *              "0" : Operation failure; _/ v: }3 Q5 T' a8 e+ O. d
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH4 r- Y* ?/ X5 l0 m  q$ e
  67.   */1 i* `4 c6 @- t( U
  68. int MassErase (void)
    4 ?4 {5 o) c5 r/ M
  69. {  
    9 `" h1 {- E- h+ z5 h$ U: D" P
  70.   sFLASH_EraseBulk();
    6 H0 `/ J& k2 Z6 V, m) b
  71.   return 1;    ) x1 g) V" A+ W& w; `
  72. }
复制代码

- R! [5 ]! |0 X. G* y8 V85.4.7 第6步,修改配置文件Dev_Inf.c
, v1 w3 q# a- Y0 g6 m模板工程里面提供简单的配置说明:
, F" E. E1 ]" _4 a  l; c* B( t
! I4 L' j8 S/ U* a! {6 x9 l
  1. #if defined (__ICCARM__)$ y( u: n: S' X( c
  2. __root struct StorageInfo const StorageInfo  =  {" m- n2 h3 D' {/ T4 g+ Q, O
  3. #else
    2 L- d! F( R5 C2 U9 h" w
  4. struct StorageInfo const StorageInfo  =  {( _! K1 }, Q: E% u, Y( P
  5. #endif
    : _8 e( ^4 t  _/ H
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    . q2 p  J: [  z$ x! P
  7.    SPI_FLASH,                       // Device Type" j) h) O& t# G4 x
  8.    0x00000000,                     // Device Start Address
    / V( R6 M$ M) w; {
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    / p% R3 F" y1 v" E8 p9 K- N; V
  10.    0x00000100,                      // Programming Page Size 16Bytes' X7 X( M; T; {) Q/ J
  11.    0xFF,                            // Initial Content of Erased Memory; \+ Q. q; G: w8 \* D( r* b1 d" H; X! W
  12. // Specify Size and Address of Sectors (view example below)) o9 x( [0 \0 C1 X& G9 d* ^
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes 6 }9 g- f5 q0 U! g# Z1 P
  14.    0x00000000, 0x00000000,( X& k7 w, v8 o" y* W) O. ^6 ]
  15. };
复制代码
6 i9 u, A2 ]6 r0 q( S6 M1 K
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
5 T& D; R& R# R0 a4 |( D7 d. ?
# S) l/ R$ Q4 R# |85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。& A. F0 E& Q  h+ L# Q6 u
C和汇编的配置都勾选上:  |3 }* {# I. e2 `2 \; I$ S6 Q/ O
, J; p+ j. v3 w1 b
afddd6e839292aa2b94b43e79544d630.png

7 o3 O  N. m) n
2 k: v, O" @1 h* Z" s汇编:
/ K6 P/ G4 F, D) Z4 v0 \: |. A/ L  T) y
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
+ A  n. J9 D) {

3 X6 H* ]7 T; e: D: ~如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
) F; x4 t3 A9 Y4 X/ G1 D3 i
8 O0 ?( m* y+ s2 |8 Q- p(1)加载以响应运行事件。' n$ A4 o6 W: f. w; ]+ q% [6 U- A. a

/ b( m! {) `% {(2)在不同情况下使用其他例程的不同组合加载到内存中。
! O# S/ i- F! o1 K% a4 P, N$ g8 v8 z. |% Y- `
(3)在执行期间映射到不同的地址。
5 T; ?/ i  V$ j0 U/ B2 V
1 E. d; ?( p% l3 J7 t使用Read-Write position independence同理,表示的可读可写数据段。5 y: q( j2 S4 `/ E4 i8 f
7 B9 d/ O) H; _" F5 u. J1 P
85.4.9 第8步,将程序可执行文件axf修改为stldr格式
# J& w4 R( j: v  l通过下面的命令就可以将生成的axf可执行文件修改为stldr。8 V( m! a% k- q8 [
# h3 u" A+ k+ I! C# t
c683673d2481d8dce75b6fa01d624600.png

3 n- B3 C, `; n( b3 N  a- [( @
85.4.10   第9步,分散加载设置" Z- Q3 O1 T4 W$ Q0 V
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。" P6 ~0 r2 s' p1 _' v/ l5 |2 y

! I' h0 e( M8 B/ E, g# J' z  A b96835a7506516319002e6b2e8db9371.png $ c. n( r; O6 `, x) e

! j. M. k' b1 c. p分散加载文件中的内容如下:
: o. a0 `; n0 O( y+ c
; W* s1 G7 J) ?  C
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    9 U: M8 B5 E+ [
  2. {; `8 o4 B7 R9 X/ D, B4 n3 o
  3.   PrgCode +0           ; Code
    6 f, z4 `. [, U1 N) b
  4.   {; r% ]2 O, [8 @, O
  5.     * (+RO)
    6 q2 P2 X/ o9 J3 M+ C, k& I$ ^
  6.   }
    ' F" s, k4 ~+ F  V# V
  7.   PrgData +0           ; Data# N6 T, K/ ?# d9 M& l5 s9 Q
  8.   {
    4 s6 N2 I, x# h! y
  9.     * (+RW,+ZI)
      l8 Y  L' p$ G: o, o3 _+ _
  10.   }3 N7 j! ?: f/ H1 C  l: S0 U
  11. }
    5 a  u& N7 j" H! E

  12. + u& J5 F" T+ b( F
  13. DEVICE_INFO +0               ; Device Info
    8 [# {8 B4 K5 k
  14. {
    ) d& }" L5 C% x, A) P9 }' v5 p0 J
  15.   DevInfo +0           ; Info structure
    / d$ b$ c, Y. B4 ?: w5 S* D! i
  16.   {: V$ t: H4 t) f* ^: T8 ~
  17.     dev_inf.o
    0 @/ R2 ?9 m5 Z! P
  18.   }
    & K( z( X& l% @+ ?. W) M, U$ s
  19. }
复制代码

) R9 q* r. Z! A这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
- `+ o( P( ?; `. H1 W0 Y" O; n' |( c7 p4 E
--diag_suppress L6305用于屏蔽L6503类型警告信息。9 d, C* g* l( s$ z, O2 Z

& O- i' x, T: b9 A特别注意,设置了分散加载后,此处的配置就不再起作用了:& i1 q- L. V) _8 w- t* S3 s

- N4 Q0 \5 b2 d) Z9 f
641395c374a3af02f1a92d3ff9ee4199.png

! N6 R; D3 f  j* R8 M  J% N( S2 o8 q9 s* e" p, X: l0 y1 j
85.5 SPI Flash的STM32CubeProg下载算法制作' M/ T! {+ U& y8 X* q
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。% b3 e# v( F/ G4 W: ~4 Q

. b  Q& r1 I1 ]0 @; k85.5.1 第1步,制作前重要提示
0 h1 H  E5 @! x. _% I5 u这两点非常重要:7 q5 |9 g' v% H0 H% I+ j8 ^4 ?) x* y

$ t9 k) ^' A/ q  程序里面不要开启任何中断,全部查询方式。
4 X: I/ Z: g# j5 Y4 f  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
) @) M& j! p* M; s" ?! y85.5.2 第2步,准备一个工程模板
( \+ g- r- W- B0 c 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
. v: p1 G' x7 T- k6 Y, o5 t. j* [) l0 q8 y0 }5 Q% r4 E7 T# y
80ad5e3abdd6a993c8621230401a1313.png

6 r" q* ?1 J* t  s& J2 f
4 R& \! f  |- |& X85.5.3 第3步,修改HAL库
# e/ c0 Q9 K4 E* [这一步比较重要,主要修改了以下三个文件:) V" `  T& g3 g5 v5 q9 v% B

5 m0 p4 x2 z% ?% B* g; ~
33bf89f25487547d4008f43b7cf10e03.png

+ \% `$ T- i: S# j/ i  Q+ }6 T
: C. u$ C4 H1 D" F1 B& r主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。; Q3 A7 y1 l+ `
6 P+ p  a/ {. f  d: |4 f+ w* ^
85.5.4 第4步,时钟初始化6 i) d) d! x% h2 `) }
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:" B% t8 \3 ]) V+ M# \) e* ^5 v' z9 O6 f8 F, g

/ q! X* C9 z  U* F1 ?
  1. /** c+ N# M5 E! k9 t7 {
  2. *********************************************************************************************************; g  K7 O0 I6 H) T# V0 U+ t& A6 P' b' w' @
  3. *    函 数 名: HAL_InitTick
    # n5 h6 k1 u" L+ Z
  4. *    功能说明: 重定向,不使用
    + L% A2 W' l2 z4 A; v5 w  p
  5. *    形    参: TickPriority
    & V" o0 k7 d4 q) R* U& E* I- v
  6. *    返 回 值: 无9 I/ i# M& B( x" `; Q' k
  7. *********************************************************************************************************
    1 f) F( Z; g7 w% D9 Z
  8. */
    , t- o9 f0 m) E8 g
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    # q2 Z$ A5 P* S% ]
  10. {, J: P; x, s# H- ?, l* K0 v! W
  11.     return HAL_OK;
    8 q0 i  [. x- c8 [3 R; z
  12. }
复制代码
% J* |4 D" k, r- G
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
' Q+ _$ V! {+ V% f2 _4 ~- L3 Y0 t" Q
  1. #if !defined  (HSE_VALUE)
    / h0 `7 u7 D3 h' X% v2 ~/ v
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */6 R4 `1 b1 [0 H7 z0 R* S* R4 q
  3. #endif /* HSE_VALUE */
复制代码

- d" {, K; U! i6 `: e# l% F最后修改PLL:/ Y  L  D" B4 `2 c; s
/ V. X& L# M3 m2 R3 p
  1. /*! O3 E& ^# P! X9 ^
  2. *********************************************************************************************************
    / \5 E3 ]5 L" B/ h8 r3 L$ e
  3. *    函 数 名: SystemClock_Config
    2 U; T$ ^: |+ u+ M0 C
  4. *    功能说明: 初始化系统时钟( F  u& `$ L( i8 e; ~% T
  5. *                System Clock source            = PLL (HSE): h. y4 L; Q/ \' b
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock); J- k* G7 ]% ]' M2 s
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    8 m6 T6 F7 X! F( r3 F* i) _4 @
  8. *                AHB Prescaler                  = 21 `# a, p- b0 a" f/ u& j
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    ( j& [! y+ I; h" ~/ A% P
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz). r9 a* C1 `. l5 R! G5 y
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)  Q6 u( H1 u7 u
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)+ e: _) U2 _6 D+ S8 R# ?! c
  13. *                HSE Frequency(Hz)              = 25000000
    ) Y) b1 J; L& L* x; y& f3 G+ K
  14. *               PLL_M                          = 51 s4 D2 }- _1 g7 I, S3 D% ~
  15. *                PLL_N                          = 1609 R7 y$ n$ T% c7 ?
  16. *                PLL_P                          = 23 `) {: N' M+ G0 h) k& {6 V" n
  17. *                PLL_Q                          = 4: @! A' q; B7 y6 x( @: j
  18. *                PLL_R                          = 2
    ; h; h0 X, K9 X& r7 \1 W
  19. *                VDD(V)                         = 3.3$ X4 g8 G( ?! r: W; O
  20. *                Flash Latency(WS)              = 4
    ' b6 |4 `" T( O/ d3 C/ R3 n) l2 Q
  21. *    形    参: 无! F( E8 C, C0 w
  22. *    返 回 值: 1 表示失败,0 表示成功
    & z9 c( N. z) E7 V4 f0 Z& L
  23. *********************************************************************************************************5 a. i. [; N4 D- j) {
  24. */) C3 q; ?( G! ?. ~0 F
  25. int SystemClock_Config(void)4 g( D/ f! S) w$ O0 K) p8 g- m
  26. {4 q$ R1 k2 S9 H* q+ J8 \( a
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    5 D1 l+ N( N  U3 B: m4 W# L) R4 ]
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    ' G  ]! ^6 s" Q0 M& @7 g2 p$ V
  29.     HAL_StatusTypeDef ret = HAL_OK;
    4 _3 J; i& I5 J! l9 \
  30. , [% @! [: A9 g
  31.     /* 锁住SCU(Supply configuration update) */' t  |* U1 N( H/ L  W
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);1 T) Z! y0 A7 w; d

  33. # W9 u5 H) a2 p4 U1 W$ h
  34.     /*
    ! F+ k9 t3 U4 r6 a$ H
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    3 K, _/ Y. R/ k+ f+ k
  36.          详情看参考手册的Table 12的表格。
    / W* ?$ T. F) o  W* T2 n% R& d' o
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    ; ~' R/ r( P" k7 O! V
  38.     */) r4 ]: |% [0 f6 H
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    8 E. w% F* [2 V- K! Y  @' p7 O
  40. 1 H  h. o* g: W1 {
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    $ j5 |& J0 T; C0 G! F4 R
  42. 6 a+ C! A/ k( P+ q# k5 U0 |
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */+ w7 ^1 Q$ g: l3 p
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;1 K$ F8 ?9 h4 g: q
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    * t9 p: p( k3 k+ N
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;; {& \  _! s; u
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;2 J) h# L' Y; u, I
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;+ v8 E+ X, t2 }/ d; ~6 R( H
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
      ~$ C$ b, T" F. x

  50. 5 C6 y/ \0 Z% f& y9 f
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    # l9 ~' r' M- [6 ~# _
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    / K9 V6 I8 x8 P! Q
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    3 D) K8 F# m& g& L, ^( i
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    . s  P2 z3 J5 V, M/ O: \2 Y
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        8 m9 T/ \2 ^; y5 i. x

  56. ( z& [' K9 J2 j3 M! n
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    0 O; L* `7 T# X6 v7 {1 F; Q
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
      T# L2 T! ?$ R! @
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    1 G: `, s; t: i# B
  60.     if(ret != HAL_OK)
    6 `2 m( z+ g, C2 }( I
  61.     {
    7 k% k. Q! ^+ r+ m& Y( U& Z0 D- k
  62.         return 1;        
    * T4 x/ X9 {6 x6 S0 l) y; `0 ]+ x
  63.     }
    1 L0 d' Y& Z; c

  64. 4 w# h& \1 \1 O6 n4 R
  65.     /*
    % z( h; A0 z. [$ ^
  66.        选择PLL的输出作为系统时钟
    + S9 V# k8 J& p  q" h
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟* J( }7 s- j5 M$ b
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    / U% w4 ^+ i9 k  j+ [" w
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线* p0 U. D+ g( w  X7 q4 z' {  R
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线/ \( g4 Q9 v# e5 F* n" a3 a+ H
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线  }1 I) p1 ^9 Q% x
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    / ~- G1 d: D3 h" l0 a
  73.     */
    ! {) l+ ~* a' I% C; |% D
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    3 L2 p! i) A, H+ a' N% _( `
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);3 q: r' u8 x+ p: m# O) \
  76. & O0 z/ F" ]6 E* S1 C. a$ t! y& _
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    - L( x$ w. m6 R+ n% D
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;) {4 M8 {6 b8 M: X2 M; q8 O8 `4 ^9 B
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    5 c& u8 S! h. `2 Q! N
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  # v( Q8 w1 h7 a
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    6 e( {7 g+ X! P6 y. V
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    # i+ s: M  ^2 z
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; 9 c: T* k' ?0 T7 Z5 k$ S% Q

  84. % P9 W3 z/ H" W; j4 T5 Z
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    : [1 `6 w, Y2 p. g( O7 E; p* C
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    " n4 E# {, L7 P+ Z! K+ s6 U
  87.     if(ret != HAL_OK)' g7 c' M6 m: n6 q% J1 k+ c
  88.     {* c9 X9 K8 K$ [
  89.         return 1;
    . \6 H. b: m0 S* d5 z8 a5 E
  90.     }+ o. `& a$ x; g

  91. # z2 c) ~. l1 ]' e
  92.     /*
    ( F, z1 R% N* f# q3 L
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    * ^1 G3 u5 x9 x. S4 ^$ F  ?3 p. k
  94.       (1)使能CSI clock+ s& k, A/ B0 ~( F
  95.       (2)使能SYSCFG clock* p- V0 Y+ v  v* ~5 P- q
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    / c& `1 D; n( g/ G# y# V
  97.     */
    3 U( Q, ?  M3 z" F
  98.     __HAL_RCC_CSI_ENABLE() ;
    8 i. W- u4 h) m0 b% S
  99. 5 i6 _: j- f0 X- Z. Q
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    0 M- u3 Z' W7 v8 x( @4 D2 S
  101. 9 q) o7 x; z+ j' M9 ^# e4 R
  102.     HAL_EnableCompensationCell();+ Q2 y9 e5 ~* L

  103. 5 I% V; A$ u/ g" U7 |5 z
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    % P3 T, o# u1 y; j. A
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    & f  k( }  x5 J# @) l, D
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    - W- t1 ]/ Z# f) T4 G6 @: C6 j
  107. + y5 L  |' |# h  n
  108.     return 0;, {$ b# \% r- I. T" j# }
  109. }
复制代码
5 v6 `7 m) Y7 e  P8 {
85.5.5 第5步,配置文件Dev_Inf.c的实现
/ g: s# e2 n) F& z+ E3 O  C配置如下:
; {! l3 Q3 E3 g# J% I" p
! P! j! e8 [9 w, G
  1. #if defined (__ICCARM__)
    # v! F  y% f: }, F3 I
  2. __root struct StorageInfo const StorageInfo  =  {2 y3 R# W6 E5 U# _
  3. #else! u0 S. {4 w5 J5 h, N- h2 A3 K( V
  4. struct StorageInfo const StorageInfo =  {0 ?* Y5 k7 r! Q1 b6 z- c* l
  5. #endif3 y, u5 i, ]2 J- K' O
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    " b( P4 t7 q/ i- {
  7.     NOR_FLASH,                      /* 设备类型 */
    " H( C7 f$ E, U+ `
  8.     0xC0000000,                     /* Flash起始地址 */
    . k: Z2 n. V- H
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
    # [& |% k3 P% W- x5 p3 e% p
  10.     4096,                           /* 编程页大小 */' \( C8 G) t# ]7 t3 V3 E
  11.     0xFF,                           /* 擦除后的数值 */
    . i1 n6 N/ V/ k9 N  h
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    8 f0 @* V9 M) y* l3 p
  13.     0x00000000, 0x00000000,2 Q' r# x) U- I
  14. };
复制代码

% ]4 a$ c; e" t6 c注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
: a4 e; Z" ~, K. x) l" Q( Z0 \
& K. E! j" W+ C  e2 P) L
51f220556177a36712977b8d140b627f.png

! I* ?9 d  ^9 G) h
. `: c, r6 d" }1 G. v& ]85.5.6 第6步,编程文件Loader_Src.c的实现
# T; X* B! H& U) W. P  E2 Y0 x下面将变成文件中实现的几个函数为大家做个说明:+ Q% ?8 s- ~( n( T2 g

" J2 u5 n, u0 V  初始化函数Init$ A7 D( l& ^( o
  1. /*% V2 S* [1 u) F, O8 H+ p
  2. *********************************************************************************************************
    0 s# g& l+ X. j
  3. *    函 数 名: Init$ I, L3 r! E# k; H! W" Y( t
  4. *    功能说明: Flash编程初始化; j! `7 U5 |  g$ u& U& [% ?
  5. *    形    参: 无
    * X& G+ V0 a2 w: |' r+ {3 L
  6. *    返 回 值: 0 表示失败, 1表示成功
    1 f, s* |' L' o' o! a
  7. *********************************************************************************************************8 g( H, C2 E2 ?: l0 _/ `
  8. */4 M+ ~: g, ^% T$ ~+ R
  9. int Init(void)1 s* P% K  N+ N* I, o
  10. {   * L# M7 F( h- E% t! v
  11.     int result = 0;
    8 p6 F& S  t5 g+ @% R( s$ Q2 Q

  12. 4 P! s; d9 L& a; a
  13.     /* 系统初始化 */. o+ g6 Z  C. {2 A* |
  14.     SystemInit(); * O! h9 U( N( l  U% W6 G# a
  15. 1 y. N; `2 I9 @/ T
  16.     /* 时钟初始化 */* ^' U$ R* m2 w$ \4 F
  17.     result = SystemClock_Config();8 z5 ~4 W" l5 i
  18.     if (result == 1)
    ; G% A. I2 N4 u9 M
  19.     {' i- `0 ^2 O: @2 C
  20.         return 0;
      Z( X$ x; N/ n" |& f
  21.     }
    + a% c. U  y1 i, v$ L; d1 S- d1 H

  22. 3 t$ x" k3 _4 B; L! Z
  23.     /* SPI Flash初始化 */
    : ?4 a8 F# W4 o6 D/ v% y3 [% W
  24.     bsp_InitSPIBus();4 G' F% V+ a0 e4 ]6 }) U/ v
  25.     bsp_InitSFlash();+ i: |9 Y8 L0 g

  26. 6 e4 p% Z. b" J- K0 U! C
  27.     return 1;/ T/ F" d9 ?- a6 E
  28. }
复制代码
! X$ H  ]& j) \. L) g
  整个芯片擦除函数MassErase
$ o4 Y( w6 P5 }! h整个芯片的擦除实现如下:( r3 ^* e6 l8 ^' e0 G
% z5 Y6 I" U; P, ~' Q* t
  1. /*4 a9 d& {! F+ p# c, l' C
  2. *********************************************************************************************************
    * m6 Z/ m+ }6 y% ?
  3. *    函 数 名: MassErase! z& C4 ~+ }$ V5 f
  4. *    功能说明: 整个芯片擦除
    1 [( |4 k# w0 Z) [" t
  5. *    形    参: 无
    2 O8 s" d& |. X& T# }
  6. *    返 回 值: 1 表示成功,0表示失败- D  \& G$ h- W9 E: \
  7. *********************************************************************************************************) p- q9 O% }) I. _
  8. */2 w6 w) u  \* W
  9. int MassErase(void)
    ; j1 k5 s7 n. Y. t4 w7 ^' G, a/ v
  10. {  \7 x, |: }7 T6 }% ]! T1 L. |3 b
  11.     sf_EraseChip();
    & T2 O+ y5 z- B! G# o& |

  12. 8 x  C- L, h! b! k. \% ?
  13.     return 1;   
    ( X* r; Y0 n! {2 `, b5 N
  14. }) w; w# o4 P. ], z, a. N
  15.   扇区擦除函数SectorErase5 R8 o" t6 B5 w; G' h" |* d8 d
  16. /*
    4 H' V# [: T9 G2 U1 m
  17. *********************************************************************************************************
    5 O+ s5 Q/ w1 B% |. X+ Y* E" N
  18. *    函 数 名: SectorErase6 T% B2 f  Y# v. A" f- [$ a
  19. *    功能说明: EraseStartAddress 擦除起始地址
    . v- x: v; O3 n+ t& U. E# f6 [
  20. *             EraseEndAddress   擦除结束地址* R4 J& \7 |3 L, w) |
  21. *    形    参: adr 擦除地址
    / c1 W" a% |6 q' ~
  22. *    返 回 值: 1 表示成功,0表示失败$ O- M: k! U" E& {1 `# ^
  23. *********************************************************************************************************2 Q8 i/ x$ L: }# f4 \# e% p3 s  g
  24. */
    1 D' p$ w7 s, t1 D- f
  25. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)& Q7 v3 t- A& F9 @+ U
  26. {) ?: U  K3 q- e
  27.     uint32_t BlockAddr;
    - j/ g" a4 x$ E* `- N9 {% ~+ j: q, @8 C0 u
  28. - p. l% R$ K) d" Z* I/ T! e
  29.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;5 n& C; C, _1 U' H
  30.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;
    0 f& Q8 ^5 Z+ |) t: \; L
  31.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */* X4 i! W) ^( D, Y( C" g( e0 [
  32. 4 n/ C( U0 t$ S& R& ~9 ~  ~) w
  33.     while (EraseEndAddress >= EraseStartAddress)% Y  _& p5 N) d5 t* L* m& N3 L) g
  34.     {
    ( e9 }+ R! V- p
  35.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;- O6 c$ ?: B- x) b7 A3 L
  36. 5 q9 c$ c4 `: s7 W/ j$ J" U3 M
  37.         sf_EraseSector(BlockAddr);   
    % E8 {  I, l( w" h8 z
  38. , E# t2 ~8 h+ E( k
  39.         EraseStartAddress += 0x1000;, n) `' k: k. G( f& _( ?' ^
  40.     }6 O( V& E! w7 ^4 l) J2 x

  41. , b4 t5 U) w: s8 h/ k) D, F# K  c6 j
  42.     return 1;* s2 l  ]. d9 i2 a+ @
  43. }
复制代码

5 S, }* D: ?: X+ \' C' s这里要注意两点:
) s; k; S8 o! p) b
! _1 U, S0 d- X& v4 S' z* k" J(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
3 e3 E, m8 X( I. O3 \9 m1 b- h* L
! y6 Q, m9 }! R, X5 h) \! ?(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。8 V! [! M' f7 F
( J! f; Q& l- g3 ?4 j- m+ {
  页编程函数Write
/ X. }" q: Y7 F' }  w页编程函数实现如下:" y$ u  R1 P1 ]8 S9 I) C
, [. o7 ?% e, T7 t" Q
  1. /*+ E% @: w9 K# `4 d5 a# p: t4 K
  2. *********************************************************************************************************
    2 ^4 c1 Q8 @0 a* z/ S3 z
  3. *    函 数 名: Write
    8 m1 t) o. c7 B% K# ?+ Y+ F
  4. *    功能说明: 写数据到Device. u  m0 X6 O; p/ m
  5. *    形    参: Address 写入地址
    * U" f- c$ x: |; |) m( s9 `0 M/ g5 w
  6. *             Size   写入大小,单位字节2 p: w9 I2 Z) T# D& }* W  l
  7. *             buffer 要写入的数据地址
    , F# @# `! A$ q3 e; s) J
  8. *    返 回 值: 1 表示成功,0表示失败
    % V0 q3 ^; H0 g- p
  9. *********************************************************************************************************' S6 `; }8 Y1 q1 w6 f9 F# u& f
  10. */
    5 P( H0 P( i) b8 h5 W/ t3 ?# n- n
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)2 G+ e2 c2 I" X6 M  V+ f
  12. {  8 W& z6 s9 ?* D3 \. w% L
  13.     Address -= SPI_FLASH_MEM_ADDR;
    # y5 [  s. \( l6 M0 i' W

  14. - i- s0 p$ z# S0 {( p6 n' D
  15.     sf_WriteBuffer(buffer, Address, Size);
    ( T* L  g2 [$ _; n" j# ]
  16. 7 E3 i1 x  B; ]& ?
  17.     return 1; 2 g0 q' P' V3 y; r3 c/ U& w6 t; p" ]" Q
  18. }
复制代码
0 o) x6 V6 z- G$ y! H$ y& N5 o5 E( Z
这里注意两点:
0 W6 b1 Y& i2 E! ?0 O; w. I
) q6 a- O+ K$ t( Q) B(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。
. ^  ~6 Y2 i, [. i+ v% Z# a% h, Y2 ]
; F- H. S8 X2 ^1 A(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。7 Y2 g. b7 \8 y# q: Y# b  K

* y8 y+ U6 k4 X+ T; X4 T, A( ]3 p  读取函数
3 \. ~/ w: V# D( h" p+ ^
  1. /*7 h5 g4 k  Y4 `$ z4 P
  2. *********************************************************************************************************) x' ~0 _+ C9 a7 Q# C3 x) i
  3. *    函 数 名: Read8 F$ n8 x1 k# E. S
  4. *    功能说明: 从SPI Flash读取数据/ g* k6 G* B# V
  5. *    形    参: Address 读取地址1 M- W, r. Y2 p( W+ u: Y9 M5 S) a
  6. *             Size   读取大小,单位字节
    . R8 |# P/ e% Z: i/ p1 h! F
  7. *             buffer 读取存放的数据缓冲( v1 G0 Q' D7 p, {
  8. *    返 回 值: 1 表示成功,0表示失败1 x# g6 K: C, P# R: u
  9. *********************************************************************************************************! O7 d* }4 q9 r! h" o
  10. */
    ( M0 g" [7 B5 J1 P  N$ \- H
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)$ t/ Q  {8 w5 i; ~1 W& |0 l$ d
  12. { # m6 q+ Z, f- F* g
  13.     Address -= SPI_FLASH_MEM_ADDR;( W# X6 B+ b/ ?* m5 W! f
  14.     sf_ReadBuffer(Buffer, Address, Size);
    ; A$ q3 Z0 r* e5 r) u4 |$ K
  15. ' [) X- P. c2 i
  16.     return 1;
    " C9 a; e! ]; R0 z
  17. }
复制代码
. f' O8 x5 ?4 h# \0 K0 B; \
  读取和校验函数( q9 ^2 U: Z) n
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。  }6 L7 A* _- r* o# l: O7 T8 I

% @8 F3 G- a5 Y) c% H1 V( d* g. v85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)  R6 T) W  `6 ?5 N& k5 E% R0 }
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
7 a" n/ L2 |1 w( O1 l
0 M( g- p3 z2 f8 d: l% N0 B6 C: W% e
  1. /** k. s5 b$ W! B) {8 F9 K, |
  2. *********************************************************************************************************
    2 C" {: _: J, T3 D
  3. *                                时钟,引脚,DMA,中断等宏定义& C% h( }4 P- G; N  w
  4. *********************************************************************************************************) A& B+ W; }7 I' i
  5. */
    . q* N6 U4 m; X4 J
  6. #define SPIx                            SPI1( i7 [' x7 G# Z  H. @. ?
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    4 r7 \* Y3 U/ _) x5 U
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    : f) f: \, C8 d" v3 B

  9. 6 S: X- C$ `0 C+ x- c( j; C4 r1 z; m
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
      _8 I" {$ F- B2 Y6 L, M
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()5 r8 t$ C' f1 \/ ^' _
  12. 9 J2 x1 T: K0 M; h. |
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()/ n6 `- C9 i' K1 s7 }: v
  14. #define SPIx_SCK_GPIO                    GPIOB
    $ {. J# {" g/ {
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    ( M7 P. y  b. x; f- g
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1  i( L) ^# l. m9 e' u9 Z

  17. 6 A: [6 c5 M8 }# f7 m. h0 ~1 t$ V
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()( B- B  u: ~/ a; ^7 P9 V" b2 ^- l
  19. #define SPIx_MISO_GPIO                    GPIOB: W9 A1 r1 [; C  J; ~* }1 C
  20. #define SPIx_MISO_PIN                     GPIO_PIN_44 O. Q: _" w1 h5 r  Y: o( E
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    & h% L3 M' q$ g* ~* |! b

  22. 8 q+ S! U6 a( R9 ?$ O5 _5 p8 T( {$ P
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    ! U6 n$ h1 v: A3 e: I9 ]
  24. #define SPIx_MOSI_GPIO                    GPIOB
      z' r; ]* p( n
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_59 C1 q. a; R( J2 v$ T
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1+ R7 J( C* U/ p! O
  27. 硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
复制代码
& @- _- e# y% t

# D  K4 N8 G4 g$ {6 G主要是下面这几个:
( q5 B, w/ O/ e
2 F1 }6 k+ n3 J; _7 p$ c7 Z1 }
  1. /* 串行Flash的片选GPIO端口, PD13  */4 Q# u- d& R, e* @
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()9 N: F! W  i2 r0 b$ J& l! D
  3. #define SF_CS_GPIO                    GPIOD
    ! Q2 c( z) T7 [- {! |5 c$ z, b' I
  4. #define SF_CS_PIN                    GPIO_PIN_13
    , L, j  u. H, [  D9 G6 b! v

  5. 0 S9 Z: K* w% E6 q; ~  R
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    . B- B* h! O, R3 ~$ Y1 a% z3 B+ q
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
    ; L: f& m, [1 {
  8. 1 C: ?  f" N3 B" F
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */3 @9 l) B6 J$ f- W- `
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
    2 ~- W% F2 W& x" o5 Q) a' V
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    . t4 K6 I# }% L- f
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    - }( m/ h5 ^3 e, ^6 d: C4 X  t
  13. #define CMD_WREN      0x06        /* 写使能命令 */" X) e* J( l% v" K
  14. #define CMD_READ      0x03      /* 读数据区命令 */
    * Q% Q0 u, e; ~# [9 @
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */- s: O! N8 \! }# i) \2 g' s
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */
    & I8 `: P4 I: W( z/ r3 |; G& B
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */5 y0 B  U0 K3 L- Z& f
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    , m3 ~! u2 X  w9 H8 M- x4 I
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */1 n! G8 x1 A) j6 f' U

  20. . u8 e1 ]6 S) l
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

8 E( b: m+ M/ f. s# e! k( ^85.6 QSPI Flash的STM32CubeProg下载算法使用方法
: |* q% S% j* H/ J4 I编译本章教程配套的例子,生成的算法文件位于此路径下:
+ G- y9 Z/ f: O( j8 @. F+ o+ C
0 \0 x# N5 M, }) X/ s5 g
5ec6d402a7646232a55f4834cd00ac0c.png
9 l9 ]2 Y4 w( X1 J5 R) V- h, X

( x) k) B7 U) U& [2 T85.6.1 下载算法存放位置9 O( }: L! j$ a' _+ C
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:7 r) u. P$ k! U1 G8 V) u

4 Q0 [" e, ~% I6 E\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader0 \( h5 E8 E% f

% d& b' F  [6 W8 L4 Q2 ^, ^
2609073778af5845a869911ab36624f4.png

, j( s1 I/ V- r) A: T( l
1 a$ {, U7 L5 t/ {. u7 y* H/ L85.6.2 STM32CubeProg下载配置7 N5 Q& G: Z) _6 e
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。! g# p2 k# T! @9 |9 U$ G& n. s
/ r1 i# F6 Z, _+ r4 C& Z% g% D
27a88335ceb606bd021c3ebc65d88e21.png

0 O% y( H7 ^* c
' u' P& Y/ V6 I& B4 U' s; S" X点击Connect后效果如下:$ U* o' `: A" c9 X9 q
0 u# G! \" F. R  l
d4e256633eafdbf4bcd8cce6e3b5909f.png

  n$ s  u4 W# K3 z  q2 }4 \4 a6 r! u* Q9 {: g
在这里选择我们制作的下载算法:
1 \0 f3 d9 _5 H: j' W7 c
6 K, ~$ T2 k  o4 n
d4fa28f64be2de98e04f3188eb2fe09e.png
% e/ }$ w+ }3 L4 r

1 y+ \4 {% w' t7 A& K6 N! ^任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming& N) i! w7 E3 `$ Z) ?3 A2 T
! d, m. G* v* h& C. T; E* ~5 f
842518a7cc1dc569598d372132b2ee58.png
" E! u! ]3 v: J) N/ G1 A' v, n

, D+ J. u2 y4 I' Q9 E% R4 O下载完成后的效果如下:
9 c2 L- }, }5 O* F! q/ z, U; P. b/ ~( s) j3 j
68a92f267829ca77f125db982d6c0479.png

: Y: f0 \5 w' v  q& S5 d
7 _4 `! J6 `2 U85.6.3 验证算法文件是否可以正常使用
* x# Y( |% U- a' J: m为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:1 x8 u" ]& S1 ]- ~: a
2 M0 V. ?6 v% X( o
566d2fba421c710da70e5656c6934466.png
4 w6 _. F: H6 ]; r) ?% Y+ l! n
* P0 u* f$ u4 D$ l; X# c& W
85.7 实验例程说明4 b/ h: G/ E& W1 {& s
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。# |3 e% ^) e% V) B8 f7 g
! n; R/ f4 K4 M0 g/ E2 b/ A
编译本章教程配套的例子,生成的算法文件位于此路径下:2 M. T# N1 L' Y/ L$ A2 U' t6 ~7 L
" ?8 @- q8 o2 X4 S- Y' B
6108d1ff7e349f2aa268ee31804c57ca.png
6 ~% \/ R/ p, Q& [1 L8 ~; f

3 g7 {/ I  p3 n$ j85.8 总结8 F; k7 u: P7 p$ y  F- ?$ t
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
: i' {% r+ T9 F; f9 y* k& J
9 s4 a1 o0 D+ y3 u% w% y! |8 h9 ~! d7 J/ z! P
赞 收藏 评论0 发布时间:2021-11-6 23:35

举报

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