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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:35
85.1 初学者重要提示; N0 c3 Q5 {: }0 a9 c
  QSPI Flash的相关知识点可以看第78章和79章。
: h5 V9 ^" _$ K9 t  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。6 i1 W% Q# u3 K' y) l, q. m
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样! @8 U! R" L6 L+ d* ?( h
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。$ j  Q/ P/ k- r7 F8 k4 f+ o& o
85.2 STM32CubeProg简介
/ i6 f2 C% y, F  H  B& ?- A. QSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。7 N% M1 q7 \  b: V
6 y$ O7 a" [9 }/ G) ?. A( F% k
d80578d217eb2a83520c6f3d797218f4.png

9 K/ s; Q7 u% s! e  D4 \2 |! L8 ^$ f) R4 j* h9 X9 e
85.3 STM32CubeProg下载算法基础知识( l3 M5 V+ S; x5 I( p1 b. ^
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。" R/ ^& ^9 D) ^

  U* e5 Y% ]% ]7 l/ r8 Q85.3.1 程序能够通过下载算法下载到芯片的核心思想+ f. I- B' L6 Q; z1 V" {5 e
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。, h- R  g6 z3 P/ T5 C; V

6 W# W/ _8 R" V4 D+ q85.3.2 算法程序中擦除操作执行流程; j. x) t+ R" ^$ y" G" F9 @
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
: t; [& A! p' p$ d( O" a3 A, Q0 D7 D+ [4 I8 n1 n
擦除操作大致流程:7 m3 u& G1 i: h) T

# Z9 A! r/ d5 L3 Q+ o' r
d75670256363bb50203bed0bbe4f9fd2.png

* O$ k) F. @4 _4 ]
/ r3 X. }  N% O  加载算法到芯片RAM。  y, j3 _" ?" b/ |( z
  执行初始化函数Init。
/ Y, |- U. A1 B% q5 l  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。) c/ \* F7 ~: L* h. b% U7 P9 v. _0 {* ^/ y
  执行Uinit函数。
/ z. f) r# }: }  操作完毕。
; y3 L% b/ H8 |6 W/ U$ U4 N$ K7 P85.3.3 算法程序中编程操作执行流程
  f/ M. Y9 s! U6 C0 M2 o  W4 z8 w注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
0 T9 F/ }5 j5 H( P4 k5 Z+ T4 h9 h, {- E8 I& Z; ?
编程操作大致流程:/ \+ H; G2 U' Q& Q+ U5 s
2 O. I( a  n. ]2 k( X
4fa96e9e33d700081f2cdbd312afb31e.png

+ I; u- \4 _) ~% i
0 n  m& k5 m: N4 s  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。0 j$ Z+ ^' |% K9 O/ M7 Y  N- {; {
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
, ?: H6 m1 u* Y; z7 h. R2 v  加载算法到RAM。
  R6 T1 W3 s$ Y, \$ [- [, Y  执行Init函数。
! n- }! A+ o, G- S  加载用户到RAM缓冲。# x# j! G7 V' W: w$ E4 M! \
  执行Program Page页编程函数。
! ~- n% }2 N" h' {* z( @+ U  执行Uninit函数。
* _/ s' w# q0 z- i  操作完毕。
9 n" B$ x1 Y! z* u% j/ D85.3.4 算法程序中校验操作执行流程! t  j! B2 S% e
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。, q9 i: q8 ~, ]; n0 j% ]/ ]+ }3 a

3 T, t/ j+ v; U* ]2 V校验操作大致流程:
& K/ K+ X9 A6 D" x/ `( y7 [/ h! |& }9 C) b- w* e
7deeae4b650bd82c457492af14fc26b7.png
& U5 g2 ]& J. D! y, P$ |) d5 i) b
, y( s6 P, N7 a& ^) v$ ]" U- M. F
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。( s8 F6 m- E+ J* v
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:  n  S' M. u0 M7 B. b
加载算法到RAM。( j3 }; l0 i) u  U* Y: R1 R1 Q& y7 o+ j
执行Init函数。2 I0 V4 R: |; y0 X  c  H  E
查看校验算法是否存在
' x. g$ k3 F0 t( A0 j3 M8 Y# Z  如果有,加载应用程序到RAM并执行校验。
$ z# n1 n5 j/ R: t& W( v  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。* {. I0 C4 ?( u# H4 K% n
  执行Uninit函数。% D$ B# j( z) \& d
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
0 Q# R0 d( L+ T$ A0 d  执行RecoverySupportStop,回复支持停止。
# L9 R& n2 W$ X2 p: V- P2 p" F* \  执行DebugCoreStop,调试内核停止。
- P( \3 P) A) j! t1 O5 Q% ^  运行应用:: U2 C* [; G$ z4 W* T, r& y
  执行失败1 W, v& C# s9 l% r% S+ H
  执行成功,再执行硬件复位。5 n0 K5 F4 O$ P. ?! G/ n9 k
  操作完毕,停止调试端口。$ }3 ~* l$ A: W. z
85.4 创建STM32CubeProg下载算法通用流程
  O: p$ H% m. |9 t1 }0 M下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。7 V: |$ ]7 v5 P
1 H/ P5 N* E2 O. M8 ^3 W5 s0 i$ m
85.4.1 第1步,使用STM32CubeProg提供好的程序模板: H  m) y2 L" E2 T! n# E, s+ ?
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader- B9 z2 W) Y: f: m
# d9 p. s+ D/ \. f+ T
97f385e3b62a055bac9b5c0214b877e5.png

. |) c3 h0 t$ T' v$ ~5 T! q& F4 g* e
以M25P64为例(注,其余步骤就以这个为例子进行说明):# m) E8 k% l) M# Q

) N8 [" R+ w4 e$ N
43b298f996937fe35e89697fc6479cb9.png

$ [9 s+ h* ]. K8 l* K6 E! p
8 A; X: |/ D8 L% C85.4.2 第2步,修改工程名
6 l0 y! o' r0 V! @) SSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
$ k( E+ p% }: E9 }) G
+ H4 Z" O5 N; P% n, j3 I! ^& F8 I. E85.4.3 第3步,修改使用的器件
6 ^( O! ^. ], G7 u! C- ~在MDK的Option选项里面设置使用的器件。( M' r8 s  c+ Z5 L  E) r# G

. t- k! }0 a4 i  p9 Y% L& q# |& O
36026b9a6d60249610d0246eb725b246.png
# K1 c) q6 W4 I/ V

