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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示
3 t. T( B' C/ G  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。5 C/ v7 f+ c. h4 t, U( G
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样0 \. ~$ J" |: U# K
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
7 S( O  O* |7 ^2 z6 q& O. ]85.2 STM32CubeProg简介
/ k2 y* y8 I- k% OSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。" O* D- [! ]8 c- @

6 Y; I' S. t) u& ?
d80578d217eb2a83520c6f3d797218f4.png

) F* C& m. Z# P: o+ T7 Q5 U  S% l7 }7 D3 z+ g8 A' X
85.3 STM32CubeProg下载算法基础知识
$ |; m, v! C0 N$ G) n" M) d
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
! `, s4 ~4 s, Z2 |. j# T+ M/ N% M/ d( }' U: R( i
85.3.1 程序能够通过下载算法下载到芯片的核心思想( Y' C# I1 A2 {) q5 I. i: A
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。4 g: C) m0 \4 a: P; C: D9 w

9 R1 m3 i  D& ?85.3.2 算法程序中擦除操作执行流程
  R+ W0 k' {/ j' p. ^3 w" N注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
8 n9 S0 X5 |9 L/ ]" x, ]3 f* w6 u) a
% g6 _( d3 w: ]擦除操作大致流程:
* v/ S: M, Z' V6 ~9 Z
6 F7 X) p; s. {- r) k! u
d75670256363bb50203bed0bbe4f9fd2.png

# c7 j  B# S& d; y2 z2 f7 K
  R; u% X5 b+ k3 c9 U  加载算法到芯片RAM。
/ d; B. X: {3 d; f( u  执行初始化函数Init。1 r5 C( e- w" x! r
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。( J' \% `5 n; q+ U( s1 E3 _, W
  执行Uinit函数。
, t. l" G' h9 E# p# j4 ~; X  操作完毕。$ s4 P& d' ]0 u  N. f8 w% N
6 Z, I2 d( R$ ]0 D$ ?
85.3.3 算法程序中编程操作执行流程0 y/ g" Q5 W. G3 T  u& R; N7 l
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。6 _2 T  h' N$ Q! ]% o. u

9 L3 E* Y$ c& `1 u+ j/ {. u4 T) E编程操作大致流程:, V4 k0 Y$ z1 ~* k( N

) M5 E1 {% I) y# G) o
4fa96e9e33d700081f2cdbd312afb31e.png
9 {4 ]& t5 A& u( n8 D" s
# o. o) }  v# T) g, s: Z
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。& E0 |) {7 E7 l* N- K
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
; p4 m) I+ c- T9 w  加载算法到RAM。
6 w  t" `. {9 A' I2 p- C: X  执行Init函数。. n5 g* l+ e& x; p' Z/ l3 w- A+ t
  加载用户到RAM缓冲。
2 W* G1 m9 @, L, D  执行Program Page页编程函数。6 |+ c5 s" g0 `5 J- k: ~4 a
  执行Uninit函数。  q0 @( h+ E8 C3 J
  操作完毕。6 W3 v* z& {" K

. s6 ]  h- s; Y0 B$ Q1 T0 p85.3.4 算法程序中校验操作执行流程
( H% G/ y# }; F$ n) i2 J注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。: ?) B0 F3 Z/ Q$ W' y* K% S

" ]$ I6 r/ W' r/ r, s- F校验操作大致流程:& y+ E! \$ S; w/ x* z$ s% _, i" z" \) _

! ~! _7 h" `) ^# }
7deeae4b650bd82c457492af14fc26b7.png

- N# C8 [; X% ?9 ~) o6 q; E, @. O% F- S1 H* Z3 @/ I
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
. v. v* `' i) C2 X9 l" H" F% h  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:3 F2 M1 h/ y' W: k, z2 X
加载算法到RAM。6 h) R/ H# B5 [! ^
执行Init函数。
$ w5 {: |% ?( o( G, G5 y 查看校验算法是否存在
- N4 X6 C9 K) k9 ?+ j( u4 P  如果有,加载应用程序到RAM并执行校验。
4 m5 B& i* f8 e4 a: L  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。  r4 r- z+ ]& [' Z# s
  执行Uninit函数。
) S& [5 b' [4 K  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。: N: }2 K  `, Q7 \
  执行RecoverySupportStop,回复支持停止。
9 S) g, r7 W- f4 ^  执行DebugCoreStop,调试内核停止。
  B+ M- _) K4 `  运行应用:
" T& e- p3 a) j5 g" W! T* l  执行失败
2 _# m- B  Q, n' a" r  执行成功,再执行硬件复位。+ a% p7 z( i' x
  操作完毕,停止调试端口。
! p" y  k$ j0 I5 c! {$ Z& u3 d# j" d# }
85.4 创建STM32CubeProg下载算法通用流程6 f1 B' K0 ^+ Y. i- ~$ o
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
. T( t4 g: H0 T, O* a% `& i
( }9 A' q7 Z1 E  ~85.4.1 第1步,使用STM32CubeProg提供好的程序模板
3 {# G$ J9 y2 A. P0 p位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader2 a  F; y3 P( K7 {) K& ~

$ ~4 L$ B- L8 m( J1 j) Q
97f385e3b62a055bac9b5c0214b877e5.png

/ W) h1 L/ a( E/ U: W7 e# Z; y2 g
$ \+ Q0 _/ u, y2 a* |. V以M25P64为例(注,其余步骤就以这个为例子进行说明):
9 ~: N! H/ m: ]! G$ W9 U4 [8 l
) Y- V+ t+ O" q2 c
43b298f996937fe35e89697fc6479cb9.png
; P* q/ @. C* u/ \4 T5 |

! z5 v5 }( w1 s9 `8 V85.4.2 第2步,修改工程名
5 u& c+ `% g3 Y/ z8 Q* i1 K3 zSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
# J7 U" U( q: F2 j- Z0 w5 u% b0 L+ G2 j1 i! C/ u9 M2 Q
85.4.3 第3步,修改使用的器件
2 {* c/ {; y7 i  |9 T* r" i
在MDK的Option选项里面设置使用的器件。
! d! |& p6 M, R2 ~; B5 U6 }8 ^: U3 W9 }
36026b9a6d60249610d0246eb725b246.png
4 [" t0 }, X3 R( \0 b

' m7 z$ G, N# b" a1 y$ q85.4.4 第4步,修改输出算法文件的名字
& L6 s1 W' f9 T5 P4 g3 w  I这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
: b) v; P; U( S1 J8 e( f
# T% h" g0 v$ K- G& t
52c72b3993f28a366a22eac56f8fd043.png
. N$ z3 a  `& g1 ?; s% Y
/ ^4 ^( `: g% T& Z. g. \' @
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:) V' e; S% R7 P' j4 @8 h, u$ [# f
2 H- b) b  Z6 x! P/ n
7b48153027a44de33926c2da008716a0.png
( |$ k" \; R( ?( @
$ y& L! Z/ k+ I2 M
85.4.5 第5步,修改使用的库文件和头文件路径
# R" J* O0 C5 s4 |6 {/ k+ U根据大家使用的主控芯片,添加相应的库文件和头文件路径。
" e  g- F" N. M
! A1 n$ c' m6 w  _6 j6 y6 D6 `85.4.6 第5步,修改编程算法文件Loader_Src.c% v$ u. p- _4 P
模板工程里面提供的是M25P64,部分代码如下:
6 r% S% O9 M- S% U' b/ f; b4 u% q+ W2 N- z. a% P  J; y& x0 T
  1. /**" Q8 l+ M7 x$ c7 e
  2.   * Description :
    ' o2 ^% g6 W0 T. J+ w
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    4 I/ v+ O& ?0 @
  4.   * device and initilize the FSMC with the chosen configuration , Q% @9 _: B0 e, _6 C2 P+ b3 y6 g
  5.   * Inputs    :4 M( }# t1 v# ~
  6.   *      None3 R; J7 n( K; r' [* K9 r; x
  7.   * outputs   :
    % S( M* A! G  o. W' j5 b! u! I
  8.   *      R0             : "1"             : Operation succeeded, |- g* B( [2 u5 j% ^( _& E7 u/ ]
  9.   *               "0"             : Operation failure
    , d/ V4 {; _: c/ S' }- L
  10.   * Note: Mandatory for all types of device $ A& Q( z# o5 D6 k/ _- C
  11.   */
    ) g  K) g2 p# A, v" ~8 X7 J
  12. int Init (void)
    , J- @( w6 ]# W; b  }
  13. {  
    ' L$ q5 z, N" Q& g7 j
  14.   SystemInit();
    " F. e2 m. D1 T8 K) z
  15.   sFLASH_Init();
    ( D+ p: o( [. c$ e  ?' ?
  16.   return 1;
    + @- P8 L# Q' K) V2 U
  17. }. I6 \% u7 Q0 d, }
  18. 2 ]* G8 v/ V# Q: U' [9 I: R' P) F0 B8 Y

  19. " g5 I( h  i+ Z+ _& P0 d
  20. /**/ P3 M9 X8 c% O+ k/ R
  21.   * Description :7 _8 A$ l2 f$ v) x
  22.   * Read data from the device 9 q3 H4 q: g: f& y
  23.   * Inputs    :
    0 s) k7 {/ B5 a- U; K# M
  24.   *      Address       : Write location4 e; H% e# {) @5 r; u
  25.   *      Size          : Length in bytes  
    1 Q5 P/ u3 s  e
  26.   *      buffer        : Address where to get the data to write- ^' G4 n- q6 }3 x
  27.   * outputs   :! H7 V1 T4 `' g
  28.   *      R0             : "1"             : Operation succeeded2 a7 h' e/ Y0 G& o, ?4 Q
  29.   *               "0"             : Operation failure
    9 S. \" Z0 e+ }1 |1 I
  30.   * Note: Mandatory for all types except SRAM and PSRAM    ! H& h9 Q. T' \- h$ ~0 R
  31.   */' a2 ^7 C- |  S* y3 P
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)) Y0 n9 F  p* q. F7 S
  33. {
    & H! I& E( d3 l4 _
  34.   sFLASH_ReadBuffer(buffer, Address, Size);- C* @/ h. G. C8 U/ x% X' x
  35.   return 1;
    6 Y3 A- W; f& f
  36. }
    7 L8 W8 m, Z( C

  37. + ]$ l. ?0 p/ A- G

  38. 1 l& ?  N4 s* q# K2 u6 R
  39. /**
    7 h: l! ^3 T( w1 @2 k  W, F
  40.   * Description :* u% L% ^/ q* C7 a3 Q+ B( k$ z
  41.   * Write data from the device
    9 h* T8 R7 K/ q" z3 ~& D
  42.   * Inputs    :3 |# R6 `- h9 G
  43.   *      Address       : Write location
    5 ]1 Y& t* V% _9 Y" X/ e. j
  44.   *      Size          : Length in bytes  
    * A) a+ s( r1 V6 s% D
  45.   *      buffer        : Address where to get the data to write
    3 u7 q1 I  f6 j  \7 S9 a/ c. s
  46.   * outputs   :
    7 k* x) p8 c' |
  47.   *      R0           : "1"             : Operation succeeded- x) f& E/ k& E2 N8 D% W
  48.   *                     "0"             : Operation failure
    # j  z+ d- v. H' g5 y1 W3 W
  49.   * Note: Mandatory for all types except SRAM and PSRAM    ' X$ x3 Z$ J, l6 V+ n
  50.   */
    6 u; o% A0 K% h
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)1 R& a5 j4 g- ]
  52. {
    ( [4 n+ y7 {! ?* F& w$ P0 `
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    ) C8 ]5 H9 Z( z1 N$ m/ p& q' w. x  ~8 p
  54.   return 1;
    " F! k4 |5 ~- |5 U  |, Q
  55. }
    3 J- g: O5 ?* j2 O+ g0 |+ t9 C- a

  56. 0 i: A1 Y  m  w8 o& V5 |: `
  57. ; o$ {; f$ S! L# G. T( _5 @' m
  58. /**
    8 p- D5 E  _- I( O
  59.   * Description :, ^3 l/ V, A% u. a4 F. W
  60.   * Erase a full sector in the device
    / W5 Q" ]/ E7 A# I4 j7 r' V: Z
  61.   * Inputs    :
    & b, l) J! X& K! \
  62.   *     None
    ; \+ A% }9 K0 v; l. K' \
  63.   * outputs   :* H- Y: h; {; q- t" v
  64.   *     R0             : "1" : Operation succeeded) e$ D' h8 O! B& Y7 z9 u2 a
  65.   *              "0" : Operation failure
      x) _$ z3 v( x( X0 s# M* K
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH3 I1 m1 i5 h0 q( `0 Z' G
  67.   */
    1 g, j+ I( h8 E8 c+ W" h# w0 m
  68. int MassErase (void)
    ' |+ Y- V+ Q, }
  69. {  
    8 }1 U2 V- O- t3 d* V# n
  70.   sFLASH_EraseBulk();* R1 @- T! Z& d' o
  71.   return 1;   
    6 c& H$ s0 p+ h; C% c! O+ u
  72. }
复制代码
7 w$ x. X, O7 _$ w: Z  I
85.4.7 第6步,修改配置文件Dev_Inf.c
7 \6 R1 H6 o1 A; k4 |1 g9 C: @模板工程里面提供简单的配置说明:
9 d- N' q  C: n- ?3 Q; z# K7 _( N5 f* D+ x2 C, z7 J+ S" v9 `
  1. #if defined (__ICCARM__)
    9 T, x8 ^6 _9 {* `5 n; a
  2. __root struct StorageInfo const StorageInfo  =  {
    " j: k6 y1 R# u5 h6 Z% @
  3. #else
      F5 a9 D- }+ J* p9 @- _4 W5 N, R# L4 R( P
  4. struct StorageInfo const StorageInfo  =  {$ ], C: O. [2 x) X' d! t
  5. #endif
    8 R4 Y& {' z( x7 |
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    9 \! b: {4 M% b5 A7 P8 D
  7.    SPI_FLASH,                       // Device Type6 H; X- g8 M# C
  8.    0x00000000,                     // Device Start Address
    - D* y: }8 A# d& L6 _2 a4 `
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits): A2 Y+ C+ o! L7 V) {0 o4 _7 G
  10.    0x00000100,                      // Programming Page Size 16Bytes
    ; t' m$ a/ U3 q* c' }, K' U
  11.    0xFF,                            // Initial Content of Erased Memory5 p  t' }# p3 p2 |
  12. // Specify Size and Address of Sectors (view example below)( r9 c4 a/ L+ p2 Z: ^5 D
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes 8 T+ [3 Q7 U, r! X) ]1 V2 k4 K
  14.    0x00000000, 0x00000000,% l) D2 E0 M0 D$ O$ U, H
  15. };
复制代码
1 X5 m2 v) R; `9 ~
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
. I) b9 `, D; D7 N7 G
* X. O- @9 c6 a7 h2 [& B85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
+ }1 n, |9 \. D, F% v
C和汇编的配置都勾选上:& c) F. ]" p5 q

