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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示+ V/ _8 w6 i3 ?( x+ F4 c1 e. L9 s$ C
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。; \; ?* t+ X% x! \
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
" N8 Y5 A8 g' B' W6 P  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。( E# T. R2 F6 c- l* f0 X
85.2 STM32CubeProg简介$ a0 x. a2 K9 G# j
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
" h$ I' D0 g. Z& ^# N( ~
8 }6 K& T- A+ _; s' l* ^$ e" ]
d80578d217eb2a83520c6f3d797218f4.png

: i. C8 a3 g9 y. e+ l% h- u/ }+ c6 U2 q) f
85.3 STM32CubeProg下载算法基础知识
- d1 W2 r( T5 I  O. k' O
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。+ [$ w! e! p! d. i
% x' b; Y& U' W2 W
85.3.1 程序能够通过下载算法下载到芯片的核心思想4 h6 ?5 D) K: q) X" s6 V
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。5 l8 R) f- T1 K0 a

2 n- ?" Y. M4 D85.3.2 算法程序中擦除操作执行流程$ n: l) {) a# t- @
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
/ U$ D) F) a( \$ [/ p
& P6 W1 z% z- n* i- X4 R擦除操作大致流程:
7 d# }& ]/ S4 L* V
1 x$ }1 y" d3 f8 q# ?' c' T
d75670256363bb50203bed0bbe4f9fd2.png

+ \" o* b  n: N! n
( M, t/ r+ Q1 E% V  S+ i  加载算法到芯片RAM。
2 m  `8 D8 F" y! G* t) ?& \  执行初始化函数Init。* @" P* H8 U' r, ?& @1 o0 ]3 T/ n
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。! \  z' [5 n8 @! u- B- p, O9 A9 Q! h
  执行Uinit函数。
5 l2 V1 U; @  Y& K  操作完毕。
3 ]* O' i8 L, d
# c% b- B( Z) c1 s. k" A  m8 b: H85.3.3 算法程序中编程操作执行流程
% K% f2 B6 l7 Z' A! d注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
6 a, k. x, ?  ]- M# v
  ~$ E: V% x- @6 W6 ^8 y编程操作大致流程:
( }, V( r  ]0 d! N+ @, t8 v; |' z) o' L: B4 f8 V. h
4fa96e9e33d700081f2cdbd312afb31e.png
+ z1 o- b' B2 \! ^

( S' x2 H% `; U, j8 l( ~  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。% \% k- h: V4 c
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:( I1 V( G5 m# F9 @; L: }9 @; D
  加载算法到RAM。: F2 {, ~1 [: I4 ?0 O- y
  执行Init函数。) q+ R  q% c7 s
  加载用户到RAM缓冲。
/ a( [9 W, `+ W/ G5 ~. b! ?% o  执行Program Page页编程函数。
! I+ X' w3 q, J. n  u' ]% l  执行Uninit函数。+ u( u0 j& V) M+ ?% h! e
  操作完毕。# M6 F/ z( e/ L# Z6 k0 g
" R- ]3 ], m2 O0 O) W  z3 w
85.3.4 算法程序中校验操作执行流程
( q# ~7 C3 j1 d& `& S注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
% ~6 u+ I* W5 \0 d+ n  C8 w4 i+ E
/ {  P* I0 d$ c校验操作大致流程:
3 J9 z7 m: r/ {5 u% Z9 e! `( U9 \) Y) @1 W: E$ ^
7deeae4b650bd82c457492af14fc26b7.png
3 ~2 N1 h3 {4 B7 x

) |# W/ @/ b9 s3 L# j# M/ b  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
* R3 u# v" r8 ?  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
8 o& y2 V  t5 L# r 加载算法到RAM。" ~* E5 Q9 ?. E
执行Init函数。
. h4 ?  p6 O/ M' Z& ] 查看校验算法是否存在3 J( `; n5 o7 h+ [2 |! _. V5 S% G8 Q
  如果有,加载应用程序到RAM并执行校验。5 z, g  g6 Z- m. ?& i; X! Q/ i
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。2 F. ^/ l+ x, J1 z# r, k& |
  执行Uninit函数。
) F. x: w9 y7 `& R/ `  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
  P! P  ?, V+ \8 L  执行RecoverySupportStop,回复支持停止。
3 K& p  U1 F' k$ L. M  执行DebugCoreStop,调试内核停止。
- n' e# x1 }: n8 q! ~  运行应用:" o/ [# X4 T, ~
  执行失败
& E& _. _* {/ O  o3 ^" z  执行成功,再执行硬件复位。- L; p. j4 `4 ^- \% I+ R2 G
  操作完毕,停止调试端口。
2 t2 ]- Z: m$ |
. k7 U0 @  n3 ^& d. x8 P85.4 创建STM32CubeProg下载算法通用流程% {4 E0 x( E0 @! T4 ^, M$ }
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
5 B- f4 j5 g9 ]' N% {0 ^. [- y( w3 J7 _5 z% P* l
85.4.1 第1步,使用STM32CubeProg提供好的程序模板
& J  F* r4 c1 z- n" e: P2 X$ k位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
6 ^8 o" v7 B' R+ x; s6 V% ?' L& g+ t; m$ v( |; x
97f385e3b62a055bac9b5c0214b877e5.png

8 H) ?' I  E8 `  [3 A
: Y) g" g7 C0 G. }! P以M25P64为例(注,其余步骤就以这个为例子进行说明):
* _* ^, H. P! f7 ?2 e& K" F  g
& G5 A! @) A' \  m) t7 ?
43b298f996937fe35e89697fc6479cb9.png
  G4 B" `" ~, v1 `5 _8 b3 ~6 {0 L

9 G4 s# i; v& p1 Y9 q7 M  b, [! S6 l85.4.2 第2步,修改工程名
% v, |& ?  d! w6 E" I+ u" @* }8 M+ _STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
! p* V0 V. s; Q1 R7 l
2 K2 {7 m: l% X& i0 j85.4.3 第3步,修改使用的器件

/ \8 P! O* O4 T1 [) e8 C) p5 {6 u在MDK的Option选项里面设置使用的器件。
* w  G& Y3 L, _; p: S
1 f9 K. K* b% g) t
36026b9a6d60249610d0246eb725b246.png
; ~; a( y/ F2 [. b1 T
9 C. P5 x2 _% b5 }, U
85.4.4 第4步,修改输出算法文件的名字
( g  b7 f% P$ q, R1 a这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
; d! q6 R6 i- {. N$ Z+ R  S/ P  m( K/ ], k3 p8 Q% v; A
52c72b3993f28a366a22eac56f8fd043.png
0 A/ J) R6 q% v9 J

' k. k' G; c* P$ I: ~4 v6 e) O( @7 C注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
& Q# C& l' S* t/ I% q. i: V
7 ^) @; [) O% c5 c. {
7b48153027a44de33926c2da008716a0.png
- r1 {- P% ?- A. c2 h% d  O

0 `6 o4 ~1 }. L/ |1 Q% s85.4.5 第5步,修改使用的库文件和头文件路径
3 y5 g9 \5 q4 l+ T根据大家使用的主控芯片,添加相应的库文件和头文件路径。' U$ O- U# B( s- T/ ?2 f. c; }

/ h. M1 w3 H& c- d+ l8 _85.4.6 第5步,修改编程算法文件Loader_Src.c
; u+ ^, \, G2 P' J! y) M/ B模板工程里面提供的是M25P64,部分代码如下:( l) N* g+ M/ e
( W7 e4 C& j, T
  1. /**$ ~2 \" A8 ?5 k% `
  2.   * Description :
    1 s( e% T3 N# V2 k- A% G
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    1 h( s$ x/ C2 h4 R0 |
  4.   * device and initilize the FSMC with the chosen configuration - S3 h3 E  f  {( m& [7 q- W+ }7 C7 d
  5.   * Inputs    :
      r6 s. W1 q, f( D, }7 z
  6.   *      None. u5 t  v5 z$ d5 D" C! a
  7.   * outputs   :
    # A, S8 }) ~& o% |" E
  8.   *      R0             : "1"             : Operation succeeded
    , Y! t" F" s8 Q4 m" H! \) U  I
  9.   *               "0"             : Operation failure" q+ {8 q: o. o; J
  10.   * Note: Mandatory for all types of device
    7 x5 e0 r6 l9 u$ [
  11.   */
    " H. q9 C3 p9 y/ o
  12. int Init (void)
    # m& O6 y5 z: G# a0 }
  13. {  7 x/ i: E' X3 c; g" Q) O, A
  14.   SystemInit();
    # \* k; w  L) w
  15.   sFLASH_Init();
    5 T- m: X& ]1 x  u9 V( p
  16.   return 1;
    ! T; q" I  z% e. ^
  17. }
    / [) v2 Z6 Y& s  v
  18. 6 D, `, c1 K1 n: v6 u, ?9 S2 J
  19. - n' a6 I: E" j. Y  R& K9 h# B
  20. /**5 J* Q( w# n+ H* l
  21.   * Description :8 o  T) G$ M  c
  22.   * Read data from the device 2 i8 `2 R/ O! W9 W9 z
  23.   * Inputs    :
    " E% x2 j) [/ h( L3 e
  24.   *      Address       : Write location0 J* l. {( P8 u
  25.   *      Size          : Length in bytes  ; h( N  ~, I: |( Q' {; D
  26.   *      buffer        : Address where to get the data to write0 H# b: k5 M1 t4 }+ h
  27.   * outputs   :2 O- c! }% h4 v5 q" c( _+ j
  28.   *      R0             : "1"             : Operation succeeded
    * W- f  ]% R8 @$ M
  29.   *               "0"             : Operation failure
    * g5 _/ l% v  A! e! J$ C$ }
  30.   * Note: Mandatory for all types except SRAM and PSRAM    1 G1 X) J- ]- n$ u9 L. a- p' O
  31.   */
    * C( ?2 C% t; B, p
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)( D# b; s/ v, y2 H7 `# f
  33. {
    & w" R: c5 j+ a' I* F
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    ' d! M+ y3 a- {1 q# E
  35.   return 1;
    * y4 _% _3 q: T' Z* d
  36. }
    7 o9 y& G& o2 q" i3 ?  O8 L
  37. # u5 L! @8 s6 i* A8 }' u; X/ \

  38. 9 O' ~% M- a' {) D5 n3 Q* m8 M: ~$ s. W
  39. /*** n0 R% g) p# [6 u3 ^: {! }/ H( A
  40.   * Description :
    7 `: O. w% z  W- f5 E3 K! L. N
  41.   * Write data from the device
    : y. n( d6 x* H, [) E) U) s8 \
  42.   * Inputs    :9 h! L( a+ b6 H# I9 |3 z! i1 a
  43.   *      Address       : Write location; g' X7 w: [  X8 H& T% C. R' L
  44.   *      Size          : Length in bytes  
    6 d1 b) _. m; o2 }0 y. x
  45.   *      buffer        : Address where to get the data to write/ d2 v( k2 F! P5 S, D
  46.   * outputs   :+ u9 \$ c- }9 t0 [' _; h
  47.   *      R0           : "1"             : Operation succeeded3 |, _; ^5 V# H
  48.   *                     "0"             : Operation failure
    * k" x& z: a% D& _' y: d9 O
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    ' E& U5 i. H; X6 q/ h& l" Y# d" M
  50.   */
    2 X: i4 N/ w) J! k& A' Z
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer), D' ^- A# B5 ]+ C! f, `
  52. {; N9 U' B! j0 l; ^0 N% j
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    / r% R! z! e, K
  54.   return 1;1 b' F+ m% N5 d4 l9 y3 j% _5 j* f, E
  55. }
    ) c7 S) Q4 m( l
  56. % F- r8 K0 `/ P9 u0 Y. \

  57. ; y5 m/ G1 O+ {, b  N, K
  58. /**5 F8 K9 ^7 o7 D
  59.   * Description :# c7 p/ ?  v6 p1 c
  60.   * Erase a full sector in the device* H9 H9 b/ n9 K
  61.   * Inputs    :& J1 K* W. S4 @$ [
  62.   *     None
    0 s1 u2 c$ s' K( U: Z' t% w" B5 Z
  63.   * outputs   :
    ' L: Z) D4 X- ~3 R  z' a7 A
  64.   *     R0             : "1" : Operation succeeded
    0 t# \! }3 D' k- v7 {/ \
  65.   *              "0" : Operation failure
    ! n  F7 F8 O. O6 a$ c
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH; ^% i% U# j. K% r
  67.   */
    - ~( _4 w: Q3 s6 i5 D1 c: s
  68. int MassErase (void). k1 {2 J+ ~% c& {; O; \# \
  69. {  
    " s' G$ M" v  M: m- I
  70.   sFLASH_EraseBulk();
    " i7 n5 J9 k0 F0 s3 p! q8 c
  71.   return 1;    , w9 H) g! Y' I& M% J% A! @
  72. }
复制代码
; \& z% b: s! @6 k
85.4.7 第6步,修改配置文件Dev_Inf.c' z& I- f4 x9 N1 l, o! f7 I
模板工程里面提供简单的配置说明:$ @' Q7 P- }1 |: Q, X

; h0 q+ {, u, d) a* Q7 A
  1. #if defined (__ICCARM__)* b: S# ^' j$ R& s8 Y; g7 y( X
  2. __root struct StorageInfo const StorageInfo  =  {5 y. ?& c' _4 K% L+ g4 z5 v
  3. #else
    % F* x! n* M6 v5 x0 h5 K, V
  4. struct StorageInfo const StorageInfo  =  {& S3 w6 F0 @; l1 b. J2 ]
  5. #endif1 n: D7 g* s* u  E5 \' g" q6 |
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    ! o( V, A+ M6 [2 X' U1 n8 S) J
  7.    SPI_FLASH,                       // Device Type
    5 l0 C- b+ K: O/ S
  8.    0x00000000,                     // Device Start Address& m8 }* P6 J" D/ u
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)3 u4 W3 B  }- s9 U6 i) i% d
  10.    0x00000100,                      // Programming Page Size 16Bytes1 d, J, e7 \3 S
  11.    0xFF,                            // Initial Content of Erased Memory
    4 c, h# N4 w6 [" ~) j
  12. // Specify Size and Address of Sectors (view example below)
    : N8 N. _+ M. U& a
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes 2 I& u% y( i4 q
  14.    0x00000000, 0x00000000,0 _" G* C9 g( X1 z1 Y3 h5 t
  15. };
复制代码

) j' o- G2 V" @( Z. Z% i% [注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
$ e" J# Q  @2 H; y0 m$ S, U, b! l  ]1 e! I! D- S
85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
9 z& e1 p9 N# M# E- z
C和汇编的配置都勾选上:" n/ t8 m! E& |' A9 N

7 D1 h& Z" w# }+ Q! }; F& F2 @
afddd6e839292aa2b94b43e79544d630.png

1 d9 K' _: t  S7 _3 ~  ^. _
/ E  ~3 K5 @- {2 D& E汇编:
8 E- Q0 v/ x# M) Q* r$ P4 B' K( g& }* w# Y, Q0 a
f639ae5b0d3c2e0e0190ec509fbc8a4d.png

  ~6 o, ?* U1 H, i. W- }" M4 ^
, t& t8 ~& q2 C如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:1 A: X5 r9 k$ }, ~/ G' q

$ R  R- E) V! }$ D(1)加载以响应运行事件。
6 ^- r+ u* `1 L  R
9 Z7 X  n0 ?& {: S(2)在不同情况下使用其他例程的不同组合加载到内存中。. V+ T  D9 l9 N- B. v3 w8 F* @
9 _2 |8 D. e6 f9 S1 i% j( F
(3)在执行期间映射到不同的地址。& X. m8 m5 _" R6 @2 O

. Q! U2 c( F% U2 b* n$ E使用Read-Write position independence同理,表示的可读可写数据段。" j8 u( M' D! x6 e; o, C2 i

- U0 |! P1 c8 N( {* T85.4.9 第8步,将程序可执行文件axf修改为stldr格式
3 S" u- j8 n9 o通过下面的命令就可以将生成的axf可执行文件修改为stldr。; g5 P8 F* z4 I% K4 g0 l

$ H* }7 M# _- H# X7 @+ h: x
c683673d2481d8dce75b6fa01d624600.png

1 u/ |( @! ^9 S+ w1 D% H3 b
' I) P% q% [; m  l1 q85.4.10   第9步,分散加载设置
4 y) ~8 H4 j7 W/ C* M; N- ^3 C3 P我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
  d7 ~" R& c1 E. F0 [; n5 s, l% P. F, B* A( ^& {  d
b96835a7506516319002e6b2e8db9371.png
: y, a9 }; a& ^3 y0 H! p
$ `# {7 i+ i6 W  m! e
分散加载文件中的内容如下:3 i$ x0 [  p: ~# m8 o0 L( q9 b

& _. S6 X* d" {" Q; ~
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    . S* P4 Y! d% p, @# r/ {( Q1 m$ O
  2. {
    1 g% s: u3 w, ~$ {5 ~
  3.   PrgCode +0           ; Code7 N* ^2 x7 `4 d3 S6 |4 p3 H( n
  4.   {
    5 v1 W. M! R6 i$ s6 u; O0 _* }
  5.     * (+RO)
    * z/ F/ I/ p* I# |
  6.   }
    , a% U: _* y% b2 M
  7.   PrgData +0           ; Data1 m6 g% P) V# p& i
  8.   {% X% u( f8 E- @
  9.     * (+RW,+ZI)- F/ B1 G9 E* P
  10.   }
    ; \  W6 A4 K' U7 G; z! K
  11. }( e; v6 ^, d4 [5 `8 y. C

  12. 5 f$ e9 X( E! ^- ?3 Q& Z( A
  13. DEVICE_INFO +0               ; Device Info0 a; ?! c3 t/ i" ^  ]& ?" |1 X
  14. {
    5 L3 `9 ^  ^& p. E  V1 T+ j- l
  15.   DevInfo +0           ; Info structure3 y" g9 }- d/ I% l6 N- V1 U4 @" ^+ L
  16.   {
    - K5 v. T4 q, S! \/ E8 d
  17.     dev_inf.o3 a! r" [" A7 y" C
  18.   }
      Q- h7 T0 r* ^9 c% j6 C5 T$ D
  19. }
复制代码
9 |/ m% V% }7 `: ^( \. _2 b
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。. g6 _7 |; ~9 o4 Y

% l, T: e8 Q" S- V" C% `--diag_suppress L6305用于屏蔽L6503类型警告信息。
6 d" ~9 \6 u' I
" j% F) r7 ~! _" V- g' \5 c特别注意,设置了分散加载后,此处的配置就不再起作用了:
# f  R8 l+ O/ X% u) [& [* d) f4 c) X6 X6 N# ~+ j; q+ \5 i3 `
641395c374a3af02f1a92d3ff9ee4199.png
! [1 t' e1 `/ h, j9 o
: \+ }& b  R0 `- W
85.5 SPI Flash的STM32CubeProg下载算法制作
0 m% V# @3 Z2 G9 y下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。) `9 e. P! T% w

2 }% ]% k! p- f+ s; H# f85.5.1 第1步,制作前重要提示

. H7 o" R6 O2 f& r这两点非常重要:2 A9 K# B+ c1 W# X2 h# H8 @

2 x& q* E( j/ Q- ?- ~) _8 [3 w2 O  程序里面不要开启任何中断,全部查询方式。+ I0 r# ~3 d$ \
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。9 @+ I9 D6 X) j& y. Q4 a* Y
3 `  U# L; Q* N! L3 f
85.5.2 第2步,准备一个工程模板
  U/ b( e1 H1 A 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
# }' A% X, I* M$ `/ q& G7 X" {- k+ W& p, n0 ]7 w5 m, n; D
80ad5e3abdd6a993c8621230401a1313.png

. D; {0 I/ m- A3 u7 c( b  m' n! M4 G& \) M0 x$ W" M
85.5.3 第3步,修改HAL库0 v' D7 N  w- ?% |& Y+ C! S
这一步比较重要,主要修改了以下三个文件:2 `$ y* R# f" q; T
/ d1 r$ u% f& ^% k5 t. U4 x. T9 O: m
33bf89f25487547d4008f43b7cf10e03.png

& C; s$ {9 }- z: g$ r$ w& w8 ~! W5 @# j# V9 p
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。6 R) g7 w1 c) r$ J- A2 B
( z& s4 L* B) L6 p% f8 v
85.5.4 第4步,时钟初始化) k  w' U1 d3 M, E1 @& C
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:8 D- x  }; e8 ~

$ A7 S* N4 X1 T) Z. Z4 B4 {
  1. /*6 P  J- x- n8 A
  2. *********************************************************************************************************; R0 [* O5 S- e; Z& {4 `% ?
  3. *    函 数 名: HAL_InitTick7 ?3 s% b% g7 h! D" x5 _
  4. *    功能说明: 重定向,不使用9 B" @7 Y0 X7 i1 N1 Z
  5. *    形    参: TickPriority4 W2 |6 X4 b! Y( O) r
  6. *    返 回 值: 无: X7 h8 H* p* _3 p5 O5 W3 U
  7. *********************************************************************************************************1 o  ^$ m; y, s$ w: C. i1 r
  8. */
      m% G. E; ~* G* n
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)0 t0 j7 \, y1 A1 d' S
  10. {; e% Q5 o& A6 R6 R1 c) }
  11.     return HAL_OK;/ |# h* E# V1 W
  12. }
复制代码

0 T( o3 L  d+ F- B% @( V( N% C然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:8 D% G* Z; c; ]% E6 _9 Q
2 R" m' ?* k: c
  1. #if !defined  (HSE_VALUE)
    ) W3 x- U; `, E, ?& R& u% K
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */# R5 M: [; Q6 h& D. e
  3. #endif /* HSE_VALUE */
复制代码

( O. O) u% Q! `: V. K% I最后修改PLL:
' j7 d. y5 [9 n& i0 V3 g9 ~( u
  1. /*
    ' g' Z5 i! g& W+ A) x% G
  2. *********************************************************************************************************9 X. X; l& s3 Q8 a
  3. *    函 数 名: SystemClock_Config
    + t6 G0 S; C) E4 V; q
  4. *    功能说明: 初始化系统时钟0 N9 ~  k# F  @+ Y) N: K
  5. *                System Clock source            = PLL (HSE)
    8 A- r0 F. l1 h! N$ s" B) n& ]! J
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    : I% N# w: I3 u9 [* Y1 C. W$ p
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    6 @! R+ ~& M$ F1 o+ N# \. X& A
  8. *                AHB Prescaler                  = 26 {4 P. G$ l/ X
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)  W. ~0 q8 B1 B( a% G
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    ) j8 O$ Y" [: \
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)# U( t6 j* X3 o  ~! q' R+ r
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)9 |0 T/ O0 t) S; t& L
  13. *                HSE Frequency(Hz)              = 25000000' c4 n$ _1 P3 Z! p/ a
  14. *               PLL_M                          = 5' S8 T9 f* {3 s& S5 T! _
  15. *                PLL_N                          = 160% E8 K$ G  C4 D! X# L3 q
  16. *                PLL_P                          = 25 j; ]3 ?) }; E
  17. *                PLL_Q                          = 4
    ) ]0 K0 v% t1 J; ]/ o" S
  18. *                PLL_R                          = 2+ b% B, z8 a4 l
  19. *                VDD(V)                         = 3.38 M# ^3 x7 h2 e) P; B3 P
  20. *                Flash Latency(WS)              = 41 e) j5 D- X- s9 U! A
  21. *    形    参: 无8 V( k/ N" r, E# \- s* |- P
  22. *    返 回 值: 1 表示失败,0 表示成功
    " F5 }+ ~: f' B& O5 y% Y6 d2 X
  23. *********************************************************************************************************
    9 O, J4 @' u2 A% N8 J% ~
  24. */3 r$ b9 z. T- m! J
  25. int SystemClock_Config(void)
    1 o% J3 m: ^- S
  26. {
    . j( }& [* q6 O1 ~3 y& i$ \4 R
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    6 }+ U( N/ g3 s: r( b% M
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};. Q2 t3 U2 u3 W% `0 i/ z3 n" ?
  29.     HAL_StatusTypeDef ret = HAL_OK;* z- e* X/ i$ d' C8 k

  30. * D8 M+ P2 K8 b7 a- A9 g8 P
  31.     /* 锁住SCU(Supply configuration update) */; m2 b5 I5 O- ]# @% K9 U! I( \
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    ; E3 q/ G( Q1 N/ f; E

  33. & _( r. o( H* r8 ~! q7 u
  34.     /*
    ' G9 k" v& g; e9 H" g
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    ' V9 U6 A; Y) j. T
  36.          详情看参考手册的Table 12的表格。/ P9 U. p9 k9 N  G9 ~) d
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。6 j& _: R. _7 E
  38.     */$ C3 G* M, D- l! z0 `7 S+ ~
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);# l3 l: Z0 K3 P' q- u. r  R
  40. ' [, i9 T6 v' O/ ~+ C+ X1 Q
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    ' u9 y# D$ X2 q5 n

  42. : I# i& g8 B/ H( L0 D
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 *// ^0 w4 F$ b1 x# W" v# t1 E
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
      |: r8 J/ C1 Q0 w: g7 Z; Y1 x7 p4 E
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;' Q* ~% P6 l* C0 J
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    8 V, t  e3 U1 N; r" G
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;* z* [2 S# @$ w* q' T3 T1 ?2 t- ^
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    ( j2 V" n5 y6 ^  ]% x
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    # Y5 o6 f3 ?, d* K: S. B1 h+ Z
  50. . |% S6 {, H. y2 {- a# _+ X; n, Y/ f
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    & O; ?3 U8 n% f6 W
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    . t0 |+ B3 |  [+ \  [4 [
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    % j9 i4 ~7 c  D" B5 I! }9 v7 a& |
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    7 _5 Q; U$ S) f- F! X% ?
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    $ N4 H/ T$ Y: L7 R4 n! H# w, u- M
  56. 8 R  l9 W' t% G& b# ?* Z
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    4 `# I4 b; h* O
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    / w. n. t" M  G6 H
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    ( Y7 v9 a' k; j. T, ], e
  60.     if(ret != HAL_OK)
    8 a+ B6 r5 D* j% W0 x, y; R# ]
  61.     {
    $ U" M) \  [3 r+ b/ a+ i
  62.         return 1;        
    6 Y6 v- Q$ Y( u$ ~- C. U9 |- }5 n
  63.     }
    ) w. |" B  Z6 n) A. V5 k! i

  64. / U/ ?! Y! h; g4 X# t7 G
  65.     /* - a" _- E. L# X3 {# d, \
  66.        选择PLL的输出作为系统时钟$ p9 h: _  G: m  {8 y
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    2 d% j' X' \4 C2 X, Z
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线3 E0 p- w6 c) g4 f/ h
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线3 y! ~! v, K% y/ c' H
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线& ~# y# C: |6 i8 e4 h- D# M
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线( `4 I$ D$ {% D& }# F
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    . a  H% W/ U5 k9 y1 v
  73.     */* Q: T- F7 w) |8 N* |  X
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \& e+ K/ g9 B! C0 H
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);+ j( \' v" Q' t2 o  s+ u1 u

  76. 0 I/ K# k" N( V0 U
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    * S; m3 |* S8 L# y
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;7 f, b# r2 g; `5 \: l  V! R9 J
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    % l2 y6 S  ]4 ?: G6 [, C- S
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    , ?, [1 f0 g& M0 a6 Z- S! X/ E
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    + C' ]7 S8 N$ {/ E
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    2 q- D; h9 I; R  E9 V6 Q# ]
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; * S, [; F2 x2 y$ z, o% u

  84. 2 y# D  ?- f8 V+ Y+ O, F+ r+ F) A, T
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    ) E9 Q& T- Z7 g8 H
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);- x, k( L* a7 ^1 l9 B  h# O
  87.     if(ret != HAL_OK); C6 I3 d/ d1 O# F& I8 K* h
  88.     {
    ; A* [  W$ V% I/ H$ k1 l
  89.         return 1;
    : P4 j1 U/ k/ f# ]
  90.     }  O( J& ]. [' g9 g; }) K
  91. ) n1 t" L5 H% ~. @2 l/ D
  92.     /*4 y$ d% q$ r3 o, I
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    6 k4 h; i7 y( E3 ^. x! a% ~4 g
  94.       (1)使能CSI clock
    5 F4 B7 R' n9 ~' R4 }
  95.       (2)使能SYSCFG clock$ z# B: \- {3 c
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    ! N6 Y& b* B8 ]7 U: C- Y0 G, Y
  97.     */1 J, B" `2 q! o3 }: Z/ b" B
  98.     __HAL_RCC_CSI_ENABLE() ;2 Y7 a/ L7 P% r$ e, ~' O1 I
  99. 7 w& o! G7 ?+ F6 H' ]$ J2 Y7 R
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    ' I& i7 z' \4 m+ y; G
  101. 7 {2 Y2 \# }7 S4 {8 H  f
  102.     HAL_EnableCompensationCell();& c* ~% e/ z) A9 |$ p/ A  d
  103. , ?+ \+ E6 c. T+ l3 c
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();4 H: v& b3 B7 q: p
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
      r; h- h, X. {+ k; }
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    1 d" s/ q6 B4 g3 y

  107. 1 C9 x: ?4 o& E! A/ s/ j$ r- h  _
  108.     return 0;
    ; y. S2 L9 @8 R) t
  109. }
复制代码
! ~( `1 z! I8 D. K9 \
85.5.5 第5步,配置文件Dev_Inf.c的实现
" f% a, n/ [4 u' x配置如下:. Q% \# q* L: f1 D% _

$ H# ]' o6 {! l' q
  1. #if defined (__ICCARM__)5 B+ M0 |# @- B8 m" ^' f! {# C
  2. __root struct StorageInfo const StorageInfo  =  {
    4 h* z; p  J  G
  3. #else! _9 `, y. e+ e, |, S
  4. struct StorageInfo const StorageInfo =  {% U/ x9 ?$ Y- N5 Y" f
  5. #endif
    + {2 [1 J3 J& A! \5 N1 Q" P# |
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */! _, F: k8 a) ~) {: W
  7.     NOR_FLASH,                      /* 设备类型 */5 t; I" \5 i. @  }) d& M8 H/ C
  8.     0xC0000000,                     /* Flash起始地址 */
    ) l  J# c% M2 Q- L4 P3 z1 u
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */7 D9 N1 a& W/ R' U
  10.     4096,                           /* 编程页大小 */
    2 J; }: ?1 v/ z" \9 V9 c' g
  11.     0xFF,                           /* 擦除后的数值 */
    ) V0 H7 U/ Q4 s0 d+ {" P
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
      |; U- {9 G7 i3 o) j7 m7 i
  13.     0x00000000, 0x00000000,- [7 O: `6 N# X1 M+ \
  14. };
复制代码
: \' r4 }( ^& Y! l* S6 y- B
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
  R' L/ A6 T* z+ o- P4 T4 }6 y( h% j$ ?2 U7 j& k* }8 v! G9 l
. M. u/ C& _2 L

+ }8 e4 v' u: X3 T( c, j1 x4 F8 |85.5.6 第6步,编程文件Loader_Src.c的实现
, \2 w7 h' l  M) O- f: O8 s下面将变成文件中实现的几个函数为大家做个说明:
1 |) j. g4 @& O8 _% W7 j) {4 L& K; ]
# I; e( n# N9 _* d$ _$ c  初始化函数Init
6 `0 U, D3 o+ k: i6 N3 T
  1. /*7 a1 f* ?( J( `, y
  2. *********************************************************************************************************% _/ n0 q! d" d6 |2 M
  3. *    函 数 名: Init+ b- m- R5 @( M" j9 U
  4. *    功能说明: Flash编程初始化
    2 p: H* r3 k0 P1 U/ e1 Q
  5. *    形    参: 无
    " l. r7 n) o" ~# Q' r4 X1 o
  6. *    返 回 值: 0 表示失败, 1表示成功
    + d2 T( O' n  \% b) y7 j# K
  7. *********************************************************************************************************5 L2 g  o' H( U* N3 K) O3 a0 S
  8. */
    - M! ^. b7 w1 `$ E  @' [
  9. int Init(void). d/ {8 P5 y8 m& M  n) `
  10. {   0 }5 n3 }- T( u
  11.     int result = 0;; |6 f+ J  d! v# g9 p, H

  12. & x+ j& [# J# a' p
  13.     /* 系统初始化 */. A3 m/ i% x1 r) d  \0 M& C8 I
  14.     SystemInit(); 1 F9 r( s6 {; |, b

  15. 2 w2 p( @8 Z  U/ Y; _0 F4 c
  16.     /* 时钟初始化 */
    ! E1 J+ H8 r0 y  `8 B8 b3 n( g7 M
  17.     result = SystemClock_Config();
    ; C; x: i$ K; k4 t
  18.     if (result == 1)
    ( k9 q) P5 Q  C# ^
  19.     {
    ! B# w0 u# V+ H: X: x' v6 `' @3 h: w
  20.         return 0;. p  K8 i5 [. E
  21.     }
    . y) h& |( g3 E* t% r- v7 \: ]) {$ Q
  22. " ], [3 m1 C  [" D! L5 ^4 D
  23.     /* SPI Flash初始化 */
    ! y; A& m7 b+ p, P0 i
  24.     bsp_InitSPIBus();
    ! {) ~. x9 r7 V- ^& Q4 G  d
  25.     bsp_InitSFlash();( ~& Q! u! V! U/ n; l. p( c* r3 \6 G
  26. 4 }- n; N6 `8 S2 b. I, G+ r
  27.     return 1;" s- y# U+ t& v
  28. }
复制代码
4 q8 l2 h2 d$ M3 @$ B6 ]
  整个芯片擦除函数MassErase
$ k9 e7 l# B% ?8 a. C整个芯片的擦除实现如下:0 U2 O+ ^( u/ I- }+ n
1 O; t' r8 l6 H( r2 {# E
  1. /*
    0 V. o' X' ?# e* `' y& b- E2 d
  2. *********************************************************************************************************
    2 O0 F$ s) R1 `" j1 I7 E
  3. *    函 数 名: MassErase3 Q9 D8 V9 s3 Z" {
  4. *    功能说明: 整个芯片擦除2 b4 o% M6 X6 z) A
  5. *    形    参: 无2 W' j4 {$ p1 r# u# Y) C4 _
  6. *    返 回 值: 1 表示成功,0表示失败" A; c$ i2 B$ h: C  Z) z- a. I
  7. *********************************************************************************************************2 y2 O" j/ ?8 A1 T+ f
  8. */& ^6 ~: A4 v# d/ g- b" V7 ^
  9. int MassErase(void)
    1 I4 _6 Q  b$ g4 W$ J% h
  10. {" j- j+ Z; p+ L
  11.     sf_EraseChip();
    3 q2 P4 E8 D( a
  12. % V" V+ k+ [$ D0 S9 a, }8 q. X$ ]  C
  13.     return 1;   
    5 s0 l& H% @/ E; R' I
  14. }
复制代码

- f( E) k* ?0 e) C* F8 i  W  扇区擦除函数SectorErase
6 A6 z: ^" Z, s2 ]1 [% b; X! X
  1. /*
    6 W6 d6 @3 A# y4 S* a' s$ J
  2. *********************************************************************************************************. [2 Z% a9 [1 \  ^) Y2 @" }
  3. *    函 数 名: SectorErase
    ! i' F8 \$ }) s. }' j* y5 T
  4. *    功能说明: EraseStartAddress 擦除起始地址
    ( ]: @2 V- h) c" [: y
  5. *             EraseEndAddress   擦除结束地址
    - l/ q1 c) [% X" m9 ?7 J
  6. *    形    参: adr 擦除地址! {) b- |4 ]) ]4 M' b( A7 s: v% e
  7. *    返 回 值: 1 表示成功,0表示失败
    ( o+ ?) ]8 I% C' ?+ n/ B
  8. *********************************************************************************************************
    $ R( A2 F+ _7 o. Z! L6 z! q" |
  9. */
    $ y- q! v% a( R6 Q) S
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)4 w8 q& [7 K% r' x- j6 e- c
  11. {
    . D6 ?% Z$ }& ?& F0 h" B$ f# G
  12.     uint32_t BlockAddr;; z1 e0 {/ t, M6 L4 b6 t6 N- K  x) v
  13.   Y; O! B# @3 }7 @- d! f% [
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;$ j# ^, ]) S; S5 W
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;
    - Y% a" N5 c+ @  b* G8 t6 i
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    6 Z+ y, V) ~. ^- b* m
  17. % h* t' P1 X4 F) B: B; ?
  18.     while (EraseEndAddress >= EraseStartAddress)9 v* s5 B6 x- C6 D$ i9 L- }
  19.     {0 B& ~* Q. }/ _! z: B
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    * s" x+ f# S" E3 X

  21. " u. l% f$ d4 U
  22.         sf_EraseSector(BlockAddr);    ( v$ l: o/ R$ U9 Q
  23. : {% W( l& p/ l! \+ l! Q
  24.         EraseStartAddress += 0x1000;# `( X. }  L+ Q% z4 R
  25.     }
    $ J  Q, d" E. L6 p! a% W  ~
  26. $ x; c, u$ `6 ]! {* V: Z+ Q
  27.     return 1;
    ( h. n2 b; W) M5 h# ?$ x! x
  28. }
复制代码

1 i% U0 f# D" F1 I' W这里要注意两点:
# N- ?  \0 Q  Q
( U; r2 _" v) e+ _- y* R: [(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。5 n8 l+ ]. x3 C7 p

4 t, K% w( g! _& t(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。
# _4 W* a! T$ U$ X
5 j3 ]9 U0 I* _" ^( }( E  页编程函数Write
" c/ X. @  h. i- x- P' X页编程函数实现如下:% v6 R: y. h( y" B5 I

" e% @2 P" j! C4 z3 ^. X
  1. /*( N# w- _, _) z0 b# O& c
  2. *********************************************************************************************************) Q, v9 t% D9 C4 k" W4 D7 T
  3. *    函 数 名: Write1 q' X& [5 q; k% \8 m. c
  4. *    功能说明: 写数据到Device/ t, p6 w" m$ ~- u0 o, G$ [- p
  5. *    形    参: Address 写入地址
    1 L9 R" S: A# [2 I, D
  6. *             Size   写入大小,单位字节
    : e3 b0 X9 m: F- `0 B* ^, i/ g* n: R/ ]
  7. *             buffer 要写入的数据地址  u1 j, A) T: r# G' `9 c
  8. *    返 回 值: 1 表示成功,0表示失败% c$ m' }' V8 D4 L- J0 p& z6 |9 R& G9 A
  9. *********************************************************************************************************
    , O; H2 Q$ f* d5 @4 N
  10. */
    : I0 ^2 y4 s! c$ q
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    , `* {+ Z$ X, n/ p
  12. {  
    0 j& V3 ?0 \# b' g( {
  13.     Address -= SPI_FLASH_MEM_ADDR;
    8 C# o) o1 [1 V' w
  14. , ~" d2 ]6 q+ P7 f! ]) k5 P
  15.     sf_WriteBuffer(buffer, Address, Size);
    8 L( v- U/ D' o) l

  16. - g/ s# C. @. S3 b6 N
  17.     return 1;
    + D* L, p3 P: _' {
  18. }
复制代码
7 B5 N$ ?7 e, [% x# _4 j
这里注意两点:
% Y' y  E" z( T; N# h; {3 E3 g  w, E/ A6 c5 @7 k
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。
' Z: e, N2 j2 g! p
8 T6 }9 g1 M# N* B; u(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
& d, c3 w" B5 |: ]7 D6 u% ^% i2 E9 w3 |3 e
  读取函数3 O) ^4 ?! L- f, ~7 ]7 d
  1. /*
    9 t# @! R, y' [: F4 K6 o
  2. *********************************************************************************************************5 c5 q/ D$ i! ]8 x6 e' c
  3. *    函 数 名: Read
    & k5 Y6 x% W0 p, i
  4. *    功能说明: 从SPI Flash读取数据& o1 P2 I+ q- r" V7 W' b/ l
  5. *    形    参: Address 读取地址
    0 j& n( r4 K! G- x+ E
  6. *             Size   读取大小,单位字节- {) D/ P  k; N" i+ y
  7. *             buffer 读取存放的数据缓冲$ X# p9 C' \" Z% ?& @9 w- i; V% {( j, g
  8. *    返 回 值: 1 表示成功,0表示失败' ]  z. M1 D# ?6 ~
  9. *********************************************************************************************************
      ^0 V$ C$ S: V4 j* d
  10. */- A1 I2 @' ~& n9 U
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer). I0 P, A* \' }9 n  U7 G
  12. { ) f3 T( }: X3 t( }1 i
  13.     Address -= SPI_FLASH_MEM_ADDR;
    ! I1 c" l) n6 w
  14.     sf_ReadBuffer(Buffer, Address, Size);/ G5 ^2 W7 h; y: O* F% y3 @* c
  15. + D: j: `, g- J" u& i- }0 O, k; }
  16.     return 1;
    % t$ l, d8 P' @) J& t" P' p
  17. }
复制代码

( T9 f) v3 \4 @, Z& W  x6 D3 J  读取和校验函数+ s1 [% _6 y4 \9 T4 U1 l
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
" A6 q: v0 V7 i2 O# e7 u2 V% E: b
85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
" g4 d- K+ U- `0 s$ J最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
& o/ T' }: D  m; j; W1 U; ?# ?2 a. ?
  1. /*5 K. r+ v5 B) B% P' e9 k
  2. *********************************************************************************************************
    4 t1 N; U1 N, W6 \; H' v: X
  3. *                                时钟,引脚,DMA,中断等宏定义
    & J; l- h. C' x0 q+ a- X. \( t
  4. *********************************************************************************************************
      U2 k  d- b: y
  5. */
    ' h3 h3 l" A( H4 E/ K
  6. #define SPIx                            SPI1
    " E/ W" k6 p* z& q1 Y  C/ d& ^; v8 a2 t
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    4 b( R2 U4 D& x
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()# n! S9 g0 @) x; f, u/ N! @
  9.   o; v7 R9 N  k/ P5 g  T
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()0 T2 M9 P# F: j% q8 e
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()' h% {, \& a* Z& b* v
  12. 1 G  n/ y  L$ I
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    ' p: ~; E$ ]0 C  @
  14. #define SPIx_SCK_GPIO                    GPIOB! v( G% C* C8 J% i/ k
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    4 o0 \. K" Q" {' W) v, A
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1) u0 a9 E) X1 ~8 D

  17. ; t  l+ M+ T* v8 z8 c% H; T
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    + ?3 x/ M6 p" \; G, z; N$ M
  19. #define SPIx_MISO_GPIO                    GPIOB
    0 m/ b1 |2 }0 W& B
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4
    , N7 F, R4 g, V9 [  `
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    , b2 v4 B# D* T; i

  22. / n6 Z2 u  h- u2 K4 _0 g$ B6 B
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()$ ]6 y, X/ c' {+ n0 n, V
  24. #define SPIx_MOSI_GPIO                    GPIOB/ |1 ^4 Y8 `( f8 H6 I5 q
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    ; ?% a& Z0 p- l( u7 @
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码

  s4 ?/ w0 J5 x! _) O* |硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:% k& z8 q) w' C& j, a1 I! g

" O" F: I3 y. f, U3 X# h0 O主要是下面这几个:
& s! W" }% D7 \) o9 q0 p6 h7 b) R
  1. /* 串行Flash的片选GPIO端口, PD13  */9 \8 H% S5 l5 N& T
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()
    6 ^! g1 }# n" @- A4 J6 V/ |
  3. #define SF_CS_GPIO                    GPIOD
    9 [0 j) j+ S5 z3 G# j
  4. #define SF_CS_PIN                    GPIO_PIN_13& t% a, a% g3 H/ g8 N. R6 O
  5. - K2 u4 k! y8 f1 E% z$ j
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    0 W5 s5 q% |! D" g1 G
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
    ! A! q4 R) }) J/ M' P; M8 F8 c
  8. + |  P$ d% U' D' M$ W
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */! \# z/ b" L" s8 t% T; A4 G
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */" v/ _8 N( Y3 L" H  o8 [* s# B# H
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    8 k: c% M3 Y+ ^- T# _
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */, J4 f* B, s+ ^0 x6 `6 ~7 J
  13. #define CMD_WREN      0x06        /* 写使能命令 */; y3 O, h7 T7 o3 b: a" ]) ]
  14. #define CMD_READ      0x03      /* 读数据区命令 */
    * j6 n- C7 Y7 q3 P
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */1 g4 c, Y* q1 o2 K: q: j
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */
    ; J, j, F* ]1 Z" `- s5 O
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */2 P: W+ b( `' q5 B
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */; T/ o& s' d' X) @8 F7 |
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */& r: T! B) ~, l. ^) p+ n
  20. ; X% ^. M- v2 Z5 T* @/ l; X. Z
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码
* D) z& i; A# o9 c$ q- W/ g1 K/ x
85.6 QSPI Flash的STM32CubeProg下载算法使用方法
. O7 k7 ?, Z/ [# @/ O/ t- i; r编译本章教程配套的例子,生成的算法文件位于此路径下:/ n- Z' |( w3 }6 O3 o9 a
& v( O- ~3 c3 R5 m; O
5ec6d402a7646232a55f4834cd00ac0c.png

7 n2 h, c: d1 C8 A2 ?; {# T; m2 w# W  q  T4 J/ G( O- y
85.6.1 下载算法存放位置  S+ a( s7 t' I- T
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:. R# [0 Z' J# \

- X8 [5 z  g! S2 L, a' L\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
4 X' z" v0 P, `/ ?% }2 `7 J# ~$ Z- F) H% ^  f  W
2609073778af5845a869911ab36624f4.png

+ v0 r: N% c. G$ a, G9 F; T' [
  z, M/ S5 l1 }* L8 H85.6.2 STM32CubeProg下载配置
& E' ~( h7 V( f: e我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。( B4 [# l9 [2 Z. y7 ~

$ f9 |- m9 K4 x$ {& ~
d4e256633eafdbf4bcd8cce6e3b5909f.png

" x8 j6 \& G! S1 ?5 k( c  k- n5 y5 B4 ~! L2 C
点击Connect后效果如下:" L: r. G3 b0 s1 k

3 M4 d3 w$ |- _* r

/ j  W+ i" t& [7 H! V. ?
% [" R" {6 c* n0 P在这里选择我们制作的下载算法:* j! `( R3 y. B& e" L9 r
# m* x+ I1 F5 m0 G  ?  ?
d4fa28f64be2de98e04f3188eb2fe09e.png

2 O8 z2 z3 n# I/ j: h$ {: P5 N# [) R9 r2 m. R; B: S
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming% {+ m( T2 G7 |! V3 W! c

0 n4 P; S- o" |( V( U1 {# y& T
842518a7cc1dc569598d372132b2ee58.png
9 K" u0 R1 J. P2 _
- k; Y, [; y6 Y6 m+ f
下载完成后的效果如下:
9 C1 E6 Y& G8 |* p0 R3 y/ y
3 @1 c; N1 R1 b
68a92f267829ca77f125db982d6c0479.png
# U# I7 O4 T4 a5 v! p
1 r) z/ u3 k5 H8 S! G
85.6.3 验证算法文件是否可以正常使用8 m# j6 O- }; D: M$ K7 W% b
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
2 F. m9 n- ]9 l) c! b( o8 C% Q& T/ g; h9 l5 v9 a# Y
566d2fba421c710da70e5656c6934466.png

1 z; j' [: J' g, {5 M+ V/ C3 b6 u5 T. Y  r2 Y
85.7 实验例程说明

8 T: r5 d/ N* |" ~4 y. k' j* A本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。7 X/ s8 Z( R" `/ h: [! ~- q
, B2 n7 J" @7 E! u( o5 d' |' Y
编译本章教程配套的例子,生成的算法文件位于此路径下:
6 u8 L5 k/ t7 G
4 N! x  d$ J# i! u
6108d1ff7e349f2aa268ee31804c57ca.png
( V. X6 U6 e1 x7 ], E
* ~4 Y! l9 n& e0 C% X, `8 J) A
85.8 总结
0 \9 }+ \" s  q6 i本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。. d5 k  [% s' ]! ~5 E! _

/ b- b4 [# h% C& o3 Q
& ?5 T# |$ \- S) _
, R0 M- B' H7 I. [) }. t
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
收藏 评论0 发布时间:2021-12-19 15:00

举报

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