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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示, b* o. x1 ~5 _9 U  _- E* D/ g1 q
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
8 L5 A& j# u6 }+ s* ]2 J  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
2 W1 F. e6 j- q5 r' W; p! ^8 H  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
2 V7 H3 B% ^* x5 I85.2 STM32CubeProg简介/ [6 L: M3 }/ u  u0 M5 a" J
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
9 G  S! Q: m' |2 l: R9 S! j' Y) i: c  }1 o3 }, I# @" v
d80578d217eb2a83520c6f3d797218f4.png

" J# k- R3 A  [6 Q( Z' z! Y8 z: h: F/ {
85.3 STM32CubeProg下载算法基础知识

% X- S) N1 {4 zSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
8 {2 j$ r$ T1 @4 n, u1 U' x; N& v4 h+ D' ?6 N
85.3.1 程序能够通过下载算法下载到芯片的核心思想; @' D! P* E# m
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。" p0 f! W8 A" E  X  e! f2 k3 |
4 E0 L3 h1 s8 U" U2 h$ G4 K
85.3.2 算法程序中擦除操作执行流程- b5 j. Q. \& ?# S, Q% _# W
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。7 S! Y, {' U: w: v! _

) Y9 l; r" ~% f" ]" X- K' Z8 ^+ u- x擦除操作大致流程:
* N1 @! }. z  b, L0 G8 S+ K) O* t  `2 {& i0 v9 T$ f2 w
d75670256363bb50203bed0bbe4f9fd2.png

9 i6 N/ W0 n. s" b1 ]* O$ w9 d4 _6 n; W# p5 F- M
  加载算法到芯片RAM。. q4 f0 B& r2 Y+ a+ }3 B  H
  执行初始化函数Init。/ P  B- o/ X  ?# ~) C* j  H
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
& J6 y: ~( k) V0 e, P7 J! L  执行Uinit函数。. C2 Z3 z& P( G: c" a
  操作完毕。
& f$ ~9 e# b  w! k$ M, J5 W) r; A$ C( E; N% N
85.3.3 算法程序中编程操作执行流程7 I0 S, _& V; p, G; |5 u- p
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
/ E8 T# W; v& x% v: `  O
% |9 Z; s7 W: w9 c+ |1 j! A编程操作大致流程:
: \1 b' J$ g4 ?1 O9 v( k$ o
) w& r# x/ O$ z# d, Q
4fa96e9e33d700081f2cdbd312afb31e.png
, \/ M6 f- u) S$ b3 O( i
! M& b" O; j6 [+ F0 g
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
2 `- {0 {/ E: V# V) \& ?0 O  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
0 [# |4 M" J. |2 C4 i  加载算法到RAM。
" M, D" e/ F' o- r$ h  执行Init函数。
) X$ B* M0 u' j$ A5 D$ c3 U9 |  加载用户到RAM缓冲。
: U3 ^% _2 ^6 l3 @  e- W- F$ G/ S+ `  执行Program Page页编程函数。. }: ~* j4 L. R1 G0 s( g" v" ~  W
  执行Uninit函数。, `- G( }( p% v8 Q' S
  操作完毕。
9 Q' g+ ]7 b: O. `4 B7 V8 E; V9 k! t0 f$ w5 o5 O
85.3.4 算法程序中校验操作执行流程
% y# N& F( x0 K- V2 T0 J. E% K/ |注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。) W6 |5 \" p/ ]  M0 f  E2 |7 {

( z9 q8 K8 C) A. L校验操作大致流程:
* b) L+ p* S5 m7 m. O. V9 n; j9 ^5 s2 T& R, q# ^
7deeae4b650bd82c457492af14fc26b7.png
; I  B% `  n+ g; r( w

' U- }% j9 h4 l! _% T5 P2 L  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。, |! _1 W& J; _/ @8 e( ]
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:1 K' |" R; ?8 P, f: a) ^
加载算法到RAM。+ F7 l6 J* z9 F6 k* V
执行Init函数。0 r/ y$ f9 }& M" Q5 t' P
查看校验算法是否存在* n  S: j' k/ T, ^
  如果有,加载应用程序到RAM并执行校验。8 F8 s3 P; n; `  d
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。6 y) ^# F. C! n: h+ |3 e/ N9 P
  执行Uninit函数。: k- I. P5 M# q- n" V
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
( c4 G2 d6 o* r4 J( R) @+ j1 [  执行RecoverySupportStop,回复支持停止。
/ v9 D, {1 n% {) g  Q  执行DebugCoreStop,调试内核停止。
; l, I# l5 a; x! }7 u" v  D. ~  运行应用:& n" k9 ^7 ^+ Y! Z! X+ X! I- k
  执行失败7 E" z6 L$ Q$ X+ c  L; S6 s5 g3 o
  执行成功,再执行硬件复位。; E" L" v# M8 t: ]
  操作完毕,停止调试端口。! a, z7 j9 R$ D- z) j" C2 x

. B  G0 l7 N3 v85.4 创建STM32CubeProg下载算法通用流程
3 L( y$ `. B5 u- Z/ C- z下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
/ ~6 Z! r4 x. O. K# ?: @4 F; Q, J
0 W0 s* [% `$ e9 Q/ S/ I85.4.1 第1步,使用STM32CubeProg提供好的程序模板
" Q0 w0 J$ R; g$ ~位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader9 W+ p* @" ^. H* b! M, Z  U
0 T6 T1 v2 S; U0 c4 W3 w
97f385e3b62a055bac9b5c0214b877e5.png
8 e/ [8 k0 J  T1 j6 b* p

  C$ J7 m) ]! t# p7 w以M25P64为例(注,其余步骤就以这个为例子进行说明):- N# ^: V$ O: K, N* y' p. r

) V$ k0 U+ F- s
43b298f996937fe35e89697fc6479cb9.png
( j! A* ^% G& t

1 i3 W) s6 P6 N85.4.2 第2步,修改工程名
! ]) y4 B  y) H4 O8 B# r; {$ h4 SSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
$ V* V: d3 D5 X6 g2 d0 E
9 ^) v5 G. d3 D% j8 W85.4.3 第3步,修改使用的器件
5 @0 {) z4 _9 D! W
在MDK的Option选项里面设置使用的器件。9 |/ b( p2 `& \* W% `" B$ p

0 L9 K% P# a7 }) ]7 N- t
36026b9a6d60249610d0246eb725b246.png
# k  e/ p  w4 ?2 v" z- p0 L
  H4 Y* B! A$ K+ i- m9 Y" c
85.4.4 第4步,修改输出算法文件的名字  y: g0 _. {: W- i
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。' i4 i$ P3 e# ?- Z* J

) Q" `  g  a+ a5 D
52c72b3993f28a366a22eac56f8fd043.png
  V( Z$ S# ]% i) A% d

% M& u+ F$ F1 G) O+ @4 V注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
$ f! a7 R+ E' ?! r$ p" ]$ Z
  V+ r$ L9 N$ N7 b! L' f* l% ]9 B0 q  Y
7b48153027a44de33926c2da008716a0.png

7 ?, h$ n8 y, f0 D) B, d' y) W4 E$ n/ P1 S! {0 |8 w1 R% R
85.4.5 第5步,修改使用的库文件和头文件路径
1 W8 h% `$ o: T8 c; a0 F根据大家使用的主控芯片,添加相应的库文件和头文件路径。4 s$ A- p7 G. V2 P

/ G) \- A$ ?5 R. q* p85.4.6 第5步,修改编程算法文件Loader_Src.c' Q5 l, [/ Y  x% |% n: K4 D( p: c, h
模板工程里面提供的是M25P64,部分代码如下:# s& a+ K$ @$ C9 |
9 e- Y! E0 v% p3 q
  1. /**& q+ h1 i" H! ^% R
  2.   * Description :
    2 F$ f& N- U, I, |9 [/ k
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the# Q- J% T" D% F* b& T
  4.   * device and initilize the FSMC with the chosen configuration
    4 e- z: d4 a* y/ Z1 i. L9 `
  5.   * Inputs    :5 v6 I$ U" ?4 G) y/ d5 L& T; j5 S
  6.   *      None( J1 B+ ~. c! L/ J
  7.   * outputs   :
    6 D) u' V2 J0 n$ c" }: N
  8.   *      R0             : "1"             : Operation succeeded
      a$ |1 T8 G9 k5 l1 J+ M
  9.   *               "0"             : Operation failure
    4 O: [, Y' m$ x% }2 K$ O
  10.   * Note: Mandatory for all types of device
      W! j" B0 C& B* p. x, b
  11.   */( H( i& v+ O4 E! A1 g! @/ ~
  12. int Init (void)+ U4 X! |0 d; j6 F: o) M; R
  13. {  
    . g  x* a0 R- L- |0 ]
  14.   SystemInit();9 ?4 ^- C! s/ T! p. j5 @- W+ s; {
  15.   sFLASH_Init();- ~' j) u$ N/ y8 l; e' F  [' W% v  \
  16.   return 1;
    ( x6 x. q& ]3 I, }: e$ t
  17. }& p& d0 B& Q' h, h8 g3 I
  18. 1 T" R6 [3 x+ n# Y! g- c5 I: C# z
  19. . d& @* V* H# m% s
  20. /**- _' A$ G- Y2 i1 O8 O2 u8 Y
  21.   * Description :7 R9 e1 V! ]( a: O
  22.   * Read data from the device
    " C( `- D0 K. H
  23.   * Inputs    :
    : U/ F: x6 m! E3 U0 E6 J2 A
  24.   *      Address       : Write location
    ! Z6 X! i! O- o5 s6 p1 n; G
  25.   *      Size          : Length in bytes  
    * S! g& h* ]" h; K3 a7 P: K
  26.   *      buffer        : Address where to get the data to write, F" x+ A* |" W  [+ y
  27.   * outputs   :- Y8 P1 F' g0 s! Y6 A
  28.   *      R0             : "1"             : Operation succeeded) U' p4 V# a  I& ]- `) D
  29.   *               "0"             : Operation failure
    / N6 I8 A4 N! G- y0 ?" P
  30.   * Note: Mandatory for all types except SRAM and PSRAM    $ b, e# H+ P! r/ j/ D  I+ G( g
  31.   */
    " f3 |% I: A4 {6 S, t  _
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    3 W9 d$ d& y0 _. U
  33. {
    2 x) F+ D, Y) h2 Z) g. _
  34.   sFLASH_ReadBuffer(buffer, Address, Size);# t! }: g4 M* P! l% a3 c& Q( a$ v
  35.   return 1;
    9 `  N# v8 \& k1 w; N" _
  36. }
    % v6 v6 P& E  ~3 F- ^$ M
  37. 1 `# [" n: I  J% [5 P% _% Q
  38. 2 x- C: U$ }/ G, f8 A& \2 i
  39. /**5 C# N% h& G! v( x9 P
  40.   * Description :
    : e' o) Q+ g" J) ]& m
  41.   * Write data from the device ( ?; D. f1 S" T  m* }
  42.   * Inputs    :
    1 f) B8 S, E6 `( }+ p7 O/ |2 m
  43.   *      Address       : Write location2 M% G* A; O5 n+ V
  44.   *      Size          : Length in bytes  
    6 y0 U( a) ]1 s( Q: T
  45.   *      buffer        : Address where to get the data to write
    . w4 P* m: h/ R! v2 f
  46.   * outputs   :
    7 r6 q9 |  n, H) f/ |1 F' ^
  47.   *      R0           : "1"             : Operation succeeded
      k0 Z, \+ X3 f& z) ]/ @3 s" V
  48.   *                     "0"             : Operation failure  t6 E# e+ G! _# F9 m3 ]) D
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    8 {) s/ t% }8 E. U4 |" c
  50.   */
    * {" i# n/ A% u  Y4 g' w# {
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer): |: p* g3 |  u7 R5 P" }& Z
  52. {3 i+ w9 T* q% z! f$ L3 T! f
  53.   sFLASH_WriteBuffer(buffer, Address, Size);1 Q8 `" Q1 q( I  h* E- z* s
  54.   return 1;, l2 q& J& ~2 Q0 H5 n, ^: ^
  55. }
    - h9 w8 _3 `/ O3 e8 A' }) X
  56. 3 c7 k* F8 s( W& x
  57. % I8 S& e* r! t/ k. }
  58. /**
    , y4 t! l+ _7 n- b
  59.   * Description :  x# s; @! {5 N9 q. _
  60.   * Erase a full sector in the device: [* C5 Q7 c2 n0 c: V
  61.   * Inputs    :
      r& G: z8 C$ ^7 r
  62.   *     None
    + c( U4 _$ K, ~& m2 i* m
  63.   * outputs   :
    " P8 `. I" {  b4 W- ~+ ?! p
  64.   *     R0             : "1" : Operation succeeded
    ( K# ?, G* y+ H$ m" [
  65.   *              "0" : Operation failure
      k) |& v9 Q. O. Y! p7 R
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH; t) V2 r* A! z% Y; [
  67.   */1 E" g6 x/ {% O  |. F2 X
  68. int MassErase (void)4 L8 |9 a+ e, j4 g2 r/ h
  69. {  
    1 E9 y  Q0 Q6 f1 n
  70.   sFLASH_EraseBulk();
    2 S: V9 M4 }/ v# ^
  71.   return 1;    ! i% ^1 w7 d$ J7 ~, a5 B6 Z0 n
  72. }