5 a5 i( ~5 u- R
afddd6e839292aa2b94b43e79544d630.png
  j. \- {* t* ?+ ~' b3 d6 ~

* X2 Y7 K: S6 z9 |) e2 K汇编:
6 P9 G4 B$ n, Z; F# Q6 P8 N; n( _8 y# k) I5 g7 o) F6 R
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
6 y0 n" o0 n5 G# |, B9 d

/ e6 k' q# U* u7 q* j如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:! G2 A! ?3 \8 x0 o' {* r' ]

' w& U5 x9 F6 G2 E; h(1)加载以响应运行事件。
9 R. e* R% s9 t& o; |+ a* r
$ T1 j: }2 H/ Y/ u( n( e(2)在不同情况下使用其他例程的不同组合加载到内存中。6 ^! k+ z: g2 ~" e+ W

1 N8 T, j3 a. s1 p) S) b(3)在执行期间映射到不同的地址。  V+ D7 c6 U! q) K0 ?& i. ?( g. C5 m3 U

& t% p6 }' S3 [" h使用Read-Write position independence同理,表示的可读可写数据段。1 m+ f- y( g4 I3 |

( f" p* N: W+ ?& _6 {85.4.9 第8步,将程序可执行文件axf修改为stldr格式
' f, l" v+ t2 v& {2 c0 L通过下面的命令就可以将生成的axf可执行文件修改为stldr。8 T* g  Y; U% t$ m
$ n3 `* {7 j# D, W- q. w
c683673d2481d8dce75b6fa01d624600.png
4 _1 I( j1 O1 @
* b7 d( @" ~* E  E& i- b+ {
85.4.10   第9步,分散加载设置
0 h* d" c# F! {# F我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
9 D( j' O' R( F' l
* F5 Q- d) V5 ]+ O
b96835a7506516319002e6b2e8db9371.png
, w$ t* Z# i6 S. H' J5 `
& ^  u/ J1 r, Y  h+ S
分散加载文件中的内容如下:1 P! Y5 s0 B* F
* `( C2 N4 Q& `  ^
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions; J' a- i1 x4 [: ^5 m) @' b2 ~
  2. {5 T$ Y1 _& k0 X  H, k& u
  3.   PrgCode +0           ; Code
    ) ?! U: s0 u" X" [0 h
  4.   {
    ! q/ i3 T& E9 x# A% y" E  D
  5.     * (+RO)4 ^! X' Z. H& {7 l
  6.   }$ K1 g, S; w% x" V# c9 v& G
  7.   PrgData +0           ; Data
    3 T  R$ A! Y5 L) |2 `, s8 G1 P
  8.   {6 p  O" f2 Q/ `, G- J& d
  9.     * (+RW,+ZI)
    - ?2 M/ v0 P5 ^6 u
  10.   }! f& k" d$ p: A8 f$ r; }
  11. }5 X' Y% Y9 E  P! b- M8 s1 H! u9 v2 K

  12. . A6 n$ r! w1 }- I6 Z7 `$ q
  13. DEVICE_INFO +0               ; Device Info
    ' N8 L+ \% p' C0 r/ @5 P& B4 X
  14. {
    + n: A1 W$ U5 L
  15.   DevInfo +0           ; Info structure4 g" m) [# u8 M% u* L
  16.   {
    ) H8 L% O2 x: p$ C  J( t
  17.     dev_inf.o
    / C' T/ C" L3 u& g- p0 o6 r' N
  18.   }9 ]& F+ w! `. {& W2 I3 i% U! ~
  19. }
