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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:35
85.1 初学者重要提示" {0 `* p4 h% C: }: K
  QSPI Flash的相关知识点可以看第78章和79章。
1 V# Y) p7 S8 G. S. {& W  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。/ S$ J: {" }7 d( U6 ^9 J
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样2 t3 ]: q( P7 k: q
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
. n8 b; r7 M& ]; C! P; J85.2 STM32CubeProg简介
5 B4 z, a( y: }STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
# |, A% s( H  Q& u% j2 C3 d) s5 ?3 F+ u: E' _/ ^, R
d80578d217eb2a83520c6f3d797218f4.png
4 R. T; E/ ?7 ?/ N+ M$ R

" R4 {; r6 R/ z' u* L# [" e85.3 STM32CubeProg下载算法基础知识
# G, {! b0 F0 s& n5 aSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
4 J( _4 V! J( o% B" O6 J! C
* o- |4 Z: O6 Q6 u9 _0 _2 B' V85.3.1 程序能够通过下载算法下载到芯片的核心思想/ i- M4 [. f. E) K* S; c4 i: _
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。% Y# c7 V; u( R3 }

5 x: H  o8 @, u& [* h0 t85.3.2 算法程序中擦除操作执行流程" \' s+ \: R: ?
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
3 A1 e  j/ f3 u; s5 V$ P
3 p/ a, u& L3 ?! f. o' n擦除操作大致流程:+ K# V: k* W& o6 t0 n
5 n' h5 {$ g; y" q
d75670256363bb50203bed0bbe4f9fd2.png

8 f; ~% E/ J( z; T& F0 [2 c5 T3 g' T4 y- D" |% y7 a
  加载算法到芯片RAM。, y% C) g7 ?# |7 R& L5 |6 S; n
  执行初始化函数Init。
, ^% j* |& A! [9 M: m8 ]  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
- N1 @9 s+ M0 t- e  执行Uinit函数。
9 K6 G6 K% x2 `! S. S5 m  操作完毕。
5 x$ e5 s4 x5 c( u! u  x+ M  D0 @85.3.3 算法程序中编程操作执行流程
3 @# x& B9 g" W9 J) x注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。1 b  {" G$ q/ k  h* y  l" u
6 P" G# _, O" L: G' R" g
编程操作大致流程:2 N' Z: o. f3 f9 H- f, B
( `; q3 _, h- h6 n( `! f" v: x
4fa96e9e33d700081f2cdbd312afb31e.png

: r4 M0 p1 ?9 F* ~, ^+ L# s, ?; e6 d; u- `% Z" _( t- l9 r
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。6 ?1 e4 O6 r1 Y
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:9 M  {, [! I+ Q1 J
  加载算法到RAM。
- v5 V# z$ X9 i: f  执行Init函数。3 G& W( x. A( p0 N. K# j9 {
  加载用户到RAM缓冲。& Q- z! _3 i* [3 T' D
  执行Program Page页编程函数。0 j! T( b" g' a8 `  v
  执行Uninit函数。
5 `6 {8 j! l* l8 Z  操作完毕。& o) ~, U7 n) ]2 Z. T
85.3.4 算法程序中校验操作执行流程3 K- o- }# n3 T7 a5 R2 h. A
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
  c, q3 N/ i2 ~6 \4 N" @8 _9 F, E/ L5 F% ^0 M; f! f) p/ J5 r
校验操作大致流程:
1 E! ]$ D) j7 \: `: w0 P
* q6 I! ?, G3 n$ u
7deeae4b650bd82c457492af14fc26b7.png
2 Q; q8 ^3 ?" t3 @: |

  U0 z2 I; H* i% b; R$ ]  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
. A# U( E6 W: v+ @! G- f9 `9 D/ r  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
, Q$ j  E7 m; t  n& [0 \5 T9 } 加载算法到RAM。
6 @" P' q  B* U/ e+ V% H, H! L 执行Init函数。
/ u0 s6 u9 t+ w# x, ^' } 查看校验算法是否存在1 C6 i9 m* `' D2 k4 y
  如果有,加载应用程序到RAM并执行校验。0 \9 a( N. D# L3 g
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
' M3 L* B7 n+ i  q, j  执行Uninit函数。
- K* w$ k/ [0 x& K  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
# V' V. z7 ^% v  D  执行RecoverySupportStop,回复支持停止。4 E3 I7 t8 C7 Y' w& ~+ x
  执行DebugCoreStop,调试内核停止。
& i) I+ I, H3 b. t5 Y. b5 w, k  运行应用:  c; Y: I0 I  ]+ B
  执行失败8 Z  s4 G$ o* U/ l/ F/ g+ n% ?0 d8 B
  执行成功,再执行硬件复位。
6 g7 O7 s6 O# R9 M) a2 L  操作完毕,停止调试端口。( @- ~9 {" M) ]: t7 _8 Z
85.4 创建STM32CubeProg下载算法通用流程- z+ {4 D; `0 }/ e2 `
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。0 i3 L; e4 c" y! s0 D2 b3 ?5 f

4 `+ @: J9 ?, \( Z: J6 f. e" ~85.4.1 第1步,使用STM32CubeProg提供好的程序模板
) b, S! Z) S( f/ N位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
7 V! b& X! Y. F+ `1 j3 p1 b) L; O& n! B6 n% S8 B
97f385e3b62a055bac9b5c0214b877e5.png

8 {6 q& b9 @$ H
. W* k. x4 |4 d) n2 G$ [4 k3 y以M25P64为例(注,其余步骤就以这个为例子进行说明):& D4 M  u9 a" X

$ e2 t; f: g3 e1 I1 [
43b298f996937fe35e89697fc6479cb9.png
& ?# r# ^5 q; q: j
$ z; J' T+ q3 j8 t8 x  f% L$ C+ t
85.4.2 第2步,修改工程名
0 a) N) N, b% d& _  L0 mSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
+ `8 H# s1 P0 \5 k- y; W
& I4 g7 u1 K! r+ O$ e2 U5 @85.4.3 第3步,修改使用的器件
7 X; H/ G' c4 ^. O在MDK的Option选项里面设置使用的器件。
" _7 R: X; Y7 X, A
! c* I* q; h4 S
36026b9a6d60249610d0246eb725b246.png
/ h: T: \* i& e4 q" q( u( D0 i
& y& e2 F4 m+ K
85.4.4 第4步,修改输出算法文件的名字+ }& t: G) Z$ G  ]/ {  B$ @. j
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
. A9 Z9 g& l# l/ C& u3 G! @+ e" j) S
3 Q3 j, \8 h6 E6 b+ P9 ?8 I2 ^4 X
52c72b3993f28a366a22eac56f8fd043.png
0 @9 F$ p; g, @" O8 q1 t- D- h
9 Y! F3 t/ C% n6 {3 P
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
4 z. K: c3 Y5 a1 r, H
& A7 _0 Q5 W/ s- D5 c# U) A- t5 X
7b48153027a44de33926c2da008716a0.png
2 R: E0 ~+ u& j1 [) J
! n; z  P6 T/ c
85.4.5 第5步,修改使用的库文件和头文件路径
: p- S2 r( P0 b6 t% W. a: J根据大家使用的主控芯片,添加相应的库文件和头文件路径。
5 x) A& O% W1 L' @. m" K3 p& u. G+ ~! x0 x
85.4.6 第5步,修改编程算法文件Loader_Src.c2 \2 I8 G* g5 l9 Q5 O$ m
模板工程里面提供的是M25P64,部分代码如下:
$ x2 k) n9 {3 d/ f  X6 Z: M0 d
$ `. ~. I# \2 i. y
  1. /**
    0 {4 i, f- C! k3 T6 D
  2.   * Description :
    : ^- ~2 N! b! Z% ?% \0 A% n# I# a2 S
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    6 \" e3 E& b. U" H0 C2 O- v! _9 ~
  4.   * device and initilize the FSMC with the chosen configuration
    . `: |  p& d% C. ?9 I
  5.   * Inputs    :
    0 T) e% y4 c& W) W3 d5 b
  6.   *      None
    7 D; I$ A) Y* y) r& M: d
  7.   * outputs   :
    + k9 j) p' K( U5 O, u1 P# t, U
  8.   *      R0             : "1"             : Operation succeeded1 o' _3 E) S  W) w
  9.   *               "0"             : Operation failure
    $ V/ O; y: f; b; T3 z, ^  d( \6 N: q
  10.   * Note: Mandatory for all types of device
    2 J0 L2 Y0 H% ^2 j0 Q! I$ f* [' ]1 D
  11.   */
    % w. N' p& e% A9 Q/ z8 x
  12. int Init (void)
    * |, A: g$ k  Q- q5 T  E6 Q8 I1 h7 R
  13. {  
    6 K3 }# I  c  k, u' G8 L3 T- c+ f7 v
  14.   SystemInit();
    . E: X) q" C' }6 s5 z# R6 ]
  15.   sFLASH_Init();& g: t( {/ Z' A$ u; \
  16.   return 1;2 V9 q+ w- I& z1 N
  17. }
    2 a. ?6 X/ i1 R& W9 B, v9 C

  18. $ a7 M3 C: t% p$ Z
  19. % n; \. q$ P) v! L, F6 q' K3 r4 K1 t
  20. /**
    : [; R$ }9 r  I6 V1 H. B
  21.   * Description :7 [6 T3 ^9 |! R! C' y
  22.   * Read data from the device , d1 Z' F# I( Z
  23.   * Inputs    :0 p' L% {$ R' x7 v% G
  24.   *      Address       : Write location& Q* L8 Z/ Y1 r* ?* A3 `
  25.   *      Size          : Length in bytes  
    ! i8 c2 _: a2 Z9 `
  26.   *      buffer        : Address where to get the data to write
    ' W8 I, |8 x( p
  27.   * outputs   :! G! ?' ^% D( c
  28.   *      R0             : "1"             : Operation succeeded) j  t' N! ]! z
  29.   *               "0"             : Operation failure2 [; }* ?/ z+ ~- v. v# _
  30.   * Note: Mandatory for all types except SRAM and PSRAM    # U# {7 h# R) s, y0 K7 T
  31.   */
    , n; \- Y0 H" d1 l$ g
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)' s" R& e" I4 Z
  33. {
    ' X) z1 \. V. h3 Q: H" W
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    : o3 }2 c  F3 \4 p, f1 d) t4 d
  35.   return 1;
    , P0 U( `5 l/ y2 {
  36. }
    & q; d$ f( B# v0 X; R/ \
  37. 8 G: f/ G+ b" X
  38. ) N8 x  q' |3 ~. O7 Q4 O: S5 l
  39. /**
    8 S# d& s) n7 P1 A  |
  40.   * Description :
    7 X: Q, m2 S, i; ~5 Q0 r3 g
  41.   * Write data from the device ! _4 ?2 {) B) r& b& K. P1 c
  42.   * Inputs    :
    ' ~& \8 I( `8 E3 u
  43.   *      Address       : Write location5 O5 Q9 b$ L0 p& j2 g5 V+ a
  44.   *      Size          : Length in bytes  
    4 w+ e' i; D% a4 |, W  ?
  45.   *      buffer        : Address where to get the data to write
    6 `5 g! o$ `& U% Z% [; H' h4 m
  46.   * outputs   :
    0 M  }8 y1 C5 E6 T
  47.   *      R0           : "1"             : Operation succeeded
      M) `0 L* p0 g) r& H: v4 l. `
  48.   *                     "0"             : Operation failure
    ; V& C& ~( x! P
  49.   * Note: Mandatory for all types except SRAM and PSRAM      ]+ s" g& j, F" Y: t
  50.   */
    & f, P/ y5 c: u
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    : ]9 }9 m2 E( b+ f
  52. {
    . P: w; S" v- x' V( D) ]
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    # L5 B# O6 v- R: q
  54.   return 1;
    4 N, u& ]  x) Y6 L8 z9 o, w; H, l
  55. }
    # @; Z" x' t7 C6 H

  56. + h( G2 Z) ]" o* g

  57. . z' q/ T' ?; O$ W: I
  58. /**
    ( e; ?4 [& b# c, S& A9 d6 \* P9 k
  59.   * Description :  C: T! F4 q$ z! l" C) ~! o5 }
  60.   * Erase a full sector in the device; `5 l! K" L, W+ l/ a5 R
  61.   * Inputs    :
    ( b- R( ?) Z/ o  K( e
  62.   *     None
    " `/ _; r/ g- r7 @3 \& q+ f  X7 J6 D
  63.   * outputs   :+ V2 }& I. u( b  ]% [6 l. G1 |" N
  64.   *     R0             : "1" : Operation succeeded2 I2 T0 Z" q$ G; e. y
  65.   *              "0" : Operation failure& c/ c1 ]$ X+ `- G1 U' ?
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    ; `' e& Z& X% ~6 M! t/ m( t
  67.   */0 R" D; V: x; S* `$ D5 M/ U6 f: @6 Y
  68. int MassErase (void)0 ^( l1 Z1 l! ?3 I+ ]! U
  69. {  
    ' |5 d: O% k" ~" k
  70.   sFLASH_EraseBulk();5 S" T0 s, W% `+ x
  71.   return 1;   
    . G0 ~; m- {* d
  72. }
复制代码
  X$ t/ f5 t5 D2 F6 o4 l- `& e! v
85.4.7 第6步,修改配置文件Dev_Inf.c
7 L- y0 |. ~$ ~! s( y模板工程里面提供简单的配置说明:% @1 E% \6 _. m3 K, ~

! j- A$ e0 e1 s# [: M" J' E1 P
  1. #if defined (__ICCARM__)7 i% G0 `6 o5 L( N% H# y2 c
  2. __root struct StorageInfo const StorageInfo  =  {
    - Y3 s: ]) _. N; V, D: B
  3. #else: I, a7 s& B# s* Y9 x1 d6 R$ I
  4. struct StorageInfo const StorageInfo  =  {
    ' l& v% R6 ]& }
  5. #endif
    # A6 j# w' u8 L+ b7 D, \  s
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    ! [5 p1 a/ D3 l6 M* x6 J5 P! ]# U
  7.    SPI_FLASH,                       // Device Type" `, H8 X$ J' x) u1 _
  8.    0x00000000,                     // Device Start Address
    ! u7 R+ h1 \/ j0 Q
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)* o7 S8 @; ]) ~- P
  10.    0x00000100,                      // Programming Page Size 16Bytes
    0 S4 v. O) {3 `# T
  11.    0xFF,                            // Initial Content of Erased Memory$ r! D: m  j* K
  12. // Specify Size and Address of Sectors (view example below)
    8 Y  v4 r- s* U7 z2 X
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes " C* Z2 g4 K8 [
  14.    0x00000000, 0x00000000,1 V4 ]9 d" k9 Y6 [
  15. };
复制代码

0 @9 Z1 k% F5 s+ `8 e6 T注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。) e  k* s# H' [( I: Q

