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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示  I* z1 p5 d+ p: ^& j3 \
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。/ Z! V) J8 G3 w
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样  w8 f8 c1 A6 A% x7 b% ~& E( Y, [
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
+ j% }$ I: z" ]85.2 STM32CubeProg简介' S: Q, n& ]3 V) l9 g" f7 E7 A, a; [
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
2 ]9 c# N) ~1 h0 q9 U8 [1 `& S
% Y  u$ O2 S5 v
d80578d217eb2a83520c6f3d797218f4.png
/ w" k) r; m6 A+ n# x
+ n* F( c  t: ?
85.3 STM32CubeProg下载算法基础知识
+ b5 Y; {/ n, k0 \
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
! j$ C; l  h# Q$ r5 S' c  V& r  @
* ^5 K" l+ a  S* q  a85.3.1 程序能够通过下载算法下载到芯片的核心思想! n6 [1 ?+ g- G
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。9 D) F+ Z9 O/ J& A5 m9 w, }
0 V& L6 e- V( H6 a, z+ Q" \0 ^
85.3.2 算法程序中擦除操作执行流程, V7 k1 l8 p% X& T* Z) Z
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
+ r( Z8 e) B, U4 C) ?$ z9 R6 S% r1 G/ R7 _7 q, K' j! r
擦除操作大致流程:
: H2 Z/ \  B  j% o; I; u+ Q. h3 o+ q
d75670256363bb50203bed0bbe4f9fd2.png
, X4 n0 h6 D# Z; Z, a' H1 ?

8 m1 i+ W: o0 B  加载算法到芯片RAM。9 A2 i' U$ s% V/ x
  执行初始化函数Init。
6 g) l3 f; w0 Q( t; L1 c; ~+ E  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
& B, U4 J; h+ {% A  执行Uinit函数。
2 y, L" z* ^  R) [: q* v0 ]  操作完毕。( Q' e( A  p" h) v. E! y

& l" B! Y# n, @, }- i. T85.3.3 算法程序中编程操作执行流程3 d5 v7 u1 _  _1 b+ M- z
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。( n' C: A8 c6 e5 N! O
$ [" g: I* P: I8 Y1 a
编程操作大致流程:
. b0 ~& G2 L9 R* r! s" [2 t% o
$ l$ f, F, S" m1 e
4fa96e9e33d700081f2cdbd312afb31e.png

/ i2 p6 S( m! j# {, Y' r8 r, c8 \8 h0 U! i' Y/ c8 n
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。  U: g9 u: g7 l2 Q  q) W
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:/ a" p: ?; {" I" ?$ `5 R3 b$ `/ ~
  加载算法到RAM。' Q: ~$ Z; f4 `( c3 c4 }# X
  执行Init函数。  [. v/ c' ]9 O- ?9 x6 n
  加载用户到RAM缓冲。: O5 {4 {* R9 c- y, Z) x
  执行Program Page页编程函数。
% v, r8 N. N4 d) j( f  执行Uninit函数。) q  s8 N/ G  l' Q: W$ n
  操作完毕。
; u! j+ P, h: X) c9 j
$ F/ ]1 w8 m4 L* B$ A3 j2 q85.3.4 算法程序中校验操作执行流程
  X- n9 o$ ^# C. U- x注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。9 L) ^  a! \" v  m6 C

9 s& l% m* u7 i+ k5 J8 |6 H校验操作大致流程:
; T: ]# d. [2 f/ E9 b0 @
7 F) }8 C8 ^: C/ O+ n
7deeae4b650bd82c457492af14fc26b7.png
' h2 _; B7 O- q; X) _

7 }& w, p& o0 Y& F. m  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
( j! I6 e7 d6 e  M$ d  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
8 }0 h" w$ ?, m6 A6 R- C1 B* M  r 加载算法到RAM。$ K$ e5 N7 x- R& X& ~) H, w
执行Init函数。% f/ j5 G7 _9 u$ d4 O  E
查看校验算法是否存在
5 P" F9 h$ i) W# _# K: i  如果有,加载应用程序到RAM并执行校验。1 ^: {9 ], c8 d' }, ^7 `$ g
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。) @+ x: ~& A, s9 s
  执行Uninit函数。
" g' w- r. H7 a  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
3 B6 Q( m6 U$ r+ J; E7 L3 S* d. ~  执行RecoverySupportStop,回复支持停止。" m& I2 l; I% r, h0 q/ H
  执行DebugCoreStop,调试内核停止。! i2 U0 H4 ^1 c5 T8 y' \
  运行应用:
, |0 a8 X! F  }' P5 a3 _8 ?  执行失败! J; B/ q$ x; s- `+ Q( ~' J$ ?: a4 h  Y
  执行成功,再执行硬件复位。