复制代码
6 e" u  E& a4 h7 m% v! |" n
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。4 \% N9 `4 L* o) q& g
9 d, y/ G4 @# M3 H0 f
--diag_suppress L6305用于屏蔽L6503类型警告信息。
. Q  B# d2 X" r8 W1 j( z0 c) F2 L/ }2 g- T; h7 j( ^
特别注意,设置了分散加载后,此处的配置就不再起作用了:% F( y0 X8 @" L3 P+ T2 M0 K8 n' t8 H2 `
/ k' R! Y$ T3 j- d0 E+ N6 q
641395c374a3af02f1a92d3ff9ee4199.png

! V% R' k. q' q) i
" m5 n. m# w+ D* U1 [' x0 m85.5 SPI Flash的STM32CubeProg下载算法制作, ], i, G. G/ _8 I! S9 J; f
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
" W% g: V- A8 q. g
( t+ W" K6 r" s; r& p5 q" `85.5.1 第1步,制作前重要提示

: N* m$ e5 U4 N这两点非常重要:; @2 P7 H6 r; @7 |) X; M$ [  P; a
/ t7 ~7 [# e: @0 i7 h2 m7 |  P
  程序里面不要开启任何中断,全部查询方式。' p# x; ]% p  }4 z" ~5 h# c
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。; x0 ?- I% N* c" s, S
# t  X  B8 }, n, f# F4 I2 I
85.5.2 第2步,准备一个工程模板
3 j8 L! h3 I6 X# @( }9 l 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
) d3 t0 C6 B' ]! v8 F- T
( G# ^( \, @  ^: F* l; z# e4 o
80ad5e3abdd6a993c8621230401a1313.png

6 T' A& W1 N  N' r2 h3 H( R0 b. l) V; _" d* L
85.5.3 第3步,修改HAL库
- M7 D- R1 a& ]; l0 [这一步比较重要,主要修改了以下三个文件:
7 K% g% S$ R+ G! \# m( v, Z" }$ n% |6 `: v' U
33bf89f25487547d4008f43b7cf10e03.png

, H& O/ ]% U, Y! P) S3 H# N! n1 Q
/ R3 p) ]& L8 [' u/ z主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
' J" s+ i" B! @8 a: s: G8 ]/ w% h/ R: ?3 e" p: b9 V
85.5.4 第4步,时钟初始化
& V  A! o3 Z$ P$ n: }8 J我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
! Z: t: o* s# |5 J: a/ u2 X9 z7 R, S+ K. A
  1. /*8 E% g% F# n' n, p
  2. *********************************************************************************************************4 i) [3 I6 B' Q: l; l% T
  3. *    函 数 名: HAL_InitTick
    : m, H  E$ U6 l# l
  4. *    功能说明: 重定向,不使用) R; r$ p4 j0 P! W" e  Q3 X1 n
  5. *    形    参: TickPriority
    : Y# x, N, z/ f, x. O
  6. *    返 回 值: 无* Y$ i' T) y+ y5 J
  7. *********************************************************************************************************
    * w1 A. f8 l8 _3 U; e$ ^; a7 u
  8. */8 K( I% b0 e, k2 S* Z! Z: t( G
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    " m; l6 M& Y5 \
  10. {: j% n7 f: [/ [7 B2 M/ d3 ?
  11.     return HAL_OK;
    ! J5 e7 s) m6 Q" ?2 D8 ~8 F
  12. }
