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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示
; }3 q7 J2 R9 @0 i# K  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
, X& T( t5 l% h- K& b( E& L  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样2 S. L! T: v, E7 E% F" S9 P
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。6 ^1 ]7 N% ~$ @7 l' [
85.2 STM32CubeProg简介
7 U) X% q" @- w) JSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
0 F+ `1 ^. D/ W0 E4 H. N6 L) Q
' O5 Z6 R& X7 B. Z. |
d80578d217eb2a83520c6f3d797218f4.png

5 s* G; N4 Q- X1 p9 t4 c: t
$ B; K9 E. y$ `4 F2 b$ w8 K85.3 STM32CubeProg下载算法基础知识

7 q, e/ W' m. ^+ uSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。# m0 Z6 U7 v( v6 q4 P0 w
# C) E+ y- E: d3 K6 V
85.3.1 程序能够通过下载算法下载到芯片的核心思想
8 B2 s* I/ g) J认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。# H& _; U1 p) G9 K  K
& O3 V2 ^& Q' ~% U
85.3.2 算法程序中擦除操作执行流程) b. [  b6 h" b. |* H
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
" W% N1 w) X3 V/ F' |8 h/ r& `6 @! }* ]2 [; T6 t- f
擦除操作大致流程:0 Q' `+ |% y1 Q5 s) ^5 C/ n
9 ~- ^! Q+ `; }( O" d4 C
d75670256363bb50203bed0bbe4f9fd2.png

$ a* P$ B6 K7 @" B/ M5 x
! s7 D* T- ^5 K5 a- }  加载算法到芯片RAM。
0 l, A5 x+ l4 x8 r5 ~  执行初始化函数Init。; M, X4 e- T, R1 o
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
9 Y! p2 F) s+ r! S  执行Uinit函数。
' `2 }! [5 V, l0 A) k4 T  h+ C  操作完毕。: s; `& n* y8 J

5 B8 z8 a2 t1 y% Q* F85.3.3 算法程序中编程操作执行流程
8 w& d7 E" f  \# j$ S: U注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。6 j8 _4 {  W9 U( G3 Q

- l, A- a$ E$ e9 }7 c编程操作大致流程:$ Z: [- g- B1 l' z% q8 o  H0 P
" {9 U" ]( e7 Y6 Y6 t# V) O
4fa96e9e33d700081f2cdbd312afb31e.png

& Q+ k( H7 z) U, i
/ @) Q, G/ e5 D/ s4 _  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。3 U# u7 r- Y# t$ M2 r
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:; e  p0 \0 o6 Z7 N0 `/ \
  加载算法到RAM。