- ~: p( K. t  }) o$ m85.4.4 第4步,修改输出算法文件的名字
! i4 U' A" k) v2 E这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
3 [2 u& M0 ^& W. e8 f2 Y+ L3 U( G9 D+ Y- O8 i2 ?3 F- `5 f
52c72b3993f28a366a22eac56f8fd043.png
9 W! K5 w; D* P2 n" \

4 C1 F/ i3 d& L2 V. |注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
4 x' k( k; q) y: B
; m# y: m$ X& c' X- k
7b48153027a44de33926c2da008716a0.png
" I/ y6 j& v4 i' `
; ?7 q$ t* `& g; m+ M+ [1 I2 m
85.4.5 第5步,修改使用的库文件和头文件路径1 k/ s: u. O( {. g% {
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
* L8 u4 w+ b" s# f3 @0 L4 z' [. M$ n* @/ u
85.4.6 第5步,修改编程算法文件Loader_Src.c
8 ], H6 O9 I" [  r! n' P模板工程里面提供的是M25P64,部分代码如下:$ f# S7 J& J0 _4 {. Q
" L6 D8 Z) @1 P+ j
  1. /**. l0 h4 C' M6 ]7 `' R
  2.   * Description :
    " z+ l3 e( V, w# g8 w% |2 I5 u, a3 m
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    . l5 O: A% ]  B2 q$ Z
  4.   * device and initilize the FSMC with the chosen configuration
    + x5 Y) r# R  ^5 i8 J  ?
  5.   * Inputs    :) K( Q* w+ E0 r* x
  6.   *      None
    ! @+ \& \  B, g: \: o, @2 q
  7.   * outputs   :. @5 w1 p( ]% I
  8.   *      R0             : "1"             : Operation succeeded
    0 {" d' ~$ B/ _7 j5 r& y5 d
  9.   *               "0"             : Operation failure  V; |( d  T5 \( w/ m" ?( Z, d
  10.   * Note: Mandatory for all types of device 1 y- P1 ?. c( S* p( B! M
  11.   */
    # h0 i* A* L: q7 i2 b0 h4 D" J
  12. int Init (void)
    - ~! [6 W7 {! k3 Z: L
  13. {  
      o' T  C9 `' Z4 G( R% `' J
  14.   SystemInit();4 t2 r* m/ p; A5 L1 ?6 r# p
  15.   sFLASH_Init();( \4 \( g4 |/ ]6 z; ?) Z0 H$ V' ], J
  16.   return 1;
    * K& H* g/ S: `8 r( u9 w0 E
  17. }( F, x* p: s$ x* H2 [- d
  18. ; b7 e( ^9 d1 o4 d6 q+ x- J
  19. , ]* f9 x( T6 U
  20. /**
    / b( j2 Y9 ~) O  {% ^. K" ]7 _
  21.   * Description :3 C6 W" f/ p) ]: [  B& c6 p7 N
  22.   * Read data from the device . K+ G" L0 W. @% B
  23.   * Inputs    :; g1 R; w2 P& G3 ~2 K
  24.   *      Address       : Write location) H' D* `; a! G0 r) o3 N
  25.   *      Size          : Length in bytes  
    6 b  Q+ U: u6 Q+ v  R9 P3 x
  26.   *      buffer        : Address where to get the data to write
    8 X) b. B5 r& a1 \* f
  27.   * outputs   :
    2 M8 \! x. ^; [! y$ A+ M  w5 }# v/ Z
  28.   *      R0             : "1"             : Operation succeeded
    9 B; C; Y4 E# J" p5 A+ h
  29.   *               "0"             : Operation failure6 O4 M' V+ F2 g, E1 g5 W1 Q6 k
  30.   * Note: Mandatory for all types except SRAM and PSRAM      T, c2 h7 W3 Q) Y
  31.   */
    : g! M! b) U" ]3 R" q
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)9 ?6 }: _. Y# U* U; C* t) A
  33. {
      X' J2 ]( `. X* I6 e9 R
  34.   sFLASH_ReadBuffer(buffer, Address, Size);3 T. U* ^  S  _1 o
  35.   return 1;9 f" v" {% M- [3 q8 M  H
  36. } & k; ^0 `  {! \4 s) l/ _% @6 E- o
  37. 9 {+ n1 `. X! I# u% K8 G
  38. # F2 }1 [4 B# @4 X  C
  39. /**
    . R: Z( |8 n$ T1 d  U
  40.   * Description :- ^% E6 r- G- Q. V, B9 o) k0 x, D
  41.   * Write data from the device
    - I! P7 C; n7 _. h* c. x
  42.   * Inputs    :
    * U+ o3 R1 Z6 u3 Q& {: k- \
  43.   *      Address       : Write location$ j/ V9 t) V/ k' n2 r, t7 ?: w. ?
  44.   *      Size          : Length in bytes  
    2 }/ Y6 {2 }: z5 A0 \, d" H* |% M
  45.   *      buffer        : Address where to get the data to write
      m, [  D6 D9 r6 M
  46.   * outputs   :
    , S" z/ u% L+ k0 r
  47.   *      R0           : "1"             : Operation succeeded
    & ]% ^7 l' o2 f* n& \5 I/ o
  48.   *                     "0"             : Operation failure
    " D% Y3 K- i$ Z6 w& ^
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    % O* U3 k) p- x. S
  50.   */
    # A0 E3 l* k% n1 a/ E2 N2 p; `
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    ; n6 Z' V; {/ O7 L) b
  52. {) j" V: w; h( V) L9 j
  53.   sFLASH_WriteBuffer(buffer, Address, Size);' r: I/ U, ]/ I5 v1 H
  54.   return 1;
    1 S9 K9 f) N- Y) C  _" Z# {
  55. } + S- r# b5 D. C5 M8 f! ~4 ~* S
  56. 0 ~1 F. o- h- z4 G% w1 w5 N

  57. / Q0 Q) L# s# R4 \, i4 G3 l
  58. /**/ j7 ~" Q; ]4 w* q$ h8 V- K4 I3 N$ v
  59.   * Description :
    , x7 g1 h4 t2 {0 G
  60.   * Erase a full sector in the device& z; ]# X8 D3 m" H; z7 M( x- @- l
  61.   * Inputs    :6 C7 b, F" b% x- r+ T
  62.   *     None
    1 s/ ]! t, b. h9 q$ Z  I
  63.   * outputs   :3 j3 G; A2 [1 K$ L* h! b8 H
  64.   *     R0             : "1" : Operation succeeded
    8 s: m8 G7 w5 u1 h6 M$ z) U, o
  65.   *              "0" : Operation failure! G% `8 a  `) D1 j2 ~
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    + [5 V4 E: v# R9 D7 C
  67.   */
    , l. |" d: G% y! K5 k1 r
  68. int MassErase (void)# b4 g0 `  ?: l9 I/ i; k
  69. {  . y2 e' }: _) i5 I3 P
  70.   sFLASH_EraseBulk();) ]* O3 b# B9 |
  71.   return 1;    ! R5 \( s1 \7 B* c
  72. }
复制代码
4 w  \. H* D! e( h
85.4.7 第6步,修改配置文件Dev_Inf.c0 g& c+ P& A$ d* c% u
模板工程里面提供简单的配置说明:; j' m& |) U0 U6 c2 x, ^
+ g3 o. J/ O! m# \/ G" s3 P! f
  1. #if defined (__ICCARM__)# a, n0 ^0 R; O% `6 D6 o8 g
  2. __root struct StorageInfo const StorageInfo  =  {
    & U' \/ h; Z3 l! ^
  3. #else6 H0 }( b7 ?6 K6 ]
  4. struct StorageInfo const StorageInfo  =  {
    4 V  T+ v& T/ g- ^) _9 f
  5. #endif
    2 n  ^# H* K' w) a, w, l+ K
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    7 X: t# q  r3 Z' i
  7.    SPI_FLASH,                       // Device Type
    8 m! ~; {8 x/ f  |, e* `' \
  8.    0x00000000,                     // Device Start Address/ C2 t7 a7 G  |9 k6 K
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    3 W; u5 v! Y) Z8 G0 ~9 c" \
  10.    0x00000100,                      // Programming Page Size 16Bytes
    % q4 ~1 \* Z) i% i: S9 i7 G
  11.    0xFF,                            // Initial Content of Erased Memory# b9 V! ^4 J) ?  t0 J9 A; I1 @
  12. // Specify Size and Address of Sectors (view example below)
    3 z( H( q" k$ a1 U. L) O
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    5 u  b& ^. a! x& l
  14.    0x00000000, 0x00000000,
    - f3 U; T. K  _! a
  15. };
复制代码
% r) a9 l$ F2 d+ A0 J" L
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
6 y# r# }# @4 O0 \* A7 H' \0 A4 E- e6 Z
85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
3 l7 p+ u) Y% J  G# yC和汇编的配置都勾选上:; l3 N  D$ A3 @$ v  b7 c
0 Z+ ^6 o% Y) l; l
afddd6e839292aa2b94b43e79544d630.png

/ t+ w! [3 C7 C
1 `/ z! ~) B! j+ z! |. D( a汇编:; O1 f  ^  o8 |1 @! i% l% u( I

  N, l$ X; A5 w: I; ~
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
# Z/ ]# s2 |' ~/ g: T# O) ~6 I

" q9 {" A; \' k1 S' x5 I% K% v2 l如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:* Z; U6 M9 _( {4 I# \+ G

3 u* t7 x7 n' M6 `6 ~(1)加载以响应运行事件。
: A. b- j) ?3 d- `; {
6 F: l$ `% S  @: v% }(2)在不同情况下使用其他例程的不同组合加载到内存中。
6 h/ i" T) ^  p: \5 N( R' K7 P3 E. ?4 y9 l# R6 Y6 w" A6 m, Q; T% E8 p
(3)在执行期间映射到不同的地址。
0 _5 B) B" y; N) c
: n* j5 o' S) C& j. y使用Read-Write position independence同理,表示的可读可写数据段。
" f. ^- e" C+ v/ B. }; Z: H2 Q4 ~4 `# T/ n: u: ~
85.4.9 第8步,将程序可执行文件axf修改为stldr格式! `1 |% D% _  O% {+ T1 C
通过下面的命令就可以将生成的axf可执行文件修改为stldr。
, G( r9 I1 ^! t1 {
# y- K6 D$ x( ?) X9 O  P5 V
c683673d2481d8dce75b6fa01d624600.png
6 R# L. i: q- g4 \# k7 u1 h

, {& `1 m' u) [* g2 E0 {85.4.10   第9步,分散加载设置( z0 C) ~$ n/ r. r8 q, G- _/ a- v
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
! E% [4 G+ l2 f0 [: [: C$ L: i8 K* D( q
b96835a7506516319002e6b2e8db9371.png # g7 k3 p  I- }  l1 _3 N: h& w

4 E2 r) f% O- P! w: }, R分散加载文件中的内容如下:
  b4 N$ J) Y! ~7 o( J: ]! X+ S% G) P, V
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions, s; ]- G- g2 ]4 V( L- ]( a1 Y) T
  2. {/ `7 ^$ z# d# j! _& z6 w6 k- `
  3.   PrgCode +0           ; Code
    7 t3 O4 \. M. P# _  O6 ]+ l
  4.   {4 E9 o4 b0 o( X7 v: x
  5.     * (+RO); e8 L) t8 D  `: r" E# [
  6.   }
    2 b1 y) l  W& ^/ a, r& k6 z
  7.   PrgData +0           ; Data
    2 z  T2 j5 s1 M+ C- b0 B
  8.   {
    4 w( \" W* |! l; y% p$ K; q
  9.     * (+RW,+ZI): o$ c5 w: Z. @
  10.   }3 w8 y3 L$ Q8 W1 f/ e' t2 `
  11. }" E" n4 }5 B- t
  12. 1 G! U. t' X3 b( U0 N( D
  13. DEVICE_INFO +0               ; Device Info
    5 `. L5 |; X; f, Q* p3 \7 G
  14. {
    8 V  S; x, p% d0 X3 f7 x  H+ b
  15.   DevInfo +0           ; Info structure1 P8 I8 a( w% s# T9 Y
  16.   {- r9 U6 |% H( S6 c
  17.     dev_inf.o0 P* p( i$ E8 v8 J1 `
  18.   }# J8 U7 d% i! E) u* Y
  19. }
复制代码

! a# Y9 w: J" ^1 |. O3 i. H$ n这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
8 H7 T( R" }# v$ P' j# ^
8 W6 l) v$ B7 @--diag_suppress L6305用于屏蔽L6503类型警告信息。# `4 L0 M7 a$ x' H/ n6 m+ Q
% D7 Q+ k8 k: w
特别注意,设置了分散加载后,此处的配置就不再起作用了:
' |  W. M* o/ B" l  _1 R
& Y# |8 P$ X* e, G2 N- A
641395c374a3af02f1a92d3ff9ee4199.png
$ e4 f# s) K# I5 U, l; @

& [9 u2 r  \9 C$ V# [% D% }! F8 M85.5 SPI Flash的STM32CubeProg下载算法制作* `( e* z8 }, K. K) P. ]" E9 ~0 z
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
1 _- U( o/ t, P
) r: ^. w$ P; z1 S& L85.5.1 第1步,制作前重要提示, i* o, v; W% e" K& x
这两点非常重要:
$ ?6 t3 v7 W+ d7 p, f# h
4 L: x& S6 ?3 Y2 y5 E4 {4 u  z  程序里面不要开启任何中断,全部查询方式。
8 E6 l- m( t; E9 P7 i6 f  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。6 X7 L- z4 E4 p3 t. J8 m
85.5.2 第2步,准备一个工程模板2 l$ n" N, J" N* w# p1 t" G2 a; y) A; h
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。. u, }, m7 P( `* X4 W! T) N5 y
. R* Y% Y" H7 }- Y' A
80ad5e3abdd6a993c8621230401a1313.png

  f1 ^# _8 p, O6 {" o2 R( \0 _4 }* q1 V4 {* X" x. K  R
85.5.3 第3步,修改HAL库
/ ?) v  j6 A" D6 V* Y% E这一步比较重要,主要修改了以下三个文件:
5 }& \7 c8 Z2 _
0 B1 _0 H: ]7 M# C5 _
33bf89f25487547d4008f43b7cf10e03.png
& L& I2 e4 E) c

% b0 b/ H. A/ o; A* F2 I/ c主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
; w& x6 @0 L: ^5 \! S% H
, Q& k) w& I9 \( \4 O7 \) x85.5.4 第4步,时钟初始化$ [3 L7 W3 a- O9 u, t0 i% k2 F
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:& b# A9 @. Q1 {! o# E6 e( V6 _
' _% n. h) {8 ~( g& W
  1. /*; }- N# M& K8 n& W' e$ i6 {
  2. *********************************************************************************************************1 S5 @/ ^/ F5 J; c0 T# O& Q
  3. *    函 数 名: HAL_InitTick
    3 t  _0 g! ]. @
  4. *    功能说明: 重定向,不使用: s% K5 Y  f6 z  E4 y9 ~
  5. *    形    参: TickPriority
    ' r3 m# D) X  ]; ?
  6. *    返 回 值: 无
      G. U( _+ ]8 O6 X& J  g2 }8 t
  7. *********************************************************************************************************  }4 v1 R7 i; r- |6 e0 F
  8. */
    ; f/ I! }1 U1 ]- Y
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)9 i; f4 g1 W4 d. V  v- X6 u
  10. {# o7 \, [$ ^* a  [4 U% C& }
  11.     return HAL_OK;
    * ]' _% B0 v; J6 P+ H5 H
  12. }
复制代码
+ w% C* {; L. `. k3 C1 \$ T
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
8 p  A- w: u1 p! H# q9 Z2 [* Y# W$ g$ n
  1. #if !defined  (HSE_VALUE) 7 ~; }4 R/ q9 y  c; B
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    # `2 P% s. l0 q* Y0 m: `
  3. #endif /* HSE_VALUE */
复制代码

3 E1 T* |# P) K9 l$ i% p& C! @' i最后修改PLL:
& ]+ H' H2 j5 e" }* J, g) |, |; _1 m& c, M" z& s' n: p% t  _
  1. /*$ I# i& L/ M7 _/ W
  2. *********************************************************************************************************
    + d3 o) O5 ^7 J: z6 R
  3. *    函 数 名: SystemClock_Config! u8 G( j( R) u; J
  4. *    功能说明: 初始化系统时钟6 B4 c9 l: i! ?* M
  5. *                System Clock source            = PLL (HSE)& p% r5 c/ N4 o& w* a  z
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    # m, Y: Z* K" a; x
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    9 ]" m; I: k: ^7 a
  8. *                AHB Prescaler                  = 2+ G8 X, N: @# ?3 Q
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    3 }# L3 \) @' p5 [) Q, \& ~; s" W
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz), {5 B$ D; D& e; f, t
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)1 m1 r' I" K# ?8 F* C) J6 s
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    1 V- v3 {' D6 O3 E
  13. *                HSE Frequency(Hz)              = 250000005 ]6 d' Y% z5 _' _5 ?+ o- s  z! J
  14. *               PLL_M                          = 5
    % c1 t' k5 V; h1 O
  15. *                PLL_N                          = 160
    ) ^. U9 W) E- D
  16. *                PLL_P                          = 28 }4 K  h) Y" A  d) t
  17. *                PLL_Q                          = 44 s, a* \, ^. h3 r  _
  18. *                PLL_R                          = 2" o) a$ {% g7 f* R" E) |( {
  19. *                VDD(V)                         = 3.3
    & P6 f( U" H3 u% [0 \
  20. *                Flash Latency(WS)              = 4( @3 A/ J$ O9 |& _2 |& o; E: U
  21. *    形    参: 无
    8 y' q2 `" u6 ~
  22. *    返 回 值: 1 表示失败,0 表示成功; x1 y2 E. B' H+ z; m
  23. *********************************************************************************************************+ W1 C$ i, ]; V# n
  24. */- L- R% [7 @4 j5 K7 k+ }0 z
  25. int SystemClock_Config(void)0 N$ j& X/ g( S7 H: B- S( ]$ B
  26. {, M, P# c7 r" K, b
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};" p" |8 U! `  ]! v# a
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    3 J. X) b% x# {" u
  29.     HAL_StatusTypeDef ret = HAL_OK;
    7 C" x4 A& l9 U) F/ L% v

  30. 2 U3 e7 }# x# a% A5 D3 e2 V
  31.     /* 锁住SCU(Supply configuration update) */5 D* x% r1 L5 X1 C
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);9 A$ P& q6 z/ v2 k  o) N

  33. 0 x; x8 T8 z4 s' J. S# B1 D- ?$ v
  34.     /*
    5 _& H0 T6 n: N" t5 T5 A
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,. s& Z. V9 ]2 |( w. a5 x
  36.          详情看参考手册的Table 12的表格。( n2 p7 f4 E, f- ]3 c7 v: Y
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。* T) I7 u' V; r
  38.     */+ p. Q: l/ `2 \' {' n2 _$ p+ c( f% n
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);$ S. {( U$ {  b0 q! E. J  r

  40. : l8 m) `7 Z# t- c3 m6 J: b
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}) |$ R- ~9 ]5 t. ~4 C/ m0 j

  42. 8 E) a5 q; o/ j! B; Z
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */8 d% C; R5 Z* Z9 Q0 F
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    9 x0 G" p/ d8 w# W4 f8 N0 l  m
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;" }; Y# m8 }, x1 @# g) Z
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    - v' s) ]: U- p+ j8 `; d
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;5 _4 v- P" G) P7 x- I
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    ) N3 s8 O5 O5 X
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    ' [# z; j& B& e0 V9 X3 l6 `3 Y& t. i

  50. - I9 ^# x0 \0 d5 {$ g3 n6 Z
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    2 w* ]+ f" D1 a" u  h
  52.     RCC_OscInitStruct.PLL.PLLN = 160;" F4 M1 [7 N! T
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    - c  r! ^5 c) J) k: F
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    8 q# C8 g. n* E0 E
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    , i2 S7 B5 x! Z

  56. + |/ x2 @& e$ v/ h3 Z2 e1 m8 X* I# m
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    ; o: k' \* R7 |. ^: e& a2 s
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    " O: [; B# G9 k
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    + d8 ~, ?  {/ Q& O7 L- g6 x5 ^  ?
  60.     if(ret != HAL_OK)
    : _9 w* r9 m6 K, x$ u
  61.     {
    0 }3 I3 h5 u' S; s+ z" U. {
  62.         return 1;        % N. F  K( G0 G' k: S2 t4 ]
  63.     }
    ; Y* {% G& N$ H

  64. 4 i9 h& L" ?2 {
  65.     /*
    - ]5 `  t& `6 }( B  V9 ~( r$ D
  66.        选择PLL的输出作为系统时钟" }- _. G7 x  l0 H! ?1 `# M' e( a
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    + E5 m% @( z4 x1 l  k( e
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    8 K1 c. D2 i/ w; e' @
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    , a& K& f" L6 I- ^* r- V- O* I; e
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线/ |. p$ Q# s5 h# M
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线, a! c6 A" Y+ m# T( A& i. E
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     & F  m7 x, N2 M5 T4 O& _% V) c1 M
  73.     */! P3 O1 i, u- d; E5 G
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    3 {, w/ q) F: V( Z, I9 Y4 v
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);* u- Q, S, f" w% ^: N  ^7 n: j% b

  76. . t7 Y& H" ^: \/ b; l% u/ S, j
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    / E) u$ W# z6 Q" Q6 r. T$ Q
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;5 y7 ?: `# ^, e
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    1 @$ V/ b3 Q  L$ H
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    8 L1 R- L+ ^2 g$ U* a2 L
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; " m+ b3 q$ x" p' z1 |
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    " u2 Z3 ?: D3 y) W7 [' \/ w: j
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; / H2 k2 w; x' k; @% `7 J. x
  84. " Z' X  c6 ~! y2 y) h
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */! i+ P8 u* ^" b; R" E3 t* I( T
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    5 Y: B6 ~. l. J) P$ g9 C3 x7 t7 S
  87.     if(ret != HAL_OK). Q6 F4 e( q; K( x- L
  88.     {
    . P0 c/ ^" n- W( G  A# \  z  l
  89.         return 1;
    8 q$ S$ a  p6 S" p! P. C
  90.     }+ N/ d. @) s% n( _
  91. # P$ n9 ]+ q4 X/ S' A) t
  92.     /*
    2 d; w  r2 H* O- V& Z9 T
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    # ]# Z1 q* a) v! G6 l8 v
  94.       (1)使能CSI clock7 _% _( K$ W" D$ m* Y2 P& h5 f/ s. ?
  95.       (2)使能SYSCFG clock. F' J9 ?4 Q8 [6 F6 N( A
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0% g! ]# R1 ^" l! Y$ q
  97.     */
    , @( k) h; s  i" b% e* q
  98.     __HAL_RCC_CSI_ENABLE() ;1 D8 Q, T) i- }  c# L' l

  99. 4 z8 ~6 t# |! m7 x2 R5 D2 D) \
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    , w* p+ n) m6 k. g% q" y$ U8 w
  101. 4 R& D/ h7 _* a9 ?) {
  102.     HAL_EnableCompensationCell();
    # {3 T! M) ?0 q, U7 p

  103. 2 V; A. W6 H0 r9 Q0 x! }4 H/ r
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();  L2 i% S! ~3 V' E: R
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();- ~% f/ d' N' E8 M7 E
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    . H8 [) X; f% @! V$ U% a) x
  107. , T# h4 {! `+ \
  108.     return 0;( L" L; h, a: L" X+ M( h' G
  109. }
复制代码
  }7 C! U8 l# g
85.5.5 第5步,配置文件Dev_Inf.c的实现
) G- C% B6 m0 t* }5 i& c6 O配置如下:
- Y1 J& {, d7 a5 x
$ c/ K+ `/ P- B6 i- k' i
  1. #if defined (__ICCARM__)3 f0 p! H* Z) ?; X. b
  2. __root struct StorageInfo const StorageInfo  =  {
    8 v) S% F# s7 y
  3. #else
    ) m6 N) r4 h2 ^* P$ D( X
  4. struct StorageInfo const StorageInfo =  {. _/ `! w$ G( f6 _; p& L; I; X
  5. #endif( d; ?& F, e9 v/ c% I* z  z
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    0 R2 M) s5 c) h- Y; W0 t) h  H
  7.     NOR_FLASH,                      /* 设备类型 */# M+ a1 M( ?( n
  8.     0xC0000000,                     /* Flash起始地址 */
    " ^1 _6 E/ u8 n8 E- d+ |8 b
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
    , w  k+ c2 d2 Z; Y
  10.     4096,                           /* 编程页大小 */
    . t$ J, A+ o$ h9 R0 L
  11.     0xFF,                           /* 擦除后的数值 */
      X3 u/ G; A6 {
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    ' y) t- g: b, O" V) O
  13.     0x00000000, 0x00000000,8 ~3 U5 G' C# r5 `6 i
  14. };
复制代码
& o4 t- K0 u- V
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
6 Q, ]  L5 i% f0 e. H1 m* N/ U" B8 r; [4 x3 g$ ?+ o. G( a
51f220556177a36712977b8d140b627f.png
) Q- O' l/ r- y" C
( j2 ]* _7 ~; B3 r- B4 d
85.5.6 第6步,编程文件Loader_Src.c的实现$ h9 ~+ \5 u% h& ?) c
下面将变成文件中实现的几个函数为大家做个说明:; y1 ^' G- G7 c4 |) |- {5 Q1 ^8 l
) s/ U. I8 d. F& U
  初始化函数Init1 F  Y' H8 k& F% K8 y
  1. /*
    9 @( A  A$ N, t( K7 j
  2. *********************************************************************************************************
    ' \: Y0 I9 L/ Q: l" d
  3. *    函 数 名: Init
    5 w2 c* C* Z) h, p1 u* ?0 |) A: ^
  4. *    功能说明: Flash编程初始化
    7 A3 g: b$ @5 b9 L& |& T$ N' [
  5. *    形    参: 无" }$ w9 e5 h$ G# O& x; a
  6. *    返 回 值: 0 表示失败, 1表示成功
    ; Q2 G/ T& ?8 {' W* p$ R) D' a$ Z
  7. *********************************************************************************************************
    ; n9 `  T' A4 `9 G' Y" X0 f
  8. */
    ; A- Q" P3 L% w& |' y7 i1 k
  9. int Init(void)9 i5 y* v5 n8 S4 H
  10. {   
    * }2 y6 N+ U! ^. H$ `; R, |) c
  11.     int result = 0;
    / s+ g; D1 S' i! X4 a
  12. / W) y1 a( p2 N3 e  {3 x
  13.     /* 系统初始化 */# o7 E: b; P- Y
  14.     SystemInit();
    % V7 ~" \4 Q; a& N

  15. 2 T5 f/ t; i3 E9 w7 M
  16.     /* 时钟初始化 */
    " {2 ^# [' }5 S
  17.     result = SystemClock_Config();
    ! G) ^, Z( ^# \  }( ~
  18.     if (result == 1)
    5 g# x1 x0 H! ?& O  B
  19.     {
    " W; q5 x5 W2 b8 i8 `: J6 @0 e
  20.         return 0;
    . o* W$ i+ _3 t# W$ a: j
  21.     }+ N/ ]( y0 m( D: q. Q" H
  22. , t2 ]! w$ W5 \" J& i# s& o
  23.     /* SPI Flash初始化 */
    # e! [# P% e0 P/ Y
  24.     bsp_InitSPIBus();
    1 ]5 W8 o" C- K3 L. b: f  ]* |
  25.     bsp_InitSFlash();
    8 k" M7 O/ Q* v; ^/ H) R% F' @% Z
  26. 4 ?: O6 T. h' m4 o' s! ^* A) b- d
  27.     return 1;' O8 m, \, s" ^4 B  V- z1 i8 ^
  28. }
