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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示
: U* V& Z1 ~7 _  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
& W6 h9 p) V0 \; P' O% k  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样7 r0 C/ k2 w0 G2 ]  t
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
0 o; ^5 j; \4 f8 ?85.2 STM32CubeProg简介
/ d2 l6 y% Z9 N5 {2 |4 RSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
- a, T! R' D0 |6 W  i2 @1 {( ]6 s- W: A" G- q+ S  e0 ]
d80578d217eb2a83520c6f3d797218f4.png
5 I# R- @5 r; c) W+ c8 J# Q
; t3 ~; b! M* s* d
85.3 STM32CubeProg下载算法基础知识
& d4 Z+ L' C2 x/ r2 _6 o& l
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。  E  n1 C0 D  T7 M" X( {+ `

/ O* q6 U5 i, \* Q2 ]& I% z: ]7 t  D85.3.1 程序能够通过下载算法下载到芯片的核心思想
7 k/ C7 K; k* c. X& j' H1 W3 T) L认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。
6 M. D5 X' P- [( |/ j; [' l! h
" r! U3 G8 r& O2 O# `% @! K/ v85.3.2 算法程序中擦除操作执行流程. F& M# z2 F7 q% @) k  ~7 L+ _
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
1 y5 Z9 {' o5 ]/ }7 W) j3 {  E! i2 t$ n2 B) V/ N
擦除操作大致流程:( N5 J1 p8 i! m, {7 C- [
% |- z- o& I2 I/ O
d75670256363bb50203bed0bbe4f9fd2.png

; ~: X& L& d6 F  R7 F$ t
/ z; {1 ~4 Z* v. s  加载算法到芯片RAM。
( J% S5 T0 @: ~. _8 B$ P/ e: @  执行初始化函数Init。
3 E! Y  f# W. W5 Z# `: M/ Z  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
' c" {1 x: r1 ]5 Q" Z  执行Uinit函数。+ m, X3 x0 {  }* m7 r3 X. y' o
  操作完毕。
: |) O! k& y4 W% x! f& H# @/ D! Z$ [% M$ K
85.3.3 算法程序中编程操作执行流程" }2 V9 R3 T$ C% O' T
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。" s  ?( Z) f/ E9 S

( Z& Z, H5 q- B! ?* d4 H8 R编程操作大致流程:
( f6 J5 ^$ H( j" r0 n/ y6 b: P  U; k  A1 x
4fa96e9e33d700081f2cdbd312afb31e.png
! P! A* n/ a. V( ?$ o
$ D+ |  t" c4 [! @! U& U
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。6 k3 |" a" U" D4 d3 O
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
5 C- c/ e" T) {7 O/ I  加载算法到RAM。7 k5 P% {- I) ~3 S
  执行Init函数。
& `1 @9 e* _% W  加载用户到RAM缓冲。$ H" ^' D- J1 Q
  执行Program Page页编程函数。% q- @2 j! g) |' r
  执行Uninit函数。
' \  P! \/ j; E* v. J, U: p$ g: F  操作完毕。
& D0 s" m- K. J. }
+ a: u* o7 o# _, ^# X# A, y! k" g85.3.4 算法程序中校验操作执行流程
+ U! g& q; S+ `6 A. L0 }注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。% \5 r1 g) J% C1 }) E6 F: _& t
/ c, N  B5 @2 ~$ L# [
校验操作大致流程:
1 d3 B/ n) D0 ^- t4 J2 C5 i4 ^5 [) w" v. V; P* h) s
7deeae4b650bd82c457492af14fc26b7.png

" F' m# a8 F  d. U& u  M9 J0 e; \
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。( [1 Q" f- U% |4 r) Q
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
8 U% t( C- u$ I* K8 A8 O 加载算法到RAM。
# [7 Y) G% X0 ^4 Y, B( @5 S' q 执行Init函数。
6 n: b' f6 u0 }! z( e6 _ 查看校验算法是否存在0 R2 X3 c" J5 W" L4 J
  如果有,加载应用程序到RAM并执行校验。2 g  q3 b7 v! L1 H! n7 e6 F
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
& Z$ {) T. K0 x  Y2 t4 A0 v  执行Uninit函数。
, `; w' t7 }) z1 Y) n+ ]$ t  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。" w3 e! G9 Y+ N: g( C; m
  执行RecoverySupportStop,回复支持停止。
. G3 ]& J6 I4 a! h) u  执行DebugCoreStop,调试内核停止。; s$ W% U( B+ ]( |
  运行应用:
7 G+ _* Q5 f/ N! W  执行失败$ u! g9 a- ^: [. C
  执行成功,再执行硬件复位。7 V: _' a$ }4 T+ R8 O4 Q8 m! S' U
  操作完毕,停止调试端口。9 T7 C. n8 @9 y5 k

/ n) q, l5 o. L8 q7 e85.4 创建STM32CubeProg下载算法通用流程. C, K- @$ j& n1 L1 ?1 S
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。7 n( J# S, t9 }& c
$ K- E, ?9 n# w( K" b$ K, x
85.4.1 第1步,使用STM32CubeProg提供好的程序模板6 W6 c% v$ R+ b- w/ u" e
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
2 R- m' N. r8 ~8 Z2 E; l) Y& q/ h6 m+ D
97f385e3b62a055bac9b5c0214b877e5.png

8 w8 P. i; E) `) @" \' y% Q$ Z: \7 _% t% I$ T
以M25P64为例(注,其余步骤就以这个为例子进行说明):3 K9 j3 {% R! A
, I% ?& S' d9 e& |# r* k5 M" B) \
43b298f996937fe35e89697fc6479cb9.png
2 E& F# N, \/ ^

6 n# D4 ]7 _" f% E% E85.4.2 第2步,修改工程名
8 m' h8 k4 C5 Y/ r( y; x* i) k! G+ O2 WSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
+ x/ _8 Z; w) n3 s) b! V! ?" F. t. Z+ f/ W
85.4.3 第3步,修改使用的器件
. C! r# ^( K) b& j9 ^0 S* B
在MDK的Option选项里面设置使用的器件。& Z: O$ p4 t) t

' d% a" Q; c) b8 L" ~+ b' M
36026b9a6d60249610d0246eb725b246.png

9 M6 t2 I2 @8 x; {+ Y2 \# v4 j- g1 z" P4 y
85.4.4 第4步,修改输出算法文件的名字# c( Q- [; B, q, t9 Y& ~
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
, ?+ ^) R, Z  f1 F* O! Y) f9 x1 X# \" ^2 }  _
52c72b3993f28a366a22eac56f8fd043.png