3 r9 I  a9 v# j+ M% A4 p1 k% F$ I  执行Init函数。
( m9 U4 v% e) z# H* S  加载用户到RAM缓冲。
% B  P2 ]1 ?: q; j  执行Program Page页编程函数。
: e! N7 b3 m: a& z1 @6 @  执行Uninit函数。
1 P. [% b# w( ]6 l7 ~& T  操作完毕。, T# r- \% L, J' W) h5 {

4 `9 G+ t) ^0 p* s- a85.3.4 算法程序中校验操作执行流程
1 B7 W* `2 R; e) X) [; ?/ ~注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。( u" }* C% L  T% x+ W0 G
/ R4 ?' o6 b! n# E
校验操作大致流程:6 j% {! T1 K8 o! H
" K& d* J; O' T2 i" l7 Y. z' K/ I  Y
7deeae4b650bd82c457492af14fc26b7.png

# x2 Y! M8 i" I; i+ B
0 y6 s/ _/ \6 W! q0 r  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
) X, G( s# r$ L* ?) Y# D- Z; U4 W  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:# L! e/ W) {+ e0 a3 j8 G
加载算法到RAM。; v1 j1 f* k5 ]1 s6 ], f1 F9 `
执行Init函数。
  a0 v+ F" U- H8 I+ c  G 查看校验算法是否存在* r+ J" I- ?! y& p" c
  如果有,加载应用程序到RAM并执行校验。7 B2 h# E2 F5 e) l1 y' V9 N8 T$ z
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。4 e4 c+ l- @* g& T9 N/ |4 j
  执行Uninit函数。  e6 D1 Z1 v& |3 b9 s: J
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。  v2 y) p# m: d
  执行RecoverySupportStop,回复支持停止。
9 F4 T$ X! X3 m' ~  执行DebugCoreStop,调试内核停止。
+ B. ~. u4 c- S( y  运行应用:) B" f1 ~4 m4 L. x4 [& j! ~
  执行失败
! ?4 z+ V3 D) X& q2 b6 c  执行成功,再执行硬件复位。
6 Y0 i/ R& _2 S  操作完毕,停止调试端口。
2 Y, k' T# g+ B# {! U$ @& o, g6 U9 q3 Q8 d, A
85.4 创建STM32CubeProg下载算法通用流程
2 |" U  V5 \* |; z/ F5 g' r$ p下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
) k% S" S1 g$ f4 ^
* C- \+ Y; W5 J4 I+ B7 X5 i! `8 \- n85.4.1 第1步,使用STM32CubeProg提供好的程序模板
( J# x# @" y! L: o7 K位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader& J2 Z  F9 r2 T+ e3 m
2 C1 K" u) A; ]
97f385e3b62a055bac9b5c0214b877e5.png

* w- i9 f# \4 g( @- w
2 m3 w$ a5 d. ^( ]3 H* y3 e! i以M25P64为例(注,其余步骤就以这个为例子进行说明):$ k& F3 A, k" G0 _
) t9 P8 x' g) b/ E+ z
43b298f996937fe35e89697fc6479cb9.png

9 q7 m" s5 H, I$ S  C) _  d  P
0 r# I# u7 o' p2 u85.4.2 第2步,修改工程名
1 i1 a. l. j: z) s% @% }; t! l) `STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。0 \: U/ }4 z' ?3 J8 T

, m4 C. W+ n  t2 S' p7 O85.4.3 第3步,修改使用的器件
( B9 s  O1 s$ H5 V4 B4 @' Y
在MDK的Option选项里面设置使用的器件。* x) W# ]$ Z6 g; c% D% I" H8 z

" e! C! r7 U$ P; t  j3 A5 q
36026b9a6d60249610d0246eb725b246.png
. a2 o) L7 p/ \

& _! A9 g: W" _85.4.4 第4步,修改输出算法文件的名字
. ]0 _- m/ J) ?2 P  k4 {7 F这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
1 A) K6 l" |1 Z6 ?
5 S7 C3 J! W7 H: [) g# V
52c72b3993f28a366a22eac56f8fd043.png
8 C6 y+ T3 ]9 U4 g3 t3 h; O; k

. y& @* }( S& {" b注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:% }& x) k* x/ {# x
' h" d: {$ o! ~! j0 b# A: e
7b48153027a44de33926c2da008716a0.png

! s4 q- Q) L) a" j' O0 d1 @- o* d' W
85.4.5 第5步,修改使用的库文件和头文件路径
! g7 t. B+ i3 {8 b* Z# c4 i4 S根据大家使用的主控芯片,添加相应的库文件和头文件路径。
$ I" t1 p" v$ E; }! o  T$ W- F; ?4 Q/ c/ ^
85.4.6 第5步,修改编程算法文件Loader_Src.c. g% r" d* E! o
模板工程里面提供的是M25P64,部分代码如下:
6 ]# X" L3 O* q0 h7 m8 F) g3 x& @
% X. L) S0 }! F# h4 v# g& [
  1. /**
    & _$ ^6 _" [$ f' j; O
  2.   * Description :
    6 V  @+ s" e# }8 D: L$ U1 f+ `
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the8 i# ?4 a$ m6 c0 E1 H: b6 r( d
  4.   * device and initilize the FSMC with the chosen configuration . V8 H- i$ {9 ]* ~0 d0 ]
  5.   * Inputs    :3 }5 [* p2 n+ H% N/ N
  6.   *      None
    7 z# A0 a1 }$ U
  7.   * outputs   :) z' g9 M7 g$ `! r# N
  8.   *      R0             : "1"             : Operation succeeded
    ) [' L/ Z9 q9 u( j1 }) _
  9.   *               "0"             : Operation failure
      X' j4 t/ X# w$ M: @
  10.   * Note: Mandatory for all types of device
    - e# M/ U6 ]) y
  11.   */
    " B7 V$ b7 W" J9 ?6 V
  12. int Init (void)7 z8 K* ?0 N: L9 {/ F: N
  13. {  
    0 J- Y# d$ u( R6 U6 q
  14.   SystemInit();
    * n& P) |2 ?- E$ }1 C% c5 y
  15.   sFLASH_Init();
    & j1 i3 N7 g6 ]- f* w: z
  16.   return 1;
    ' T! K1 u+ J4 r! A7 s* O7 c
  17. }
    2 |# E$ E4 R3 T7 `. \2 _
  18. 1 V0 j7 [! \5 q( o' b7 v+ `
  19. ' ^6 s- @8 j& b. Z+ b$ d
  20. /**- t5 _% A3 b7 H( U/ s
  21.   * Description :
    . J9 R" y' M9 V4 r$ j+ j
  22.   * Read data from the device
    3 o; M9 Z4 U7 F# t- m  I
  23.   * Inputs    :
    , N! Q7 K8 C* [3 v
  24.   *      Address       : Write location, @! f; k  y% e: N* M
  25.   *      Size          : Length in bytes  / Z* n, D+ ]# N0 \- @
  26.   *      buffer        : Address where to get the data to write( ~9 @- w0 W0 u8 j9 v' S6 P4 D
  27.   * outputs   :. ^* u: L# g/ X
  28.   *      R0             : "1"             : Operation succeeded# V' Y- _$ t- R' C: W; M
  29.   *               "0"             : Operation failure
    : e5 {" M7 _- h4 @/ T6 _  f
  30.   * Note: Mandatory for all types except SRAM and PSRAM    # T% v* G: b  s/ L
  31.   */
    % N4 p- C0 w+ G/ T" M- o' ~$ @
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    0 m- o8 k- o; Z4 r
  33. {
    ; n2 F6 T6 c7 L# f% z' b
  34.   sFLASH_ReadBuffer(buffer, Address, Size);! X: s+ w. r- z( E6 w2 [8 r7 B6 Q
  35.   return 1;
    5 _' G4 e7 z3 G1 f; @+ g8 X
  36. } 7 B3 ]4 k0 E$ ]9 P/ _
  37. 2 d, Q/ F# j. W5 d0 T

  38. / D$ {) D& Q4 p  l- H+ s
  39. /**
    ) \" N! G# u3 U0 Q
  40.   * Description :  g( t$ M. p4 G
  41.   * Write data from the device
    3 s) p) v  v$ `9 `6 \( j' r4 @
  42.   * Inputs    :
    * H  a+ J. z1 r: g- \2 V; \
  43.   *      Address       : Write location& U( Z6 I& k8 V! N, X8 h* H7 U
  44.   *      Size          : Length in bytes  
    . u" l: l/ a4 a( _) `  s: s$ b  Q. }
  45.   *      buffer        : Address where to get the data to write
    1 X* L5 B4 `8 S5 o
  46.   * outputs   :
    ) D% ^4 s* V$ ^' v
  47.   *      R0           : "1"             : Operation succeeded; Z9 e3 I2 ]8 V9 K
  48.   *                     "0"             : Operation failure$ |1 `  ~; h3 @/ V  N# N5 }* p0 t
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    ( o* I' m4 J' y5 s: n  G) {
  50.   */
    7 M% L& A4 D+ `$ Z7 c7 I
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    0 M. ]$ M  A7 Y$ T: z2 P: V2 k
  52. {9 u# p3 k- \# Q& L5 y/ {4 j- [
  53.   sFLASH_WriteBuffer(buffer, Address, Size);! b& p3 u, L% ^' F7 X
  54.   return 1;
    5 x/ w9 a) f$ B9 o- z3 m( J
  55. } * i0 E5 W: J% z9 F- `& g

  56. : G( x5 C' |# F8 o; k: [* R  E
  57. $ \9 V8 Y& _' V( ~
  58. /**) M$ g! p' z: I
  59.   * Description :  ]" o+ T  m* U* j
  60.   * Erase a full sector in the device. ~. }0 M5 T( M
  61.   * Inputs    :& X2 _9 t0 p) X/ b& `! ?3 k
  62.   *     None
    1 h! m2 [# U) p/ q" O, P$ ]
  63.   * outputs   :! B! U" r& _# b: o, _' `7 h  s+ n
  64.   *     R0             : "1" : Operation succeeded
    / i+ u. F' s- r( G: S( v8 A
  65.   *              "0" : Operation failure, D4 b6 n" d5 k9 c# o8 T% O
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH' e1 ?0 L# ~8 ~& u$ v7 P
  67.   */3 ?4 ]# f0 L9 F: C' H5 F* Y( g5 K
  68. int MassErase (void)
    1 U( f0 E$ J; r  [. y5 m
  69. {  
    * J' C9 O+ Z7 q  Z
  70.   sFLASH_EraseBulk();
    ; S( O8 U5 l7 Y6 s' X; A7 u, r
  71.   return 1;   
    7 `$ O. H# e+ r
  72. }
复制代码
' ^2 T. L; N7 ]9 R
85.4.7 第6步,修改配置文件Dev_Inf.c: |. ?5 i( c: B- l) R
模板工程里面提供简单的配置说明:
" b: c* c" I6 a" f; ~5 W
4 [" [# A1 R/ e2 `% B) r
  1. #if defined (__ICCARM__)3 d$ n1 f  S& Q1 @+ N2 u! x2 d8 z
  2. __root struct StorageInfo const StorageInfo  =  {
    . s' g: {- T8 g: s8 q
  3. #else' M& H  g/ Y+ h
  4. struct StorageInfo const StorageInfo  =  {  l/ ^/ j0 t0 _# q8 f" N$ Q
  5. #endif: }6 [% j& H' y8 a
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number: z+ }4 G) {5 f' t* L; O* b6 L
  7.    SPI_FLASH,                       // Device Type
    % @( z7 ^6 Y  K" P
  8.    0x00000000,                     // Device Start Address
    . i! ^4 x4 w& v) P
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
      u6 I# i' g. W4 G0 h% f6 [: O
  10.    0x00000100,                      // Programming Page Size 16Bytes
    6 G( n: f/ L: u/ l% u: Z" o
  11.    0xFF,                            // Initial Content of Erased Memory' H- p6 {5 t, \) S
  12. // Specify Size and Address of Sectors (view example below)
    1 E3 k0 ]7 A. A8 ^8 ~$ r2 y' B
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    7 e9 k! g/ Y) m! L) ^/ {, B
  14.    0x00000000, 0x00000000,
    1 l  ~9 L, A1 b2 j& t3 N) f
  15. };
复制代码

! e* }, F- x  f3 q, e+ Y注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
: @0 n/ y5 j5 `& M6 x' N& S$ c, k' x  U+ h- O+ f2 S( {) z3 O4 B
85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。

. |, L8 x7 i( `% k$ @C和汇编的配置都勾选上:
8 t* ^: \+ j& w9 l5 b' T
. E' z- |6 Q% b' d
afddd6e839292aa2b94b43e79544d630.png

- e8 a! N# I  ^, [  q$ J/ a
* o* g0 Q% z3 R& i* x3 [7 x汇编:. ~8 M; _- ]  B- h9 Q% i: i8 b7 I
# X! F( t, \3 E1 `$ h( B% B( e
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
( U! `8 E! R* j/ @& y% ?- `7 C
( k- n/ F" Y5 o: ~3 F1 h* P6 n8 t0 V: z
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:# q4 E, j* A% _" x" L: E
5 N: O/ s+ r% G4 V( T, N  X$ U
(1)加载以响应运行事件。7 G' W5 K0 L: E

! O. |' c1 Y: r(2)在不同情况下使用其他例程的不同组合加载到内存中。
" |) Y  c9 R, V+ u- B: L' m1 @: K4 w
(3)在执行期间映射到不同的地址。
/ i% {+ @$ X' G/ O+ t- c! f. S- y( C! ]) y2 ~
使用Read-Write position independence同理,表示的可读可写数据段。) x8 D6 |; i/ f1 _8 T1 R
7 g  k8 b9 u; W/ m& m. X4 \* K( ?, I
85.4.9 第8步,将程序可执行文件axf修改为stldr格式4 ]: [$ E, a1 P. L2 K3 W5 U
通过下面的命令就可以将生成的axf可执行文件修改为stldr。" T* A8 \5 C: {# {% z
4 @- t% Q) ^/ g. C6 `$ a
c683673d2481d8dce75b6fa01d624600.png

: r8 B* p  d' r
9 |: g0 }0 ~: m6 u( A85.4.10   第9步,分散加载设置9 a9 U& n1 b" k! a: U' Y. n
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
2 w6 r/ D5 q' \5 B( M. O' }* V, \: P' F$ z& y4 B; Z3 V
b96835a7506516319002e6b2e8db9371.png
" z0 T3 ^% W/ U8 X
" d. \  ^% p0 a& l
分散加载文件中的内容如下:
: r4 m& r4 K& z$ t# F1 k9 R0 u/ W& B& m
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions; J" W3 t, S6 V3 t
  2. {- ~, f* ?: i* c
  3.   PrgCode +0           ; Code( p8 w$ m: `8 s) |  o8 m
  4.   {! D- E9 R, Z  M# q* P" b' C5 [0 o
  5.     * (+RO); z; f. l7 K9 T7 s) j
  6.   }: V4 |6 H$ B6 {. m9 s) E
  7.   PrgData +0           ; Data0 ]- {& F; h7 v
  8.   {
    # p  _0 M0 w# J& `7 J6 x: t; Y
  9.     * (+RW,+ZI); d* }9 o/ I( L7 j: G  K0 Z% m
  10.   }
    & m1 u6 P9 K. m0 h
  11. }) B: V4 s( o# P" c$ Q4 }% C
  12. / O! F. O4 _, y) i( z/ w
  13. DEVICE_INFO +0               ; Device Info+ Z  ]0 F" n+ c, X! y' i
  14. {( L, S1 I. ~0 R" |5 @0 t  k
  15.   DevInfo +0           ; Info structure1 I4 n9 H6 z5 a
  16.   {' R  `3 G5 Q2 e' y$ w8 d
  17.     dev_inf.o
    9 E! }: w* S% j6 o$ R- x- W! v
  18.   }
    ; K/ f1 e- q1 ~
  19. }
复制代码

3 p4 E9 u3 B% O7 ?. ?+ F+ W这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。* S% M* O% n$ i3 o

* X. s; h% O+ M# G, H--diag_suppress L6305用于屏蔽L6503类型警告信息。
' h$ a; R+ q* ^/ E% B7 l2 I1 ]* ~) G# o3 r3 _9 h9 ?
特别注意,设置了分散加载后,此处的配置就不再起作用了:: U9 b2 `' `) R, C" J