6 h* j) M! q5 l% Q7 |  k. X/ W85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。3 j- d9 y+ W; m9 f2 }2 B: \
C和汇编的配置都勾选上:
& ~  \  L1 q& ?, x# d6 Q$ ~! s: {' ^1 P: b
afddd6e839292aa2b94b43e79544d630.png
) k! Y6 u* Z+ N
9 m- ~* y  g  q7 P6 p" ?9 @! t
汇编:9 A" X* W$ g9 D' S
: F; O3 l* C6 _" i( z" w6 E4 G
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
& w6 K4 M6 H% Q, e9 ^
0 n6 ~2 Z1 c: a' X4 Z
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
7 `* j8 q- s9 b+ c: q. }# D5 F! m# |7 _: r* b: A. @
(1)加载以响应运行事件。+ L: q" S6 ~  J9 m- o2 |
& S8 F. I" q. d: G1 J! U% g
(2)在不同情况下使用其他例程的不同组合加载到内存中。
. T) a4 r( ^+ t8 R
+ L2 e- q, M3 t% m(3)在执行期间映射到不同的地址。
3 c$ W( P/ X( W/ v. \2 e2 n
+ G, o/ H& y8 f# v7 ~0 x使用Read-Write position independence同理,表示的可读可写数据段。
& g$ S" h" ^7 L' K  w' k7 a( z3 u7 ^9 Q9 s' Q
85.4.9 第8步,将程序可执行文件axf修改为stldr格式6 o1 B( b" g& I  Y
通过下面的命令就可以将生成的axf可执行文件修改为stldr。
% [# W) l2 ]* V% B1 W/ g* y4 G
2 L0 r7 Z; R: x' J, ^& Q/ @3 J
c683673d2481d8dce75b6fa01d624600.png

/ r$ _6 M" E- J* C' _4 z4 P( F1 o( L6 z. p; q% D
85.4.10   第9步,分散加载设置
# t$ T3 v* f$ o# C我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。, g' G! p# L) P9 }' Z' J! H

1 X, I% V/ a) b2 ]) z b96835a7506516319002e6b2e8db9371.png ! |6 x  Y" M. D' O  V2 S
, v. v8 ?/ a6 |& q% R
分散加载文件中的内容如下:
" O2 t; ^# w5 Q6 c: N1 ~
8 Q/ a1 M+ ^8 C" G* I! }' k
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    1 Z  I, M. F1 y/ K  i* b
  2. {4 |/ c" V1 U0 g% L* s( P
  3.   PrgCode +0           ; Code8 m' D# e6 _& d, x) A) c
  4.   {
    ! c3 T+ |& M; ~
  5.     * (+RO)
    6 \1 B8 z: M% T
  6.   }
    # F0 U0 b" n3 y' f$ F) h' X4 _: F- u
  7.   PrgData +0           ; Data
    1 c# Q$ M9 y! E0 f$ y
  8.   {3 h" _4 j5 B6 a% p9 C8 ^7 T" Y$ ]
  9.     * (+RW,+ZI)
    5 u7 ?  s+ U, z
  10.   }# g2 v4 P8 g0 C; O: d
  11. }/ f* h, h4 }: L: J# {9 ^

  12. . X, Y* |( ?* {8 ?2 U
  13. DEVICE_INFO +0               ; Device Info
    ( a8 X7 K0 \3 D. d) j) H
  14. {1 l2 v5 C* ]0 }8 {, c/ [+ b0 K
  15.   DevInfo +0           ; Info structure
    - f4 y5 b: Y7 I% W6 Q9 I
  16.   {  J" o# }& L& O/ C
  17.     dev_inf.o) X$ Z. L9 V7 a$ s) {4 L
  18.   }+ b: s" `* M9 \( P1 g0 p: [0 Q
  19. }
复制代码
' b* z+ R! D4 A: G+ E- T& a
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。! B8 Y! b$ I' Q: p9 @: U
& T* A" ^2 m6 a
--diag_suppress L6305用于屏蔽L6503类型警告信息。
' t: R- x: r8 n. m( ]3 f3 u  M2 X. y2 V$ ^1 F. c* ?5 J/ l
特别注意,设置了分散加载后,此处的配置就不再起作用了:
$ X5 O9 X9 y' R- X- p) |( i
! b0 @1 [4 `* K( N! p4 W, u2 j0 }, ^
641395c374a3af02f1a92d3ff9ee4199.png
. \* L7 w! w4 X6 D% S  J

2 \5 ?6 r- s6 n0 O6 b& }- b85.5 SPI Flash的STM32CubeProg下载算法制作* A( v; V9 ?( e0 I1 m
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
: j+ Y) X9 `# x' L
2 o; v4 A* }7 f" T85.5.1 第1步,制作前重要提示! l- T, m6 O/ L0 N$ k
这两点非常重要:
& t* h' c5 i! J
& Z3 j& `1 ?5 p; r8 q  程序里面不要开启任何中断,全部查询方式。4 `1 }7 G! }. W
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。$ ~& h- t2 x. r7 [1 @' v3 V3 P
85.5.2 第2步,准备一个工程模板
8 V9 s! r$ L" v9 T( J 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
% }7 v. G2 E6 a  [6 j( d1 S8 t, o, J8 C! Q4 L' E6 R
80ad5e3abdd6a993c8621230401a1313.png
5 d5 G4 d- Z2 B7 {" O& [# B
$ z4 g& i! G; U: w1 K% L
85.5.3 第3步,修改HAL库
* W  T' K4 Z# T- j! `% U3 D这一步比较重要,主要修改了以下三个文件:+ k$ S6 p2 g2 O- O1 f
: C2 |& O; I) g' O) l
33bf89f25487547d4008f43b7cf10e03.png

/ _  O; b: P, D8 ?. }6 O# a. V+ s/ A" f6 ?! G/ [6 q' I
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
7 Y, U+ Y' u1 ]7 ^
2 R3 Q2 F, q3 r( p$ T$ `% h+ q85.5.4 第4步,时钟初始化% m/ E+ H/ t; b# l6 u+ N( H8 z
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
# Q0 V4 s5 _2 |9 Z+ f  A5 }2 w" e5 `5 r
  1. /*
    6 D& |! C; I9 r+ y. B3 Z
  2. *********************************************************************************************************
    # Q0 |" @9 z" a+ m+ j: N& d- u
  3. *    函 数 名: HAL_InitTick" q# V) }0 h) G7 @
  4. *    功能说明: 重定向,不使用
    : s, Y: D5 F0 Y/ A
  5. *    形    参: TickPriority9 i5 q: X6 p, s$ }* W( g0 I
  6. *    返 回 值: 无
    ) D/ P2 l$ a$ t) J1 }6 b
  7. *********************************************************************************************************1 z! j" z, `9 e. _( S+ B8 g
  8. */
    + X" l) E  ^; z% {5 w$ e& q$ ]( a
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)8 h( }: J$ f# c# D4 \
  10. {& f* p5 X; N3 _( D3 V5 W
  11.     return HAL_OK;$ b  Z/ g1 p$ m' q  r
  12. }
复制代码
, I$ I3 V( Z* z# ]2 A& j
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:' l6 f/ j: O0 O3 n! E+ b

6 N8 |! D% }) q9 K$ w
  1. #if !defined  (HSE_VALUE)
    / F9 F' Y' R6 J7 O9 t
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    0 u4 v# O+ `" W. C
  3. #endif /* HSE_VALUE */
复制代码

7 w1 d! l* I+ A+ y0 ]  m2 A' \5 J最后修改PLL:/ E. m0 m, R- b, b# G: N) E
/ |( k2 [& y$ ?* ?3 ?6 c
  1. /*
    3 W9 s% i& t8 e$ y8 Q$ H+ V3 L
  2. *********************************************************************************************************; V# Q8 u7 `4 \  V6 K
  3. *    函 数 名: SystemClock_Config
    / `: Q* n. y2 c7 |8 O
  4. *    功能说明: 初始化系统时钟( h5 r- ~# \7 F6 `% e1 u
  5. *                System Clock source            = PLL (HSE)% \& F3 ~2 K+ F/ T5 z/ X& N
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    $ G  m4 Q8 @3 O. [$ s: w# V% Z
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    / g( f" ~4 N! K' d- n- R2 y
  8. *                AHB Prescaler                  = 2# `* m* B, g/ D/ Y  \
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)( I; Y5 c$ G8 A: }  b# @$ d( J2 |
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    ( m2 h6 U4 {+ L: B6 K- R% ~; d
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    0 w) N% o3 C2 s) w. Y6 p3 K
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)0 j9 F# u2 u! F7 `" ~+ }& c
  13. *                HSE Frequency(Hz)              = 25000000& i8 l' f9 Z# |& ]
  14. *               PLL_M                          = 5
    ! Y, a& U8 _0 D" Z8 W# c; ^
  15. *                PLL_N                          = 160
    % n& e+ Q' ]. I7 F( H+ f
  16. *                PLL_P                          = 2
    4 h) D% d6 G& O5 A3 T
  17. *                PLL_Q                          = 4
    2 H$ S" Y( R; s4 L" [" ~6 ?
  18. *                PLL_R                          = 2
    . W) L4 U) p$ M& v) O' }
  19. *                VDD(V)                         = 3.31 x# I2 b4 b9 c& l
  20. *                Flash Latency(WS)              = 4+ n$ p' B# O6 W6 T4 F
  21. *    形    参: 无8 E, S: M) S: r; v1 @: k/ T. N; }
  22. *    返 回 值: 1 表示失败,0 表示成功
    / z9 P" P3 m. B
  23. ********************************************************************************************************** I3 [6 A! z  y& Z7 A
  24. */
    3 _. Q+ b+ \' y3 T
  25. int SystemClock_Config(void)# i, v: V: _/ Z' G- A
  26. {. Y6 p  D& s/ g% j0 W) M
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};) F" N& [. t. s2 }2 @
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    - E4 g4 }  ]! |( j
  29.     HAL_StatusTypeDef ret = HAL_OK;, [0 C. }8 I; V, e/ u5 }3 T

  30. # q6 |) E+ W9 ^2 [
  31.     /* 锁住SCU(Supply configuration update) */
    9 N+ Z, W+ A* j) g" [
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    3 c0 |6 l8 _- q* A8 A

  33. 6 [1 y) v" i" z( G" }! V
  34.     /* 0 y8 e1 g& u$ x
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    ( E6 N- I& |% x) _! w' I
  36.          详情看参考手册的Table 12的表格。! l8 T2 y+ }$ W# Z/ c! H/ R/ S3 a
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。1 v) N) I+ E3 V% Q: Q5 m' ^
  38.     */
    : H; S1 S/ ?* z6 x/ k
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);; j8 C$ F0 E6 n

  40. # ^$ B! f/ v8 v+ a% t, l2 x
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    " R5 a0 x* x; ^  L" _8 I0 c( D
  42. + ^: R: o+ h" `
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    * l0 w# ~0 T! w' N
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
      N) A0 O. R3 |# S# R2 M: Z
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    , @2 z& e6 ^3 ]- h
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    8 y' W7 A; c: S% Z- |1 |8 c3 D- K% A& q
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;% M$ ?4 s! U9 w* D
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;8 ?  d8 {0 n+ N( @( ?% V$ M
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;) V6 V9 j% ?% w  p1 Z+ ^* T
  50. * [( {$ _" o6 X8 ~7 ~$ _
  51.     RCC_OscInitStruct.PLL.PLLM = 5;( \* o  _4 L) l# v
  52.     RCC_OscInitStruct.PLL.PLLN = 160;4 {6 Q( H' B8 ?+ |5 ^
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    1 j( ^# U; I; e; n  t: k; [
  54.     RCC_OscInitStruct.PLL.PLLR = 2;4 A6 a  w8 f0 u2 s: R9 X* g
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    2 |9 y2 N! ]: S5 B8 g, ?( x; [
  56. : V) ^% q" d8 m6 J! J: H
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;4 h- g* J: P, B
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    ) |1 {* Z# ~4 m6 m9 `5 I8 ]
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);: m) G; ^* i5 c4 |2 Q
  60.     if(ret != HAL_OK)
    * P* m2 H6 t- b5 B. H5 y
  61.     {/ Q2 w# Y- `- z/ C9 g  X2 s) ~7 c
  62.         return 1;        " z$ E5 N4 H. r' {* d
  63.     }
    : c1 V1 R' _( y/ r5 z
  64. 0 o9 v: I% X+ t0 o& W1 m7 n9 b8 F& I
  65.     /* # H9 t; d3 Q. g
  66.        选择PLL的输出作为系统时钟
    & b+ Y/ G' @* q+ `
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟( ^# k/ Z* [0 z! n# {
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线+ X* K( Y# l) {3 Q
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    / v  z/ [# u! R+ o' A
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    / i+ p  \9 Z- s8 q  Y
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线& R4 `6 S; U8 Y; N7 N; R
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    6 y1 Y) M* o4 {$ g8 o0 x
  73.     */6 W1 `4 ~6 \, T' Q$ }& D/ |6 f
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \) N: x7 }/ P: V: `) A! `6 [
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    : ^5 b, H2 t2 }- |

  76. * |3 i: F$ h9 a  ^' g& b
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;. \5 e  t5 N+ w$ I; j. J7 Q" b
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;, ?8 O% X1 O" F
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    # c3 {" W0 }* @! A$ I6 W" e2 g
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  / E" [4 f' R& K0 E2 K
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    4 q  @# g! X3 }. I  h* s& C6 t6 n
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; ! p. s' I4 [, s4 g& t
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    9 G9 y9 T, R' L$ k- |( M

  84. 9 j9 f0 P) I$ @* U
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */& B* k- `. ^2 Q0 _) y2 @' [& e
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);0 n# z. y/ Y! U# O
  87.     if(ret != HAL_OK)) n: |+ f5 `  H1 ~
  88.     {$ [  }- @9 m* R1 T% g# Z8 _
  89.         return 1;
    % B0 G3 y7 ^5 ?" q! h4 h# l2 E
  90.     }
    6 U8 R7 h: a7 y' l1 O
  91. : q, M% n* K$ e5 ]
  92.     /*! ~+ q2 I5 _+ U) S4 G8 C- n3 }9 p
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 $ K5 C8 }3 H2 \/ P/ W) o
  94.       (1)使能CSI clock" p0 C9 I/ a  w2 t% @
  95.       (2)使能SYSCFG clock
      F- o# S3 v/ d
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0) |5 m, L4 r! c
  97.     */) N) A  }' u% i! z. l4 q* E( J; W
  98.     __HAL_RCC_CSI_ENABLE() ;9 }: h. T! l6 p6 H4 `4 V

  99.   p  R' y  X4 P; V% _3 D3 i
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;" j. \- b5 Q% \  S
  101. 8 [+ L' b  G8 K0 ?
  102.     HAL_EnableCompensationCell();
    * S0 b/ q9 o6 j4 N3 h, W
  103. 5 ~( v  ]9 a  p# x5 d
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    4 I/ N6 F* {' G
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    * U2 l9 c6 S4 K6 w7 P6 ?; H' V
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    : b8 I/ ?$ R3 D2 J

  107. 0 J% k  z( ?8 N! h$ V, }
  108.     return 0;; q6 H2 R8 z: o" `2 h/ c
  109. }
复制代码

0 Y% M% T) _+ q( l( u$ w85.5.5 第5步,配置文件Dev_Inf.c的实现
0 G7 c% q0 w7 I+ i( U) I6 J配置如下:
; Z! j) J% ~* L
. l' Q3 |; v% I' E) g* j
  1. #if defined (__ICCARM__)
    2 a/ x7 M  `# q) ^) m* m
  2. __root struct StorageInfo const StorageInfo  =  {
    9 @# Z$ @4 A1 Z4 S7 ?3 u
  3. #else
    ( H' u, i& C1 d& [6 T4 Y
  4. struct StorageInfo const StorageInfo =  {  K8 M" \& p* s" c* Q
  5. #endif
    4 D9 W0 p0 [, {1 O0 ]3 [
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */9 @. N9 ?* w4 M0 d# K/ h
  7.     NOR_FLASH,                      /* 设备类型 */
    9 j( j$ i/ _% ~* S: K9 j5 M+ [. x
  8.     0xC0000000,                     /* Flash起始地址 */! f- N/ i/ |/ Q, T" n
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */2 h9 M4 ?, V. w9 d
  10.     4096,                           /* 编程页大小 */
    3 U# J6 G" T4 c
  11.     0xFF,                           /* 擦除后的数值 */
    : L5 i4 \  Q) K
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
      F1 X4 a6 R, K: m) o! n$ T* j
  13.     0x00000000, 0x00000000,5 x9 C7 E) c, e' M; b
  14. };
复制代码
  |% g8 A- e' a! o& m1 X5 G7 S3 Y
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
) _% u% t) O: ~& m3 w9 d; W; t9 Z3 a# j7 d: e5 a( u  [
51f220556177a36712977b8d140b627f.png
5 D- h8 A' u1 z2 T7 n/ g+ F. H

# {7 J: P; C  R, i$ @" z85.5.6 第6步,编程文件Loader_Src.c的实现8 P% p$ g8 Q! G" R
下面将变成文件中实现的几个函数为大家做个说明:1 e1 k* |+ h. P+ @# G0 S1 {

2 y* ]7 n5 J( X* F  初始化函数Init* T" r5 c8 I6 w5 m
  1. /*
    * |, X& @" S+ C& Q1 }
  2. *********************************************************************************************************3 Q2 t. J1 E( o, @
  3. *    函 数 名: Init
    - N5 N8 |  {' H8 Q- h
  4. *    功能说明: Flash编程初始化) D& K: A' E- w8 c  a5 S7 T2 x
  5. *    形    参: 无
    / i1 F! x' J; F5 a" P
  6. *    返 回 值: 0 表示失败, 1表示成功1 j2 N6 x& f+ D. G& T( b; x
  7. *********************************************************************************************************
    ; s% f4 x1 M1 ]. l2 a, \
  8. */
    * a% L% W4 J9 G
  9. int Init(void)
    3 e3 A7 g' l  ]! I
  10. {   6 c7 z5 Z) ^3 q4 B: z
  11.     int result = 0;# c, b4 r: I9 s/ i
  12.   a) ^: d* r2 q  ^1 i% U9 H
  13.     /* 系统初始化 */
    ( b, [, d9 Z& T5 q
  14.     SystemInit();
      {" K6 g3 P1 ^8 R: Y8 v6 e

  15. 5 I: z( n5 r' @+ p; h* C
  16.     /* 时钟初始化 */7 X$ |! Y2 J0 n, h" B, [) B' Z
  17.     result = SystemClock_Config();: k' Z. Z- i* }* I- ], a( K  f2 o
  18.     if (result == 1)) h% G7 r9 b% D4 m6 i, P
  19.     {
    8 u8 `& p2 [% I% ]
  20.         return 0;
    " I: A9 R- b5 K5 R5 O
  21.     }
    , m# P9 a( C6 J4 `6 ?
  22. ' F" T/ k1 \% f
  23.     /* SPI Flash初始化 */
    " y5 s! S6 o) C/ l2 C
  24.     bsp_InitSPIBus();
    & u( k8 A! u. T) l( H5 d5 x- ~7 J
  25.     bsp_InitSFlash();
    ! W, B* w& L5 Q2 V0 H+ h
  26. ' V- z4 O# m/ V$ L
  27.     return 1;( d& F+ Z4 z. L, e" a
  28. }
复制代码

  x( I2 u( [# ?  S: J; |  整个芯片擦除函数MassErase
/ F2 K! r0 L# ~) e2 `# G7 @整个芯片的擦除实现如下:1 }) M9 Y, A) ^" g" r
0 v  `2 E6 G& {& ~) C% I
  1. /*
    0 b, I5 S0 s: Q, l( v
  2. *********************************************************************************************************/ N2 w- q& G0 \$ c& q& N" Z
  3. *    函 数 名: MassErase+ ]' Q/ c! E! p2 _, P* n. h- }
  4. *    功能说明: 整个芯片擦除# m, w9 h. ?5 G4 h% _
  5. *    形    参: 无6 M+ C2 v) W  k8 I& k2 V, ?
  6. *    返 回 值: 1 表示成功,0表示失败
    2 {' b$ M- i/ V& n$ e
  7. *********************************************************************************************************
    ! d  H8 z, c5 Y3 ~, F0 M( G: I
  8. */* P! k4 j2 ]- I* g/ U
  9. int MassErase(void)
    ) f2 `; G4 S5 N$ I- G
  10. {
    $ N6 p2 M5 k6 G/ s2 T' R
  11.     sf_EraseChip();: _, O; {, I. p$ i

  12. 9 U& ]# C. t0 I7 P# d
  13.     return 1;   9 e! o; X+ z* x9 Y8 |  r9 P( G
  14. }+ A! s. W" ^& e
  15.   扇区擦除函数SectorErase
      l( t, S! W  q3 u) u9 |! E
  16. /*
    - w# p# j3 j" S6 y  D1 i3 C6 a" v
  17. *********************************************************************************************************2 g7 K- H" M: k9 `
  18. *    函 数 名: SectorErase
    ' u+ m- i* Q2 Z& |9 W7 x- _
  19. *    功能说明: EraseStartAddress 擦除起始地址, n7 Q& A: [, }7 U& {  f
  20. *             EraseEndAddress   擦除结束地址% S6 h  D7 `* }$ ?+ ?
  21. *    形    参: adr 擦除地址
    / C' G" @$ ]7 X" d
  22. *    返 回 值: 1 表示成功,0表示失败
    ! ?4 f0 @, B: y/ J* ]  ?/ M& A
  23. *********************************************************************************************************
    4 H! H/ m9 ?6 ^# B
  24. */
    ' b  h" s3 O. P9 X* y
  25. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)0 \# l# r/ \' N- h
  26. {- P8 X+ I$ i( X% ?
  27.     uint32_t BlockAddr;
    $ M9 o3 r8 x( w, I
  28. - y$ B* C) _# [
  29.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;5 C. q# l: n# C) e1 c
  30.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;/ z, s- s* l! ]# o4 {' t
  31.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    ! c! b* a# x( ]
  32. # O: _; P) K4 i' V8 f& h$ ]; B
  33.     while (EraseEndAddress >= EraseStartAddress)5 n$ g9 k8 @) [( P
  34.     {
    0 ]1 c/ F) }# M; e8 x  [3 M
  35.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    1 s. e. [, ]5 n- m8 M; H, q; N

  36. " n) S9 F$ _4 p# i* T# a9 u
  37.         sf_EraseSector(BlockAddr);   
      `' t9 t9 r7 Z; n" Y' T4 w
  38. 9 V4 _! ]0 k$ H! x# G0 S
  39.         EraseStartAddress += 0x1000;
    7 J2 B' j+ _5 w6 L4 v1 H
  40.     }& [$ D8 ]% D6 C) e/ Z) }- y
  41. ' x( O. }" |: V
  42.     return 1;
    + T2 X9 H- h, G, H
  43. }
复制代码
* K' C1 ?) N$ f( z* u" k4 X$ m
这里要注意两点:/ M- E& x) Y8 K! z
8 u( l9 Q- M) I$ g! Z
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
- h& H% V3 S& |3 X. [
' g6 R+ j7 {! R5 A3 l(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。; A1 H  y2 I: \/ S- P, j

  D, y7 c* ~% R& B2 M6 o  页编程函数Write
( N; e) p$ _1 i1 ?+ s页编程函数实现如下:4 |8 ?0 t+ u7 z8 w+ m# \7 X: I$ }, C
% L  e2 C. }3 `4 C4 o; Z
  1. /*
    , E( n8 C2 `. Q- h; K. `
  2. *********************************************************************************************************
    # `6 B9 G1 F5 a6 n
  3. *    函 数 名: Write
    . I+ F8 {4 ^) p9 E$ f9 g
  4. *    功能说明: 写数据到Device
    + x4 @/ ~* k' `* S. X3 t6 N
  5. *    形    参: Address 写入地址! n2 f9 ]3 [% _4 v3 n2 T
  6. *             Size   写入大小,单位字节
    % k' V( Z3 g' Z2 B2 u
  7. *             buffer 要写入的数据地址
    / I8 s( y. H7 U3 V; w& m
  8. *    返 回 值: 1 表示成功,0表示失败* T. N# y2 R) e! `1 z# z
  9. *********************************************************************************************************' p' b8 t  M/ x: H1 Y" u  [
  10. */, R2 `: d7 t( F: e6 N% a, @$ D
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)9 @/ O! f# G- Y5 G3 \' ?% b
  12. {  * t5 b; G3 G/ b) Z* [
  13.     Address -= SPI_FLASH_MEM_ADDR;: E, H' V& r: U4 l! y

  14. 2 E7 M, q& {: B6 v
  15.     sf_WriteBuffer(buffer, Address, Size);) [) m/ ]5 |$ e2 [8 B  a0 m0 r

  16. 5 d2 `  p) |( N2 Z
  17.     return 1;
    " J: O9 [& c. `+ e
  18. }
复制代码
" v& ]( c4 J( k/ I% t/ ]
这里注意两点:9 i1 X5 n* h) @

6 r# Z: z. ]6 ]8 Y2 c(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。- i) i5 ?! f0 _. u0 I, a

9 o) T& }6 W0 Z5 N3 P2 i& X(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
& f1 Y& l$ D+ p2 H+ w! E/ q# F1 L5 @: T4 |, u7 W  e
  读取函数
/ I: X# ^3 k1 d4 F3 }
  1. /*
    ; n+ Y: k% ^$ b" \, l
  2. *********************************************************************************************************! k3 w5 W) |2 J. z$ P5 \
  3. *    函 数 名: Read$ `. K4 [$ d9 @, A2 b
  4. *    功能说明: 从SPI Flash读取数据
    - ~6 R2 l/ f/ ?- D) Y, f
  5. *    形    参: Address 读取地址, i' v- t7 t# O6 o$ Q# X7 X& O+ _
  6. *             Size   读取大小,单位字节' t( K$ K+ F5 w/ ?- B# s
  7. *             buffer 读取存放的数据缓冲
    " n! d; L/ _# e! U, ~
  8. *    返 回 值: 1 表示成功,0表示失败
    % O+ C9 R3 ]: y; U2 C2 K4 X! t
  9. *********************************************************************************************************
      b# T/ ], o2 ]+ h  }
  10. */
      t3 B8 n( y+ L
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    . j0 y0 l/ B7 F/ V6 W! ^& R3 T
  12. {
    4 u$ i: G, {3 V: q3 z
  13.     Address -= SPI_FLASH_MEM_ADDR;& x! }- M) W( W2 z% ?( q! ]
  14.     sf_ReadBuffer(Buffer, Address, Size);
    & R* U$ F/ g7 ]/ X" p, y

  15. 3 C) I8 m* j+ S" _: E# d4 [' E
  16.     return 1;
    ' {* t- G4 `, l& A
  17. }
复制代码
, f4 R4 t5 q" D) |
  读取和校验函数
4 I  s' E  `, n0 i3 B我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
( f; u9 j' M8 d4 Y* ~9 v( N7 G& A5 g" x& s/ B7 Y
85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)0 F6 K1 U# y2 k4 P, |5 j2 `. O
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
+ q- ~' J- L2 u" f
8 S; O1 r' \/ p6 @! z; e
  1. /*
    ' O# b6 w3 y# h- \, P# a  v. e
  2. *********************************************************************************************************3 Q* j- P2 r/ d+ y3 O2 V- s
  3. *                                时钟,引脚,DMA,中断等宏定义: v, F- D& ~4 C% \( N  j2 B
  4. *********************************************************************************************************
    2 z2 e# @8 h& i+ U+ q
  5. */  o( @& E4 v; I0 R
  6. #define SPIx                            SPI1
    + }9 F) b0 g8 q- C2 o; y7 z
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    7 K. |: h0 \" t
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    # `2 ~0 I# H: H2 |* T
  9. ) Q+ l( M1 a9 B5 b$ x. h
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET(), e* Y4 u9 Z. C9 J) B5 V% P
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    2 ~4 E9 T# _9 H+ ~- e3 s% h

  12. 0 ^: t8 c+ Y- m$ C
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    / I% K+ B+ ~1 a* T. M; ~, r( }% e
  14. #define SPIx_SCK_GPIO                    GPIOB0 m4 w! ^5 ~$ A' V. i. }
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    4 n6 I+ ]+ u9 B) }, L! u9 t) a7 Y
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    0 w: g3 t8 W9 F9 r# x3 p

  17. ! q: x3 l" E; q! m4 C1 k% _8 E2 D
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    * \. i* B: i3 E8 k
  19. #define SPIx_MISO_GPIO                    GPIOB' A  n# c7 e- K9 G2 X1 {( i$ V0 M
  20. #define SPIx_MISO_PIN                     GPIO_PIN_47 A2 ?; t0 K$ n
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    5 ?6 {$ P* X/ f4 i2 {

  22. 1 w: b9 S2 o9 J2 X& i' a  O
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()/ w5 I: h7 y; Z7 G
  24. #define SPIx_MOSI_GPIO                    GPIOB: W7 w& j3 G5 k5 T  u- v/ h
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_50 s) n. ~1 K: J% M& D. R
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI18 ?2 ~# Y; `# j' h
  27. 硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
复制代码
/ Z2 \- l/ }& U7 f% H) K
8 j5 ?% E: b9 V
主要是下面这几个:3 W+ M6 ?$ a; A3 `
) \+ r/ m8 I9 @( d
  1. /* 串行Flash的片选GPIO端口, PD13  */
    ) K$ m2 N' [) j0 p: J4 D  @  f, K4 i. w
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()
    " O# T. P5 }! |9 l" _* R
  3. #define SF_CS_GPIO                    GPIOD' i. e2 G! a- F
  4. #define SF_CS_PIN                    GPIO_PIN_13+ Y! t' e7 `/ Y- Y7 }

  5. % t' A9 d  {$ G  Y0 {2 o3 C
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    $ K4 k: e; o1 f
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN5 p  T1 _! [* R; ]

  8. . d; N+ m0 m( _: H' U- g
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */  u# Y6 w" _# E" N6 e7 t
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
      S1 k% q; i' p
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */% Z1 J( S  `- |( ^9 q: n, D
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    / p# I: D7 S2 o2 c0 X
  13. #define CMD_WREN      0x06        /* 写使能命令 */$ X& O* _! u! y! s& V7 v3 T2 G
  14. #define CMD_READ      0x03      /* 读数据区命令 */
    4 _: s2 i- Z% a4 \/ F1 t
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */  y, ~  J$ l! Z& {8 M, P& r. R
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */7 {! L3 [4 G+ b0 E& \' g
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */8 f9 s/ p9 k5 R
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    & U. v! W* e1 A- {& A% F
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */7 r" N. L( j' g9 ^$ Z
  20. 6 _. k" p3 t( H8 y! i5 j: u/ O  ]
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

! m! L+ S3 m* r! Y! i  c/ `85.6 QSPI Flash的STM32CubeProg下载算法使用方法
7 o* e: r/ b( y1 D, N编译本章教程配套的例子,生成的算法文件位于此路径下:* I: b  t" Y; b7 S; ~# ~
" j4 Q3 J$ a* f/ ?, B
5ec6d402a7646232a55f4834cd00ac0c.png
9 z7 x" i, \: z$ k3 E

+ h7 |. q) W) j: _) B' M" R85.6.1 下载算法存放位置& b; o' w, d+ R; v6 S
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
0 D  ^7 A3 {- o  S6 u
* v' A% X7 z8 A7 w% s\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader2 \3 J4 E% [1 M/ k, l0 P" H

/ i9 q1 i* p/ P! U, a
2609073778af5845a869911ab36624f4.png

5 h9 D* R1 s  X! M$ Q/ T6 P- G/ h2 ]" @( |2 O
85.6.2 STM32CubeProg下载配置
, g7 C8 G$ e: v; o$ m1 c9 E+ J我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
, a& t: k) l* d; w4 W1 n9 M$ N9 T% n& v4 J
27a88335ceb606bd021c3ebc65d88e21.png
+ L0 @8 n( o0 {5 U

: |- ~8 E7 J7 T/ }) v; T( e9 b/ F点击Connect后效果如下:
: Y# f; N$ I0 j0 \9 x2 b1 ?4 u" V4 f" a! ^8 W& o8 N
d4e256633eafdbf4bcd8cce6e3b5909f.png
5 E/ |. @/ R/ |* T1 F% a) }5 M( k
8 f: |7 M) i: P: c9 s# A0 ^
在这里选择我们制作的下载算法:
6 b/ R: w7 k7 C6 |
! V6 B+ i+ Z* e
d4fa28f64be2de98e04f3188eb2fe09e.png
: Q, Q# T! ], ?8 o; H" z5 F. F/ W