- c- U; z! c  N5 ~: }: c  f9 ^4 x3 P1 Z$ Z* t+ y% h" T% s+ e
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:% s( y5 k' w: m  x5 P

) v4 X! |4 b) b) Y
7b48153027a44de33926c2da008716a0.png
/ @  Y  @. C1 K2 T# v4 x/ \" z9 S  q
- C" W- a9 m) Z( Y7 N. d' v) J; H
85.4.5 第5步,修改使用的库文件和头文件路径
0 l& f! B) h3 _/ Z/ r根据大家使用的主控芯片,添加相应的库文件和头文件路径。
7 c8 n$ Z0 z+ d! r& f
) X, b! \3 ^& k85.4.6 第5步,修改编程算法文件Loader_Src.c9 @. S0 `# P, G" {: M
模板工程里面提供的是M25P64,部分代码如下:
  d- k" S8 d. c5 Q5 {7 _0 G6 q0 R+ r" D: E; i( C
  1. /**( j& O. L/ e' ?9 q/ F0 z
  2.   * Description :
    6 v5 F* `, j3 S  m: a1 p* `
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the- U+ O% ?. w5 K
  4.   * device and initilize the FSMC with the chosen configuration
    . ]$ D5 d8 [9 G" N! s) v7 {
  5.   * Inputs    :9 N7 Q3 U1 p! T" k
  6.   *      None# N: j+ F4 t) B) j
  7.   * outputs   :3 ?. [/ l, l2 T5 I) _) {3 q
  8.   *      R0             : "1"             : Operation succeeded
    & ?" @3 L: c+ |
  9.   *               "0"             : Operation failure; \4 F6 Z1 N( Z4 p7 W; y9 p
  10.   * Note: Mandatory for all types of device
    & R- `' c+ o2 n( N8 Q
  11.   */
    : D6 M& w% K! w, i7 u
  12. int Init (void)* O5 X7 a( U: y) F  b7 Z0 U
  13. {  ( F5 O, b+ l9 y8 G, p+ y
  14.   SystemInit();+ ?; \+ A- p5 ]6 E
  15.   sFLASH_Init();" I$ N' j. ^" h0 g" ?
  16.   return 1;: S. ^8 ^; ~4 \" d
  17. }$ O# Q4 V3 [' x8 k- a$ A/ F
  18. $ C; I. y$ v) u2 u1 O# g2 n: M7 e

  19. ! v6 k8 i; B! H. B
  20. /**
    . a, |0 I0 @# L; x
  21.   * Description :! }/ E) z$ ^, e+ ~
  22.   * Read data from the device
    & W+ k, i/ j: U1 l5 S( [( }
  23.   * Inputs    :' W2 j2 X9 Z  m8 G7 M# x0 I3 \
  24.   *      Address       : Write location
    ) X- B2 ?% P% v/ R1 b3 H- e. `
  25.   *      Size          : Length in bytes  
    ; Q9 F4 r5 ~$ s4 ~/ X" _
  26.   *      buffer        : Address where to get the data to write
    3 K; X7 S" N8 j7 [- p, L- J) G" f
  27.   * outputs   :
    & k+ u9 N' a2 r3 ?2 X8 ?
  28.   *      R0             : "1"             : Operation succeeded
    ( w1 j9 _# _( J- ^( e: V' Q2 z
  29.   *               "0"             : Operation failure8 |0 _9 X' Y7 q0 _, p" J0 y# T5 Z
  30.   * Note: Mandatory for all types except SRAM and PSRAM    ' c: c% H# I1 `$ A: }
  31.   */
    0 g$ K6 a) ]+ W1 X! y: U  U
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    6 Z9 o- {  n0 R+ I
  33. {
      b& B: k  K0 i# G
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    ! s# J9 |3 E) \# E' n# N
  35.   return 1;7 X8 ]1 L) N3 c4 \' Q! e
  36. }
    9 \$ V6 V! Q8 |* @2 y% Y

  37. + U. U4 u0 T3 s6 I# o

  38. / e' _* O+ ^- a  R4 M
  39. /**
    ) _" w, P0 x! c. J" M
  40.   * Description :
    5 ^/ a- i$ T& V- z( |
  41.   * Write data from the device ! u; K  n4 @4 a' [  T( o1 L
  42.   * Inputs    :0 l8 W9 [+ V2 f  T/ |2 i* H8 Q" d
  43.   *      Address       : Write location& U# ?7 G0 l: K4 B' {& J
  44.   *      Size          : Length in bytes  
    0 }9 B" h: x5 |' W9 }/ w+ g
  45.   *      buffer        : Address where to get the data to write! }5 H9 b$ T7 E4 h, H7 r$ \
  46.   * outputs   :, C0 M& r4 p' v7 x
  47.   *      R0           : "1"             : Operation succeeded
    ! D: l" `* ?+ v
  48.   *                     "0"             : Operation failure
    # X% C, J# n0 ~+ C
  49.   * Note: Mandatory for all types except SRAM and PSRAM    ' D9 D7 b6 q0 w& j# t
  50.   */
    " f9 x2 g3 V. q% N
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    $ R0 V$ I! v4 v7 @
  52. {
    9 e% R. [( h& P" g9 k1 \
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    ) B- }. i% V7 d8 B
  54.   return 1;
    5 x; s. G* I# v- H& g8 [( e  l
  55. } + e% z4 s0 }' D- i/ z- b+ Q

  56. & Z1 q! {5 ]4 i4 n" I! W; R
  57. $ f& K) A, o# m- r
  58. /**
    9 V. X" `$ Y" ~
  59.   * Description :
    / Z: h, Z7 C2 J8 l" z" U
  60.   * Erase a full sector in the device$ w" {8 e! ?, d7 [2 L) c
  61.   * Inputs    :
    3 f+ B' ?& c8 M6 C1 P. u& X% p
  62.   *     None
    " o; p* B1 f8 w5 V
  63.   * outputs   :+ b( v' w. q2 Q+ ~9 O" C4 I0 O
  64.   *     R0             : "1" : Operation succeeded
    " [+ c" [5 d3 E6 _5 O3 i( s& l" ?
  65.   *              "0" : Operation failure4 n0 R' L, j, D. B% Z/ `
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    - b: D- g/ i4 `  s9 }% u* ?: N+ N
  67.   */4 `2 d- P7 l1 ]
  68. int MassErase (void)! @% M7 E/ x6 f! n* A! c& R! j
  69. {  / z' Q5 X, X6 h
  70.   sFLASH_EraseBulk();
    ( E- l/ {& R- e
  71.   return 1;   
    8 H! A* j4 R2 U/ M. {; u. C
  72. }
复制代码
# q: v' z. p# {; S4 i0 W
85.4.7 第6步,修改配置文件Dev_Inf.c
( F3 \& f  t% k' @  e2 _模板工程里面提供简单的配置说明:$ D. M' v/ K1 X! B+ w! A
+ l. g0 c  x: a
  1. #if defined (__ICCARM__)8 W+ G+ ~- x3 z( \
  2. __root struct StorageInfo const StorageInfo  =  {
    * O4 `( A- o: W/ ~$ X
  3. #else
    & `" _4 m) u5 h$ [: j: X; R
  4. struct StorageInfo const StorageInfo  =  {
    . x+ {3 G  x0 ?5 A
  5. #endif7 m$ l. a) O; `' Y
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number/ d7 |4 W9 }' n! B
  7.    SPI_FLASH,                       // Device Type- O% _$ \6 U1 j: N3 Q5 A9 k3 M& B& g
  8.    0x00000000,                     // Device Start Address- D  M2 _, z6 N4 w6 A3 f& S& U  g
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    ; H# b, E( I- M, k' |) [
  10.    0x00000100,                      // Programming Page Size 16Bytes
    7 }: {: ?" {# i, e& W: T
  11.    0xFF,                            // Initial Content of Erased Memory
    / l6 ~% N0 o: I; n, T
  12. // Specify Size and Address of Sectors (view example below)
    : G  @+ i1 E  e7 M. B* U
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    9 D* k) u0 h( @" }+ w, Y
  14.    0x00000000, 0x00000000,1 }  I2 v3 S1 ?% r+ o2 @0 j0 _
  15. };