复制代码

0 U$ b0 H- i: z6 X& ]8 a( a( k85.4.7 第6步,修改配置文件Dev_Inf.c! G0 t$ ^  K8 @5 \
模板工程里面提供简单的配置说明:% u1 ], q! T! I, Y2 Q; P

9 ]3 T# t! Q2 {: T
  1. #if defined (__ICCARM__)
    9 ?4 o: d# y: X- E/ x
  2. __root struct StorageInfo const StorageInfo  =  {
    . S* l# W: }- z+ r( {' {% K
  3. #else6 B7 A9 I* q+ m4 f1 D
  4. struct StorageInfo const StorageInfo  =  {
    ! g) o3 ^/ ]" G% J4 d$ K
  5. #endif( B- i" y+ U5 ~% u8 ]& b
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    . i; t) T; q+ t# z) h( Q9 u
  7.    SPI_FLASH,                       // Device Type  T2 _/ }  E0 W
  8.    0x00000000,                     // Device Start Address
    7 b4 f2 @7 N, p0 _* o1 L2 w# u  L( Y
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    $ W  Q" z8 p. n/ \, ^0 n% f
  10.    0x00000100,                      // Programming Page Size 16Bytes
    , R9 o9 x! t2 n* Z& ?3 _, y* t
  11.    0xFF,                            // Initial Content of Erased Memory
    ( ~8 L5 B! R6 {& f7 C' L/ j+ s/ V
  12. // Specify Size and Address of Sectors (view example below)) M) i2 q+ J! h7 h& u
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes ! g4 K9 H2 B! C4 @' d
  14.    0x00000000, 0x00000000,
    - _6 S/ f3 w" J+ }2 Q3 s5 z( C
  15. };
复制代码
0 O8 b; w  G2 v/ ^
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
) p# y) j8 q; g9 T& _& E* \" z5 b/ A" d! x) `4 t* f
85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。