复制代码

% J( U4 x8 o; e; e  整个芯片擦除函数MassErase# \, m- M# z; q2 S  S. Q7 H
整个芯片的擦除实现如下:
7 J7 Q3 e% D& h4 j' r! M" G/ a& O/ t' t' c( f
  1. /*: o+ [# P0 r" ]) Z( f, X
  2. *********************************************************************************************************6 }! R9 J. r/ y1 G& L
  3. *    函 数 名: MassErase" U. m2 ~! S2 v' g
  4. *    功能说明: 整个芯片擦除
    9 P6 \) m# z4 E7 s& }/ j- G
  5. *    形    参: 无
    * B: l: |/ U7 B9 \. L$ w, C
  6. *    返 回 值: 1 表示成功,0表示失败% q/ s$ Q3 p8 x- i
  7. *********************************************************************************************************
    0 m2 B5 V; M5 c$ l$ [
  8. */
    : |- h, z+ V" e4 t
  9. int MassErase(void)
    2 g2 ?; Q8 L7 o3 |, H: a6 W9 l
  10. {
    0 k6 m  `" R( k$ h
  11.     sf_EraseChip();
    4 B- f7 z0 t7 e

  12. - C0 H* F+ w2 v" U. r9 _) s3 l
  13.     return 1;   . w9 P4 G1 N: [8 T- J
  14. }
    + E( j, e, e/ b/ h: E" v
  15.   扇区擦除函数SectorErase& \& N9 |$ C$ ^% g4 B* K
  16. /*0 o) h5 E7 q3 `/ m: E8 s6 y
  17. *********************************************************************************************************
    8 d: J- k: o) `3 x! V9 p! _$ [: a
  18. *    函 数 名: SectorErase; i, E7 e; G2 m
  19. *    功能说明: EraseStartAddress 擦除起始地址
    % o; w3 r) o* y( W
  20. *             EraseEndAddress   擦除结束地址
    6 {; C  i) ~. A8 |7 b
  21. *    形    参: adr 擦除地址
    * d' k) M, W5 n7 I
  22. *    返 回 值: 1 表示成功,0表示失败
    0 b5 N+ m+ l  f: t+ T
  23. *********************************************************************************************************  Z5 C& r' i1 z2 ^
  24. */3 R+ X& g/ p. V- t- m
  25. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    - ?0 s2 r$ G! q% I
  26. {' W# Q' R" B3 {+ A0 [; e$ Q8 ?
  27.     uint32_t BlockAddr;* ]- T% f2 L6 j; w1 R

  28. % c; h2 g' O# p; n% h6 Y: ^3 C
  29.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;3 _9 v& w5 F9 c: ~
  30.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;
    ' N* z8 |/ b* n( Y; q5 g5 ]; w) D
  31.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */5 m/ A- ]2 z9 ^9 E- \

  32. : U' |, R8 _7 E6 l
  33.     while (EraseEndAddress >= EraseStartAddress)
    0 A" B/ r$ q9 ?2 w
  34.     {
    8 ], n/ L/ G. y7 G& g
  35.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    9 z- z5 L: l- H% `: c: L& m
  36. 9 @- ~- ^' C. g/ N' ?
  37.         sf_EraseSector(BlockAddr);   
    ( ~! a" L" q: }
  38. 1 ~6 h; E8 v# q. J% u" ?
  39.         EraseStartAddress += 0x1000;5 h' R+ a. v) O5 e1 m
  40.     }8 o, i  N8 n. T$ m

  41. / [+ c9 G+ _: V# ~2 [
  42.     return 1;% B* R! }' L8 f" Q
  43. }