- S+ Y7 E0 j& l0 h8 [  操作完毕,停止调试端口。
. n  C% T; t- _1 z. L
) ]1 S5 }% L' ^2 e85.4 创建STM32CubeProg下载算法通用流程' i5 k" w( `9 Y9 o; q
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
% m* V( `6 P% D  E8 [. i* N1 |% z* p3 g$ I6 h; j- D4 \
85.4.1 第1步,使用STM32CubeProg提供好的程序模板0 d1 G" t& a, Z8 o
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader) U$ b8 W! M+ U  z! s
7 A; G, H) j- F. @' ?) `
97f385e3b62a055bac9b5c0214b877e5.png

+ n9 f4 B! k2 p4 d( s7 T4 M8 D7 H
  e1 P3 S2 X4 e% X9 b& [7 B0 w1 k5 ~以M25P64为例(注,其余步骤就以这个为例子进行说明):
- v9 U3 v7 ?0 f" ?1 }: R- W
( n3 [0 v2 R+ w0 a% {% e% t: b
43b298f996937fe35e89697fc6479cb9.png

) [- ?( h) x3 p% u+ e# b- \/ L4 Y/ }, E0 K9 _( W
85.4.2 第2步,修改工程名% D, n, h$ c8 g
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。: u3 Y/ u. Z2 v6 f8 r, F

* Z5 m9 V7 F6 B85.4.3 第3步,修改使用的器件

& R* G6 M/ w& U: p" \在MDK的Option选项里面设置使用的器件。
6 M# H' e/ Y1 K7 ], F7 o9 s
% [/ f( u9 L8 Y' G
36026b9a6d60249610d0246eb725b246.png

  a' K# `' x! v# J( S4 J7 b2 u, U- ?2 u# w/ d# a4 _
85.4.4 第4步,修改输出算法文件的名字
6 A" T. P! m, ]6 z7 _  K这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。+ n0 X% K+ m" e4 k& [2 o* G

4 J4 w! C  B8 j5 T5 ?) f
52c72b3993f28a366a22eac56f8fd043.png
' P7 P& q" u. u/ f- O% W# N

$ i, C! e& d" {6 F% h注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
2 c# _( j0 j; o" E9 }, P' \( {$ j# r( Z( [4 i  W
7b48153027a44de33926c2da008716a0.png

5 b& {; F& A0 ~& J5 C) m. q4 k4 q9 \) s& ~5 }8 G
85.4.5 第5步,修改使用的库文件和头文件路径
; [! C- {* ~( M: Z" w  J根据大家使用的主控芯片,添加相应的库文件和头文件路径。
! O$ M' {4 |" x5 N; ]& |- W5 N/ ]* x6 Y5 _
85.4.6 第5步,修改编程算法文件Loader_Src.c. e" F8 `# U& ~7 I! {0 K7 O
模板工程里面提供的是M25P64,部分代码如下:& N( o+ U3 I# y$ h+ P( g
! ?0 l- J0 _! F8 C1 M6 d2 y
  1. /**
    7 Z# Y9 H: u" Q5 E5 C. y8 j# h
  2.   * Description :
    % q/ L' V8 Z- V7 c3 q$ Q4 l+ C
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    + F: x1 J- j2 j9 Q0 B' A
  4.   * device and initilize the FSMC with the chosen configuration 1 T8 a4 \! H( @6 R! l* B, r1 O! l
  5.   * Inputs    :! w; W9 u* e5 H8 W  U5 O+ O& v
  6.   *      None# s) w( U+ m( Y1 D
  7.   * outputs   :/ ?! D# K4 j% }5 q( S# E) j
  8.   *      R0             : "1"             : Operation succeeded
    2 N+ L4 h3 }9 `' _; Z
  9.   *               "0"             : Operation failure
    7 h" D% Q1 [  X! \% \- p, x5 K
  10.   * Note: Mandatory for all types of device
    2 W# w8 _$ l5 r' o; B( f
  11.   */
      _0 ^/ S+ h/ S! T8 {3 f* w; S3 T
  12. int Init (void)
    5 t) ?4 S0 b. z6 h7 p, `- q9 D
  13. {  
    2 I' B. S- \4 f& @# E
  14.   SystemInit();: o% }/ G+ L: G2 n  Y( T
  15.   sFLASH_Init();8 L, t. ]+ v* `* ^7 L" n
  16.   return 1;* q9 G: A0 Y. K# n+ z# a' h" x
  17. }$ ~( l7 s$ t) z( Y5 Z

  18.   L2 I0 L; u/ a2 o

  19. " i: T' K8 L3 {9 j3 t: u% r
  20. /**2 ~; ^% {  O4 N2 y/ h$ |9 l/ ?* e  {
  21.   * Description :& S9 ?* B& ~! F! H8 M% \
  22.   * Read data from the device
    - T6 b2 i' |" K
  23.   * Inputs    :
    ) u: A' u% ~4 T% f( C
  24.   *      Address       : Write location
    , s0 f% R9 ]  X6 F) f' R1 ?. Z3 H
  25.   *      Size          : Length in bytes  5 I2 Q' S+ K2 c5 q9 j! u( ?
  26.   *      buffer        : Address where to get the data to write, t) J* N- c$ a1 z' i! U' B9 Y
  27.   * outputs   :- \0 M& x+ }. W1 J/ ^3 E
  28.   *      R0             : "1"             : Operation succeeded
    ( P! v- E1 `# x/ F; t
  29.   *               "0"             : Operation failure
    4 E/ L; q6 R, w! D5 J
  30.   * Note: Mandatory for all types except SRAM and PSRAM    2 d5 C$ J9 C. {7 J4 S' F
  31.   */
      I( q) a8 {9 s* Y+ c7 a7 d8 T
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    - q  K+ k. n( n% [" C( D  l# x9 [4 r
  33. {
    0 T; f+ y; w$ c" {6 D- T5 j
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    " W3 p$ N0 v! R9 h- Z6 G% v
  35.   return 1;
    " ?  h6 x* _/ ^4 b. h
  36. }
    9 Y7 b1 D/ @) }4 Q4 s

  37. ' o, D5 m6 U% i+ J0 \/ N
  38. # _& h9 s* G2 z6 C7 S" e
  39. /**
    ; M  O; k/ L, ?5 z, C$ z
  40.   * Description :
      D2 b! ~' [7 O; p
  41.   * Write data from the device
    3 {, W* ?, n# _1 ?2 S
  42.   * Inputs    :
    2 ?, N. m' m2 |" [9 M
  43.   *      Address       : Write location
    * w% [* p3 o" {2 V7 u2 A2 d8 ^  A
  44.   *      Size          : Length in bytes  
    ) @: V% R# K5 y$ ~' d; r, T
  45.   *      buffer        : Address where to get the data to write
    $ H/ W  [* b5 I% @
  46.   * outputs   :
    " Q2 r$ k2 t2 O& m
  47.   *      R0           : "1"             : Operation succeeded
    # Q$ l5 ]4 `+ f& n' V. G% E, `" ?$ P
  48.   *                     "0"             : Operation failure
    & o0 Q# P: Z: i
  49.   * Note: Mandatory for all types except SRAM and PSRAM    & J. s3 d; h' u8 ^7 p) ^9 d! }8 l
  50.   */) n6 I+ I1 t6 |( Z/ o5 g0 [7 D
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    3 s' i2 y2 K; g9 U: t1 R
  52. {
    ' W- P- f3 u; [0 d3 G
  53.   sFLASH_WriteBuffer(buffer, Address, Size);$ u" W4 }: n  q! e3 p  k
  54.   return 1;
    0 Z  ^& b) @+ v) i8 g$ n
  55. } & M) b& X% h3 T# s

  56. 2 D& }. ?; g  [: M8 x
  57. % M2 M$ ?% T1 k7 p) W+ n8 E
  58. /**2 `+ @& L  m3 ?: J
  59.   * Description :5 m8 n( @! E" O- h1 P8 Q
  60.   * Erase a full sector in the device
    / _1 Z8 i9 c' F' v4 j# a/ _& w- _
  61.   * Inputs    :
    8 B8 K0 F- `8 v5 B6 X; O* C
  62.   *     None3 U1 y. I) G2 A$ R5 C
  63.   * outputs   :
    8 l8 a; f* n5 R7 Z1 G0 F
  64.   *     R0             : "1" : Operation succeeded
    / Q9 K: h  O( h& G( r( t8 l! n
  65.   *              "0" : Operation failure
    . Z! o4 h$ g4 C
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    % N1 I: T7 N$ P) K- l7 q  ?
  67.   */4 a( S/ Y4 T2 s0 L+ ?
  68. int MassErase (void)
    , \5 x+ K+ U, x1 w( F" I3 |
  69. {  ) k, g; J& S' u
  70.   sFLASH_EraseBulk();/ q* f) ^" f; J4 v5 \0 {$ P
  71.   return 1;    ) v" N/ E0 B2 Q" H$ O8 g+ r" T' T
  72. }
复制代码

4 b% a* f6 b! S8 u/ w' e& l85.4.7 第6步,修改配置文件Dev_Inf.c
/ k& Z2 U( ^0 X$ u7 m% b模板工程里面提供简单的配置说明:
6 D; V: \& T$ N$ c
& y% M& E7 R8 a3 m* [6 M5 }
  1. #if defined (__ICCARM__)
    7 H0 E1 X8 [* \1 P
  2. __root struct StorageInfo const StorageInfo  =  {5 g$ b) x6 b* [: O: c4 S3 M
  3. #else( r2 s" k2 ?: ]9 w8 N1 |
  4. struct StorageInfo const StorageInfo  =  {
    $ B1 g; a- X. d4 v' \4 W! z/ ]* z
  5. #endif1 v. V- b8 l# C/ G( T
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number; L8 K! e! d. E
  7.    SPI_FLASH,                       // Device Type
    2 o, g% g3 B. @$ h+ y
  8.    0x00000000,                     // Device Start Address
    $ Q- I) _" }# n7 E0 H
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)' V* h* u. Y# M0 Q  s: v* Y
  10.    0x00000100,                      // Programming Page Size 16Bytes
    ; O2 O& G% d3 T' J
  11.    0xFF,                            // Initial Content of Erased Memory
    9 A9 r9 t; D* u5 ~
  12. // Specify Size and Address of Sectors (view example below); ^2 E8 Z- a% S1 n/ p7 c" z9 N6 D
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes - }/ ?  D7 P" M, a, q
  14.    0x00000000, 0x00000000,, Y8 Y, j9 f; `' s' \7 M" g
  15. };
复制代码

$ Z* b/ H' P6 J6 b注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
; r8 o0 c: t2 Q1 X+ a' \' L/ [5 s/ y" }3 w) @  b: X' l
85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
; c1 d" n7 J! Y, @
C和汇编的配置都勾选上:# x  e! @7 \2 s

( a* P2 i4 E/ V+ C
afddd6e839292aa2b94b43e79544d630.png
7 \  o7 F, S2 H1 u: E

' Y  T. w8 `9 p  H% u7 `! L* X9 o汇编:
0 w# X" y; T1 D2 m8 r7 I( N$ e! p  K0 \% k7 D' O& S$ g" H2 p
f639ae5b0d3c2e0e0190ec509fbc8a4d.png

1 i5 B& q" B' j: Q9 P
& u( t6 _+ c% z8 a* ~+ W/ D# G如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
& R9 G: S3 C, t" x' @1 o  \: F, b. ]4 U" S8 Y
(1)加载以响应运行事件。
) K" Q) y0 M% q( E( R. T: O$ I$ J8 Y  Y# e9 ~9 c
(2)在不同情况下使用其他例程的不同组合加载到内存中。
' k) T( Z2 f& ]: u( }8 ?( b1 S/ c" p0 k* w9 z8 v0 G7 ?
(3)在执行期间映射到不同的地址。
4 s/ E: h' b1 z$ ~+ y5 G2 e6 I
8 [# X$ o" Y/ h( C6 R使用Read-Write position independence同理,表示的可读可写数据段。) X% ^/ B2 k8 m
" r. Z5 G8 }- ~% p
85.4.9 第8步,将程序可执行文件axf修改为stldr格式
3 L. H' T+ M$ n' F通过下面的命令就可以将生成的axf可执行文件修改为stldr。- r. {+ u1 J4 i$ M/ m8 ?4 P/ O
0 a% @% ^" P$ R
c683673d2481d8dce75b6fa01d624600.png
$ S5 ~. q( @$ [

& |" @# A, I: \* ~' P: N6 _85.4.10   第9步,分散加载设置% f; s2 n, E) T8 |" P1 B
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。1 ]( m4 k9 H( r1 ]$ r
- }& o* M7 q! V4 ^2 R2 v; |( X
b96835a7506516319002e6b2e8db9371.png

! r: L4 a! r" u* g& f! n, [: V0 Q9 l3 F( \+ l# g$ D& ~$ _; C
分散加载文件中的内容如下:! ~: k# n  _' v% Q  E

+ K5 \. d6 B+ n" n; R
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions2 W* i  E6 z* p: x8 p' ]
  2. {
    2 ?: Q0 i5 s$ V4 X
  3.   PrgCode +0           ; Code  F. A8 y! G' a
  4.   {
    ' I4 a8 |& `& C4 W; w5 Y
  5.     * (+RO), d2 s$ `" C4 X. k$ O  O0 p6 X
  6.   }
    & {$ |+ I3 X+ c8 y
  7.   PrgData +0           ; Data
    7 ]0 F5 Z) K( h5 ]( s) a- d
  8.   {0 {) _' ?; i: J
  9.     * (+RW,+ZI)& o" [2 u  W# W) d
  10.   }
    + r4 U% {+ T" E+ v: ^$ o
  11. }
    / N1 N% L. I4 E

  12. ' C/ b0 f4 d; i
  13. DEVICE_INFO +0               ; Device Info/ J( x2 ^7 I: z9 Z  b
  14. {
    0 v; g; e' _- X" G0 N4 _
  15.   DevInfo +0           ; Info structure
    - n- o! a! o9 O$ x& J
  16.   {2 u, Y  u6 n7 v: V
  17.     dev_inf.o- n7 I- `' c. P
  18.   }7 O7 |0 U% B: S7 Z( a
  19. }
复制代码
% ^; b! ], g5 g7 |" Q) L
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。% M( J& @) v* u  c) j3 z

! P8 g8 Y  ?  c& @--diag_suppress L6305用于屏蔽L6503类型警告信息。9 U: G7 ~( Y5 P! ?, `8 y4 c
2 [7 B: o7 u2 u( V
特别注意,设置了分散加载后,此处的配置就不再起作用了:
/ N* B/ T- N# |6 e1 e
, \' g" z1 X* e2 N/ V* v
641395c374a3af02f1a92d3ff9ee4199.png

! Q$ R- p& W, t% `0 \5 J
4 c! v. D: N. k  ~3 l. T85.5 SPI Flash的STM32CubeProg下载算法制作& M; w% l% w- i( N# h; O- _/ y
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。4 V, \! W* [) l" m

! j- v; M5 Z2 r. B1 j& t7 Y" d85.5.1 第1步,制作前重要提示
5 `$ I+ Y6 a  Z! w& T; K
这两点非常重要:/ Z7 Y( ^6 J* d1 C

7 ~% v# t' K9 b+ I  h  程序里面不要开启任何中断,全部查询方式。& O4 o* }7 K' w( F5 B6 D8 P' H. @
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。- K- X! T# y: M& ]7 C) E

2 ~1 w$ n6 V& b& C1 j  Y/ F9 P85.5.2 第2步,准备一个工程模板
. H: Z0 `6 ~& i, k% i* r; F3 v3 u6 F 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。- S  H! g( Y& }; I2 }
) m% U8 @, Y2 [: n4 @5 q
80ad5e3abdd6a993c8621230401a1313.png
1 V: C' ^3 U0 E

! |; t$ z% w( _85.5.3 第3步,修改HAL库" \5 s9 v8 }, B
这一步比较重要,主要修改了以下三个文件:9 K7 S% _( J1 h0 D; N5 ^7 E

% l1 v" Z: i& L7 }& A: \0 _+ e
33bf89f25487547d4008f43b7cf10e03.png

0 K7 u  J: h6 Y/ d. x- w9 {7 e& S: Z& R
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
, n2 n% r7 T) @; b  y$ k! {1 H1 E8 E0 ]" l+ G! M' }8 C) k
85.5.4 第4步,时钟初始化4 I+ D* r% I4 C
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
3 J; n# @0 q. w
0 y9 a1 W& V: B. S/ t, N' z
  1. /*0 f/ q3 Z' C' t+ O$ X2 b& Q
  2. *********************************************************************************************************
    / ^1 I9 B$ {! M3 A
  3. *    函 数 名: HAL_InitTick/ f+ G0 w; v& W7 O6 R1 O1 D
  4. *    功能说明: 重定向,不使用0 q: [5 g2 y& E3 ]
  5. *    形    参: TickPriority
    ) b5 q7 G' i) p) t8 Q; W
  6. *    返 回 值: 无
    7 g* z' `6 y# J$ D9 f
  7. *********************************************************************************************************
    9 d5 `8 M; m9 |- k. X( H
  8. */
    ; \1 k) W6 X7 E3 w
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    + L1 W5 x# x2 Z1 n
  10. {1 c0 @+ \% I3 D5 _  Q4 K
  11.     return HAL_OK;0 [0 M# D% r) Z2 [7 o+ w% s
  12. }
复制代码
# w+ o0 A) O' r; t/ ^/ _- D
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:6 u) l: x7 i5 Q( R# g5 ~

2 o1 c8 X4 E. z% r2 `  [2 ^
  1. #if !defined  (HSE_VALUE)   E8 \% N2 r6 v& F& n1 u
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    . P8 q' w: S4 G* ]1 c3 f
  3. #endif /* HSE_VALUE */
复制代码

* A% `- |- E7 V0 C1 _9 k1 `" X0 ?最后修改PLL:
1 @9 e0 A: s& [) [* a
9 V6 d6 C0 u( x* x( Q$ \& }
  1. /*
    $ `& Z' x; D# `
  2. *********************************************************************************************************% o( }# I/ n0 ]: d! s: O
  3. *    函 数 名: SystemClock_Config9 _8 b* r0 B; m
  4. *    功能说明: 初始化系统时钟
    - N# t* z: l- p" e2 Z5 B2 ]
  5. *                System Clock source            = PLL (HSE)
    # r2 N* e9 B3 O. _" l1 y/ w$ q
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    + V) m( k$ w% `8 _8 L6 |. @
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    $ U( ?5 v  O2 A+ {0 I* N, M
  8. *                AHB Prescaler                  = 23 ?$ U- I8 k/ w% M  x& G
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    . j* Z. k9 U* f$ R' N% ?
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    2 n& R* ]" W6 P. N7 x+ o
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)$ f# I8 M" s  v7 N/ D* N; v( P7 S
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)9 t. H. f$ [# U+ z: E5 U
  13. *                HSE Frequency(Hz)              = 25000000( V9 ]8 U# s& F
  14. *               PLL_M                          = 5
    / j6 y& F  Q0 H2 `
  15. *                PLL_N                          = 160
      K7 H# m$ E* j8 q4 j- C* H6 L6 u1 F
  16. *                PLL_P                          = 2
    # M+ J" m( ~# T4 N* @/ p1 e- u( S
  17. *                PLL_Q                          = 4/ w9 Y8 ], e$ l
  18. *                PLL_R                          = 2
    8 |  s% x5 m, N& @
  19. *                VDD(V)                         = 3.3
    5 N( N8 h$ e! J3 t6 f
  20. *                Flash Latency(WS)              = 4
      ^7 U* P' H1 P- Y. @$ c6 `
  21. *    形    参: 无/ d1 v- k8 n3 _5 z
  22. *    返 回 值: 1 表示失败,0 表示成功) s* r+ T) N7 F$ r( b) m
  23. *********************************************************************************************************0 |# W, ^& U8 o
  24. */+ r  A# R% z7 h; j) ~
  25. int SystemClock_Config(void)5 q! u% u! }; ]9 s
  26. {" U) ~+ f2 B% R( g1 ?
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};3 X2 H! M( Z/ d5 i: j$ z, c( B
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    ) T. Q0 `) l+ ?
  29.     HAL_StatusTypeDef ret = HAL_OK;9 s9 a9 n* K. C6 C8 o  n
  30. . r1 d; A% G" M# w$ }% G' j
  31.     /* 锁住SCU(Supply configuration update) */9 M. D: ~" U/ b# Z( ~* ^' o
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    % l% n% P' l% t

  33. # V* ~- b9 `/ Y" P$ v$ X
  34.     /*
    # J9 y% B- t' l- r
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,; \" h- ?4 f% E9 Q( x; Z
  36.          详情看参考手册的Table 12的表格。
    8 ?0 M$ [: X! C( e9 \, ^1 S
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    4 J6 v8 {& S" S0 g2 _
  38.     */
    ! k, t: a, X; B% g
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/ h: |. x! M5 W: f! }6 h! S. D4 }
  40. / W2 g. i4 p5 ^) w: L
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}4 q9 m. M' U/ T) z1 W2 L1 \8 ]5 n
  42. " P1 m! h8 z5 s/ N0 k5 Y5 n
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    2 s# L3 z* f, y1 G; [
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    8 \& X7 g  @% e7 p
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    9 z( }8 {0 [! T3 T1 J2 M
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;1 s5 J" G: K; i! x  A4 Z6 x, c; ?
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    : M1 W) H: i, j7 j4 I6 T
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    6 x3 r$ l( b# _! j* X* L
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;% p9 ]9 E" d; p. o+ @2 \6 E  L3 y# ^
  50. 9 D  @* x4 y3 c) T+ \) h# Z3 ]0 _
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    " f* E6 K' j- Z. t6 @+ |
  52.     RCC_OscInitStruct.PLL.PLLN = 160;0 v7 Z# f" v9 z8 P* k/ F7 }5 H! G
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    - `& G9 Y) S& n. d- X8 y
  54.     RCC_OscInitStruct.PLL.PLLR = 2;: L/ \4 \; a" v6 y# S8 J7 n' D
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
      p% v3 P: ?3 o# Q

  56. 5 S2 W$ z. X' Y9 ~& O
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;1 b% w" H7 I/ [! Y% I7 Q" m2 \" k
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    & Q0 \. V" K, ]6 S9 Y- \  F
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);1 Y3 I2 e3 ]5 x8 P" L; S* n# s
  60.     if(ret != HAL_OK), U$ r& f% m; |
  61.     {
    + E5 b# h! d4 L: c8 l& \, Q
  62.         return 1;        ! B) ]* `: h( V, p7 L1 [
  63.     }2 Q! n# n& U8 _
  64. : a, I. ?$ @: n) w2 \. j3 [
  65.     /*
    - r0 w" U# w$ f% _. v
  66.        选择PLL的输出作为系统时钟7 k5 W; r4 h9 _3 [) r' J
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟! C( h0 x2 z# g; T6 Q& `% C
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线1 g+ L9 v# h7 N8 w4 q. ]4 m
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    ; Z6 G- f8 x4 R) f3 I$ K2 B5 k' Z
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线9 P/ S/ d! F+ E6 K& j- d
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    ; ]6 c- x! _* W
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    / D7 i- }1 ?. Y' w$ v# m
  73.     */
    0 e- T6 e9 n/ G
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \3 ~$ Z3 ]) N0 T& s1 V
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    5 D- |# s) h& {! i# X: Y3 G& m  o$ e
  76.   l! i" ]: O! Q1 f& L9 A' \" k
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;) N- n) d* |6 w4 v
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;( x: U! N5 U5 J
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;" Z0 r. R  I6 }, p! r. j, @
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  . b# g4 E0 y7 k: h9 }! @" X
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    & |- F/ p/ [3 Z0 n2 ]" ^
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 7 b0 m8 G, n' d
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; . q; ~0 g; W" {: \5 a9 F

  84. $ p; n$ Q0 g$ P; G5 ^
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */6 g- O  O4 D0 N( i- ~8 `
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
      O8 @/ G9 Q1 L6 r# C
  87.     if(ret != HAL_OK)
    : Z4 u. s8 {, `1 o' Q8 ]# _
  88.     {  p: p" H; Y% W" s" Q0 A3 l6 \
  89.         return 1;
    + O& J! \: b- Z5 k
  90.     }
    / [* [+ ?8 O* n# U3 b
  91. ' s- {7 v; ]8 Q3 S3 M
  92.     /*
    0 e( o( ^3 z5 P+ [
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    9 X: Y, V% ~/ W# v0 O+ w* u8 m
  94.       (1)使能CSI clock
    / V8 K  G' L( W  \* n! W' [" Y. i
  95.       (2)使能SYSCFG clock$ h) m. l  u  }7 b
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    / O( l6 Y  [" L* D2 \
  97.     */
    - b- c, u- l, n1 F
  98.     __HAL_RCC_CSI_ENABLE() ;+ z0 @' `3 a* u5 G' q: F& y$ S

  99. ! Z" W/ q, W) u: s, p
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;: z8 d* q* I1 X: @: D5 Y
  101. 8 e7 i8 D% o6 K' E4 v" y$ X
  102.     HAL_EnableCompensationCell();
    & U  A5 `; h8 E, ?

  103. - i4 @+ f5 _, B. X2 Q! n
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    # J, p( i3 s1 H3 y! X7 m: {
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();3 [& M3 `6 c* G: {
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    ) D( J$ [( P% K! K# A, |
  107. # O- _& ?( {2 g, C
  108.     return 0;/ E% E) u/ d3 c$ t( K
  109. }
复制代码
; n# H! _( M" d9 b+ Q" M
85.5.5 第5步,配置文件Dev_Inf.c的实现( D$ f% J+ z$ [( q6 h3 B$ [
配置如下:8 B: I: l$ s) C

% N# w$ f6 C- Q' H% X3 O9 d
  1. #if defined (__ICCARM__)
    7 g" W+ M1 b( G' [
  2. __root struct StorageInfo const StorageInfo  =  {
    2 d  e3 [7 i8 ~4 M3 x8 [! l
  3. #else# B9 B! a( Y4 N" G: h
  4. struct StorageInfo const StorageInfo =  {6 u4 ^" `/ U. |- L$ ?' K. }
  5. #endif9 S) [6 C) m& r+ a( P& [
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */( q5 C8 R6 t3 `8 r1 @0 B$ M
  7.     NOR_FLASH,                      /* 设备类型 */
    5 u& Y/ _% q8 P6 {
  8.     0xC0000000,                     /* Flash起始地址 */# x- X6 }4 @1 |+ K, J
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */1 S, {4 v/ m/ E- I6 l% K- F
  10.     4096,                           /* 编程页大小 */
    ; a9 X9 _/ M. b* v/ ?+ T; b' w
  11.     0xFF,                           /* 擦除后的数值 */7 R5 E8 C4 M2 f% A
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    & F( d7 ^7 M7 b) x
  13.     0x00000000, 0x00000000,
    8 \  q1 s& ]3 i2 o& n# c+ ~4 p
  14. };
复制代码
$ N9 M/ ~# m* V) \) X
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
4 X$ s( Q* n  ^% {( G% m/ U- G* `- m4 r  B$ B

4 f0 w6 R5 s% s1 p! U
* u' I3 F2 q  A4 E85.5.6 第6步,编程文件Loader_Src.c的实现
( @7 }# B( u2 Z; L' L, K下面将变成文件中实现的几个函数为大家做个说明:& _9 O; K% C9 k, m" Q6 Z

0 `& }( z( n3 L# N. h$ l/ E  初始化函数Init8 y0 a, A. I6 T  W; F% y
  1. /*2 V( Z2 [1 R( U: d
  2. *********************************************************************************************************
    # Q# }9 K/ H# N9 X3 T
  3. *    函 数 名: Init
    # Y( X* D* c( j" B) V' Z' G
  4. *    功能说明: Flash编程初始化
    7 s2 K9 x; y+ z$ x5 ^( a3 U  A* B
  5. *    形    参: 无
    # C2 ?/ U& [/ t5 Q( N
  6. *    返 回 值: 0 表示失败, 1表示成功
    * a6 W0 a0 L/ J% }6 Y. D3 X1 w6 v; g
  7. *********************************************************************************************************  J9 N- Z8 n" ]
  8. */0 X5 H, ~% `+ U; ~' f
  9. int Init(void)4 G# c+ }* {) _1 [5 U
  10. {   ) a1 y7 y% C, K. K4 Z
  11.     int result = 0;# s: p( Z" `3 H4 S, F! |( E% |% l

  12. 5 I4 a* h' n% M' s9 c2 t, G# Z3 L
  13.     /* 系统初始化 */
    ( [, O# i: p6 p4 _0 w) v! }  T
  14.     SystemInit();
    ! q: h' O$ j7 {& i8 w2 a
  15. % b, s* D1 y$ G; g8 J
  16.     /* 时钟初始化 */6 D) E( l1 c6 w0 F& }2 Z
  17.     result = SystemClock_Config();
      O5 \0 R0 V' n: X1 f
  18.     if (result == 1)  P; _, x/ e( B9 y0 c& G9 q
  19.     {
    5 U4 Z% T! ?. S
  20.         return 0;' c1 V6 U( X& H+ \! G
  21.     }: C# h0 w5 k6 }% }

  22. ) R0 R3 S- S% u2 }4 z4 ^" |7 n
  23.     /* SPI Flash初始化 */
    4 a: s2 m* K, C! a: O% e# ]
  24.     bsp_InitSPIBus();
    6 ]& V9 f+ h) b8 o6 K% E9 _
  25.     bsp_InitSFlash();7 f; P& a# k, ?+ B( K/ S' Y0 i4 R: k

  26. * r9 a( Y( j- J; b! y6 M
  27.     return 1;
    - H' Z! c2 @0 r; H
  28. }
复制代码

& x8 X, Z- d* R6 X# g3 @6 X  整个芯片擦除函数MassErase9 E8 J4 l, ?* H9 Q# ]& q  A
整个芯片的擦除实现如下:
! [$ C' `* l3 A: o$ }( w6 s6 ~3 R9 B: T! h9 Q( a
  1. /*
    ) L* t, h0 P& R: G
  2. *********************************************************************************************************
    & {1 E* N( |& Y% q( W3 b
  3. *    函 数 名: MassErase! M/ P1 n9 s6 @- v5 o1 c
  4. *    功能说明: 整个芯片擦除$ e6 N) z/ I6 S5 n9 G/ T' m3 P& @
  5. *    形    参: 无7 U( w8 M1 S. C+ h
  6. *    返 回 值: 1 表示成功,0表示失败
    6 @4 f% |0 W( O! T# s2 E
  7. *********************************************************************************************************2 {8 v# \7 T3 T# ]4 F
  8. *// n( i$ R% [" c  z* E) n* i
  9. int MassErase(void)
    ( R) @6 s/ @. h, c0 b* l) r
  10. {
    6 f9 V6 E' s4 r2 f8 E7 }$ a
  11.     sf_EraseChip();
    : s4 r8 V& z0 Z5 F
  12.   F# n: V; T/ Q: E
  13.     return 1;   # F0 _. O9 A" Y! K9 R! d: A3 M3 {
  14. }
复制代码

/ Q; d7 M6 m& F7 h5 H/ D  扇区擦除函数SectorErase
( B  L2 i  O% E% W4 u, J
  1. /*# i: [! ]9 }9 z  Y' P' g0 a6 X* J
  2. *********************************************************************************************************
    2 H2 c5 Q% b2 x+ W; I
  3. *    函 数 名: SectorErase( r6 U+ Z" ^3 w2 P& Z( G
  4. *    功能说明: EraseStartAddress 擦除起始地址
    * r1 B& _) [6 E; K
  5. *             EraseEndAddress   擦除结束地址. m) M1 S0 d% L% I, x) P3 F! T
  6. *    形    参: adr 擦除地址
    # Q2 _  y9 f) \# P: g9 w
  7. *    返 回 值: 1 表示成功,0表示失败) ~5 m; e1 A" j; X  X6 C% i
  8. *********************************************************************************************************
    5 L' K+ M% t9 N$ x6 s  B
  9. */
    ; K  r3 H% Z5 A/ q0 w$ e
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    ( O/ s( z8 j8 E# ]
  11. {
    1 ~9 X- o( E; ]( F
  12.     uint32_t BlockAddr;. @0 L0 R& z! V/ t& C3 b7 w1 l

  13. 4 c0 C$ t( F# w5 O
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
    : b+ r# {0 T. v6 a3 z5 h- v4 M6 i
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;
    $ f* i2 y3 O# w7 E3 S5 |* s* p
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    2 G( Z" W9 t& w$ ^5 R- f- \9 T- l
  17. - j/ k. A3 H* Y( Y
  18.     while (EraseEndAddress >= EraseStartAddress)' B2 r- [6 U/ c" z8 f9 W0 Y; `8 J
  19.     {* k0 l8 n( A9 }8 K- @$ e5 F
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;" g$ M1 F- {  E: |

  21. ! u  {6 E7 p7 s
  22.         sf_EraseSector(BlockAddr);   
    % {/ Z; q7 n8 j' ~8 p

  23. 2 ~" b0 Y; V, h, ^; B. B& Z
  24.         EraseStartAddress += 0x1000;) J  s- ]( G: ^7 B
  25.     }* N, D% `+ U1 b- W# c# }
  26. , p! ]; K5 y9 p2 T4 ]( ?1 t
  27.     return 1;$ h! R# \! K. M, o2 n) A
  28. }
复制代码

/ T# O4 T7 h7 i. V4 ^2 r* a. x这里要注意两点:" k' |2 W9 }. |' t) S" l/ T% b& C8 X
' ]- e# _) [; f5 A$ x
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
- p! w$ G" o! ~7 f% S& B0 ~. ^& I. o; d7 `8 `
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。7 q" d% O; V+ G# Z: h' {

& y: M- Q. r- l% g1 C( d2 H- q  页编程函数Write
1 S* j+ f: J7 D0 l8 b; s  H页编程函数实现如下:  ^' M; J2 P; e5 |# s- J

, ]. Q# Q# _8 Q( x! e9 k  \; y( U
  1. /*
    3 Q8 J9 W, ?; ]5 _1 {
  2. *********************************************************************************************************
    ( c/ A; A& p( l; b% i- R
  3. *    函 数 名: Write
    6 Y, y6 ~2 i" z; e9 ~
  4. *    功能说明: 写数据到Device
    % S+ Q, C9 s0 M# [- b3 j# Z
  5. *    形    参: Address 写入地址4 Z" I2 v2 d( }. B) U
  6. *             Size   写入大小,单位字节
    $ O' B1 t3 Y. W/ T% S
  7. *             buffer 要写入的数据地址$ ?5 D( e8 p0 `
  8. *    返 回 值: 1 表示成功,0表示失败, j* Y$ r3 ?8 w8 Y' y2 R: T/ r) W
  9. *********************************************************************************************************, s( G/ l. m% {5 p% ]) ^3 E  R1 p
  10. */8 r7 ]% I6 T2 @, E% l
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
      H5 i: e$ K3 }8 V$ C8 g
  12. {  0 o$ C1 F, \$ {7 f7 c
  13.     Address -= SPI_FLASH_MEM_ADDR;" _3 p% W; [7 {" d5 n) Y  r

  14. # L) y5 U0 ~: v- D5 t  W
  15.     sf_WriteBuffer(buffer, Address, Size);
    4 [% l8 N9 f# ^) V: `' b

  16. $ K7 L! ~# O* s
  17.     return 1;
    * s; A. f* g6 t% ?2 \0 @& O/ \
  18. }
复制代码
# D5 v) G0 |; }8 Z! p8 f
这里注意两点:
5 V; S9 W9 q: r  p! p" s
! b0 v! ?+ H0 y1 H' b" b, y(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。
  [: d0 z4 a6 `3 i- e
/ T: N: }, M) `* }: J( x8 A# H(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
7 p$ U5 r8 V4 H* i* M) n9 f& T! g. X
  读取函数
6 j% \: d& g& {/ a# @- |
  1. /*
    : y1 a3 B' ^$ r% ?) X% ^
  2. *********************************************************************************************************' X' h) C. j' [
  3. *    函 数 名: Read% z+ Y9 y9 o' S& m! V2 c* }
  4. *    功能说明: 从SPI Flash读取数据
    # m  J% Q3 B6 f4 V( g$ a
  5. *    形    参: Address 读取地址9 W9 b1 \% H1 S" w0 U
  6. *             Size   读取大小,单位字节+ B! x1 _9 j6 \, Q3 W  E1 l
  7. *             buffer 读取存放的数据缓冲  ~) u. ?6 `* r8 S; y
  8. *    返 回 值: 1 表示成功,0表示失败
      M9 w1 C2 G# ^0 n
  9. *********************************************************************************************************% r# Z) n6 g4 P
  10. */7 T# k& L. n( t4 u
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)# p5 l% [/ L' d: F$ o9 X3 y
  12. { / q8 |" Y: w9 _" p
  13.     Address -= SPI_FLASH_MEM_ADDR;
    1 B3 ?5 P3 b( R0 Y. \
  14.     sf_ReadBuffer(Buffer, Address, Size);
    7 g6 @4 ]+ t# X# |( c; `2 |2 g
  15. 8 \1 E9 p8 Y8 N
  16.     return 1;
    ! x3 t9 l* W( k% C
  17. }
复制代码

7 h  ?: [8 I! A  读取和校验函数
' Z1 `0 ?2 @6 G+ P3 I) W# q8 f我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
4 \2 Y; ^  v! c% {
% R+ |, G+ B, @8 g85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等). o$ r3 ~% t/ m
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
8 F5 _2 a% M1 C4 P5 F" w
: O5 K7 j" I9 v. B
  1. /*# m6 Y  R( u2 w" [  f
  2. *********************************************************************************************************
    % H! e& ?6 G! p" L
  3. *                                时钟,引脚,DMA,中断等宏定义
    ' u# X3 [+ Y% l3 c" h; D
  4. *********************************************************************************************************: P* x- |$ B2 _( W7 l' B9 k/ A
  5. */
    " k4 T2 ^1 \% s0 F7 N2 h7 k7 i; B
  6. #define SPIx                            SPI1
    . I: D) x8 q# e9 G0 n
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
      g2 w; x) Y: k7 Q: Z
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()" W' d0 X6 b: ~- c2 B, }4 t

  9. : Z+ Y0 j* P: @" M( H
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()$ R( ]4 k: N% V( M# u' j3 T3 c0 W
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    / y6 F' _' J+ h% l* k
  12. " F3 Q5 X, i) \: k& Z
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()4 o; m6 Y/ A  O
  14. #define SPIx_SCK_GPIO                    GPIOB
    & ?, ?4 @* d* z' Q3 S
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    ) c4 b$ U3 I  W9 d8 H1 v: M$ D0 u
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1" u$ C, ?' E) f
  17. 7 P1 V+ q" Y! }0 a" i9 ?2 _) k4 V  {
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    ; {2 n5 K4 I% u) e8 Q
  19. #define SPIx_MISO_GPIO                    GPIOB
    ' \: Q; p% f, ^8 A0 u+ J
  20. #define SPIx_MISO_PIN                     GPIO_PIN_48 ]6 H' m! j0 Q, ?: W# d
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI17 \; r6 v. t7 r6 \* V( g
  22. ! A* U* C% j( ?
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    " Y( W5 |# T, j& i$ v
  24. #define SPIx_MOSI_GPIO                    GPIOB
    7 p' c+ b7 M7 v: C+ U) T9 ^: q
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    $ w+ l. Y1 `* ?
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码

" k6 H/ D3 b$ K7 L; w) b硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:( l6 ?- \5 K. n; e

; B2 l! E% u; s" K主要是下面这几个:
8 c" d. @  J( i( V6 W8 g
* C/ o* d2 k% l5 f: E4 p
  1. /* 串行Flash的片选GPIO端口, PD13  */
    9 K5 J3 z3 V, r, t8 l1 C0 {. `
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()+ }8 N' Z( H; v5 {+ I
  3. #define SF_CS_GPIO                    GPIOD
    * `1 K  Z( a) h- b( _) x/ [
  4. #define SF_CS_PIN                    GPIO_PIN_13, C4 [, J$ v" \$ x, Q/ V

  5. , O. \& x# j% x8 f8 t+ y6 T
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) / G8 ^3 g* x1 s2 a2 n4 X6 y
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
    , p: `8 ?" C% ^! r
  8. ! M' h- Y! R4 |) l; L$ ]- t; ~
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */4 J2 C2 P2 v- ]6 B( R
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */1 c6 q  z& e& M" Y, a
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */9 Y* E8 p; K* w8 q0 {
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */; u) g1 s) L. U/ f! ?7 s
  13. #define CMD_WREN      0x06        /* 写使能命令 */' L1 q2 T8 U: ~  R
  14. #define CMD_READ      0x03      /* 读数据区命令 */* U. B8 e: [' N
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
    8 a1 k+ r( v+ V1 N
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */
    0 F) T/ z  Z6 a; ~
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    * Z2 N/ i' D  R" v4 O6 Z
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */9 l2 M& _* T6 c
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */
    3 t0 D- ~3 h, w. h' L

  20. 6 Z! [  t$ q& z. H* N
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

+ G0 U, p. b% v& \/ x2 X4 W85.6 QSPI Flash的STM32CubeProg下载算法使用方法8 D* C' j: l8 n% R5 o
编译本章教程配套的例子,生成的算法文件位于此路径下:
1 Q7 }" t6 i' z+ N3 t' Y0 f: u) C) _! r  ^' w9 x
5ec6d402a7646232a55f4834cd00ac0c.png

9 v9 p) d5 b" ^' \) _. u  K. w4 k& ^. v7 k; z
85.6.1 下载算法存放位置$ D6 d$ d, A, N2 F! Y* ?
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
9 ]" K' ^: T! j) W; W
6 ^& T, k  @0 z4 b. l\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
# y. s1 F5 M, M, E
4 ~% j6 o) B: T5 W9 k/ B3 H
2609073778af5845a869911ab36624f4.png

& h- p6 V9 R1 L  q! y, J' j, V! O2 Y" w/ r& _; {- e) q
85.6.2 STM32CubeProg下载配置/ k$ y4 W7 M6 A, i9 t9 J
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。9 g- o( [/ |( {8 O% a) x
2 @) `/ r9 \3 F5 U
d4e256633eafdbf4bcd8cce6e3b5909f.png

, A8 Y- r: X# y; x, R( i$ \3 J3 A( m5 e4 d& m
点击Connect后效果如下:
, A! |7 H  x+ ]1 z0 G( @9 {1 @9 W4 L: H
; ^+ u/ `6 Z" P$ O' ^+ X
& ]* |# c8 {5 w$ l" t) ]
在这里选择我们制作的下载算法:8 g4 P- ?' @$ @

7 A& ?) l9 y$ b" U/ X) e
d4fa28f64be2de98e04f3188eb2fe09e.png
: u6 C/ }) y. L: L
) X: D' Z+ u0 n8 q% U! g/ b, g
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming7 B; w3 S3 i% H0 d- t: ^9 R
0 R. {  |6 ]0 `: K
842518a7cc1dc569598d372132b2ee58.png
2 V4 H/ w& \3 f- G2 t3 R' c* ?! E( |
3 i1 U9 q( ^/ f2 r: `  ^( `- d- ?8 t
下载完成后的效果如下:
8 g/ e0 n3 [$ G. b" t( K+ U3 W3 i+ X
68a92f267829ca77f125db982d6c0479.png
" z% |1 a6 Q4 I

: r' ~7 O# ]9 o7 K! y1 }/ B5 K' n85.6.3 验证算法文件是否可以正常使用- D1 J) {8 |( ^, A8 W! S/ J
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:- A2 k" O! a- u- a

7 n3 [7 @* `* ^; t
566d2fba421c710da70e5656c6934466.png
0 I" }2 Z* M& g. \- f

3 x7 i7 ]0 i& z0 b' o85.7 实验例程说明
1 f- g1 u1 s$ F( z
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。
# R! f3 U- ?2 I9 \4 F& f( M7 {( D$ W
编译本章教程配套的例子,生成的算法文件位于此路径下:# |! f( j: L' a/ n4 N- h$ G

0 H: b0 g- U/ a( {
6108d1ff7e349f2aa268ee31804c57ca.png

# L% z( |; `2 l. C2 K1 C
) R9 U  e& ^& _) f" |, f85.8 总结6 a; R0 T2 b: Y# _+ e8 [
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。( i1 m: [, D( t2 }, s0 t- V+ v) B
/ L- ~5 \" ?* w- Q3 u- I
2 n- D% z) Q- I9 b7 `. F

. s9 h; y+ @# `, A% `
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
收藏 评论0 发布时间:2021-12-19 15:00

举报

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