复制代码

% ]& K& \1 s  J& x然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
" G, Y' ~& l# L! ^4 r, R8 L. Y, t6 y. O
  1. #if !defined  (HSE_VALUE)
    . y* w8 |* ^% ^; {' B1 \
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    ; H; G* G, s& H. M. S& B
  3. #endif /* HSE_VALUE */
复制代码
0 f* I- M8 K# i: b" R( X
最后修改PLL:
& V# l: N9 G$ g- r- u  {8 t0 \1 ~4 r: D
  1. /*
    / B2 o& y2 B" \5 s
  2. *********************************************************************************************************+ T" G5 I4 K1 n( @7 c
  3. *    函 数 名: SystemClock_Config" X5 z' p9 H8 V: B2 q4 q5 b. I
  4. *    功能说明: 初始化系统时钟
    2 ]3 d; D7 V& l, w
  5. *                System Clock source            = PLL (HSE)7 U& z* V) q% B: k! g3 U. c3 ~
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock); ^; Q' D9 B8 v, _, [' i5 g$ Z
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)1 A. K  n9 J+ t/ h* A1 U/ l' z
  8. *                AHB Prescaler                  = 2* x2 [" ~; N8 t: P3 C+ H8 r& p
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)- t* E& ^: T5 p% f& w. C
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)# t1 J% [8 @4 l! s( V3 P; M& ]  V
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)  k) v9 w, N1 s5 u2 P
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    1 k+ G4 o: @! d! _  d2 N
  13. *                HSE Frequency(Hz)              = 25000000
    * K+ [9 `$ X; a- \
  14. *               PLL_M                          = 5
    % |) s- L" g/ w- n( _
  15. *                PLL_N                          = 160) [: m  H* R: v9 m4 e
  16. *                PLL_P                          = 24 F/ P* u! K" a/ r" b  W) v
  17. *                PLL_Q                          = 48 V" A0 P4 ?. @; }
  18. *                PLL_R                          = 2( [2 F" q! G$ m+ d7 {; c
  19. *                VDD(V)                         = 3.3
    & \% d' C$ v: d" {5 @# Y1 R
  20. *                Flash Latency(WS)              = 4
    . J1 ?; E/ E1 g' M5 K0 V
  21. *    形    参: 无+ `6 I4 G( n, I" A  D/ f
  22. *    返 回 值: 1 表示失败,0 表示成功
    + y8 [3 T7 m/ ^
  23. *********************************************************************************************************
    6 U4 l# ]6 i1 P  R, O
  24. */9 |: p8 E" w8 B& `5 ~; T2 T& g
  25. int SystemClock_Config(void)" S  F  ~8 |) l3 u; a
  26. {
    : W! X/ l* n6 G  a: I
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};# I  M1 ^4 a3 N/ |
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    6 h& _: J' R8 c% h7 F) G
  29.     HAL_StatusTypeDef ret = HAL_OK;2 i3 G, d* ~% z0 t9 c
  30. ) B- x$ M3 w3 o* P* F6 Y/ E
  31.     /* 锁住SCU(Supply configuration update) */
    ! H1 N) U7 J0 F' O' T
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    ) t8 j9 l6 X! Z" P/ o% v
  33. ; j$ }% v5 x' |% O/ w0 _1 V( X$ G
  34.     /* - i1 x' W9 H: Q8 Q0 P3 b4 e
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    , e/ g- {5 W, m7 I) e
  36.          详情看参考手册的Table 12的表格。
    ) V) I" e2 Y% D: ]. J
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。# }% W7 T( {, G
  38.     */8 j; w7 w# s- k9 ]: \
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);$ m& A/ `& S- k& f9 E- l

  40. " @7 o; O0 b2 R$ |% ~3 d; m
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}1 t6 X# b$ k/ ]$ s6 V' |& |

  42. 8 ?, A0 P# z/ {7 X% d+ M1 R
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    * `* W# o# T1 i* R3 p3 {+ B$ ?
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;& j* I- v1 N9 v5 S4 V$ U" i
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;) {; c1 _# j) R5 f* z. ?
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    : K( z7 z4 q: k1 @4 `3 i! E
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
      b5 u$ ?: j8 \
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    , h1 S9 d1 R; o  U
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;/ o* r- @3 V# O2 n( a0 |0 I! t" L- ^
  50. ! Y/ ?) b* c$ M8 @. |' ?- V( ^: S" H, \
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    7 b4 @) @2 J) z/ K. X
  52.     RCC_OscInitStruct.PLL.PLLN = 160;9 p. H  Z4 m0 S+ d- _
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    1 i. _6 Q, e( N2 }4 G4 F% v
  54.     RCC_OscInitStruct.PLL.PLLR = 2;, e6 K0 K! j& O/ T
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        ' L8 ?2 s6 W1 u- w
  56. ' `2 a2 O9 H# _- d5 x. U: Y
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    # e. |7 R5 m6 M. I
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    * }! }6 O. _- e7 I4 Q3 i
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);& N3 x, D7 e) ^( p8 L
  60.     if(ret != HAL_OK), I2 A- R' a& A9 x
  61.     {
    " i- M. R, |+ V9 Q/ }
  62.         return 1;        % V1 I( x$ e; q  `) z
  63.     }; \, n& H9 t7 B% I6 G, c
  64. 3 `8 @4 ~8 D0 s4 B
  65.     /* : X! g5 U  w, M8 I2 i( y( o
  66.        选择PLL的输出作为系统时钟9 r3 o: P4 I% Y; }: D( @
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟9 @( x% X9 Y7 Y7 I5 a) M/ i9 @
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线! ?8 [6 N+ V; A/ t2 m, J* x2 _
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线- V/ s* Y; r) }# S8 y. ~% n
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线+ W, ]( Q% _% Z4 ^( _# D0 |' C
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线5 o. k7 f! r% p' [) C  A+ @* ], F
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     % h  L7 d/ L& v3 B) r- t
  73.     */# N- x1 D$ L; i9 B
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \: I' _0 U1 a2 S; D
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    % P0 {; j9 f* i' V) |; X

  76. + J9 V8 O( q2 Q: @
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    : X) g5 {1 K% ^9 m' x" n" Z2 v! G2 `
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;* |4 |$ L/ l- i) F9 U/ H
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;2 x8 o) M$ o' R" `4 r% y, S6 D) I
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    7 [0 `& W% N  N9 Y1 I
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    ( d8 Q8 ^1 e+ Y, x  S' m( D7 N7 k2 z
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    , s: m, V8 p7 M4 U) c  t, I$ U
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    ' h7 C9 ^4 m( w. X+ W  I1 w
  84. 3 T8 ^: u7 a9 ]: J' c
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    $ `9 _. @" x" P2 o; M9 q. @8 s8 y
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    ; `& \* P) V. l# B3 ^
  87.     if(ret != HAL_OK)
    / e4 `. D6 N7 _8 X+ N) {; {8 L
  88.     {+ h- \- ?! R/ O6 B8 ~& V
  89.         return 1;" B# V& G% z$ f+ \
  90.     }) w, y! ?" g$ S5 O" [' `5 D
  91. # K( h* R+ D& c2 V1 A' D1 ^
  92.     /*
      ~9 u$ v0 o3 ]9 G6 H
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    * u7 Y# i" [; m! k  z7 `
  94.       (1)使能CSI clock( k  j. k" p$ ~8 w( N; |& Q2 ?
  95.       (2)使能SYSCFG clock6 _! Z% m  P5 B& w$ z
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0% ^, q% N' h+ R' O
  97.     */
    1 v/ _% [% c) u. t. ^
  98.     __HAL_RCC_CSI_ENABLE() ;
    ; }: ^  y1 g& y" S! D
  99. 6 S* O8 e# W1 J. D- t
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    0 {+ N. o' p2 Y4 A2 K

  101. 8 t. r( h) K, Z8 x6 ^2 v
  102.     HAL_EnableCompensationCell();% {: @. j# ]- q; S9 i8 j

  103. 5 Y% m8 e& J( n+ M% p7 X7 Y$ ?
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    ; P! k8 d, O+ ?6 e
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();6 r- g! d! Q% k, \! s
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();+ M6 V- Z4 j; e6 Z

  107. : C9 R# v. M) }: w
  108.     return 0;- W1 j$ `. k, a4 r0 D' k0 J
  109. }
复制代码

# E1 m5 y6 I  p- [/ C0 W( [85.5.5 第5步,配置文件Dev_Inf.c的实现
* A4 c! Y3 S0 L配置如下:
6 f8 Y& q" c$ m% g1 G. w0 I! Y" R8 g
  1. #if defined (__ICCARM__)# o  w8 C! Q9 t6 ?  K
  2. __root struct StorageInfo const StorageInfo  =  {
    * \; i; \# P& v7 B( x8 ]7 B) V" T
  3. #else+ r# j5 W; j2 Q3 \4 O: w5 _
  4. struct StorageInfo const StorageInfo =  {; f* L# B& y/ t' O8 C! C
  5. #endif' W" e$ l& f( l) B7 Z8 w; Y
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */, u8 r) G6 u: Q3 ~. _# ^& |
  7.     NOR_FLASH,                      /* 设备类型 */
    " u7 E: j5 J% c; r- q
  8.     0xC0000000,                     /* Flash起始地址 */
    " o7 H- i4 y  J4 n" ^% }; J  k2 S
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */1 r8 T" U" {5 x# h% y: Q5 ~! J8 `
  10.     4096,                           /* 编程页大小 */) `" e! i0 R4 _+ X
  11.     0xFF,                           /* 擦除后的数值 */
    " C! r0 k7 z/ [
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */1 t) g0 J4 Z9 n7 Y/ `  |& @7 a
  13.     0x00000000, 0x00000000,
    ; Q: R/ K! v9 j( u! i8 t$ ~! u; z6 r8 R; j
  14. };
复制代码

1 [4 [4 h/ `! R' [' S注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:& F8 F' |/ g- W' m
6 g; H, j3 z: a" e$ I# N) g
7 x; b5 i9 |: j1 c8 w, ~5 L7 q

# I3 Y8 j" d, d/ H$ m. N( y85.5.6 第6步,编程文件Loader_Src.c的实现  b) i# Z& V1 X! Z& e: [6 G
下面将变成文件中实现的几个函数为大家做个说明:
; b: R, }& T1 z  ]1 h# ]4 \  Q
, x7 f  ?" e1 f, y# U0 @  初始化函数Init
* T# _2 O% r3 Z: I
  1. /*) w# `$ ~4 H' u0 V
  2. *********************************************************************************************************& E& E0 C+ Y  F5 \( ^" j
  3. *    函 数 名: Init
    9 o8 J6 c. j% A3 k. ?) c8 `
  4. *    功能说明: Flash编程初始化# u0 ~- H0 s0 g
  5. *    形    参: 无4 J/ F! |6 c* ]0 R$ w( S8 b
  6. *    返 回 值: 0 表示失败, 1表示成功) M3 r" X3 u0 e
  7. *********************************************************************************************************
    : q3 q' }) X- L7 j' {5 d& r
  8. */
    % g& r. ?3 ~% _3 ]
  9. int Init(void)
    + U, B3 }& R7 ^
  10. {   
    - d0 N( X7 ]) H& P
  11.     int result = 0;6 B: V' L# R, D) c* N

  12. - O7 {: ]* Q) J* A+ V1 ~
  13.     /* 系统初始化 */
    ; P3 i+ _; r5 H) i; C7 a9 Z
  14.     SystemInit(); ! K3 R. d6 N3 g
  15. + b* @$ u! c% U* a) T
  16.     /* 时钟初始化 */
    0 m3 K) t3 j' \$ ^& z/ _! b0 H. J
  17.     result = SystemClock_Config();. T: A2 W. U7 k+ K  \7 {- q
  18.     if (result == 1)
    3 I5 U, d) p* R6 ^" A
  19.     {
    1 B/ w' @/ b+ P
  20.         return 0;
    1 G* N4 F; }7 l( l! m- `
  21.     }8 I  U. {/ H9 C& B. }$ B

  22. 2 y3 a1 ]5 [" K! t
  23.     /* SPI Flash初始化 */5 N3 I5 }6 U6 G7 T1 b1 f
  24.     bsp_InitSPIBus();' F1 u& ^  q  W8 T* L+ T7 u
  25.     bsp_InitSFlash();( C1 o* u+ ?* l- W( q( s
  26. ( F, B. Z, U, z: ~
  27.     return 1;
    ' W, u5 k/ G% K: Y5 |* H2 t
  28. }
复制代码
! p0 \& U! D3 e7 @$ J6 C, I
  整个芯片擦除函数MassErase  \4 F# r* ^3 ~' U' {* D+ `  |
整个芯片的擦除实现如下:. a: k1 I, e- F! G  Y

* L. L+ c2 F6 F) {
  1. /*
    $ U0 x; M. V% B% @
  2. *********************************************************************************************************  Q1 W9 L" J% D; t5 N* Z
  3. *    函 数 名: MassErase
    5 C% w3 G# ?8 n% u, [% p
  4. *    功能说明: 整个芯片擦除! }) U: ^. d9 w, x  b0 d
  5. *    形    参: 无0 {% D4 T$ ?2 I, l5 [7 l: E
  6. *    返 回 值: 1 表示成功,0表示失败
    ' S- `9 l. a, v9 ~5 t( z
  7. *********************************************************************************************************2 |) h& @* M$ y8 c6 [' ]
  8. */
    7 d5 o4 E4 p, L# d# E
  9. int MassErase(void)
    / w% q* s3 C1 l/ x# l* O+ h
  10. {' R; Y1 K9 B7 S+ R6 \
  11.     sf_EraseChip();( G: |, D8 r2 j7 I* g% i1 O
  12. ) V* F, Y) d# T7 z2 Y0 N. C& E: d/ ~
  13.     return 1;   
    $ U* x! \: f! e
  14. }
复制代码
, q) r9 o; N6 n- A' y
  扇区擦除函数SectorErase" x3 T4 P7 U# ]- ?
  1. /*2 d0 i2 H1 v: d: I% k
  2. *********************************************************************************************************& f$ d, M3 p$ C5 G3 h' T
  3. *    函 数 名: SectorErase
    0 L, _3 ?# a7 F) h4 T  ?
  4. *    功能说明: EraseStartAddress 擦除起始地址
    : I, h! i+ Q& s
  5. *             EraseEndAddress   擦除结束地址( H0 g5 d* X1 d( s, k9 N" ^
  6. *    形    参: adr 擦除地址  X; L/ g( |, N! v% N$ }/ l; h) ?# P$ v
  7. *    返 回 值: 1 表示成功,0表示失败3 E& Q. P5 [# \& w8 }
  8. *********************************************************************************************************6 k# ]+ ]! b; B, p1 U
  9. */' K/ z' N7 |7 x( {
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    ( B- n# o* d) K4 @: F
  11. {( t/ O4 n# A' Z3 ~
  12.     uint32_t BlockAddr;
    ( k0 K; P* f( l( R

  13. 1 s! S$ }  f9 N  y' V
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;# D  Y! g8 h/ K- D
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;1 H( Q' W0 @' m# w' @0 R
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 *// t8 i: q# G0 U1 ?% R

  17. 8 H1 |8 W) m: B/ J( E9 `
  18.     while (EraseEndAddress >= EraseStartAddress)7 |* w7 \) L+ Z
  19.     {
    ; z3 j9 y4 T" T' z% J/ N3 C# O+ f
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;" K" j/ l, v$ T0 W. t. l3 m
  21. - F+ N, l* ^" G: Y
  22.         sf_EraseSector(BlockAddr);   
    " M2 M) h0 E8 e# V* N, A- A

  23. 6 C' Y/ @8 A9 o8 n0 w  i7 b: I/ @
  24.         EraseStartAddress += 0x1000;
    ( F+ z3 M% e9 r: \8 y
  25.     }0 ~  K3 I& J" Q4 R- f1 A4 B+ |

  26. ( H8 F. n" |: Y7 ]: y2 {- {! m
  27.     return 1;
    0 z' ?+ b9 L' [# G" x& _
  28. }
复制代码

2 n: j" j' v) a6 F这里要注意两点:" p4 G, u1 T( _; k$ F
9 t3 h( r7 q6 I& H- @3 }4 h/ z
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
& x9 S6 i3 R, I) J& j& H0 G) K' l3 q7 f- P" O0 K
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。" g" P# E+ R" m7 S1 \( D
4 I  F; c% q! K' o2 l0 P
  页编程函数Write
; ~- I* k' N: ^3 w页编程函数实现如下:! y  z1 v5 H& Z

. K. D- ?3 p% N# A* ~
  1. /*
    : {; z4 x: y. Q: ^0 K# f* S
  2. *********************************************************************************************************
    & J6 F. e# B9 s) s! @6 f
  3. *    函 数 名: Write4 j, r2 c' @4 ^5 Y& B+ d
  4. *    功能说明: 写数据到Device
    6 ?; N! U* K1 A9 e/ O
  5. *    形    参: Address 写入地址
    + e; G3 k! K5 j
  6. *             Size   写入大小,单位字节4 D  P, m1 t2 j3 C9 Z' L7 C* d
  7. *             buffer 要写入的数据地址
    ! e3 }; |# ~% a& |. Z
  8. *    返 回 值: 1 表示成功,0表示失败
    1 ?* I, \: O0 r! @. [0 X
  9. *********************************************************************************************************8 b+ E! G0 ~. b+ P/ Z
  10. */1 [% R. O% z; B. W
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    7 p% C3 ]4 A- _% H
  12. {  7 [- p) ~$ v9 F: C$ U, }) _5 ^. d1 b
  13.     Address -= SPI_FLASH_MEM_ADDR;' l' C+ W4 K* D6 \: Q4 ~4 H/ h( _

  14. ! _- O3 @6 d' I$ K3 ~; r# h
  15.     sf_WriteBuffer(buffer, Address, Size);" z. t8 ~# G! o: ~* C" c

  16. 6 M' z6 w( o# E3 f0 ]& {  r0 g
  17.     return 1;
    , L8 r- {) z- T: n
  18. }
复制代码
9 g9 Z) q" I9 h$ l
这里注意两点:
, I  T7 a1 `5 l5 _  ]6 c9 W
# N# H5 u, g3 w" z  s8 m+ g* D(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。+ M) [! Q& ?1 o9 x2 O# j3 n# l
- Z) o3 [' u& q" l+ S/ q0 o
(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
5 R! R5 o- C; p" P7 X
( g1 K: }, E8 X1 h  W! o5 h4 q  读取函数
7 e+ M" Q- u( N& z4 ^
  1. /*
    & ?: U( m7 c3 a" n6 Q# W
  2. *********************************************************************************************************
    9 ?; _8 M+ c0 J  Y/ J9 n! z
  3. *    函 数 名: Read) S; ~4 @+ z! a" c7 ~: E1 T
  4. *    功能说明: 从SPI Flash读取数据' u$ C" j0 y! o2 {) ?$ E7 b: l: ]
  5. *    形    参: Address 读取地址' ?9 H% M. E4 r7 S# q0 j
  6. *             Size   读取大小,单位字节
    - L. R4 e, p  T6 b
  7. *             buffer 读取存放的数据缓冲- ~, M+ q' q" ^) g  f4 V. z
  8. *    返 回 值: 1 表示成功,0表示失败
    0 {) ?1 [5 V5 X) U
  9. *********************************************************************************************************
    * C* g8 T1 \: _- O) W$ n% [
  10. *// Z5 K% c! E6 c# H) N* ?3 R$ g
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)$ B- ^0 _( V1 H
  12. { $ F# k/ }% X2 T2 u' u2 O$ H
  13.     Address -= SPI_FLASH_MEM_ADDR;8 t3 U1 U2 L* j  p. @; j& q
  14.     sf_ReadBuffer(Buffer, Address, Size);# C, w6 ?1 l" ?1 O
  15. # w! v* d! `, }
  16.     return 1;
    * h$ l9 r/ E& e+ `
  17. }
复制代码
; S; D& i* |: I, r% i/ B
  读取和校验函数* g- V. n+ `% R" ^/ R4 ^3 |
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
- o* F, }7 V# x1 d8 o5 f1 [
8 \2 b+ H+ i6 i) c" U85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)+ Z# `2 O2 Y6 s* X0 Q
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:6 H/ l! e/ V: X2 m3 D3 X2 r# Q

9 u) ^$ O+ C0 C. N4 x$ ~: n+ ]0 c& T6 K
  1. /*
    # H1 {. f' z" [
  2. *********************************************************************************************************
    - ]) Q. X3 ]% E% B' x; G8 O& R
  3. *                                时钟,引脚,DMA,中断等宏定义: l3 z4 [' e6 J# c5 ^  s6 r
  4. *********************************************************************************************************
    9 \, U# }+ v: c; S" S
  5. */8 t- L* ]- @0 f) `
  6. #define SPIx                            SPI11 X3 f0 [0 u, ^6 J
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
      X) A$ P6 M* a* Q' T& }$ @3 h) N, R
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    0 F: w5 @4 F  E6 v% {: T
  9. 8 W7 E4 @% X1 W1 E1 s* U3 j, ]& g
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()% b2 @3 A6 R3 z4 |  t: s' ?$ V
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    ) o/ A, b! y- q1 l% W/ ~4 a; R

  12. 3 i/ n7 v; ?# @- P9 g
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()) m- H& @. S# {0 q* W5 E
  14. #define SPIx_SCK_GPIO                    GPIOB
    . ~' F0 j0 P- a0 L1 j. E
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    1 Z, Z' r' A7 {$ D4 t4 X# L
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI10 Q1 ~" c% X" ]/ ~6 O

  17. ! |( D3 M: R; T" ~( A
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
      S& n* s8 u  l  v0 _
  19. #define SPIx_MISO_GPIO                    GPIOB+ j, ?, X! C) n" Z# N. ^
  20. #define SPIx_MISO_PIN                     GPIO_PIN_44 c! l. q- I0 }; Q
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    & K* s# {& ?/ _8 f6 C/ F

  22. + O' ^+ c- ~7 v, X4 l
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()  j3 G; `  M: h8 e% l/ ^  U
  24. #define SPIx_MOSI_GPIO                    GPIOB
    , T: v# e1 x- ]! f  A4 J
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_56 o6 W. w' t9 R$ T6 z0 N
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码
, z( K+ `+ j  l
硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:' Q' S/ M. ]+ D! Z; \# }

: `8 P3 W* K" O8 [. Z主要是下面这几个:
5 E+ k& O2 O: f6 z
, q/ T: X& x3 C
  1. /* 串行Flash的片选GPIO端口, PD13  */
    " l+ F" Z. a( Y; L- s
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()3 M/ z9 G" s' P, |
  3. #define SF_CS_GPIO                    GPIOD
    + A6 }  ]- c8 x$ j( k$ y
  4. #define SF_CS_PIN                    GPIO_PIN_13
    + e; N9 [8 \7 P' N$ k5 |

  5. 7 M4 t) ]' F6 v9 B
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    & S( D- x1 N" n) G2 V
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
    7 \5 T( d2 a% x, Y. F: J& J; y, w' P

  8. ( m2 i' }0 s+ L$ t/ V. e
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */+ Y- g- M; @( o+ u
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
    ; x) }& b6 F* b# I; Y! j+ t  @
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */# \# W/ K0 ^/ H, M" ?& m. b+ @) R
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    9 B: \; w  R6 z1 G+ W
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    8 ~! N3 ~% L/ R7 v& f5 @7 E# U
  14. #define CMD_READ      0x03      /* 读数据区命令 */: i3 M, x% }$ ?/ _0 x  ~$ J
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */! H/ S) e8 l" s& v! R3 c
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */
    * j% n  u: Z0 h" K  m
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */3 s5 I. l- R& \- _( B
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    - J' _+ s; G' ?/ }
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */
    ' ]8 d' f6 f; Q% b0 A

  20. 5 _0 A' P1 O; I" ^; R; }
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

/ T1 A. t" F  i' y' ]85.6 QSPI Flash的STM32CubeProg下载算法使用方法
6 ?- {* n6 q4 h; @6 r0 ?! X4 A% j" v0 f编译本章教程配套的例子,生成的算法文件位于此路径下:
5 c+ Q; d. Q5 Z+ \+ Z+ u
. Z8 c+ q! M( i3 B. @
5ec6d402a7646232a55f4834cd00ac0c.png
8 j6 G) I. C* \+ e9 r
' Z5 E1 ^0 t5 x5 Q' }5 [1 D
85.6.1 下载算法存放位置
( ^3 r9 z  S; Z8 d6 t9 [+ T5 T生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:9 e) [' Q% x, Y2 F; N

8 n5 U$ S% b7 X: x\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
( C* L5 i- q) E& _  a6 G3 w( f' Y# M
7 o" w8 O1 n6 ^# f
2609073778af5845a869911ab36624f4.png

9 n$ B& H0 }& l
. F7 t' r! I' N: D2 L- s) Z' p0 K85.6.2 STM32CubeProg下载配置5 ?/ e9 V# ]: l, t. Q
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
3 O8 B  b' ^0 t4 v$ N: T. c1 Z: q' ~: f; T
d4e256633eafdbf4bcd8cce6e3b5909f.png

# {* h# |- h+ A& t9 p- [1 w1 q4 A* T6 H+ A+ Y1 y% C3 U- R9 p
点击Connect后效果如下:
; w- R, W6 G0 B$ Y1 d/ R7 |: \) F5 c7 a: D! U# L6 p4 r7 Q7 e
( H. m* y) H  T
3 l8 @+ C+ L" }: d8 g
在这里选择我们制作的下载算法:/ w/ F8 l: n6 ?* l1 m

8 w' }  x5 f7 e! N: l! Z2 V' R
d4fa28f64be2de98e04f3188eb2fe09e.png

) i# Z5 e; G8 N2 M0 n2 D* r7 e) D/ O. Q, Y7 D7 i1 [+ b
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
8 Z4 {: v, b( T. N: \( n- k/ r8 v. n
842518a7cc1dc569598d372132b2ee58.png

3 i$ B* {8 J7 E! ?4 V+ D4 E  z0 {2 i; G' h$ e: u+ F
下载完成后的效果如下:9 `3 H6 @$ F  D$ h* _
+ {+ @8 m  W  p( ^% r9 T/ Z
68a92f267829ca77f125db982d6c0479.png

* Y" Q) N0 P1 x/ [2 d) Q3 O; f* D5 m! X  O! E4 R, G
85.6.3 验证算法文件是否可以正常使用( R9 g  Y' {; d4 G! d' R) N7 w$ T
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:- K2 `3 {/ |* P9 h; h5 [
9 n2 R0 o: s$ j* A
566d2fba421c710da70e5656c6934466.png

9 m% b$ f. b- j2 }0 j
7 V& p; a( w" n# I/ V85.7 实验例程说明
7 z2 D1 f# @" [# X8 |) i* Z' Y1 f
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。2 ~* k9 ~; v# l- v% W
1 f6 a, D. F4 P) Z$ F
编译本章教程配套的例子,生成的算法文件位于此路径下:
9 k3 t9 t& |0 f, G: d+ n" f4 r5 L6 t- K3 Z+ u& n! c3 B
6108d1ff7e349f2aa268ee31804c57ca.png
8 B5 j; Z" z) ^' H6 X  M

4 m  R; A# b4 Q85.8 总结
2 f: h: F  d7 T# o2 |' Q6 t/ K! e本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
3 P1 T5 j  u+ M! \) }( D' T9 c6 \+ W0 X: \$ C4 n9 I9 ]

/ J" K6 d, r& x: ^$ c' @/ H. |5 C
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
赞 收藏 评论0 发布时间:2021-12-19 15:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版