# B  k+ \) d* P- n1 ^C和汇编的配置都勾选上:
9 [& l: T6 Z; x. R7 \3 T
- \4 G4 h% P. v6 d( \& G% _7 R
afddd6e839292aa2b94b43e79544d630.png

  b' a  L, v% L$ u, R' p
9 H# B  N5 ?( e汇编:
# g+ j! R; }0 U$ M* C, w3 D% Y( v
- l& @5 P) l4 w' D
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
8 S& W5 C; e- F6 z3 _

, k' p0 G, _( I0 {, F+ ?, Q9 v( s' f如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:  X# E  \8 t- M! q, U) q7 o1 T2 J

. [, h: d( u) \; d) V# G(1)加载以响应运行事件。, E' x! G6 ~0 Y' `
$ ?0 i% Y3 \8 ?+ r) U* {
(2)在不同情况下使用其他例程的不同组合加载到内存中。
, s0 K1 ]: O+ ?8 o& v
$ [; \0 y/ w' M1 D' N5 ]( \. ?(3)在执行期间映射到不同的地址。3 B- Z  j4 x9 y$ |( y0 M
3 x9 V: `$ t! h# C  K
使用Read-Write position independence同理,表示的可读可写数据段。8 }" l, V7 f5 [% L
1 C' X1 g* w1 v5 h1 p$ [
85.4.9 第8步,将程序可执行文件axf修改为stldr格式  F3 h! y$ O' y9 |/ Q& f7 {
通过下面的命令就可以将生成的axf可执行文件修改为stldr。
5 P* ~. p3 {  f; @6 k& X
$ c# X$ l/ Z, r( C2 t( Q
c683673d2481d8dce75b6fa01d624600.png
/ E1 ~& o( `% S
+ A$ ^1 h0 V4 \
85.4.10   第9步,分散加载设置0 _  C4 X0 u$ N* P3 ~' `; S) I
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
# _6 o2 G7 e8 R& A. S; w& J* B% i% i; x/ R6 x: x. q* C
b96835a7506516319002e6b2e8db9371.png
$ q, }4 B6 Q) j! A+ \
7 w4 Y& d: Y' r* c6 T2 C1 [
分散加载文件中的内容如下:
+ \  N' {' G" S. W$ Y
+ n5 M9 D+ U7 i$ _- `9 ]$ b
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions5 w3 H6 g1 m. I: Z
  2. {8 v5 n  J+ x3 `( C" Y  D/ d) O
  3.   PrgCode +0           ; Code
      U1 P& V+ }, t/ v* \
  4.   {
    ' [8 ^2 k$ H, @' M
  5.     * (+RO)8 a( @6 s  v5 u
  6.   }9 z* ^6 K% D6 w8 Y
  7.   PrgData +0           ; Data
    . Y. N: @2 }" x) d( ]
  8.   {% y4 {( U3 n9 ?6 I9 e
  9.     * (+RW,+ZI)8 C3 c5 t/ ?9 S: q6 {2 H- y
  10.   }: _% i# t. ~# c/ ~1 c' o  d) {
  11. }
    , c- v4 o% N% r' q; O& Q3 l3 g( G
  12. 9 e) I- p6 }  g& H) z: _5 y8 h
  13. DEVICE_INFO +0               ; Device Info1 f; U, y% @, A# |8 i2 \- }* p
  14. {0 J1 \' Q) N6 d  z
  15.   DevInfo +0           ; Info structure, j$ y, Y/ z* d4 C: Y% W
  16.   {, L: v! t) n8 }- L0 Z8 i
  17.     dev_inf.o
    7 r( W- u0 _1 P
  18.   }+ B. m' \- [+ E( N
  19. }