复制代码
! t- ?4 M* g6 o: y. `! w
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
& z/ _4 B9 v6 m- q. j6 r6 X* ?
5 h" |2 p* P7 p6 ~85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
& {+ p8 K* `1 t# }
C和汇编的配置都勾选上:2 s/ v5 U) {0 w
5 f9 c! r4 |9 c' Q. l. `# m: h! X
afddd6e839292aa2b94b43e79544d630.png

9 j7 I6 P2 m/ |- w8 @( C/ ~# Q8 u  L* B# a" l, t! I& U9 ?+ h
汇编:+ A+ b8 g% G2 C6 J

+ E) t" f5 M7 f6 {/ k5 U  ?& X" s# y
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
9 m4 d& b/ z2 a% K$ b

+ F' e; A& }% z# F如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
5 [% a. z' }4 L/ S. f' \' z& ~, x5 G
(1)加载以响应运行事件。9 i2 R( d# x, x& W4 L" v& X- ^' }

( X, ?8 x: D; w3 d! H(2)在不同情况下使用其他例程的不同组合加载到内存中。. ~' T( G3 m3 u6 ?

5 B# K, m" u' S' i+ ]6 n  u/ M(3)在执行期间映射到不同的地址。
; \# W% f3 C7 p! ~/ ~  p$ |! \3 V9 x) s* ~
使用Read-Write position independence同理,表示的可读可写数据段。. X- A, e3 `0 V/ e$ E1 b

6 f/ m- V0 L+ s6 R6 U( p; O  w, r85.4.9 第8步,将程序可执行文件axf修改为stldr格式+ l- `3 C, Q1 V
通过下面的命令就可以将生成的axf可执行文件修改为stldr。, A! g3 E- c% U, E& @, W
& C% m% [; l3 Y  ]9 G8 C; f2 X4 O& ]! q
c683673d2481d8dce75b6fa01d624600.png

9 N9 _) Q- \* T% G
( c% o# ?3 `5 p: p4 i7 u) Z85.4.10   第9步,分散加载设置* d/ U" E2 @5 r: G( d# Q* d
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
: |' y8 A$ ~2 Z0 u, ~& I' E. j9 C# y# j8 e( ?( \
b96835a7506516319002e6b2e8db9371.png
( y4 X0 A& R1 p  x
0 M; ?7 L  k% M* X% P) C
分散加载文件中的内容如下:# d9 t7 v4 E: C* F: K1 @) H9 f- e

3 L1 X9 i+ c8 R. ]% h) i2 V' _
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    & h) Z  S2 m% s3 G0 O5 [# f
  2. {: M  ^" P; q) ~5 Q0 R
  3.   PrgCode +0           ; Code
    * M, u! W: {6 r" @7 n# H
  4.   {
    " p3 c3 _! h1 ~+ F2 `3 m0 |$ i6 B
  5.     * (+RO)* O! c5 {' S# u2 v
  6.   }
    4 d: B3 D8 ]) I% L! \
  7.   PrgData +0           ; Data
    3 r9 L1 j) A! U, i& V. ^9 P, J5 v5 F
  8.   {
    ' D- C! E* C  T4 @% v
  9.     * (+RW,+ZI)
    8 n; {4 S9 Z5 H' n4 @6 _8 E
  10.   }
    $ b1 F# g9 M4 f% L; i4 p. d
  11. }
    ' F% m  i  m* P5 L* G2 s: B
  12. 5 a2 q1 [( L/ m, V7 D9 g
  13. DEVICE_INFO +0               ; Device Info- b0 @3 A* Y7 i4 m5 d4 X7 I
  14. {
    # A- u8 D$ S% X" i) r
  15.   DevInfo +0           ; Info structure
    : ^  J2 M/ d# G1 K3 J% z! a. w
  16.   {
    ) y$ F3 ]4 T- g6 T0 b9 q# O8 [
  17.     dev_inf.o
    9 m: o+ A0 w. ]5 w! y3 d4 O
  18.   }
    5 L9 U2 F  f5 q: d* m
  19. }