! g- w% z7 n1 o/ X% {3 \7 s" y任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
0 |8 p& ?5 p0 D# d$ N5 k8 @2 v( Y. [9 O1 U6 _6 z. B7 A* e1 Z
842518a7cc1dc569598d372132b2ee58.png
$ k" K4 \/ n# |" v/ v8 R: j

2 [  d+ b* E: E/ w& _7 J6 l下载完成后的效果如下:
& r$ |; Z. A, n/ ]4 v/ R
. k" d+ V! _+ u! T; N1 e; G/ g
68a92f267829ca77f125db982d6c0479.png

' J6 N' n% N; Q+ w2 h$ m! z/ H" |# o6 |$ Q
85.6.3 验证算法文件是否可以正常使用
6 ]+ M' d$ p( C' p) }9 c$ I为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:% j' f7 Y: ]% B6 m3 I
5 m" m- @0 b- A" h" {
566d2fba421c710da70e5656c6934466.png

$ v) ~& Z# g9 V8 e7 Z3 v0 l8 t% o) T, _; _+ K) c% l* r! j
85.7 实验例程说明( y* n# [5 E; g* H1 c
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。" k  u4 i% V: r

- |# f4 g5 |9 Q  f2 H5 M) {编译本章教程配套的例子,生成的算法文件位于此路径下:
2 |- H- t+ H7 R, Y9 |
* s6 o3 u+ S4 y- }0 c6 i/ a) P. o
6108d1ff7e349f2aa268ee31804c57ca.png
! Q  l* N& S1 n3 m

+ j! d1 `  m1 q0 \85.8 总结+ C$ U7 k! F+ c8 s! `  W: [* h
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
  a: z$ u& ]3 g% P3 V9 z0 G! ?7 ^& I- q2 j4 l- ~0 b( z6 G/ j5 z& c

2 `) w! o. D7 P, a+ G( u
收藏 评论0 发布时间:2021-11-6 23:35

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版