复制代码
8 x) l- D4 T  S& ^, J8 Z* c4 y- a
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
4 ^6 o8 R+ [! ]+ ~# V3 B6 J- G7 H; D
--diag_suppress L6305用于屏蔽L6503类型警告信息。
: x2 G+ w" W4 R1 p
& L  I7 \. h3 P# e; `特别注意,设置了分散加载后,此处的配置就不再起作用了:
% s. |6 `2 p0 W
) G# F( C- p9 Z, R. e
641395c374a3af02f1a92d3ff9ee4199.png
* T  m$ ^9 w$ L* A6 t
4 S) d% L2 m# t. x" u5 n
85.5 SPI Flash的STM32CubeProg下载算法制作
0 ^" h9 }1 \& d3 ?下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。/ }1 {9 L; N0 ?# z' e& i

8 N+ z- D) D. `) r: J6 x85.5.1 第1步,制作前重要提示

1 n" [$ ]# X) t/ P( G这两点非常重要:
3 ~% W1 C+ ^# g! }: T* [2 F: q0 b. u  v( P1 S0 c
  程序里面不要开启任何中断,全部查询方式。
: i; h" o# s, Y8 R2 E  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。% N0 C0 x  P, q$ D: J2 \
. Y- W+ H$ y" s! g3 d! S  ?
85.5.2 第2步,准备一个工程模板
7 v) D, n, c; w 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。: G/ {$ w8 n1 T, j7 n6 X  v

; [3 [* [# Y2 j  t3 h$ q
80ad5e3abdd6a993c8621230401a1313.png

1 y7 T, c& \2 m
( \6 p/ r; Z5 j85.5.3 第3步,修改HAL库( i+ G3 l' u3 B/ v( W$ ?2 j8 G
这一步比较重要,主要修改了以下三个文件:4 C- H! Z; F+ E3 k
2 v0 T  Y, w' [6 i
33bf89f25487547d4008f43b7cf10e03.png
6 u( i* A+ t* K# h) o0 k+ z

# p- x) N+ A/ ~* L- t主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。2 T) @4 `% u7 t2 O& Z
8 d# l* U6 D0 v; {
85.5.4 第4步,时钟初始化
. d* p* ?: w1 `2 V我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
$ X7 v, i$ u" a) v' s, i3 r5 K2 J% E4 r( B
  1. /*) U5 b$ W) {2 T
  2. *********************************************************************************************************
    , L, J* @/ p6 Q! G, x
  3. *    函 数 名: HAL_InitTick
    $ B" |) |. P$ z
  4. *    功能说明: 重定向,不使用
    ( l& R4 ^8 b5 C
  5. *    形    参: TickPriority9 A* I% ]  n3 z6 `' p
  6. *    返 回 值: 无" q( h# n1 E, [' Y/ p6 U: C
  7. *********************************************************************************************************
    - o0 q2 t3 ^6 `1 Q) F4 O4 f
  8. */
    3 e9 [. I+ i+ p' v, [
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)( R; y* E# P8 ?, }, A1 G, }
  10. {
    4 t& s5 h0 Z6 K, N: L" h
  11.     return HAL_OK;
    0 i9 _4 W: b6 e
  12. }
复制代码
$ m/ l* I2 s* A* A
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:2 {# s, h4 f. X8 f

# t& B4 G: K! E" J) J7 ^8 X
  1. #if !defined  (HSE_VALUE)
    2 L+ ^3 {& ?  I; x2 N2 j& C
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    $ }! D0 P$ R" K6 e0 s0 L
  3. #endif /* HSE_VALUE */
复制代码

; S/ _: z0 o# R" g7 f+ z最后修改PLL:
7 U3 g$ x: z  P( p& d: \! o8 H7 c
$ |; Y2 v$ z- e+ I
  1. /*
    9 f; D& s9 \' m; l
  2. *********************************************************************************************************& {# O2 o4 }( y+ j2 F
  3. *    函 数 名: SystemClock_Config/ W- e! u- ^, \2 U, _5 O) a
  4. *    功能说明: 初始化系统时钟
    4 ]9 s0 h, A, ~* s  I7 r# ], ?/ E5 L
  5. *                System Clock source            = PLL (HSE)1 O6 X: I: F) q, D4 {
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)$ O; _% \& e4 }3 [+ R
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    * R1 h2 x6 i6 n3 t
  8. *                AHB Prescaler                  = 2& D: t. _) M1 ?! h5 |& {
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)+ D' s4 c3 }9 U+ `4 E4 |: p
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)3 n! F( c* Z9 h
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    5 D# s3 u+ a4 v. [/ e6 B
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)4 h9 x! l4 W! B# B7 W
  13. *                HSE Frequency(Hz)              = 25000000
    ) I2 i+ z5 k& q" v# ~9 Y1 G2 L2 y
  14. *               PLL_M                          = 53 `' b% G0 Y  S$ g4 M+ m7 \( d
  15. *                PLL_N                          = 160% V# O& V8 z# A+ z- X: _
  16. *                PLL_P                          = 2; t; H) H# t" H4 N0 l) O
  17. *                PLL_Q                          = 4
    * v8 ?4 [( G: I
  18. *                PLL_R                          = 2/ r) n- ]" p" O* X5 E
  19. *                VDD(V)                         = 3.39 q" s0 b, n5 i0 z7 S8 o% o* y
  20. *                Flash Latency(WS)              = 48 E" M% }: S( M# D4 n7 E
  21. *    形    参: 无
    . B& A$ f3 a, s) |+ J6 }4 W
  22. *    返 回 值: 1 表示失败,0 表示成功
    & w- M7 ^8 n# v) Q
  23. *********************************************************************************************************
    % j( v  U# _$ I" j  U6 c1 X
  24. */6 @1 H$ _2 @+ F( M
  25. int SystemClock_Config(void)
    " q1 F# }- K$ ?) E2 {/ S0 M& q0 _5 a* \3 _
  26. {8 V0 I/ B% y# e( O  K4 o
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    3 y8 @# P7 O; y; Q
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};' R8 H: J$ U7 r+ }- m" F% W( V
  29.     HAL_StatusTypeDef ret = HAL_OK;
    5 l7 u9 k- }: I+ u5 Z( o/ v

  30. % o+ K& [* ?/ m0 ?( d% R% m8 ?- }
  31.     /* 锁住SCU(Supply configuration update) */% R: G2 b7 f: m1 \  L
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);4 G9 _$ k0 B( Y9 g0 h8 |0 G+ \: ]
  33. 0 ~1 w& _. z, Q' y, C" ~
  34.     /*
    - ~# X) [; w! g& y# W- V
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,1 S$ x; }& \% B( s  i6 K/ h
  36.          详情看参考手册的Table 12的表格。% r  E. b8 d$ d% \
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    ' P6 A. }7 `# H
  38.     */
    + w% ^; ^/ f, f2 {! \$ M0 E
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);* Y" z1 ~5 J$ Z  Y

  40. . ~$ B) o3 b! c1 h' K) _
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    / F6 e' h& X0 j- N( Z7 q

  42. # @" p% T+ ?, b$ t# d9 t, r8 O4 {
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    + z4 p0 d8 j7 ]+ a! D+ R
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    ; ^; X0 Y+ v) ~$ s4 U
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;6 U. D9 n2 e- A! a4 o% N
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;5 \. r# w0 ?% X: U7 e* E
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    ' o, Y! H- t8 K4 q8 v) R
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;. A# b( S# l7 K  E( W" G
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;+ T& o# r: o+ h" t+ I
  50. % Z+ J& I- c+ U
  51.     RCC_OscInitStruct.PLL.PLLM = 5;5 F' v* y! y, @) q6 M
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    5 Q; ^6 J: t% q/ l) g
  53.     RCC_OscInitStruct.PLL.PLLP = 2;3 m" T% R7 x% o+ B
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    6 A4 L) Y, I1 t% G
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    : `; U7 K0 w* L0 D. E0 `

  56. $ m+ }6 Y. ~% p- x& n
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    ( @1 T' y3 }: t% Q2 L8 E$ g
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    & C3 p9 {  k+ s6 Y: d. L
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    - z) v4 e7 n, c8 r- L+ g4 M/ v/ e/ a
  60.     if(ret != HAL_OK)
    ' b/ }1 B0 I7 |& S
  61.     {
    0 O2 e' j. X1 v. U
  62.         return 1;        3 c4 z& Z6 f$ s' _) l. ~
  63.     }( l% R4 I4 [  m

  64. $ u# u2 X. L# _0 t) |% v9 [
  65.     /* , h/ ^' K) ]- ~+ N* _5 [
  66.        选择PLL的输出作为系统时钟
    2 b4 h: O8 h; u9 l; n* h" m- m
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟& M! ?  ^3 L# U& n: y
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线3 x3 ]5 m; H% j* K
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线5 p3 @* x8 m! P3 Y9 M3 o
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    2 D% {% N. a0 f) H$ }
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线1 m3 ]( ?( D9 m) K0 x4 F, [
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    & P- B0 K5 G$ h6 j2 i
  73.     */' ]+ ]. X6 a- v# Y" F6 E- ]2 e5 p
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    : `' y* z$ e+ m/ j7 t! U
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);) S& z. C( ]4 ?; `+ S

  76. 1 a- D' ]- q; V7 _
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    / g& H% T- ~9 Y8 f9 B+ a2 W
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;( r; H) t  t) h) Z* e4 l
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    % X5 @2 o. \, P  P% K- o% P
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  ( v, u5 E5 Q! |5 k1 n/ t: F  S' s1 R
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; 7 @+ B  Q+ D$ N6 M( o: l8 x
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    + }7 e) k  `  v0 M" @
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
      g2 r) I+ C/ K- n
  84. " W& `0 I% ~9 X
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */6 G+ ?& U; A* Y9 f
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);% `/ @9 J5 T( R! I
  87.     if(ret != HAL_OK)
    4 m, A$ I: O3 @. j5 y
  88.     {
    * I) Q6 B0 K" @( G
  89.         return 1;
    # e  f1 R" M2 n4 @; e
  90.     }2 W+ _& g: r( E

  91. 8 [$ l' `, a# r; d; S
  92.     /*
      z# K) x1 i3 O9 K2 f7 |
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    , i1 }$ s' C" K; F% I; X
  94.       (1)使能CSI clock+ Q; h- B# k$ T4 u
  95.       (2)使能SYSCFG clock% ?' R' I* ^( d# R: c8 e4 o
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    ; \- g* l: E% J3 V5 v
  97.     */, M( }  D  q3 g
  98.     __HAL_RCC_CSI_ENABLE() ;- y4 P4 z' M6 J8 J* Z" M7 }
  99. & k2 ~6 B3 V' E' F/ ]6 [
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    : _0 z  K2 S- F: I: _

  101. 7 R& Z% A8 }, L2 K, [9 \% f+ V- T: U
  102.     HAL_EnableCompensationCell();
    2 A9 _/ v2 Q8 B+ ~: W% @3 G  u& ]

  103. 8 x1 x& _; U7 e
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();/ l8 i+ y( J/ G7 x7 ~& C+ N
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    3 ?1 ?" _/ D- V5 g
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    + E/ t; B, g& ?! z2 @* W& m2 f9 J
  107. & B& [2 S; a4 U4 X5 K7 Y
  108.     return 0;: C' o: c& E3 ^: @* ~! a% z
  109. }
复制代码
, d! _' V3 N6 ?% O  b4 T- B
85.5.5 第5步,配置文件Dev_Inf.c的实现3 K3 ~- u$ f: b- p- W) U
配置如下:
' B# \1 \% f0 j7 S" @
/ G( W8 O- {4 l! E
  1. #if defined (__ICCARM__)- _3 |( X: |/ e  q+ N6 m. o. i: T
  2. __root struct StorageInfo const StorageInfo  =  {2 q, K: m3 n1 G( j* i. V1 A
  3. #else0 a6 Y7 ?/ B7 o8 h, c! L  H/ H
  4. struct StorageInfo const StorageInfo =  {
    ( K- ^2 I; ~% C! v+ k; \# U0 w
  5. #endif3 B( P: E7 [) Y4 d+ C! L
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    4 N) R' W; A, C5 _6 F9 Q: w
  7.     NOR_FLASH,                      /* 设备类型 */
    1 M) v: D( |) I5 Y4 U2 I
  8.     0xC0000000,                     /* Flash起始地址 */
    & z  E. x$ b. w
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */2 ?# f+ x* \+ r# a6 k5 [
  10.     4096,                           /* 编程页大小 */
    # G6 i' o! V5 {. U  P- g/ Z
  11.     0xFF,                           /* 擦除后的数值 */
    . T4 |7 u- ?" i! a* F6 |
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    9 v* B  D4 W. V, r9 V' o  Z
  13.     0x00000000, 0x00000000,0 m# _0 r& i3 T
  14. };
复制代码

9 M2 {( n. t8 W8 r1 M; h& t6 t  \$ J注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:2 g6 R- w, l3 n4 E) p- g
6 H% l. y, ~  s" G
2 v. ], v: `7 A* u( ~

; V- F1 ~5 ]9 @8 r+ }6 M# ]7 Q85.5.6 第6步,编程文件Loader_Src.c的实现
/ K4 k2 ~0 ]* Q0 U$ h2 l下面将变成文件中实现的几个函数为大家做个说明:% M. G0 M& M" P% O
# Q! a+ `8 P1 j$ J5 i! Z
  初始化函数Init/ N* G* E) H  \4 l: V* n! v! H
  1. /*
    3 q9 l9 t# b. z" r) Z; y
  2. *********************************************************************************************************
    7 z6 h2 b  _5 l& L1 V' v
  3. *    函 数 名: Init9 b, `4 C' o% u, e: }& f9 @+ n" v: O
  4. *    功能说明: Flash编程初始化
    0 i: c0 ]5 H* ?5 n5 ]6 t+ l
  5. *    形    参: 无- e1 l; H  I, \; ?% H& S4 x% e, n
  6. *    返 回 值: 0 表示失败, 1表示成功  P, @4 ]: ^9 b! P( I) \
  7. *********************************************************************************************************
    0 ^" s. J+ L- K' ]5 v
  8. */
    7 l6 e1 [, v! x
  9. int Init(void)
    ! ?7 ]; N7 y) m3 ?* x, ?! I
  10. {   ) k7 r% n/ P5 S8 Y
  11.     int result = 0;9 s1 b0 ^4 {0 h/ r
  12. ! n# B2 |5 Z$ s  _, b
  13.     /* 系统初始化 */% e' s* n, b* l+ y0 I. e
  14.     SystemInit(); 3 R4 s7 W% o% _; f
  15. ! a& T( b8 n5 N+ o8 U$ a
  16.     /* 时钟初始化 */0 d- ]8 Y/ X) c$ V
  17.     result = SystemClock_Config();
    % u0 G! ~1 t3 r: o4 M
  18.     if (result == 1)
    7 K$ N4 Z. u( ?9 Q& a$ N" a
  19.     {
    ! f6 O' [4 D( U" y4 b0 f% B
  20.         return 0;, a- r6 v# f1 d" N  U1 Q6 Y
  21.     }) N( {& [8 n0 J9 [, p) n: d+ G

  22. / R, k5 l; v8 G7 e' C6 @
  23.     /* SPI Flash初始化 */, _1 K1 o% F4 W
  24.     bsp_InitSPIBus();
    + k5 x6 l$ F5 b6 Z
  25.     bsp_InitSFlash();* B0 ?0 \% h: n" k: x4 q

  26. ) I. |  z$ V( b$ {& f: V5 A0 h: z
  27.     return 1;  x+ O" Q! H6 C% s! t
  28. }
复制代码
+ }3 H# S% j9 R$ s3 V+ Z/ o  [
  整个芯片擦除函数MassErase
7 L) k2 f  U1 Q7 y6 G, a6 W整个芯片的擦除实现如下:2 j+ I) T2 L) h0 Z* @