复制代码
" L+ x6 t; ?% \$ e0 U) m. Q
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
5 _. m2 e6 [0 ]6 B( N; E( c9 T% a. F, p
--diag_suppress L6305用于屏蔽L6503类型警告信息。  D: P) N4 ~" W, T
) K& n( B" Z5 g+ [
特别注意,设置了分散加载后,此处的配置就不再起作用了:
4 ?* A2 h% N6 i' F4 b( d! q$ B  ?/ F3 i- }4 Q% ^, H7 }
641395c374a3af02f1a92d3ff9ee4199.png
6 K- ^% N1 t, Y) h% i

% S: k1 o2 g# Y/ B85.5 SPI Flash的STM32CubeProg下载算法制作
+ ^  b' e: ^( c) D8 c下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。9 K- }$ G+ S7 a& L- U( U8 W
* P' ~7 d9 K2 ?3 j; i9 K7 n
85.5.1 第1步,制作前重要提示
: v, `* D8 k$ n8 g
这两点非常重要:1 U) x/ k4 @* _- W
7 Y! b7 h+ @# C& L" i
  程序里面不要开启任何中断,全部查询方式。! I# k  _: Z* N/ v6 \* X3 F) r
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
  X; u: U; l+ R# y5 _
$ L* b: ]4 {0 F& l/ }5 Y- |7 V& l85.5.2 第2步,准备一个工程模板) d0 |7 J$ E) @
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。4 j$ a/ N/ [% \: F7 l* V
6 z  b6 v! H. f
80ad5e3abdd6a993c8621230401a1313.png

2 H" Y3 B) w7 L: X( k
- t! L1 E- [" f9 p# W6 b85.5.3 第3步,修改HAL库( z3 t. d& K( n( o
这一步比较重要,主要修改了以下三个文件:
: {  Q: X7 i" Q7 n& r9 B, a, ~  }8 Q* L
33bf89f25487547d4008f43b7cf10e03.png
# P! r6 l2 j0 ~# g7 j6 N- |

2 q( w. z/ C# A9 |4 p  \主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。( Y0 d0 G. m5 O
# E1 m. y7 k  }6 a# k3 e, J/ e7 c9 l
85.5.4 第4步,时钟初始化% l. {7 W& @, j, o' x+ o5 {
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
. Z1 n. g& J8 H$ |+ o
* i/ P% B+ w; X
  1. /*
    8 b  k0 T$ _4 z# t5 H4 z
  2. *********************************************************************************************************
    ) B, C$ G) {/ J& O% V( s: ?- e, t
  3. *    函 数 名: HAL_InitTick
    6 t! _$ G- \! m$ o' T0 Z: j! j
  4. *    功能说明: 重定向,不使用
    8 s5 u4 A) W6 B& l% T1 K5 X
  5. *    形    参: TickPriority0 O/ B9 l, t' W6 s3 ~2 q1 J
  6. *    返 回 值: 无
    4 R' ?0 F( \& d# r
  7. *********************************************************************************************************' `7 k7 W, A! L+ B2 ~7 _% `
  8. */; P( B" |9 A  b
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    : q- t7 F/ [, k) f+ _  ]
  10. {
    # m" l  v, a3 M9 z1 P+ v
  11.     return HAL_OK;: c+ C# e0 \2 _6 r) u1 ^8 ^
  12. }
复制代码

0 S" U5 t! @# ]& i然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
" O5 ?/ F& \! D, `& u! |
9 s8 h& `0 {  U
  1. #if !defined  (HSE_VALUE)
    - t8 {+ |2 Y2 e+ i9 f& }
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    9 c- h% I# G% E
  3. #endif /* HSE_VALUE */
复制代码
# p1 f* }" `1 M4 n1 [1 t
最后修改PLL:8 c3 e6 k. J1 Q: r0 W; K9 `
; n7 \3 [+ m" u$ u. x
  1. /*
    $ |2 S; k$ s% G5 R; g: K
  2. *********************************************************************************************************; j. T5 e; L1 I3 m& c
  3. *    函 数 名: SystemClock_Config# A; p4 E  Z4 l7 q
  4. *    功能说明: 初始化系统时钟$ Z  p( b! f7 e
  5. *                System Clock source            = PLL (HSE)& z- X0 w2 {* r1 t  z1 g
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    ! W3 R" u( s  w  _$ ]+ o* o- b4 |5 Y  Y
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    0 h1 `0 b, x4 d  g& c9 k  _3 y
  8. *                AHB Prescaler                  = 2
    ! d% q2 O; h6 H& R4 k4 W$ M
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz); b7 X3 M( h3 b/ E
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    ! H: q. S1 \8 ?+ W6 S" k
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)! k8 _: l* z9 y: t( }
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)7 m/ e6 a, p  p' T8 E' L! Y
  13. *                HSE Frequency(Hz)              = 25000000/ h# {) L4 g. S' d5 ~
  14. *               PLL_M                          = 5  [5 y/ U" v" |5 i5 Z
  15. *                PLL_N                          = 160. ?4 t& i' z2 ~& G. y
  16. *                PLL_P                          = 25 h) @8 `9 Z7 _* v% K8 H6 q- W3 Q+ o
  17. *                PLL_Q                          = 4; F( x- i0 V6 e
  18. *                PLL_R                          = 2
    # i2 w: s+ W3 K$ n# u
  19. *                VDD(V)                         = 3.3
    5 q8 v6 e. ~/ N+ v' K, U% D4 C8 P& v
  20. *                Flash Latency(WS)              = 4
    9 ~) X5 l; u8 v; q, S+ R
  21. *    形    参: 无+ ]2 b& G# ~0 r' G
  22. *    返 回 值: 1 表示失败,0 表示成功3 D5 |# f) B( w! Y
  23. *********************************************************************************************************4 ^9 e( n) _& h% D/ V$ A1 F
  24. */- u: C" J5 K% ~9 {
  25. int SystemClock_Config(void)
    ) @4 X& }* l, m
  26. {
    % G/ C- t$ j; {
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    - n& w3 Z* u( H* t
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    : O; a; i' _: e' Q
  29.     HAL_StatusTypeDef ret = HAL_OK;
    ' J( A4 H+ b% J* `
  30. 0 W0 P  R9 Z5 F+ p
  31.     /* 锁住SCU(Supply configuration update) */4 B: P: g& Y6 u3 a, T
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    5 L( p" q2 u( M# g! S
  33. ' j# f- }( q2 q
  34.     /* # x( Y8 V" ]  ^& L. i& i1 {
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,  G  t1 c: T' @# x6 Q+ c
  36.          详情看参考手册的Table 12的表格。- n; j, I1 u- D) J. X5 |7 K
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    0 F" ?2 ~" ?* }' j" k' w1 J* F( ]4 ]
  38.     */
    5 `$ b8 m) F- p$ y1 |- u
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);# p8 @2 T6 @6 h, T9 G0 i
  40. / ^5 b' ?( |# G) @2 ^
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}* k. P/ N1 s7 ?4 v$ X

  42. " [6 i/ N! z2 n( ^5 V$ ~
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    2 _/ p* L! @, i1 g
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;$ B" n5 p8 J- o1 B$ X1 t' J
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;4 _- C7 d! l9 P8 Y8 `2 T4 ?
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;. I7 Q  Q/ f2 c! U! y/ {/ h
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    $ _* u) v& |. h) {: N" l
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    * S) n- w2 G/ ~% K
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;$ O! T( B  F' Y4 o) V* Q

  50. " J9 V+ U  u9 g6 b# Y
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    ' V  ?1 s9 J# j$ q
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    2 ~. z  v6 c' l$ s0 t
  53.     RCC_OscInitStruct.PLL.PLLP = 2;5 r1 F( z6 R! R, }1 G
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    2 o  g! h/ U0 x7 j9 ?1 s
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    8 A0 l4 y& P1 n/ _+ x( w' ?$ P

  56. # h0 M4 b( X' n" D4 [3 C+ h) C$ U
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;* W- L- V" B6 V+ G/ R
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    1 [9 K# O$ K" T! @$ B' w% I
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    ( D* M2 i8 B% p5 F6 }9 C4 t6 D
  60.     if(ret != HAL_OK), J9 }( f, H3 J  V% c
  61.     {
    / M+ J+ A# _7 K
  62.         return 1;        
    & `: z, Y- F5 f
  63.     }
    0 t7 q7 u& S7 n; g2 E2 u
  64. : S1 v) n8 b" s- a$ p" b7 C  m  Q
  65.     /* 0 b9 n' ]2 F$ \. m/ h8 A
  66.        选择PLL的输出作为系统时钟
    ! ?" b/ D: t: _
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    2 f& O) h: g9 B1 V- R; E7 M/ h
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    ' i: T: Q) O5 p1 b2 V; u/ e8 o
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    9 K0 w+ }; P* }" r) o
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线/ E  X! c% i# U( O5 p4 y3 I
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线/ ]! B; ~# R0 [7 l
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线       Q1 X: Q  e# v3 |' b6 c
  73.     */) M2 w8 @1 b8 }8 q9 k4 I
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \3 r$ G" a- f5 \* ?+ C/ {' W/ ^4 ]
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    # _) @/ K  j& W/ W4 |
  76. : a3 u* i! V& P/ f
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;. ~. g( V' X$ ]( ~
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    ' x: |3 t1 p  ^' t+ |+ z
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    : G- g# K% h# j
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  " ~9 j; e- f6 C9 ~
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; " a1 q5 A% _' S* W  x! U1 y$ L
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 3 T" |* X6 J6 {2 K* M
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; , G; X' O; w, [
  84. 7 [! Y' v9 x, G" ?; s0 H0 D
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    6 C& X3 W. M/ `; ?5 c& e( F
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    ; [3 L; s; O% c9 j+ F
  87.     if(ret != HAL_OK)7 S& Q3 x, P* b+ P
  88.     {
    " M1 _2 x0 v5 |5 v
  89.         return 1;
      y& y3 k) ?1 k* z7 P# @
  90.     }
    : Y# y% {6 [7 o1 V8 U

  91. 1 ~/ @% I0 u% \
  92.     /*
    : S/ |+ v: V7 z* T1 ]! x  @
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 4 {- V1 ~! R9 f8 ~. X
  94.       (1)使能CSI clock
    : T! k7 O1 W1 ?3 U1 d
  95.       (2)使能SYSCFG clock" K6 E: D5 I' n* c8 V2 Y
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    ' X( P" v5 Y" l2 Z
  97.     */  n0 k/ b( n9 g7 }  ?& M8 y7 ?
  98.     __HAL_RCC_CSI_ENABLE() ;% n. Y' a5 V% o/ h
  99.   a- J4 M/ P' }' z
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;2 ]( r9 P/ D4 T

  101. 1 H: ~6 T: t, _- _/ Y
  102.     HAL_EnableCompensationCell();1 {* V+ S$ V% R5 H) W
  103. 9 {5 N- e; j& r+ B6 h' O0 n
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    4 j9 [: Q1 t3 V8 z. `3 P. m, k
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    8 d  y( V( B/ h4 M; Z
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    % v9 d5 s, ?: k* m' G- L: x# P
  107. " O; f3 v5 X" Y0 `3 u, o; N" I2 W$ j
  108.     return 0;! ?& d/ d  Y9 K: p
  109. }
复制代码
1 v) C* D& S- h; Q
85.5.5 第5步,配置文件Dev_Inf.c的实现5 ?* ~# N. L' N( N
配置如下:
3 E$ K4 Q" I: y7 D+ [6 a  C' x: z" ^" a3 q  z# p% N3 J, Q4 e
  1. #if defined (__ICCARM__)
    8 g6 k2 w1 P$ N. b
  2. __root struct StorageInfo const StorageInfo  =  {
    " p" R, _- {  |$ ~
  3. #else6 P: i( i( w# c4 e, G1 X
  4. struct StorageInfo const StorageInfo =  {
    6 ]3 G" X$ g  {2 d- @+ C
  5. #endif9 k, U5 p  M; u2 M9 K! J9 V3 F: M
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    3 N% F  _$ j  \5 Z( }! S" ~
  7.     NOR_FLASH,                      /* 设备类型 */, T2 \5 s& c2 M9 X8 h; G( x
  8.     0xC0000000,                     /* Flash起始地址 */
      c& w* N, j2 l  K% n: {
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */4 h$ \# N3 R0 l% x; L/ b2 f5 r
  10.     4096,                           /* 编程页大小 */
    2 H6 F+ _- B/ E
  11.     0xFF,                           /* 擦除后的数值 */
    % \) B% B! E" Y8 w. |- G
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */  }. E$ H9 ~1 U7 J& K
  13.     0x00000000, 0x00000000,
    ; W7 o. V1 K. U3 q! T0 c
  14. };
复制代码

- F* M0 h* l- S1 Q) l9 s; ]( E$ \注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
8 A2 r, f: O, N. n! e9 V2 O- s& y- M0 g9 O$ D' @* g0 z
2 e- \5 [  k) y/ [0 k4 m

' d0 P$ ^, W5 d" D8 |* {3 e85.5.6 第6步,编程文件Loader_Src.c的实现7 O- j2 z1 V& _  z0 r! E
下面将变成文件中实现的几个函数为大家做个说明:/ z' K% T7 Y! N" p- N6 F1 q' g

/ e: ~$ K3 W' T+ b" b0 R5 ~  初始化函数Init
, }, X- n3 R. `' a7 M- g
  1. /*+ l) ~- A$ i5 X# Q$ k  u
  2. *********************************************************************************************************
    . D8 n; f# ]8 M
  3. *    函 数 名: Init0 n7 T; `2 _: m* h( N( ^: _
  4. *    功能说明: Flash编程初始化
    " _0 u% b8 @! x6 u$ p7 |4 K
  5. *    形    参: 无
    7 n( p; G8 F/ J" P! j, I
  6. *    返 回 值: 0 表示失败, 1表示成功
    " S& l7 s- e7 V2 F) ^$ H- Y
  7. *********************************************************************************************************
    2 m+ |& W+ h: h5 C/ O4 S! h
  8. */
    ; ?2 y9 M+ _3 o% `* y/ v6 o, R
  9. int Init(void)% E+ \  l  M* U  Y) D9 X/ z
  10. {   
    , V4 T* O0 [- Z# g# W& T  F" _: d
  11.     int result = 0;# H6 S  h" ]! |
  12. 7 g: `+ s) C9 p
  13.     /* 系统初始化 */
    & f8 P; j0 g# Z( `) \  I- }
  14.     SystemInit(); % g# v+ U. f5 U. ~* t
  15. 9 G- M0 `7 E5 ]) Z  z$ J8 o2 \
  16.     /* 时钟初始化 */
    9 |) S9 h! S$ g, m% e2 Q5 F# R" `
  17.     result = SystemClock_Config();0 A3 T9 a$ C% J5 b$ Y8 _
  18.     if (result == 1)
    % M: |' E, P) H* N( [8 [+ Q; I. d
  19.     {2 z+ t% f  J7 ~" E7 p( p" E
  20.         return 0;
    8 s0 Y' q4 h7 n/ a3 i5 j
  21.     }( ^% H6 U" E5 Z4 L6 k) Y4 v
  22. 6 |+ n# Q. W" r$ X# e# r" Z3 V  u
  23.     /* SPI Flash初始化 */1 b  D0 A! H! s$ ?/ _
  24.     bsp_InitSPIBus();2 E" y3 n# q4 b" H
  25.     bsp_InitSFlash();
    # G) I5 w- _% o& d7 b0 T! C1 ~

  26. 0 N: Y/ l4 A5 d9 e2 D
  27.     return 1;
    # H) J: G( f2 N( T( R
  28. }
复制代码
. p% I  D1 w- G0 g0 x0 ~0 P9 k
  整个芯片擦除函数MassErase8 W  q4 M9 P2 @3 J8 P) D, ~; ?3 ~
整个芯片的擦除实现如下:
" d6 x7 g. C% t2 |: t3 S3 B$ r
5 N* u5 N* F$ q' d' w% @- s8 {
  1. /*
    ; a( r+ R2 f$ E6 O0 |( F
  2. *********************************************************************************************************7 ]' H9 R) u0 N) y" j( Z5 P7 k- v" W
  3. *    函 数 名: MassErase" Y- W8 Q, ]  i  ~7 c) M0 S+ U5 y
  4. *    功能说明: 整个芯片擦除
    ( u; c8 ^3 c  {) z* S; V' w$ H
  5. *    形    参: 无8 g( X1 h$ W  p
  6. *    返 回 值: 1 表示成功,0表示失败
    : W+ y9 Q' T! Y: ~
  7. *********************************************************************************************************. _8 h1 y* y" s9 [
  8. */
    * l( d: Q8 ~% t. J& _& P' F+ h
  9. int MassErase(void)- F' t6 Y$ M8 L9 Q5 I2 Q
  10. {# n0 Z5 Z8 W- d1 A& M
  11.     sf_EraseChip();
    / L+ h4 s# ^6 I7 t# c; x2 q

  12. ; x: p/ ?' ]- s, o
  13.     return 1;   
    % r5 b) I: }$ a  b- d' Y
  14. }
复制代码

3 L; m' U2 }8 O9 Q1 o# e! ~  扇区擦除函数SectorErase
/ @' F0 d# Q+ V0 Q+ M- j: o
  1. /*: S/ }$ _  L4 e2 e
  2. *********************************************************************************************************9 G2 K" `& @9 X& F* n
  3. *    函 数 名: SectorErase
    : ~6 e2 v% t9 K  l0 G3 l
  4. *    功能说明: EraseStartAddress 擦除起始地址
    , F7 G) `' V1 d( p. p4 o( h
  5. *             EraseEndAddress   擦除结束地址( l( q1 F8 p+ d  p/ V
  6. *    形    参: adr 擦除地址
    9 K5 P$ Y# \/ E: d. E" W: I+ l) ?4 ^
  7. *    返 回 值: 1 表示成功,0表示失败
    : m: w  z- I' k
  8. *********************************************************************************************************/ F( `$ Q9 `1 z* o2 T$ G3 N
  9. */% `1 |9 k) X; _; R, _
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)! z9 `9 z1 R* `0 G. I" e  q' M8 A
  11. {
    # R$ X+ C% i( }. E3 ?- G
  12.     uint32_t BlockAddr;
    8 N  z6 y$ u' h3 V" Y
  13. ' m* @" y2 Y1 A! c, Y1 @7 R) B
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;9 e9 A# V2 F* z9 x4 Z: F. z
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;
    2 f5 J% o0 k# e/ \
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    : Z4 k4 c, j5 e& ?

  17. + G) ^; c* `; E5 n6 C: f
  18.     while (EraseEndAddress >= EraseStartAddress)
    3 S" o2 Y+ k- h5 D
  19.     {( C7 Q/ m$ U6 i% j3 s) v# n3 O
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;, b5 E5 e& v' L( j, ^2 W$ q7 {* n
  21. . `/ N) w% k- S) M  K& P! p% @
  22.         sf_EraseSector(BlockAddr);    0 I9 t& U8 y+ O: {6 x' q

  23. . [9 }7 q) L2 [, \' ?( ]
  24.         EraseStartAddress += 0x1000;
    ' L, M; {& Y+ {! B. m
  25.     }, v0 h. ~% f4 n0 L, T; s
  26. + ~, c, ~4 E' n8 @. i9 p7 ?
  27.     return 1;
    + m* S$ h, }+ g* }7 \& {: w7 Z
  28. }
复制代码

' O3 S) F$ ]0 Y- Z: m2 @这里要注意两点:7 H- E+ f4 e7 g4 c. W1 y  _2 i0 q+ W

% W) R4 z& `( W) t(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。- c8 N! b2 }/ h, c$ Z$ t5 x( F
* P1 s: P: E: O) s# f
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。
! |' d: G5 H* U2 ~1 \, r/ M5 U" I: b5 D% }. K$ w
  页编程函数Write4 c4 s+ E3 g6 g. Z! i
页编程函数实现如下:/ ^/ S* s8 c3 U* m2 j" L

/ O+ x5 q" M) W6 J
  1. /*8 c) b7 z% b: X6 ~3 G
  2. *********************************************************************************************************
    " c9 V( ]" \5 S2 R" ^
  3. *    函 数 名: Write
    $ e. r7 g5 }$ B# K0 A* H; n
  4. *    功能说明: 写数据到Device
    ( z& ~  D! ~+ u2 J4 A3 M
  5. *    形    参: Address 写入地址+ k# u& p8 d/ @% T
  6. *             Size   写入大小,单位字节; t& [/ x3 b2 U2 b+ D  |
  7. *             buffer 要写入的数据地址
    * g$ m9 K) A8 }. [- R, R7 |: O
  8. *    返 回 值: 1 表示成功,0表示失败% J% p) _8 W# X/ @3 Z$ @' V
  9. *********************************************************************************************************
    8 ~/ `+ J% d9 h' C
  10. */
    / J6 Z0 }4 V2 Z+ G6 F( C' r
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)4 u' S3 t) u: s7 h2 o6 j
  12. {  1 H/ i9 A: P! z; [$ b1 ~3 y
  13.     Address -= SPI_FLASH_MEM_ADDR;2 u! r7 T) c5 R* {8 _
  14. * l$ e9 R0 A' i7 d
  15.     sf_WriteBuffer(buffer, Address, Size);8 v8 y5 m2 }- z, x3 j# L9 ]

  16. $ M9 Y; c7 |0 c$ P* H% q8 u) V% A
  17.     return 1;
    7 F. o0 H' q* g) F9 E
  18. }
复制代码

0 k4 J8 n  j7 l5 C. p- r5 W4 y( ~这里注意两点:
8 r% R9 m) Q+ r0 q& Q
: K7 j  p- Q; B$ P7 C5 O(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。* {7 j/ l: v- D: a1 ^
+ e1 T9 b0 F7 v" R/ e" w
(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
" l6 x7 x6 a$ j4 W8 c- O4 _5 V( g6 k  W3 v1 b% ^* z# c# M
  读取函数
7 h( \: _' s2 c
  1. /*
    0 L, b( D  `, r& q' c  T# H  _+ f
  2. *********************************************************************************************************) f/ `, U. C2 n# R- c# B
  3. *    函 数 名: Read
    , S4 e, y  Q5 e3 c5 z$ {! o) x; {
  4. *    功能说明: 从SPI Flash读取数据3 }% m. p6 X+ P& K" K: q0 f$ A! L& C
  5. *    形    参: Address 读取地址
    5 l; e; R: S, b. `
  6. *             Size   读取大小,单位字节( U6 G# k7 f6 A* w. D
  7. *             buffer 读取存放的数据缓冲- d( i3 n7 z( g4 h% k! O4 |1 A
  8. *    返 回 值: 1 表示成功,0表示失败/ [/ N, b7 L6 o) D
  9. *********************************************************************************************************" _3 F% C6 C6 _. X% p, \
  10. */
    & M# F: M$ G. i8 T5 ?
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    6 }# ]) e$ v  v# K, G+ Q* c+ y
  12. { 4 m* O5 r" l& g) y( m' E% w  \
  13.     Address -= SPI_FLASH_MEM_ADDR;/ ]& b8 R. b% [. k  R0 g/ u3 b5 p: E
  14.     sf_ReadBuffer(Buffer, Address, Size);
    8 D3 f' l3 }3 Z) J# U

  15. " k/ x0 x" }8 T. W& b9 R
  16.     return 1;
    & g& l: j& M6 e3 @( s" V
  17. }
复制代码

3 R0 u5 P' j$ p9 r  y( m  读取和校验函数# W# F# ]- }! t' U: C- [
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。0 Y# y  L1 i$ |% c

: B- D( T: u/ p) r' Z# g85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)5 x- G, H3 f0 Z* m# a& M
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
# p" n2 t. s4 ^- L) Q- e; J4 ~$ d" M" @( x6 s! K
  1. /*
    " R. Z! Y  A) a
  2. *********************************************************************************************************
    2 ^( L1 x/ V8 ?' B1 u1 Z
  3. *                                时钟,引脚,DMA,中断等宏定义
    . W5 Y4 C7 t# ?
  4. *********************************************************************************************************4 r( `% w8 V# ?: c( P' Q; ^) G
  5. */% M$ y+ C" s4 n$ D6 E$ l, S
  6. #define SPIx                            SPI1# ^) K8 ]+ Z0 D! ^2 {
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
      `+ D6 ^% p' S8 s$ U$ Q
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()6 E, U" s$ m) A6 k4 ?4 C) o
  9. * d! V% _5 I7 p& g9 ?: q0 k5 U
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    & M: }' K5 [' F, v! v" N. E
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    + K8 J2 {7 J. e8 a6 m3 Y6 m5 \
  12. " A" a& R# ]& \" s1 G; k% @
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()5 N0 R; N5 g3 w$ k) |3 c  w- ?
  14. #define SPIx_SCK_GPIO                    GPIOB. p; `$ n; b; C4 x# w
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    $ o- H& t* A: ~& f
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
      B* c5 _- W! f

  17. 7 F7 u/ ?) l/ y9 W# N+ _
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    - i/ u) k/ r7 g9 J; J1 X/ h: ^
  19. #define SPIx_MISO_GPIO                    GPIOB3 H- w2 c3 N! J9 w8 x. O
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4. |1 x+ Z+ R7 \
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    " A2 o, J) j2 W. C) y. Q7 B
  22. 8 z+ ]* U' V$ B; z
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    5 C/ o) G! t: {8 d5 ^6 I' {; ]! ]
  24. #define SPIx_MOSI_GPIO                    GPIOB
    ) ^+ @' A: G/ z5 D8 V6 v
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5: a' J$ ?: c1 h8 @' B" I6 ]( U
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码
' ~0 j0 @% G3 ?5 K
硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
" M/ G) s2 a3 c6 t9 m# Z1 o" i, [4 b) ]
主要是下面这几个:4 n. ]( S0 Z. f& A! e( Y
0 Z  ~6 l% l+ o' L4 g
  1. /* 串行Flash的片选GPIO端口, PD13  */
    1 |' U. y  V1 d( g0 q
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()3 v% B2 r  Y( f! R/ T. F' {2 f
  3. #define SF_CS_GPIO                    GPIOD7 t2 M2 p$ h. U- k
  4. #define SF_CS_PIN                    GPIO_PIN_13: V( T* |( ~- K* f! a$ L3 R2 v
  5. 0 N) x% I- l" |- b
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) 6 G2 V; P# ?1 {) G" G5 x
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
    ( d( T$ c' A# Z3 n6 E" U- ^$ A
  8. ) z3 o' b, R0 _- [  q$ ]9 T
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */
    ' Q1 O5 z1 B" z0 p" S
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */" e; H0 ~7 ]% c( m2 W
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    1 t: {; V: ]+ a
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    - q6 }1 A  _- L& t
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    / P0 Z" w0 D! Y) W( t1 U# }: z
  14. #define CMD_READ      0x03      /* 读数据区命令 *// l6 V, A, Q2 M
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */4 z; N' c3 H4 ~$ A! w( a
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */
    " l* f+ I4 P8 W
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    7 j: Q& v/ C- C% \! s7 K4 y
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    ' h. q9 C# t% D
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */7 {. k0 I' C1 \5 ]: {; b
  20. . l, l! O1 o- s, g- K
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

' i' ^$ ]! z9 j* N& X( `85.6 QSPI Flash的STM32CubeProg下载算法使用方法
+ U, i8 `: A* ]9 m* }编译本章教程配套的例子,生成的算法文件位于此路径下:
6 t; y+ c, G/ }
9 y4 E; R4 t8 H: x
5ec6d402a7646232a55f4834cd00ac0c.png

; A! ~1 J9 z1 U' b
3 P; g+ @6 C( I0 Y5 \- V( k85.6.1 下载算法存放位置+ s' V8 i- y- h- t# X1 u' L7 E
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:. w  v! |4 k! _8 a. E: e

- [( L6 a" I# B8 _\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
; t& ^  S9 g( j( S- }3 @' k# V
0 l  R% c6 G" H' n5 H+ M* E1 _
2609073778af5845a869911ab36624f4.png
5 F4 f& s: C; ^; r3 ?4 R0 A
. D: _3 D  g+ t) L
85.6.2 STM32CubeProg下载配置
. Y+ m; F) l9 v我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
) P# I" s7 g$ K- {# C* @. {* [
7 B' K7 r' K2 d8 S9 v, n& W5 c( W- K
d4e256633eafdbf4bcd8cce6e3b5909f.png

7 n* {% K$ E7 q: p9 u% A! q6 X+ c0 V9 E  E: T/ m
点击Connect后效果如下:0 O3 l1 Y3 ?- c1 j' p( p

% }; h8 Z+ p* P* ]: i+ v! w
" I9 \+ z5 h/ p, y5 G: D1 p

  w. ^0 b# U7 @! B2 G1 o在这里选择我们制作的下载算法:; C/ L$ e5 V9 N: ~5 c
! {( s. I9 t; |' U( m7 ^* j
d4fa28f64be2de98e04f3188eb2fe09e.png
. {0 W  L* ?, l( A$ K% u( a  N- c

# B! a! `8 a! C) F/ z任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming9 l3 U+ X' \2 M- T- H  ^
: t7 s2 N0 d) G4 j/ @  m
842518a7cc1dc569598d372132b2ee58.png

2 h7 S$ q" ?, h8 A5 ]% p! U
8 l2 n' f* N+ A% o" ~下载完成后的效果如下:
$ q# Y. N3 x8 p1 S2 ]& w4 U) E$ R. [8 n1 L$ ~+ ]8 c% B/ s
68a92f267829ca77f125db982d6c0479.png
; O1 B% ]: T; [  D( h# B
) M3 W& u; S! U3 D) d
85.6.3 验证算法文件是否可以正常使用
: e. p+ r! T' \4 ?为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
3 ~7 F* F( {) P( l; N9 D) ~
! l) o; |) ~: Q* d; C
566d2fba421c710da70e5656c6934466.png

; T, I- A0 K3 Q2 D, k9 J6 b, Q, A" w) a
85.7 实验例程说明
% n- \4 T0 \- |" j; e. G: b
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。
- M/ n! c' b0 m' J& v# c
' ^1 ~% d" w7 q/ J编译本章教程配套的例子,生成的算法文件位于此路径下:3 c/ N; J/ \3 t0 w3 S2 x5 a6 {* K

1 j8 i6 T$ n# S2 V% @( p: i
6108d1ff7e349f2aa268ee31804c57ca.png

1 j  E4 Q! X3 Y0 T2 n* T$ w' t5 y3 W& y8 L
85.8 总结
  M5 j' L% N0 V! S& ?) Y  ]本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
4 Y( |, P; O) Z" j( C: @
& z$ `9 |( }9 B# Q* @
5 ~- j/ i* n% x8 I0 k& b
; l; C1 X/ C- b  z2 G/ k5 g: H/ K1 n
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
收藏 评论0 发布时间:2021-12-19 15:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版