7 X+ n8 _0 a. v. h
641395c374a3af02f1a92d3ff9ee4199.png
  L# b4 y/ w% K8 F! @2 X

6 ?" Y0 U/ e8 {* |0 ]6 u3 s0 R85.5 SPI Flash的STM32CubeProg下载算法制作3 i0 a/ g4 ]) B5 Z$ j6 z" x9 s1 t
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
4 S% M$ Q/ X9 W# i
5 ]' x) a2 _% M9 X/ N85.5.1 第1步,制作前重要提示
. A5 D' T* w% {# V
这两点非常重要:" ~( Z) k' L! X! F' N% U
5 o$ ?$ W& K4 Q' Q; m# _
  程序里面不要开启任何中断,全部查询方式。
" G! E, e: e7 e" J9 D$ c1 I  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
" V$ _6 L* U4 ^( ?  R, v5 _# I1 X/ ~  l
85.5.2 第2步,准备一个工程模板, n6 C0 T" [. `  U1 \- O  n) V% E" s
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
) g7 z' M( M& o, J. m9 @
& Q6 M( N9 {7 C3 D# r
80ad5e3abdd6a993c8621230401a1313.png
1 g, A! B2 D1 g; z3 i

/ P# r" f2 T8 b6 r6 n85.5.3 第3步,修改HAL库  ^8 L: |' J/ L$ i$ ^1 e* J  q
这一步比较重要,主要修改了以下三个文件:/ g0 a  g. e- q0 M

( {, t* D: G7 {9 o! k  b7 W$ `
33bf89f25487547d4008f43b7cf10e03.png
- Z" p1 A8 Y1 l4 h4 T  A( k  T+ ~

2 D$ s! ?' e* o主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。+ Z& S! [  E) O" d

8 i1 J8 x  i- e( z5 M, m  R4 q1 F85.5.4 第4步,时钟初始化
8 W: Y2 y$ `% }- g- `7 W% j我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:, X+ R4 X9 R5 \7 z8 D! K8 N0 Q
) r7 r, h' p) E9 z& n
  1. /*/ ^* Z( {8 g3 B5 |& Q
  2. *********************************************************************************************************
    ! J& ~/ ?5 D8 v2 o, Y
  3. *    函 数 名: HAL_InitTick2 T: X: a1 R0 R* X: _6 {
  4. *    功能说明: 重定向,不使用% t% c: s& X6 X- V0 i8 }  c
  5. *    形    参: TickPriority/ P6 c' l: E* y' P
  6. *    返 回 值: 无
    : t$ r  S* ?0 [; [" ]4 ?1 A7 H. [
  7. *********************************************************************************************************- I- F% J: R. y: j' w. v
  8. */' O' k$ y2 ]) y* b& m+ G# O) x9 n
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    ' [; n% z) B% s1 i/ N; ~- H2 q
  10. {" C% K- o* u! V
  11.     return HAL_OK;
    , z2 |3 L. I# J2 |+ u+ ?
  12. }
复制代码

  @( R& ^" j; q6 \* @- g  T5 c% ]然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:) i  s- B$ }/ [- Y- H  {& L

  F2 x8 h( ]3 A
  1. #if !defined  (HSE_VALUE)
    / Q: q" T% M/ a' [1 V
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    ! P$ z; H1 s, b% Z2 {* g8 M
  3. #endif /* HSE_VALUE */
复制代码

8 S# m" v9 w+ N' D% m  x最后修改PLL:
! }7 Q! S- C% ~5 D# d" f  u2 V3 \5 Z3 S' k; ]8 [9 \; L
  1. /*7 M* r6 f6 D& @9 f3 ~$ q
  2. *********************************************************************************************************) V  V* U9 D& `0 u* P. O! J
  3. *    函 数 名: SystemClock_Config
    8 K% P# A$ K' J% o. t
  4. *    功能说明: 初始化系统时钟
    ( I, F' j& H0 N8 A/ K
  5. *                System Clock source            = PLL (HSE)9 ~0 J+ w- z) [" w2 b3 E' u
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)7 ~) W. y8 r$ \5 z' s
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    9 g5 h  M" }, g  s
  8. *                AHB Prescaler                  = 2
    , n7 R$ l# s% l6 p$ n9 ?& x
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    - ^; V# V9 _7 ]3 G5 r
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    ) k  F- c: f% v7 X) e" ~1 W
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    # l  v; Q  x1 S2 q" j& p
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)- ^: N' k3 Y- g+ d/ i: [4 z
  13. *                HSE Frequency(Hz)              = 25000000; V  b& i5 e0 {
  14. *               PLL_M                          = 5; c3 ~  s2 b. x4 i1 h
  15. *                PLL_N                          = 160/ [! Z: k( h: N! e
  16. *                PLL_P                          = 27 ^5 s6 ~/ b- a  ]. r1 [
  17. *                PLL_Q                          = 4* r( S* U. T5 A$ U- y9 f
  18. *                PLL_R                          = 2* S2 q0 R" s. J; ^' j4 V
  19. *                VDD(V)                         = 3.3+ a6 d  c) c+ m
  20. *                Flash Latency(WS)              = 4
    " f* E$ ~" p. A: n3 M
  21. *    形    参: 无
    . H  n$ L: e# f9 m3 Q
  22. *    返 回 值: 1 表示失败,0 表示成功7 j) \! h* R2 ^/ C0 u: v
  23. *********************************************************************************************************
    4 M% L- F- \" |# x# S0 @) s  O! b) [
  24. */* m: y- u5 F, R4 a- s
  25. int SystemClock_Config(void)
    8 x+ g6 l4 D9 m" ~! ^  u' P
  26. {  F) j& r% F& _" j9 z- D" D1 R6 B
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    / |2 I( Q) b4 E6 P* C5 W/ I) o
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};# |# ?5 w6 T! }$ U
  29.     HAL_StatusTypeDef ret = HAL_OK;2 l" L& Y, M* m  y+ y* D
  30. 7 t( z8 X) L/ p7 G0 c
  31.     /* 锁住SCU(Supply configuration update) */
    " `( O" T% W8 k) f3 ?, T/ a1 [  j
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    # F, D* F3 s' S
  33. " E+ x5 m0 l/ T1 j: Q6 q
  34.     /* . U3 g7 b5 l2 t2 I: X9 r
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    7 w* d8 Z0 O, u9 q% \: a
  36.          详情看参考手册的Table 12的表格。
    ' w; r% {) h9 T
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。2 z- }9 C6 }& }% c$ T. s
  38.     */+ w. @& C' h$ F4 S: g% z
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    6 q  N% K* \0 Z

  40. 6 d( o4 h' T5 [9 G; R: p; o% }3 t* T
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}/ c& [- n& N8 t7 @) d  O

  42. 8 Q# ^5 h" M: c3 y2 F1 O
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    1 n% T! o7 e/ L
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;& o2 k) [1 _+ Y# U; K
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;; h1 C' l$ d$ h& X9 r
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    % I' B1 e$ B/ a7 a0 b4 V& e3 ~
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    5 S9 N  s: I! H# i1 x' r/ y1 V& N" v
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;" b, a* X8 P2 V. s
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;0 L! `' B- G! i2 g0 X
  50. 6 A/ k' {7 w: H3 j* C# v# D
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    2 ?" q0 X: D3 I; g' l  Z& K/ }
  52.     RCC_OscInitStruct.PLL.PLLN = 160;; h/ C) G4 s  k; y0 u4 P
  53.     RCC_OscInitStruct.PLL.PLLP = 2;/ Y: f+ j5 O. N9 n9 N2 R6 K3 t
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    ( j/ h4 }8 t% f
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        7 C- F. l1 S6 D) m+ q4 F
  56. " u$ w9 u4 \5 n. s7 S
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;* V2 G; p  w# H3 s" e8 N+ E! {
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    # m" F0 v1 W( `6 f6 u4 i+ G# h
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);0 S( q. Y6 r# l) j* R1 [; _
  60.     if(ret != HAL_OK)
    3 J+ i. P3 A" h- l" p0 [
  61.     {
    % L6 \* C+ u# ]. G
  62.         return 1;        1 z6 ~0 q3 T  p* ]. v; b
  63.     }3 ~. \% X: Z* n, F, J* N( `) r
  64. $ R( W7 I% ]+ _" s6 z" ~
  65.     /*
    " Z9 ?  \5 J3 {+ B4 z0 c  H- u, X
  66.        选择PLL的输出作为系统时钟
    1 e1 c9 Y; W6 E/ ^9 A7 ]( v
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    ' E$ v  I/ _2 F% J5 E& k0 B
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线% ^5 E( a% A$ r& z, y, X6 h, b/ k
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    & C* L2 w; g1 |1 ~2 h3 H# V1 s
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线! n; M4 ]5 h7 f* @# R1 q
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    4 }  t7 O7 [, Q6 O% g1 i9 n! P
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     : G- Z# q8 G. _& E% U4 ]: C8 _! x$ m
  73.     */+ E  S* f# r$ t! T& C
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \. G+ [( r$ _& U, g/ q# x) T
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    1 f6 X7 }7 |! G

  76. 2 N5 p: i& d6 X" d. [5 ?  r5 O: \2 y% l
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;! n$ Y6 y6 O. L0 Z% K
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;4 |/ o& w+ L  c" W, s5 S; N
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;7 J% ]6 B; E8 E- O  W
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  3 P6 c/ ~, D1 c) q1 d
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    ) }( [, x+ O" l4 r( [0 X
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 0 Z- c$ G- Z6 U/ Y4 h* U5 D3 f
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; + c. O. O/ I1 ~' N

  84. / k. q* G8 q$ U: M
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */) N% q  a( M+ b# l, }: W8 T5 n
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);; H. \/ r6 Z5 o' A* t5 \
  87.     if(ret != HAL_OK)- }& f5 S6 _2 q, ~+ ^' @
  88.     {
    2 ]- l9 v# j- a: u5 T2 Z
  89.         return 1;% _* ?3 ^; o, s/ D" J6 I
  90.     }
    $ ^- x( ^; ?" m6 X
  91. 4 c0 z' I9 v/ R' e5 P% t
  92.     /*  B/ E$ D& C1 t/ ]' {" v) l
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 4 k& K+ z9 B, }, y5 q
  94.       (1)使能CSI clock$ W8 r1 `0 J: k/ ^0 l4 z+ d1 k
  95.       (2)使能SYSCFG clock, s6 V" r+ b4 O" V4 V, g# @- e) r0 V
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0" B4 [. k- A& W5 u* |# K/ U; R
  97.     */
    ; I, ^( b" Q( I7 H  x$ n
  98.     __HAL_RCC_CSI_ENABLE() ;
    1 |" q- X, j- h' m4 y  b

  99. . m3 P+ O" o5 ]2 x# Y! \, k7 K
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
      B  S* X# s7 j
  101. , e$ ?) }7 m* _
  102.     HAL_EnableCompensationCell();8 b/ g0 p4 Z: L% A% O' h5 h' n
  103. 2 l" S+ f  s, o
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();" r/ c' T' ]( O+ N! r1 }8 p% B
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();* A% A3 Y" n& T# w% F  y5 I4 l
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    ! o& u0 W2 o/ _' x8 M; Y: _+ J- q; I

  107. : _3 A5 ~2 M/ V3 q, W  V
  108.     return 0;& t+ Q7 [) d, Q5 n, H! _
  109. }
复制代码
4 J, X( y, r( S+ \/ f. Y6 z% Q. g
85.5.5 第5步,配置文件Dev_Inf.c的实现% y* [1 M, Z6 d
配置如下:/ x5 k4 m" W* B9 J( g& C

' e: d9 A0 @5 N4 |# V% ~/ {
  1. #if defined (__ICCARM__)
    ; X8 v5 k5 R, x1 G
  2. __root struct StorageInfo const StorageInfo  =  {/ S8 ]0 w1 [& |4 g
  3. #else
    $ `- H# m8 J9 v' R% n1 u# {
  4. struct StorageInfo const StorageInfo =  {6 f. t- {' b0 ?& e, r, i
  5. #endif
    0 b: M) U8 o6 W3 g# T  b( H% ?
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */8 ?+ B: Z" N0 V' a# c0 D4 j
  7.     NOR_FLASH,                      /* 设备类型 */
    8 S) x. C) B' B. f( W9 W& _7 Q
  8.     0xC0000000,                     /* Flash起始地址 */2 }1 J* V/ b/ q  b$ }2 o- o. N
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */7 d/ [$ u6 q7 l3 P# I% g6 u4 ^- W
  10.     4096,                           /* 编程页大小 */& P! B! q2 g8 ]5 a3 H
  11.     0xFF,                           /* 擦除后的数值 */" ?& x4 B: c+ a
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    & p/ q0 S' ]+ Y  C" K
  13.     0x00000000, 0x00000000,
    6 M4 H0 i5 W$ m: r& e
  14. };
复制代码
: ?, l7 [; A: z( O
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
: v) |9 y, D/ V3 Z# \* _8 v/ r3 t3 f

( m. v6 w2 ?% m
9 w/ Y7 {- u( @' F5 u; ?85.5.6 第6步,编程文件Loader_Src.c的实现% \5 t7 S) D: a+ i& B
下面将变成文件中实现的几个函数为大家做个说明:6 c8 O5 ~- P* p$ Q6 v0 g) W% ?

0 W1 v. [5 w5 ^" {  初始化函数Init
' t2 e* ]# n+ h4 }8 p8 F, P, J
  1. /** v0 Y2 q# O0 r
  2. *********************************************************************************************************2 _: c/ g7 `2 T) X
  3. *    函 数 名: Init$ ?5 C) F! }7 x4 Q/ _- C- q
  4. *    功能说明: Flash编程初始化
    ' a# ?  G) S1 Y) V: [
  5. *    形    参: 无
    / `  |$ \! B" n. B
  6. *    返 回 值: 0 表示失败, 1表示成功
    3 J. B6 z: G4 D8 u
  7. *********************************************************************************************************
    " K) V# V$ w+ `" H+ i
  8. */
    & N2 z" o$ y" u) E! Y
  9. int Init(void)
    " |6 `' T1 e  e+ ^' ~4 }
  10. {   
    : ~* }. Y, H! S! n
  11.     int result = 0;
    , ~9 M8 I' z0 I9 D  A

  12. 4 _7 c; H5 ^) h9 G7 A
  13.     /* 系统初始化 */
    5 E+ V* G( j2 Q4 C, k
  14.     SystemInit();
    0 y* [- ?/ u4 z
  15. ( x1 P  @8 k" ~3 h) E( O
  16.     /* 时钟初始化 */
    ) Z1 X: t2 T, V( m# L
  17.     result = SystemClock_Config();$ d! f, x: d8 c- l% N
  18.     if (result == 1)0 f$ R# G4 f3 R2 w
  19.     {$ d" Y! c; T" E2 Q+ d4 S9 x
  20.         return 0;
    , y. r6 x' M" V& `/ Z0 i
  21.     }3 T/ f8 |  E" U& n* g4 J
  22. 6 d0 R+ c4 W) V$ P" F8 a. T7 X
  23.     /* SPI Flash初始化 */" k$ [5 c5 U8 _* M9 k
  24.     bsp_InitSPIBus();+ h9 i+ `- m' g4 q6 l4 ?
  25.     bsp_InitSFlash();3 p4 t$ w1 g# V) x, z

  26. + z7 l2 j; |: }& I  |6 w! r: S# {
  27.     return 1;, B9 k  g, E) p. o$ I  N; N, k' m
  28. }
复制代码

& f! b- o# J$ n. m% K& r: V  整个芯片擦除函数MassErase# K7 |; q% I" ?2 V2 ?7 ?4 n! a
整个芯片的擦除实现如下:; a4 H3 V6 }7 z3 {) x. l
7 b- S7 _$ Z7 Z+ s. f1 l5 k0 U9 N
  1. /*
      b- S# \" j1 Y9 W* _
  2. *********************************************************************************************************% W+ ]) s% D9 c. Z' x- e. S9 I
  3. *    函 数 名: MassErase1 j4 c" W; p6 J6 j0 _
  4. *    功能说明: 整个芯片擦除
    ; {( a( U* N" c& B; n$ s' z
  5. *    形    参: 无
    3 s8 r0 H. P9 N5 w0 k1 {
  6. *    返 回 值: 1 表示成功,0表示失败
    # \8 n7 O7 n3 f2 v" i8 K) M
  7. *********************************************************************************************************
    ' ~( x% d# n1 s1 w6 f
  8. */) q6 P! o! y6 T: u- G; J
  9. int MassErase(void)
    " f3 e: D7 L, Q. y+ |
  10. {4 I" B$ T# b# i$ o
  11.     sf_EraseChip();
      j. R' E8 }" z* k

  12. + {' ^, {- D# @4 d
  13.     return 1;   
    . N9 G% X1 \/ ?) I
  14. }
复制代码
" [* m  N" x9 N
  扇区擦除函数SectorErase! \" N0 J# z5 O( a1 l
  1. /** b* E8 C( c8 G* u
  2. *********************************************************************************************************
    " E; L& B# m; P* `# v' c
  3. *    函 数 名: SectorErase" R1 z8 u" N7 b' M
  4. *    功能说明: EraseStartAddress 擦除起始地址* k/ k, C5 R- b5 ~+ v) _- g4 W
  5. *             EraseEndAddress   擦除结束地址! _$ }4 Q5 R- q9 `
  6. *    形    参: adr 擦除地址8 Q; }6 k! j; T
  7. *    返 回 值: 1 表示成功,0表示失败" n5 {! c" {3 |$ ^, t
  8. *********************************************************************************************************
    # I+ p4 g6 D4 t. }  o/ J. a- @& A
  9. */8 X( z) C& l4 k- r' a1 t* [( T
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)0 X' C- |. I$ z/ U) D% k; @0 M: m
  11. {
    8 J0 ~+ _9 a9 [4 R
  12.     uint32_t BlockAddr;
    - p! L4 o1 Z# g  f3 k+ u
  13. 9 l  y) k8 {; J
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
    - F0 P1 a" E4 n: w; [$ H# ^9 E) S
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;2 i" d, q, `9 R
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    1 u, Y# G. {* @0 V& E5 e+ M3 j6 t
  17. + ~$ F8 ~* r( j2 P$ X( e8 _
  18.     while (EraseEndAddress >= EraseStartAddress)/ M( j% u: O1 X- c$ c. r# i
  19.     {
    5 i/ Z1 \0 F3 r
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    - H, ^% @" B5 r% x$ J5 D
  21. ; h$ Y5 A( G( F0 r
  22.         sf_EraseSector(BlockAddr);    8 N( f. Z  [0 ~0 u* P! G. Y- T# U: {
  23. $ ?& D" w  V4 R2 Z
  24.         EraseStartAddress += 0x1000;9 E) }9 P+ Y1 B+ x0 X
  25.     }
    9 K4 I  ^+ B# Q6 `" @! `% W8 R9 d
  26. % o# I# \) g+ z% P6 h9 H1 y
  27.     return 1;
    / W6 u& W! P% A2 G5 C: }
  28. }
复制代码
' K9 K' Z$ _. D$ B) `7 a' ]7 ]
这里要注意两点:% g3 e7 T) a/ W' F! l' G! n) c* r

/ i# o, Q  O, ~(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
; h9 X3 s0 N( v9 ]/ b. q$ o
2 z3 J) O1 ]; M+ P(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。2 s2 Z* T: K+ Q# L7 Q
7 x; `3 {- L4 e; Y
  页编程函数Write: ^* |& J0 O! U# L" P
页编程函数实现如下:( T0 V1 G6 K+ c- q

3 ~- H/ ^0 Z1 T7 }9 S4 l: N
  1. /*, z, Q$ V+ g" p: `
  2. *********************************************************************************************************2 Y% ~) r6 e4 J$ q/ J, P0 M' U
  3. *    函 数 名: Write
    9 M" C" p( \& c# c+ P' _5 f  w* F
  4. *    功能说明: 写数据到Device
    5 s! e! B/ I9 J- X6 g/ V' {
  5. *    形    参: Address 写入地址) ]  ^+ Q. f$ @3 s3 O
  6. *             Size   写入大小,单位字节/ p" p! [- N6 m+ j( R3 C; R
  7. *             buffer 要写入的数据地址
    # v$ t* l: n" z9 K8 m1 g& o
  8. *    返 回 值: 1 表示成功,0表示失败
    + j2 W3 G% S% @6 _: r
  9. *********************************************************************************************************6 \, B- C. b- H. I
  10. */' i2 w& V/ @- z/ r! g
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    9 j- p0 e# v- v+ [% D, ^6 i
  12. {  
    $ ?( Y) Z) |. Z5 s# I
  13.     Address -= SPI_FLASH_MEM_ADDR;9 U8 j3 D7 H: a

  14. " {" i% R. J4 j
  15.     sf_WriteBuffer(buffer, Address, Size);
    3 C' r7 N* S* @* E( U. X" P
  16. . i8 I; x8 u  O+ b' ~3 l
  17.     return 1;
    " [; E2 p3 K. J( [1 [) o. Z0 g
  18. }
复制代码
% D; n) o8 s7 C. e; P
这里注意两点:2 q" W; ~" B+ T# I. w; v' u* T  d
" a4 ^: A: u1 N1 {
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。2 i% j, A4 E; W0 t

7 P, T5 V3 Q0 ~9 e9 S6 p(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。$ H) [, ~+ J2 t# c

6 R5 s& a# b* D/ }8 u( {, H  读取函数# W$ x. u7 H6 F% o
  1. /*
    9 x+ p, \5 ]% {' L% M  y2 W6 _4 o
  2. *********************************************************************************************************
    6 H4 r& ]% U0 e, N: A
  3. *    函 数 名: Read
    4 `. h# c$ u2 o/ h: b
  4. *    功能说明: 从SPI Flash读取数据( _7 `) B4 m; V0 \  \- a
  5. *    形    参: Address 读取地址+ w. W+ o# Z9 S9 j
  6. *             Size   读取大小,单位字节
    2 T  T3 X* |/ U
  7. *             buffer 读取存放的数据缓冲
    + T# l8 c. Z0 b. z2 {! h7 v
  8. *    返 回 值: 1 表示成功,0表示失败
    $ k6 X' Z/ h0 [! P6 e0 j, a
  9. *********************************************************************************************************9 f+ ^0 B/ K& O; [
  10. */: @* h$ R8 n- P  Z2 }$ w8 c, J
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)7 L1 {: b$ Q5 t! ]5 A5 ^
  12. {
    ; n2 `0 G" t0 h
  13.     Address -= SPI_FLASH_MEM_ADDR;1 o+ p8 F' E* G8 L4 Q2 L0 W: M
  14.     sf_ReadBuffer(Buffer, Address, Size);( q1 b: q8 H& n. @
  15. ( z4 a+ Z1 Z/ }
  16.     return 1;+ g- D8 V( v# Y9 g' w6 m) l( n3 g
  17. }
复制代码

2 k* I9 E2 H; N  读取和校验函数) a* |" r- e0 Y! N
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
" V! A2 g. I/ t* i+ x# u- I
" S! g! Y$ S$ ^/ S85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)% o. {( R% q; H" c6 I( Y7 U5 i6 a
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
8 @$ d3 Y$ u; O( N; X
; u& J+ v. p8 d9 }( {5 |6 e8 p
  1. /*9 r, b' Z5 T6 r5 X
  2. *********************************************************************************************************4 P0 k, Q! L" N
  3. *                                时钟,引脚,DMA,中断等宏定义
    4 o' J' M2 t9 M/ @
  4. *********************************************************************************************************) p) |) x# [! k- j
  5. *// ], I/ l( d3 Y! ~6 M
  6. #define SPIx                            SPI1! v, ], W8 N; U! \# ]* w
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    * |' b  {; n0 F; R* I9 \
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    0 J4 V8 i. r1 N# Y; @3 r
  9. # M5 @% r  a# p+ f9 O4 h7 q5 K5 ]
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    0 ^9 v6 K% @# B6 s# g2 a
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()7 J; c$ i' Z$ B; ]- L, b

  12. 8 s0 r! W1 x! V# A
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    4 H8 P: _# e$ n+ V
  14. #define SPIx_SCK_GPIO                    GPIOB2 T: H- B4 v$ t4 k, ?% W
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    : ^3 M) h. `  f6 N! x) y+ k
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    9 p& w0 L$ s6 g0 O8 {* m7 q
  17. 8 W/ a& v' L( V" N1 O' E
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    7 z9 O2 H0 t: s1 O) O$ T
  19. #define SPIx_MISO_GPIO                    GPIOB
    1 [! W0 |/ L$ z) ?4 f/ t
  20. #define SPIx_MISO_PIN                     GPIO_PIN_46 i$ e7 G+ }) J4 ~& i
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    * h4 v' G7 U; b, F! |- N

  22. / q  y8 \, p  m9 D  I- R
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()9 p8 {& v  L$ o7 I
  24. #define SPIx_MOSI_GPIO                    GPIOB3 [* ~% ^9 _6 N" }, q' Z
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    ! j; w5 d+ R, q+ U% k$ y! t. Q3 N
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码

# L( _( w3 f, [) H0 C硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
, ~1 B& r( s. _* M1 h" [: ]/ |+ i
5 Z: C" I; d9 u' r7 J( T% L! j" m主要是下面这几个:1 a/ `1 p, d4 E" d8 k/ `: G8 z
/ U( K* H% J$ I0 w7 W
  1. /* 串行Flash的片选GPIO端口, PD13  */
    ' a. S& e3 o! P" o" r2 s9 j. C4 e4 X- l
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()/ e5 l! z, J4 q" _3 K5 B2 {6 A& [
  3. #define SF_CS_GPIO                    GPIOD
    7 y: w7 s7 b4 O) c4 c! N
  4. #define SF_CS_PIN                    GPIO_PIN_13, W8 Y, p. A& a0 f  j1 ], K4 A& Y8 j
  5. % R: G: s# y9 s2 `) H4 B
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    - g$ y( u/ U1 Q, }
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN( r' F, t0 u+ g. ^$ Z2 t- q

  8. 8 z; E5 V& f* b  j! u
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */+ U+ ~' Z1 Z3 W( f1 j6 V
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */$ n( J; Q1 T8 G* y0 ?
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */: {. {! \7 b# M3 w
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    , K8 I. ?. j) p& a
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    ' a; m# B5 j0 W' w9 Q& F
  14. #define CMD_READ      0x03      /* 读数据区命令 */
    ; F3 u) j' q  g. I
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
    ( m0 D% h+ ^4 A/ f- O, Q8 F; |& f, N
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */3 a3 T! C. M& H- J7 P
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */2 v- n, r; R2 J
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    " `; H- i6 v- v% m8 a
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */
    " R  P5 n6 a; G  h0 m5 K% d0 s

  20.   _! F9 `% A; V7 z
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码
& R  `% p8 C5 V5 L( X7 K- L# H
85.6 QSPI Flash的STM32CubeProg下载算法使用方法3 D' d, w% i9 n; K* x3 A# Q& I; j- n
编译本章教程配套的例子,生成的算法文件位于此路径下:+ R/ a- q- u+ J' `6 m8 m& X( E
+ p6 y2 R/ }9 j% Y
5ec6d402a7646232a55f4834cd00ac0c.png

6 o8 i3 M* y% G! J  x& v2 |  n
2 B$ _: }. Z, @* s1 e  f% T85.6.1 下载算法存放位置
7 n# Q3 {# `5 ~生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:+ q9 z9 {" ?% o) F
% W% g& j* I( S7 z' G1 \
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader, [) o( J9 F3 e+ e+ Y

; K! l* V" ~1 X/ l
2609073778af5845a869911ab36624f4.png

2 \& J% F' {4 W/ k5 g) C0 [. }$ [' C; a' ^+ G
85.6.2 STM32CubeProg下载配置5 c. e9 o" i0 B  l, l5 [
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。* i/ z1 k6 v' m3 f

0 F7 b- P; X, Y! \: O
d4e256633eafdbf4bcd8cce6e3b5909f.png
6 n# j- W* K+ C# S' n
2 F; k0 k8 {( Q0 b
点击Connect后效果如下:
0 [4 z  i2 Y' ]; e: M: K" j, `" a- f4 _5 X7 R* Q. B/ U
6 o  Z% Z* f! y2 i9 }
5 L2 H3 |: Z4 `/ _5 l5 N
在这里选择我们制作的下载算法:
4 z& Z, Y; Q3 z
" ~6 A3 r2 U0 D* e8 I
d4fa28f64be2de98e04f3188eb2fe09e.png

" X; r, V  T& v) M0 a8 x3 ^$ S
9 W/ ^5 j" X( G. Y4 [1 L任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming0 M9 }4 p; z$ u" U, z
5 u3 J6 l+ @# R4 H) g3 A
842518a7cc1dc569598d372132b2ee58.png

7 \- f2 D! [# P4 a; f6 Z7 F- [, Z  k* s) r' ^
下载完成后的效果如下:& c) Z, e! r) c* O1 M
1 N- G5 f8 g) z) T! s9 d+ r6 t
68a92f267829ca77f125db982d6c0479.png

% W, s8 a+ `  \% u5 f6 @- _
3 B, D" s% l; d4 B/ k: M* i+ e85.6.3 验证算法文件是否可以正常使用, C  ^+ v$ @( }! U* o# f# E, s4 {
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
$ J' W+ H4 i6 e4 {. t+ e, ^+ \6 d9 F8 @
566d2fba421c710da70e5656c6934466.png
4 Y) P, A: `+ n# G( t
0 D, q# I5 p0 S! l+ p9 g9 l" o
85.7 实验例程说明

* N: O8 f( S6 S9 b4 ^5 I/ ^本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。1 k0 A( U" T" R
/ T' ~) E0 z, m. u, q# n
编译本章教程配套的例子,生成的算法文件位于此路径下:8 C* L3 b  ]0 O

8 c# p6 b7 E5 U5 r
6108d1ff7e349f2aa268ee31804c57ca.png

, p+ U$ b6 w) d% H
' [# J, @+ Z' p9 y' K2 l' a5 @85.8 总结
0 s, t! [. U7 k4 G) A本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。+ c6 |, k9 t% C% k# d4 {' \
! `. d# p- W/ [" r

1 h8 U( m  j, t5 ]6 v2 s1 f% P. L+ U( _& q& \+ S; V/ p/ g+ B6 z& F
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
收藏 评论0 发布时间:2021-12-19 15:00

举报

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