8 z% {( h# O4 d
  1. /*6 p' x& Y& a1 R, O' Y, s; O
  2. *********************************************************************************************************
    & W) Q1 g. C7 E* h! V
  3. *    函 数 名: MassErase6 C$ Y4 Q' t# c& J1 c$ }( W
  4. *    功能说明: 整个芯片擦除* }: Q8 p) y1 \' N0 d
  5. *    形    参: 无7 c4 y! w/ z& g2 g4 W) `
  6. *    返 回 值: 1 表示成功,0表示失败
    ' h  T' }" j4 @
  7. *********************************************************************************************************0 L& D8 I3 d4 A$ K% w, X
  8. */
    , {3 O4 ~+ Q8 Z, k0 a
  9. int MassErase(void)
    0 i. S& y  q! d+ I3 S; W
  10. {: v" ^! u, w  s5 T
  11.     sf_EraseChip();
    5 F) n* n! e7 @# y) x4 A

  12. # n7 F( b" c5 r
  13.     return 1;   
    0 J! n. V( |& J
  14. }
复制代码
; C% m) n9 q& [( G
  扇区擦除函数SectorErase  N( R8 H, f7 }+ h
  1. /*5 Q8 t1 E( g0 Z; _3 A% B
  2. *********************************************************************************************************
    , u* H/ z  D3 g, f
  3. *    函 数 名: SectorErase( d. I$ b, Q9 X8 V
  4. *    功能说明: EraseStartAddress 擦除起始地址
    6 n# `7 a& k3 j) o/ y& }6 G, a8 [
  5. *             EraseEndAddress   擦除结束地址+ v4 q7 B9 f) L- l! B, C+ t8 z
  6. *    形    参: adr 擦除地址0 X; S& \$ k* q3 M
  7. *    返 回 值: 1 表示成功,0表示失败* Q- [+ U7 h3 ^# h
  8. *********************************************************************************************************
    / n! S1 ?$ Z% c# Y( j$ x3 P
  9. */9 W- Y: W8 W- K4 X3 |( B  w' g
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    3 V  ?5 c7 q# m# v
  11. {" ~1 g. H5 ^' }. |  T
  12.     uint32_t BlockAddr;
    . R7 X! i* N, G& P1 l: T) F
  13. ; N" R: E% C! d. L) m( v! {' j5 H
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;0 o, w  ]6 ^6 v$ S# S* J- }
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;2 Q, o5 M# N/ ]% _0 Q9 Z
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    % G4 L  V! t: W6 e9 L

  17.   g' a0 w: `) A/ p. M/ v
  18.     while (EraseEndAddress >= EraseStartAddress)
    " c+ y( d' ~2 `1 s% j" ?
  19.     {
    3 I. s* s$ j2 _
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    0 g2 q: E2 n7 x) |
  21. + V# \3 G# k  i; k9 i' }3 m
  22.         sf_EraseSector(BlockAddr);   
    " K/ N  a( ?% k% f
  23. $ `# X& o& w: S. o$ @) B: u. m( E
  24.         EraseStartAddress += 0x1000;; ~8 F1 Y% a1 X4 A3 m2 Z
  25.     }) O6 V* k5 V, G1 K$ o5 C5 f2 E2 ^

  26. 5 [0 s( ^& r6 Z8 |) D$ v8 |
  27.     return 1;" s& M8 G, S2 b# i; f) B% d8 Y/ G* _
  28. }
复制代码

) u* [% z1 V) ?: o! s0 t% m- b这里要注意两点:. i* @; X5 e) e+ P1 |3 |
% _# _4 p  Z) V+ @. R
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。1 `- u- [! g% q  Y
& _" ?( ^: w& H- P
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。( \5 Y$ f7 D) \# |5 t

& U! M: N3 A! H& @. t7 T0 ?( U# l  页编程函数Write
* N+ K1 z- K0 r9 D页编程函数实现如下:: o, p  O+ m. w# Q2 O8 \0 G# {3 W
! {$ _: k% V( _2 r/ z3 L9 E! U% r" x9 M
  1. /** v' Z# U* ?' c2 Y$ }2 A* ?! ~( {2 Q
  2. *********************************************************************************************************
    & s5 l- ~3 t8 |- K; E1 r
  3. *    函 数 名: Write* y) T+ G+ m( X9 m
  4. *    功能说明: 写数据到Device& B) }( z) F' ?0 B3 ~
  5. *    形    参: Address 写入地址
    ' n0 c# E! i' |" t- @
  6. *             Size   写入大小,单位字节9 }. `9 w1 @1 ]5 P0 Z1 x
  7. *             buffer 要写入的数据地址, M# K1 M& K5 Y. P- j5 T' i+ M! W
  8. *    返 回 值: 1 表示成功,0表示失败
      N( M! y% Q3 l9 _. D5 l3 s% Y/ c$ M2 d
  9. *********************************************************************************************************' y, v( f: d: `, d# k+ j
  10. */' q7 Q2 ]. V$ C! B1 ]8 @7 t
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    " u; u' k6 s' R6 v: E
  12. {  
    4 l4 C, t) M  J$ h
  13.     Address -= SPI_FLASH_MEM_ADDR;
      e6 I/ x  j' q

  14. " I+ w8 E5 w) p* I! ?$ f
  15.     sf_WriteBuffer(buffer, Address, Size);
    . q! n" g/ T+ p0 T1 T5 A: K

  16. " Y7 n! Y/ o3 o
  17.     return 1; - B5 V5 X& R. W& L& A
  18. }
复制代码
6 e2 ]  s& ^" f, P/ [- t
这里注意两点:
+ G$ G7 c; g4 K$ v$ {, ^5 Q2 V7 ?
/ T" D4 }( `0 M* c+ D1 W(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。
4 _: k- Y, D5 w2 V/ n( a9 u0 E# g! w7 Z; T) z
(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
) h2 A8 s8 B& `& |6 M( P1 n8 f2 J2 J: b  I5 p& Q/ A* Y, z$ Q3 @, M
  读取函数4 N: c4 W% @# Y6 O* V- h
  1. /*. C; F" {6 R! c9 r; ], D
  2. *********************************************************************************************************
    - P. _2 @8 L* `2 Z3 v$ K
  3. *    函 数 名: Read
    8 U# Z0 v4 g8 K- U5 b1 T
  4. *    功能说明: 从SPI Flash读取数据
    ) \/ R9 ]9 D& y* a% X+ w( ^
  5. *    形    参: Address 读取地址
    ' k/ m6 k7 _- x: D
  6. *             Size   读取大小,单位字节) g1 r5 g! W0 @+ h  t
  7. *             buffer 读取存放的数据缓冲- A% n3 Q* G2 E' {# e/ X1 v
  8. *    返 回 值: 1 表示成功,0表示失败
    . Y8 |! {+ Z  T, t) F3 A" ?8 P0 b
  9. *********************************************************************************************************
    1 _' H! n- c) S  w; A2 I' G
  10. */
    . l2 q. q2 |7 W3 n4 ^# U5 y2 S: w$ t
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    6 k9 m4 u& T0 a" R3 V
  12. {
    , [# D+ }# I% h: g4 ~
  13.     Address -= SPI_FLASH_MEM_ADDR;% g: _) e$ a6 V8 |$ A& `8 K
  14.     sf_ReadBuffer(Buffer, Address, Size);, o7 d) {2 ~2 }1 T+ a1 y( ^
  15. 4 \3 F% M2 x) K- P
  16.     return 1;
    9 V1 ?! R: C8 u
  17. }
复制代码

# n" L9 B  ?+ n1 D9 U  读取和校验函数
3 u8 x8 W. s- I) p$ s我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
9 J$ n+ v1 ]- {* X$ i9 i; H4 q$ i# L- ~& K& [2 D
85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)# p. U+ p: M1 H1 W- c
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:$ z+ x6 u; X5 _  [& C& m

+ J' [- F  s3 a# f6 S2 ]0 l
  1. /*
    0 {; [# y$ w2 O+ W
  2. *********************************************************************************************************# m7 v" c0 ?! D; V7 Y
  3. *                                时钟,引脚,DMA,中断等宏定义; C1 w) s; ~7 N; c4 o
  4. *********************************************************************************************************1 N$ U+ s6 l$ ?: r4 l1 L
  5. */
    . i2 n8 [  \7 g$ b% ]
  6. #define SPIx                            SPI1
    ( b& N- s# c: B0 {
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
      ~/ C# l/ [# L& W# X& H
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    7 \0 ~+ h, h; `2 x  J1 ~

  9. 0 U) I! i% a4 Y1 V7 Q# w
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET(): I8 Y/ y: S/ \9 m
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET(), v; J; W1 _* K! y' x- p& @. X9 A
  12. # H! ?5 N% }* r
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()5 k$ ~5 x+ R# m
  14. #define SPIx_SCK_GPIO                    GPIOB
    ) e2 R% N7 d" V1 G
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
      W1 O+ l! S1 R& Q0 X
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1+ n0 b$ b# @( e" W
  17. ( u: Q3 C5 k# l1 B% s0 ~
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    ( B% W! J' I! ^
  19. #define SPIx_MISO_GPIO                    GPIOB* g% a5 F1 e+ _7 M
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4' T/ k5 B$ h8 s: M2 ]& s8 M
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    + X/ V1 O$ o$ [" ^+ x
  22. " P" l7 |/ z+ F7 n3 G1 d3 n
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    5 y4 o5 X" U7 q; R, p
  24. #define SPIx_MOSI_GPIO                    GPIOB/ l4 d3 }) M' v' b
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    ( \! g; g' h" C7 ~& @6 a8 b  x) Q, z* Z
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码

! R6 x7 T8 A( L" D- x. O3 ?! @硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
! l$ O' f8 }! T- z  e- m& L1 ]3 y; V( ~" U$ J1 f3 I, |  O' n
主要是下面这几个:$ M& i2 I9 m+ ?

: F) o$ ]$ `; ~/ B  z) Z7 K
  1. /* 串行Flash的片选GPIO端口, PD13  */, r6 r% r4 y+ b% X) L4 J& _
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()
    4 {$ J4 n$ N  G
  3. #define SF_CS_GPIO                    GPIOD7 ?0 D6 g8 B; k6 R1 o
  4. #define SF_CS_PIN                    GPIO_PIN_13
    0 P* u0 y% S- r% n1 \4 O/ @, x8 q
  5. 1 V9 a% Z' c5 C& p' t
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) , o; o. ~, j& p% O( U
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN7 k' m1 k9 e" B! }

  8. ; c' |  p  l' S. n+ b# P; Z# N
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */+ I0 m1 G; \' v  C5 M
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
    6 v8 l1 [* T6 d: ]
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    ( _! y: `  _% u5 {5 H* L0 H
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    - O; a( @0 Y+ [
  13. #define CMD_WREN      0x06        /* 写使能命令 *// X: w* T! V3 Z0 Y+ n+ _
  14. #define CMD_READ      0x03      /* 读数据区命令 */, ^/ ?! Y9 W' W' Z+ Q
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
    ! S" l$ X, [. G' T
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */8 z) z( A: ]. j. `- h$ p
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
      w1 j9 j+ ^0 L, @( e5 n. r3 o
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    5 O" r8 n3 M: j6 q! N8 w3 G& }( k
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */
    + g* ]# s1 Q$ T& F( S- ~

  20. 4 h  k. N. J: r0 {7 y7 [
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码
& n* A1 p+ q$ E2 {
85.6 QSPI Flash的STM32CubeProg下载算法使用方法- G; M' v: c5 G. |4 [
编译本章教程配套的例子,生成的算法文件位于此路径下:
/ m% T6 `0 e" K) j/ \; v3 j( O+ R2 S" T3 Z8 w
5ec6d402a7646232a55f4834cd00ac0c.png

. A" a. T6 V0 ]# h5 W3 `' T  N% o% q/ c6 g& f* m! P
85.6.1 下载算法存放位置$ K. |5 |% D8 X: ^
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:3 M$ j3 [, J, N

" I! I6 t0 R% \9 f3 z  q3 B\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
! D. @3 q' ~/ \! A, b( V0 X/ d- T5 O0 n) x( d" F! S2 z9 K. ^5 A2 h
2609073778af5845a869911ab36624f4.png

9 t% B0 R0 s; |0 `4 H4 l& P
. h: o6 V3 s. E9 R& y0 Y85.6.2 STM32CubeProg下载配置# u& B- u! t- H
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。# T; k1 v) V+ n1 r! f
; i+ Z8 Q- ?8 C/ V$ p: }# U
d4e256633eafdbf4bcd8cce6e3b5909f.png
5 n/ l  {& P/ I* h& z' `* m

' b& R# L  T2 v3 g点击Connect后效果如下:9 w: O4 G" k& v& [& i6 R2 ^7 t; A
( E# z! l' R2 B- p! u

! w* H7 l7 e1 u, Y6 Y: v: o8 s( r, b) M: @' ~
在这里选择我们制作的下载算法:, ?* Z6 t1 |+ c( G, m
5 I8 @- e" @2 L) h
d4fa28f64be2de98e04f3188eb2fe09e.png
& C6 a3 V0 T6 d6 L9 r& G

* v# P# L% ~7 N# U0 Z任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming' B2 l9 _: k8 G/ v* _& Z0 X+ @. \& A
/ S3 \6 U6 U4 N9 ^8 J9 O1 R2 q
842518a7cc1dc569598d372132b2ee58.png

6 i8 ]  W9 F1 c. n+ D' {0 N4 {8 Y. E  q
下载完成后的效果如下:! f' W% L% [, X: U9 I; k4 F

' C3 `( o' |7 W
68a92f267829ca77f125db982d6c0479.png
4 E6 C- A! g5 |9 r5 ~
6 j: F4 ]! ]' A* }& v" A
85.6.3 验证算法文件是否可以正常使用+ ?! m2 E0 e) V8 M6 D" R
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:  m) z/ t5 S2 B4 B! X- L4 l
, c0 ~. g* r& T
566d2fba421c710da70e5656c6934466.png
+ c& ^/ X1 C8 @. P

% W7 X5 D* V8 @. |( v( T! w85.7 实验例程说明
" h  b1 E3 j6 s) k
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。+ h! ^5 Y8 ]( s9 F" v
: o5 n$ q5 x: U4 q. ~- V4 b9 r# U% ~
编译本章教程配套的例子,生成的算法文件位于此路径下:. q7 M9 M2 X0 C- ~  l* h

. H+ f& }9 _. S3 I. J
6108d1ff7e349f2aa268ee31804c57ca.png

* B2 I6 @3 }- y, ^! n% s2 K& z! U: Z! f
85.8 总结
8 `, ^1 x4 V( Y+ q+ T本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
+ o1 R' q$ Q& Y. }: W3 ]0 n2 P! A2 u- }: Q3 Y1 n

. W: L% R: w& U
5 E) D) X7 z+ O" @4 Y/ r
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
赞 收藏 评论0 发布时间:2021-12-19 15:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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