复制代码
! \; |, ^3 |4 r
这里要注意两点:
* L( z1 ~% W% c7 a% c* B7 P$ K8 C3 o; Y* t9 W9 X! B# A
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。2 I* b" Y5 |4 f$ E& k
1 m5 y4 C' U, H8 n5 T, X- J
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。7 G: `: ^' f/ t1 |+ F  t  H
% D3 O; |8 h) i  m4 q: J# B# {5 g
  页编程函数Write3 f! I9 A" w& q# P
页编程函数实现如下:
* P6 N" ]$ x1 \4 N1 ]
& [, I+ \( W7 r3 E; |) n/ H
  1. /*
    7 d8 l6 ?' I7 D9 F# f3 w
  2. *********************************************************************************************************7 |4 c9 z/ Q9 {
  3. *    函 数 名: Write0 i$ K2 q$ P5 n$ [) c# ?; s& y9 X
  4. *    功能说明: 写数据到Device
    8 J+ k: v2 n- `' r
  5. *    形    参: Address 写入地址
    : k- q6 \; E, H2 S$ d* p. E8 r/ n: Q
  6. *             Size   写入大小,单位字节
    + u& J' Z: C& w! y
  7. *             buffer 要写入的数据地址
    6 F, E* v8 w$ I, V! y  e4 q+ M
  8. *    返 回 值: 1 表示成功,0表示失败5 {" z& E9 {! X6 Z
  9. *********************************************************************************************************
    " Z4 r: _  A" Y8 z" V2 O' S
  10. */
    $ p, F  B7 N- q+ X/ V
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer), A( G, w* r  z$ T) z# ^( k# a% V
  12. {  
    * r! m$ O! C) t# f* _/ L- Y
  13.     Address -= SPI_FLASH_MEM_ADDR;
    ; U: W. e; L4 [* \  S- m/ I  t8 c
  14. * V( v0 [  S, m+ c! f! d% J
  15.     sf_WriteBuffer(buffer, Address, Size);1 _/ J0 e* {4 B' B

  16. 0 G+ G/ V8 ?( _* F# B
  17.     return 1; 4 g4 ~; M2 U; m: Z
  18. }
复制代码
$ }% {2 ], K) O, l- {
这里注意两点:
4 B- s4 a: M) B. k5 V( l/ I0 t* k
- B  l8 ?/ W2 R. F, S: E. i(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。
. D; U" X+ I5 J8 y: f: Z3 o' S3 {) W* t/ u+ f. i) d& W. u1 g
(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。& d4 N4 v; ?' o6 D2 E3 b' [

& d5 J: q& Y' k/ `  读取函数& B7 a/ q, N! c7 ~0 ?
  1. /*2 r+ {; X- @: \* c
  2. *********************************************************************************************************
    . e4 n0 a5 e$ W5 C. Q
  3. *    函 数 名: Read
    ! s- m6 }$ O) x- x
  4. *    功能说明: 从SPI Flash读取数据
    4 P/ h' D3 }5 @
  5. *    形    参: Address 读取地址$ O- E9 T3 i$ t% }4 d1 l
  6. *             Size   读取大小,单位字节: Q$ J! u' W4 b# p. W9 M
  7. *             buffer 读取存放的数据缓冲
    0 c# O* P2 O7 d* z# \, E1 J1 Y
  8. *    返 回 值: 1 表示成功,0表示失败
    & ~; Z) h; U" o+ S; w7 Z; ^
  9. *********************************************************************************************************
    8 a0 p! n/ w' A, S& E: p( o
  10. */
    : L; h7 i1 f, A: c# N) |
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    # m% R) K1 P$ j; m5 N  {; s- A  j
  12. {
    " t5 A: b8 }/ g3 V
  13.     Address -= SPI_FLASH_MEM_ADDR;: I, a. Y. K/ r9 f* z' O
  14.     sf_ReadBuffer(Buffer, Address, Size);5 R: D/ {6 z' L

  15. ! m5 _- i$ _3 b5 W/ y" ]' O
  16.     return 1;' q! U3 U* ?) p" ]$ }; P/ h
  17. }
复制代码
& R& K/ o8 u( X- \7 t$ K8 ~
  读取和校验函数
) _! C* y; H8 |1 q我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。2 |4 n% n! I* P
$ M& n6 n, G/ j' B, c" [- L
85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)# D1 i) r# k& L& C4 n5 D" i6 |
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
, R) B1 A6 n- j6 l! D. h* S( q; S# W- N3 ^+ R' ?9 ?
  1. /*$ }! J' b, B0 ?3 ~& s
  2. *********************************************************************************************************
    ; s8 ], z; r$ S7 Q  N' q* ^) a, N
  3. *                                时钟,引脚,DMA,中断等宏定义
    ' |+ a5 ~8 L5 z1 _4 M# Z, X
  4. *********************************************************************************************************$ f& t) K1 D3 ?: R7 F
  5. */- }2 d4 b! ~/ L4 j7 z' r" p
  6. #define SPIx                            SPI1! \! p- U+ C! |" k' I% ^2 G8 z
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()' f! m4 J; N9 S8 \5 I' A
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()* B3 c/ M, E' ^

  9. 2 n- u1 H+ U: P
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    1 G8 S* W5 F, L
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()1 P+ F  P2 t, o; ~' @- n

  12. ' B; \$ ~3 a* x8 `
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()/ l4 Y, {6 `2 T- N( S, ^
  14. #define SPIx_SCK_GPIO                    GPIOB
    2 D" t' H/ {4 X7 I  ?' C- z
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3' }3 P# F0 H# O4 ]# r  M
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    + h2 v& K$ F: z, T. Y7 _4 |0 `

  17. : t- w% t2 G- L* U* q
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()5 S3 w& N" \( R( v7 f. z; K/ ?5 u+ x
  19. #define SPIx_MISO_GPIO                    GPIOB
    / |' V. A" h1 h4 D% [; Y$ O
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4  i$ Q& |: B1 r" x5 J
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI12 d- v0 s9 ?! Z& P" \, K4 S2 b' d. [

  22. - r% N0 o; {  u
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()  Y! b: u( v) l! _% ?
  24. #define SPIx_MOSI_GPIO                    GPIOB
    * p* U+ e& v/ X6 C2 N
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5" Q! [2 S5 ^# C  A; q& C6 y1 E
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1! }  R* e3 e% ?
  27. 硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
复制代码
+ ~- j+ N4 O/ e: L7 n9 P

2 d/ Y* O. i. W主要是下面这几个:- U8 i) Q; N/ w4 Y; b

2 k7 I& p% N, Z3 G$ l
  1. /* 串行Flash的片选GPIO端口, PD13  */
    $ w7 H# a& E7 R! w' y
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()+ Y% y* D: O6 E: l9 B, H- S' J
  3. #define SF_CS_GPIO                    GPIOD
    8 O8 O7 U# P; `: F1 G. a0 M
  4. #define SF_CS_PIN                    GPIO_PIN_13
    5 h4 f; l" y# B& V% h( J$ D1 O0 K

  5. 4 C- X( a. ^" ^3 K! J
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    # n( n4 e  W) S% r1 j5 t
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
    - R3 ~4 Z( @) `& o3 o/ |! Y, ~; ]
  8. 2 k- m$ M, ]& d9 {, Z. m) P
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */9 z, Z" v9 g% u/ ?
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */! g5 P* I% i7 v$ _8 I( R9 f5 B
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */3 F' ~9 }& E+ q
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    % X4 J( x- h% D
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    5 h9 S5 Q5 }- d0 k$ m
  14. #define CMD_READ      0x03      /* 读数据区命令 */6 u2 [$ @* G, o
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */" A+ M9 d1 _: h! V; I! L8 g4 R
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */
    # A0 h8 U7 T& h; C8 _* V
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    8 N# ]. r3 W9 S0 K  V: r$ {( ?
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */9 c8 Y1 @7 ?0 C
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */5 h5 X% T: B1 L, A

  20. " o' h. b9 N. Y9 ]3 m+ O- q
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

! x) [. k( z% B* a85.6 QSPI Flash的STM32CubeProg下载算法使用方法7 |% }0 `; n2 z
编译本章教程配套的例子,生成的算法文件位于此路径下:) Y0 N- i( {# j
7 G1 s' c$ w& C( s% L2 q; D' Z2 Y
5ec6d402a7646232a55f4834cd00ac0c.png

1 Q# g5 b7 e9 ]4 T5 X, P
" e- K0 Y" k' C  @9 K- _85.6.1 下载算法存放位置
/ r% E3 [$ n0 x$ ]' }+ z生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
1 Y4 A' K0 T9 F; |" S/ L1 o8 |# J, r5 d3 `0 _- I; Z
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
- m( H, F8 z) }# F) g2 ]. S8 s. Y' e' O5 {
2609073778af5845a869911ab36624f4.png

9 \7 b$ |: F/ q
( g4 s4 i: I7 A( O9 g2 V85.6.2 STM32CubeProg下载配置
; |+ U0 d$ i3 i7 ^$ i我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
$ u) E" H8 S- J' g1 y) G
( c$ Q# p% J" H. d* J# h
27a88335ceb606bd021c3ebc65d88e21.png

$ D* x' l6 d9 ^+ V& h
7 z" w: x6 R4 O点击Connect后效果如下:) m5 V# C. c4 s6 a, k( ]
  K4 `0 F' Z* N# I* |' `% W) u
d4e256633eafdbf4bcd8cce6e3b5909f.png
  I; m8 ]$ L3 f- J: `8 M# U
: W# Y) I6 i$ ?, L
在这里选择我们制作的下载算法:5 e; C7 H$ O' `

& c4 r  T) @( K0 G4 W, d
d4fa28f64be2de98e04f3188eb2fe09e.png
1 u) o& _: E% R  I4 |0 \# p0 I9 J
) }6 M, R, C4 _) Q$ c! K
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
8 c4 C* v4 x& T: D- O$ N8 ?% Z$ W
" E# D9 W- c; Q' {  ]7 ]9 q
842518a7cc1dc569598d372132b2ee58.png
6 Y) d# q  c' ^: j: p$ _. H

" A3 g; K, \: H. R8 @( V, u下载完成后的效果如下:" W; X. _# [6 ~5 H
7 o5 b8 @+ E& Q5 h- q
68a92f267829ca77f125db982d6c0479.png
$ G. `; U$ k1 O9 y5 @5 X- r
$ l8 P! v" E6 a+ G( s5 b
85.6.3 验证算法文件是否可以正常使用9 P$ Y6 U, c* b! l+ B1 D6 k
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:9 F2 f6 G6 R' d+ w4 w4 F

9 w/ d; k9 d5 F: K% ~, i& J
566d2fba421c710da70e5656c6934466.png

- ^8 F# l9 |3 j0 E
- `3 y, c  T* b8 y& F7 ]: F85.7 实验例程说明
/ v0 R& |7 Q- g/ s( L+ a; S本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。
% t) O% W* C- S/ U0 h; \
* W2 n, x; Q1 D编译本章教程配套的例子,生成的算法文件位于此路径下:' g) C! @& A' P% X, K' ?, Q

; J  M  y' t! }* ]) h4 K' d
6108d1ff7e349f2aa268ee31804c57ca.png
+ y& s6 P# N9 D
" L0 g: \+ X& R) R7 L6 d
85.8 总结% ]) ]2 K. D% \, Z9 V. v
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。3 v2 n- E8 c8 M! v
1 |( b4 M; {/ m

+ R: _0 e+ \( G
收藏 评论0 发布时间:2021-11-6 23:35

举报

0个回答

所属标签

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