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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:35
85.1 初学者重要提示
# o3 D9 g/ g0 n  QSPI Flash的相关知识点可以看第78章和79章。$ B& k. n; t9 Y! T& G8 T  k
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
, {" _6 q8 m3 v* Q7 |9 {) M" a  Y! q  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样7 R4 m3 D; s% o7 `+ B2 e" y7 C1 o
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。& v* }4 K% M4 Q) }
85.2 STM32CubeProg简介0 M8 I4 }4 i+ V$ I+ U0 X$ @
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。" C, f+ U6 x% c& e! K

  ]* l' d' z, u8 P. X8 l' }& h. Y
d80578d217eb2a83520c6f3d797218f4.png

5 n8 A1 V! E( I7 M/ {+ y. R2 `6 F9 y+ W( v$ |; K
85.3 STM32CubeProg下载算法基础知识: h; K. I: ?; V/ B( a* C; e, N' J
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
& B: ?0 h) n$ p4 I1 u8 a3 y. T/ o8 z+ {% R# w! R- D
85.3.1 程序能够通过下载算法下载到芯片的核心思想
  z- Y! W/ e; K认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。7 F( K) e1 u& {4 `  H
% o4 h6 C5 I5 V: g( J
85.3.2 算法程序中擦除操作执行流程
: Y/ e5 G! S2 x' M注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
- _9 F6 F5 ~, {. _2 p: a
2 t% l- W7 G! j! `% o擦除操作大致流程:- v: n% ]; x. N8 M
# E6 A7 q2 V, g, c# R6 [$ m2 p; n; J
d75670256363bb50203bed0bbe4f9fd2.png

$ I6 d+ G) ?7 a: j; ~0 J; h  j; U- a
  加载算法到芯片RAM。# u& s. r4 e0 [  l
  执行初始化函数Init。
$ g  O8 P; {2 p5 b8 F. r  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。% d/ ?  Z( m5 r7 h. u+ Q
  执行Uinit函数。+ T) Q7 Q1 _. ~9 G  d6 S% [0 R# ]
  操作完毕。  A$ I! I( E, M4 t
85.3.3 算法程序中编程操作执行流程: Z  z  {" H( v7 N! M) T
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。8 x* b" X* p1 |: O

/ V  G; @, ^5 p4 Y$ u' h编程操作大致流程:
' i# g+ ~; P# ~9 l% x' U, h' f* F7 X, p- f8 _4 G
4fa96e9e33d700081f2cdbd312afb31e.png
9 n- E% A7 m8 @; f

! d3 r2 k# C' W3 e: a& ?8 X  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
, ~) g6 h& x3 A/ G" g: K! O: I  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
% a) W$ D. }6 @3 y$ L* _8 P; R  加载算法到RAM。
& r/ J% Z2 A" {, Q5 i  执行Init函数。3 I9 e: P9 |: i, ?! R& A
  加载用户到RAM缓冲。0 O: {; M; n! h" `
  执行Program Page页编程函数。, e+ [5 h, o, E. G7 w
  执行Uninit函数。+ u& Q& ]/ f, T. s
  操作完毕。( j# }& @( F9 Q5 L2 ]6 U4 d& z% ]
85.3.4 算法程序中校验操作执行流程
5 c( D& u4 ~' C2 u注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
8 f4 G% Y) E9 Z5 }3 k1 d% T. R+ ]2 x2 r$ h2 O) |
校验操作大致流程:
# n1 g+ Z* R; z/ r) x1 B; I7 U
  C( U! J( W: w9 L% }
7deeae4b650bd82c457492af14fc26b7.png

3 u9 @+ T2 S/ v, S/ \. O$ g# u$ i) j/ |. y8 P* w- {! e, U
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。" w. W8 N* u) K& y% z5 \: I( I
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:9 k8 o0 V' z3 ~9 f
加载算法到RAM。* s, Y' y/ Q5 S# Y
执行Init函数。. s  O4 y; c2 Q! ~+ G
查看校验算法是否存在$ f) Q  p+ m; I7 R: {% n& U& y1 T
  如果有,加载应用程序到RAM并执行校验。
2 g4 `' D5 W+ `. ^! h7 _  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
+ n! j1 t* s/ t  执行Uninit函数。1 M% I# G7 u5 Q7 I
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
) z: w* U' X+ `( @0 t1 @" y! O  执行RecoverySupportStop,回复支持停止。$ N" O- ~% u5 s8 O$ @
  执行DebugCoreStop,调试内核停止。+ q9 G: `2 v6 X# }8 e. ^
  运行应用:  s5 A9 G+ d5 n( ]% A' |" x  [. C; E
  执行失败
$ J- @0 _* I$ s, o& B  执行成功,再执行硬件复位。
( k( ^  F+ w3 Q% ^$ S1 v& K* Q. a  操作完毕,停止调试端口。
8 I2 q: l# E4 D8 k5 B% N85.4 创建STM32CubeProg下载算法通用流程$ j" u: x: w. N, X  H
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
0 t1 J+ J3 \+ \2 N
4 {7 F5 m' [+ q- G85.4.1 第1步,使用STM32CubeProg提供好的程序模板
2 k; t" ]) [7 }- z' r  v6 c位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
/ V+ R/ H2 u# k1 f8 V6 _, S/ l. {1 E* A
97f385e3b62a055bac9b5c0214b877e5.png
  N; M! [4 C7 ]; ~. O
4 y  z. q5 a6 A3 d! f
以M25P64为例(注,其余步骤就以这个为例子进行说明):
* `6 w3 m# M& C9 s6 }, U9 t  W  V  [' Q% _2 X/ T
43b298f996937fe35e89697fc6479cb9.png

1 G4 n. Z% h# `4 h4 f& G5 {& B
2 H8 j: E+ M" q85.4.2 第2步,修改工程名
' r" e( G1 o' x7 YSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。2 _& y( E' |$ Q' X" {" D
( U4 r- }+ o1 c  R8 [
85.4.3 第3步,修改使用的器件  _! A1 g2 A8 U9 P. H6 n4 C
在MDK的Option选项里面设置使用的器件。
; m5 }: F; I7 g6 A9 b6 ]$ Z1 d7 ~( ~; i$ n! d) C
36026b9a6d60249610d0246eb725b246.png

8 R7 g4 o, \9 h( @/ T
& a6 p. L* S0 h& L0 x+ q- K85.4.4 第4步,修改输出算法文件的名字; z- g" r! G  ~* I. S$ ^) L
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
/ D, i' e- z, T6 @7 j9 W0 P3 ]- P4 v6 f& y7 {
52c72b3993f28a366a22eac56f8fd043.png

, a6 a% y( m0 M6 q' C' [. ]5 Y. \7 {$ \/ |9 ]4 r- i: ]
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
; l5 Z) e: R/ z5 N2 m% b* M& p% n( i. o' q* C
7b48153027a44de33926c2da008716a0.png
% U& E3 C9 @! t8 f. s
, z, `! \8 w0 ^* L3 [
85.4.5 第5步,修改使用的库文件和头文件路径. r6 T+ u( @$ l1 L$ P# p6 i7 q
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
* F% y; l! z$ A# o* C' k0 o5 ^0 v7 ^& i+ O( r1 l
85.4.6 第5步,修改编程算法文件Loader_Src.c
& r7 @- i4 b- B$ y! p8 S模板工程里面提供的是M25P64,部分代码如下:& b  o* U! A" A% |. i' X2 ?

8 Z8 Z# |8 _0 p
  1. /**
    0 R* H) }6 Z5 e& b# y' O: J) {
  2.   * Description :/ |$ G! n. Y" ?6 U+ i/ ?) ^0 o" d! A
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the1 w# V( U1 S" k  U
  4.   * device and initilize the FSMC with the chosen configuration
      R" S5 |' |  R" @; V+ o+ I
  5.   * Inputs    :% Y$ B" d; q( C* p4 s- t  f
  6.   *      None, u1 X) X5 w( l3 \) z! S1 W9 d
  7.   * outputs   :
    , ^) `2 t- k1 S& V7 f
  8.   *      R0             : "1"             : Operation succeeded7 s2 E' d% O# Z* ]' h2 F- D  G
  9.   *               "0"             : Operation failure* ~1 \' ]% G- t2 J2 r0 M
  10.   * Note: Mandatory for all types of device
    4 p* x1 |1 j; y5 V8 ^
  11.   *// ?9 _* c1 F6 }: d- P' p
  12. int Init (void)
    3 n+ d% o/ i0 V/ s+ c
  13. {  # o' U3 J+ G1 m! Q% O* O% G
  14.   SystemInit();
    ' v( [* w- p  V& Y
  15.   sFLASH_Init();% g$ L0 [4 i" a& Y8 |  ]6 W
  16.   return 1;8 D( N! i+ k, X7 g
  17. }
    - y* H+ m, i9 M1 p
  18. ! n( H" O& b9 ?" Q+ i
  19. 4 q- f. {+ u; X" o5 {
  20. /**
    ; o9 q% I7 q7 n9 {  n3 ?' O
  21.   * Description :: u/ A& K% N8 `2 L( }0 j2 x$ d
  22.   * Read data from the device
    + ]  t$ |+ S+ _$ E
  23.   * Inputs    :8 j7 C. M% C) {: P8 P4 T, w
  24.   *      Address       : Write location. \# E' Q* z5 s+ x7 N( O6 Y
  25.   *      Size          : Length in bytes  + o: Q/ w% C3 Z5 K
  26.   *      buffer        : Address where to get the data to write
    " z0 U; i# n, T2 \
  27.   * outputs   :* i! @* Y5 g( ^% x0 S7 w9 p
  28.   *      R0             : "1"             : Operation succeeded" L4 m; g* m" z% J
  29.   *               "0"             : Operation failure
    # O# W5 f2 p8 W
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    . [/ J* f' ^) \* a
  31.   */
    ! z. B6 J! j& M4 G: U$ {" f
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    $ d5 G# b2 Q* l& K
  33. {
    1 Z3 k* U+ H7 U. W- N0 L" c; V7 J
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    9 l, ]8 z, u* ?, y$ s9 m1 V
  35.   return 1;
    - G/ e6 [) Y: `/ O( P
  36. }
    ; I! S, {  g1 H& H; B- \4 x! G) z

  37. 1 h) {6 F7 x4 H
  38. 7 S, G. B; k# A: |8 G7 {/ `$ b# n
  39. /**
    , i) _$ h6 S3 o0 W: y
  40.   * Description :
    1 N; S) s# D" H* B
  41.   * Write data from the device
    / G) x6 d! \, E% Y. |) X  _
  42.   * Inputs    :- Y- ~+ C7 O% a# ^7 y0 k6 c0 R: i
  43.   *      Address       : Write location, H2 ~6 c4 ]: R4 o8 M) ^! N
  44.   *      Size          : Length in bytes  0 R# w# |2 Y/ y3 Y9 t- R4 j/ e8 Z
  45.   *      buffer        : Address where to get the data to write
    ! y2 F8 Y- [/ k  C$ c4 \6 u2 A
  46.   * outputs   :
      o! f2 @3 C. |$ ]4 d
  47.   *      R0           : "1"             : Operation succeeded
    / F6 D+ m  `; ^7 }9 l( g- ^
  48.   *                     "0"             : Operation failure
    " T& G6 }% ~: W# ^9 y( s6 T& r9 @
  49.   * Note: Mandatory for all types except SRAM and PSRAM    $ ?! d! v! y0 F9 L3 ~  @, |) d
  50.   */. A9 g7 [( ?! a" \' o( E" O
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    % c- ?; H$ ~% R/ t; ~& K
  52. {
    # l# t- ~4 }( U( ~# A+ T
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    ( @. n3 Y7 V! Z! q$ z
  54.   return 1;
    - b7 Z& s, F0 U
  55. } ' w/ a) e( j6 ]9 M4 I0 N
  56. + A* F. @" P- O0 r4 |6 w6 I. F% v$ b
  57. 6 L$ C4 j% @) y# o
  58. /**4 S6 _2 D* b) I! q; P/ Y
  59.   * Description :; h! |8 t' @3 Q- I. L$ M& w  P
  60.   * Erase a full sector in the device
    0 I, F( p% c, W; k
  61.   * Inputs    :7 _3 i- C) \6 n5 G3 ?+ l
  62.   *     None5 `; z6 ]5 l  H) @
  63.   * outputs   :, Z9 J! }& G8 g, l# S" j2 R: E
  64.   *     R0             : "1" : Operation succeeded
    4 Q/ a( y# G1 U' D2 G
  65.   *              "0" : Operation failure
    , G1 T4 ~! Y2 Y6 ^7 o. j1 k
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH) e! \, |9 Q' q: Z* b- r  ]% i
  67.   */
    6 ~' O" o; o+ g$ e
  68. int MassErase (void)
    8 Z5 \. C% R6 X. c$ a. _
  69. {  3 Z* s4 Y4 \* r/ d& }
  70.   sFLASH_EraseBulk();  d$ k; C% u; c! H
  71.   return 1;   
    / o3 V/ A5 S6 q% t: u/ ~
  72. }
复制代码

) e8 J* i; A  |. L- a8 k* x" {: L: m85.4.7 第6步,修改配置文件Dev_Inf.c
7 K+ n9 L% g) U3 V' g& O' Z模板工程里面提供简单的配置说明:
8 ?" H) \" ~$ W$ `) j
* Q1 ]6 u. X1 D( L) |6 _1 @& s
  1. #if defined (__ICCARM__)0 M4 N& [9 D1 j9 }4 Y( |+ E; T2 \, h
  2. __root struct StorageInfo const StorageInfo  =  {
    ; N; Q& C7 k3 t' Y
  3. #else
    8 y" f8 g' G- a& Z
  4. struct StorageInfo const StorageInfo  =  {
    . P: v! k1 [: q! S
  5. #endif* t5 E$ J% k/ {6 D- f1 s- [
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number0 i* E9 S$ \* l- X1 S9 {& y
  7.    SPI_FLASH,                       // Device Type+ d7 @$ A  T" J  H2 D/ E$ R
  8.    0x00000000,                     // Device Start Address9 S% \$ x; f1 L
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)  w/ z! R/ J, R! o
  10.    0x00000100,                      // Programming Page Size 16Bytes
    2 I1 V+ J, J9 q4 Z/ e# x; t6 i# h9 Z
  11.    0xFF,                            // Initial Content of Erased Memory
    ' G6 A7 ~5 f9 M" A* W& ?
  12. // Specify Size and Address of Sectors (view example below): G% q1 s* l4 w. Q3 y" q
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    # N  ]2 _+ B' p5 ^2 Z0 @" V
  14.    0x00000000, 0x00000000,/ U6 F9 H. X: ^: O
  15. };
复制代码

0 [! s* w" h: K- T, ~2 [+ j" V注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。) b) `! {, O( J8 c0 |# s7 T! [

( F$ R, I% w* v" k8 v( m85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。0 c) i; K" z3 E( H' W' {1 S
C和汇编的配置都勾选上:
* b0 b+ G8 X- }) @  M) j2 ?4 l$ A
- }4 S/ K) q4 U& m$ G+ x
afddd6e839292aa2b94b43e79544d630.png

+ ?1 u+ j4 l3 T5 L+ v* V/ i+ Q4 V$ z0 n" S& }# ]0 K( H7 ~
汇编:) ~6 Z' @- X! f0 F, M, \6 ]* o
: v7 j: a! g3 Q& R1 ?
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
- @/ ]% H# p! y4 T# l7 H4 w
% m1 }" f4 C  a
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:- V2 y) k7 T' E9 O6 H
1 T3 g# K* a1 ]+ T# r- z
(1)加载以响应运行事件。
! A  w  [: [+ N5 o8 ?& i3 j% |% @/ k! H# v) p  m1 F; b- N2 K" q
(2)在不同情况下使用其他例程的不同组合加载到内存中。
/ C0 q( s7 |8 D$ F
/ {/ u  L# A  w8 G0 f& c0 x8 n; D# }: `(3)在执行期间映射到不同的地址。
: E8 W3 I, V6 E# A2 d1 k5 |6 I( [9 H% M* T# g, s
使用Read-Write position independence同理,表示的可读可写数据段。- n% B2 ^! ?' a: n$ c& B
  K; c4 z; Q+ H. }3 Y
85.4.9 第8步,将程序可执行文件axf修改为stldr格式- ?4 e5 l7 \% ~! D* S
通过下面的命令就可以将生成的axf可执行文件修改为stldr。9 F; s: O* k5 z* x! @2 ^
  [2 F1 p; X& ]3 x6 x( ^
c683673d2481d8dce75b6fa01d624600.png
0 F0 |9 G* h! H6 j. x/ a7 v9 K

9 ]' n! U) _- U3 |9 E2 B6 U. v& {85.4.10   第9步,分散加载设置
0 [4 [9 S, L: j9 ]/ v我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
1 q. j: J+ B6 R. t- i& C; x3 k, V% ~, O
b96835a7506516319002e6b2e8db9371.png 4 }  N  q1 v9 L& C4 Y

) x$ ^7 }, s7 ]' ^分散加载文件中的内容如下:4 u  A% `, s, C& ]! C
( w' }) I6 R! x4 f0 L+ y0 D
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    % o! O# O- G# _9 c9 r: s
  2. {, @; G$ ?9 ~, W' `
  3.   PrgCode +0           ; Code8 U: L# s* j; H  E3 X
  4.   {
    5 K; e) |( i- _+ K% l( Y; s
  5.     * (+RO)
    7 C7 Q* I/ k1 g7 u$ r
  6.   }* Q% v0 n. p' p  }- j  k
  7.   PrgData +0           ; Data
    ; f. J/ _- {/ {$ E: T7 u
  8.   {
    ) b% X9 L- C& k) p
  9.     * (+RW,+ZI); p6 G8 \, Y9 ]0 E5 ~6 X
  10.   }, B; Z$ T6 {# M% L4 J0 s
  11. }
    5 j; J9 N: N. P- ?

  12. ' f7 k) }$ s& E, f* Z& `/ B
  13. DEVICE_INFO +0               ; Device Info% t5 p/ D* n5 F
  14. {
    5 V5 J" P3 a( e! L
  15.   DevInfo +0           ; Info structure' G8 g5 c  M8 y% X% }. g% a4 u9 {  B
  16.   {" J+ u2 V" K  m9 S  p
  17.     dev_inf.o
    8 J' A" h4 E: O& `+ V0 t/ B* s
  18.   }
    1 \1 X/ h/ r9 P; @; W
  19. }
复制代码
* F. y4 p0 e) L* q6 r1 E
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
9 ]. b) Q  a) f4 ~
5 U9 S2 p% {! g7 Q- N# d--diag_suppress L6305用于屏蔽L6503类型警告信息。
/ b4 @; h6 H9 D. @4 q+ V# G
+ v/ b6 q2 V4 Y: g' h% V5 v特别注意,设置了分散加载后,此处的配置就不再起作用了:* }0 U% ^- J+ x

! O7 B6 _4 L* H. J
641395c374a3af02f1a92d3ff9ee4199.png

6 o0 o8 {# _4 B2 m" f5 ]0 @: `4 m4 C' `5 O7 x9 s
85.5 SPI Flash的STM32CubeProg下载算法制作9 C* }4 S1 z, ^+ [2 Z6 O
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
' `; W1 u8 W4 V6 j
$ f: u& p, Z* {3 ?5 q85.5.1 第1步,制作前重要提示
+ ]4 H3 N* B2 a& x这两点非常重要:
3 w0 M5 u; D& ]7 e. W1 O6 ?8 f6 i% d, G3 S0 p" Z& E/ b
  程序里面不要开启任何中断,全部查询方式。" |* P& n; `7 n* k8 ?
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
0 @8 \) H# S9 j( y0 o+ _6 M85.5.2 第2步,准备一个工程模板
9 S9 i+ C. z* ~9 k$ @! K% D 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
2 S& ~* a- O' z; G4 s( z7 }& Q2 K" M2 x' w8 ?: n
80ad5e3abdd6a993c8621230401a1313.png
5 d' E( x! g" g, X% `+ k. Q

  D. r& j2 C5 w) [85.5.3 第3步,修改HAL库
5 f4 g6 f1 w6 `8 O5 w2 F- e' ^这一步比较重要,主要修改了以下三个文件:/ J/ d5 w1 |5 `4 ?5 r  s
/ z. B" g/ z$ Y. ~' Y
33bf89f25487547d4008f43b7cf10e03.png
. Q" D( i1 _: |$ S: P
& k9 p& d  M3 R4 r3 o) A$ S
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。) {0 m8 D. y: O9 A0 z) K
; B: f2 d) D9 g7 N3 D% V
85.5.4 第4步,时钟初始化" M" l0 m% D. N1 j# y2 U: ]
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:0 Q. E0 g) x) H. T
- y7 @6 |4 G, ]4 h
  1. /*; U  g. v% X, M9 f% _
  2. *********************************************************************************************************7 R2 t& ?* D$ m3 J% Q
  3. *    函 数 名: HAL_InitTick0 p4 W6 g% V) c" U
  4. *    功能说明: 重定向,不使用
    5 ]# |% Q- O$ d- P* G
  5. *    形    参: TickPriority
    / n: f: J" L% t" A' S1 I+ L
  6. *    返 回 值: 无' y$ y! ~( O  `. [/ R
  7. *********************************************************************************************************
    7 ^$ x4 M. {0 H+ t5 B( K3 p" A$ P
  8. */
    # g5 p/ u. x/ t3 a( z7 L
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)) D$ n# T7 A( W: H9 P/ k: z
  10. {
    ( ^) a" W; `5 `- B
  11.     return HAL_OK;
    # A& A/ A! E- Y2 A4 [1 ^
  12. }
复制代码

+ C( [/ m. z+ C然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:: T/ w% @' B+ D. Q: g

" ^) i9 _: `6 h, E5 C5 }
  1. #if !defined  (HSE_VALUE)
      L6 A/ {. i! ~- i, j$ N
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */* k. U3 x9 R6 d! {! A. L* o
  3. #endif /* HSE_VALUE */
复制代码

; [# r- a4 T! T. V# Y最后修改PLL:, Q" G; a( D( ^8 c

) l, z1 o0 m( W8 o: H; l3 o
  1. /*
    8 U) P! C* z" j* U! p; q
  2. *********************************************************************************************************
    1 S9 x4 M+ [; F! _: S0 J
  3. *    函 数 名: SystemClock_Config& U  Q: K7 J& z6 x
  4. *    功能说明: 初始化系统时钟
    $ {: t+ u) O, x; g" }
  5. *                System Clock source            = PLL (HSE)
    " x( {# y1 W! |8 v8 B+ j
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)% i. z) y; L' h" L6 C  ]
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    ( I. n; ?& N# ~1 b
  8. *                AHB Prescaler                  = 2
    ( R+ ?! b1 ?3 q: G: A5 e
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    9 e! N: B8 M4 }, o1 [: G
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz): l* d+ s$ r) B9 B! A4 P8 T
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    ( A5 E3 F) V' _
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)% G2 y  M  U9 f- z
  13. *                HSE Frequency(Hz)              = 25000000
    / T( e8 i1 \; M- }1 p( w$ u; Q" i
  14. *               PLL_M                          = 50 q# S: `; b8 F4 J7 L1 S
  15. *                PLL_N                          = 160
    9 Q' X+ M( u7 V' e% ^5 t) a
  16. *                PLL_P                          = 2' l, P$ c7 T/ u( `
  17. *                PLL_Q                          = 49 E6 I- z' M$ c/ [0 `5 ]
  18. *                PLL_R                          = 2# `$ {6 [6 K# F, _. k! n
  19. *                VDD(V)                         = 3.3$ }2 O5 l. O2 ~: g0 u& q
  20. *                Flash Latency(WS)              = 4
    $ Z$ i  L: B* t( f) E- k+ P
  21. *    形    参: 无
    ; z+ @5 M) _, R% ?
  22. *    返 回 值: 1 表示失败,0 表示成功/ K+ O/ E6 d4 G! x9 @
  23. *********************************************************************************************************
      s; J0 r- I5 G# r
  24. */3 U  |+ g7 \1 B/ a* j1 @
  25. int SystemClock_Config(void), M: c5 b  @" F0 R
  26. {9 y* X8 h! _6 L- u& s3 v
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};1 W4 Z! l/ L% V
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};! L8 u8 t! ^5 v# C; K7 U
  29.     HAL_StatusTypeDef ret = HAL_OK;9 t0 E& w4 g7 A9 q2 ]

  30. 9 q. F; U- f% i- r1 d  A$ N
  31.     /* 锁住SCU(Supply configuration update) */; C$ }: e; }7 ^3 |2 w
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    7 G) [& ?2 n8 n

  33. - j% i/ J& m! r3 @* ^. R" c
  34.     /* ) E1 N+ E9 X% {5 R6 |! I# d% e
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,3 c7 K& r& Z) i3 h, ~+ P$ U+ j
  36.          详情看参考手册的Table 12的表格。
    ' z3 {# }2 _! W+ Z. `  h
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。7 A% l0 i( q( _4 g
  38.     */
    - p% R4 F, o! q) n+ k
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    , b/ B$ G4 [1 i
  40. : ~, M8 D3 M9 O0 @' S, ?$ i
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    - ?+ m8 o* V2 c7 E7 k
  42. ; K2 x+ K( m; c/ j
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    0 f8 p/ v$ C0 i) N
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;% ~5 A9 S% W' _3 P- Z
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    7 ]2 Z% Y6 \5 @! u2 ]& J" s. ?
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;! Q6 m" E+ N  g: I4 {8 C5 {
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;8 O# O* N- T! Y- j3 B
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    9 W, c2 a# o: H* C
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;5 c0 a2 @% y3 v) M: t

  50. ( I- n: N! f+ r
  51.     RCC_OscInitStruct.PLL.PLLM = 5;  X* W4 `0 n) j" k( C
  52.     RCC_OscInitStruct.PLL.PLLN = 160;# c. A1 v# Z, X/ J( f4 q& z) \
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    0 I5 n5 A6 d( J& B% L; B! {6 @
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    0 [4 n- _9 a; s+ Y, B$ N
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        6 M: ]+ F& k5 ^5 ~! r

  56. / B6 x6 z; O/ i+ i
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;+ h3 V4 F# w* m3 ?4 Q
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    1 b. ^; x6 V/ t% t
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    ' L" ~) w6 w. j) N
  60.     if(ret != HAL_OK)
    + M: k* G) ^0 N& B% n* z
  61.     {; v, ^7 I! G. _. y! \0 v, D
  62.         return 1;        , ^9 K  u7 Y5 G; M
  63.     }
    ) F6 g9 l# g% c  O; l  U0 y

  64. / V! _7 N- T. O+ a6 x! n5 k
  65.     /*
    3 N& {( n5 y! v" e
  66.        选择PLL的输出作为系统时钟% f% P3 a6 w# K; I
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    - d( s6 u' V0 w& D7 o
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线) s& [% n/ s6 x: u
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    + U7 w6 i0 N9 J' f" `! \
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    9 L) B# J3 Z  Q* \/ n
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线9 ]4 x! H+ }6 ^* o* H( l* U  f
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     3 @5 ^/ n: @8 y- j6 U9 ]
  73.     */
    + Q, @) S- d7 j( B
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    7 @0 ^) d2 _! o
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);1 W; W7 U0 e: S5 q7 t' N
  76. ! G# p( m" l1 b* l
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    $ e% `6 |* ~# [9 d5 E4 k; u! y
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    ' Z' _+ }9 ]  H  d" I
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;. Z4 N# ~+ l: @& a; f( C
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    : X+ C5 A9 p1 |' k; e
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; $ c* n8 x: p. f( u3 g0 ]: h* K
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; - B3 b- r8 S0 k* s
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    / P; g3 b1 U; v  W* D
  84. $ ]4 u5 m1 x+ L! a' B
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    $ m2 C4 l: {, e
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    / g, O% e+ _; o9 c$ X4 t. Y
  87.     if(ret != HAL_OK)
    5 Z; m- Z& n# N# r9 v( G. n/ |- W
  88.     {! V3 Y0 l( {% f7 D! F* [' z7 \
  89.         return 1;
    / o% O! D3 v9 [; Q! z# `2 H
  90.     }
      P5 r; Z/ f) d) @* s6 Y$ f% X

  91. 1 n# d. |2 ^0 _# v
  92.     /*0 g( N# K) ^2 P/ @, ~8 c' M
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    & c" |1 v* X5 P/ C
  94.       (1)使能CSI clock
    % K9 j% z# ?- X) e% M
  95.       (2)使能SYSCFG clock- Y$ J9 k4 z* v8 H7 d: I, o
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0% f; W! n2 A6 s9 G$ l5 U2 c8 h
  97.     */
    1 T# E! j! v  ]9 A! O$ s
  98.     __HAL_RCC_CSI_ENABLE() ;
    5 ]; w1 v9 ]; k# r# p9 J) d% z

  99. , a+ R4 e& i, I4 f& N, K
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;, Y8 k* o: z' t, b* d* Z4 D0 @

  101. . J$ G9 c9 u2 \8 m
  102.     HAL_EnableCompensationCell();9 Y0 t2 B: k: m; K
  103.   a1 Y9 R1 Q- \) J
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    ! S/ @* `2 O$ K1 F  G# h
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    ) V3 p" h) V* q4 D9 y+ V* y
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    % `( i6 k. S8 H& R- p

  107. ( K& e) P1 l9 D, w' Z# [& W5 j
  108.     return 0;9 l8 e7 E$ `! u4 a4 B/ c5 ^% ]( ?
  109. }
复制代码
; h9 h, q; \7 b" Y, l. {
85.5.5 第5步,配置文件Dev_Inf.c的实现
# C/ ]0 E' [: B+ r7 P* q  g7 ]$ _配置如下:
: X& R4 [: \& m
* j/ s  h3 ^/ N: P) T6 }
  1. #if defined (__ICCARM__)
    5 \, L3 G3 E* k# u
  2. __root struct StorageInfo const StorageInfo  =  {
    , h% k  Q3 C+ @
  3. #else
    3 s( C6 t) F2 W; c" C
  4. struct StorageInfo const StorageInfo =  {
    9 l: m4 t; L$ D; u' Q0 e
  5. #endif
    " H& i7 X6 b' B, Y1 ]2 p0 [
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */) Y9 z9 H, Y' ~8 o) K' M, f/ h
  7.     NOR_FLASH,                      /* 设备类型 */& |9 {9 C% x' u0 ]1 ~' v
  8.     0xC0000000,                     /* Flash起始地址 *// m. K: B* {0 ~! j( e
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */" R+ L- Q, E/ S' m' P
  10.     4096,                           /* 编程页大小 *// @+ U9 G0 m) A7 w
  11.     0xFF,                           /* 擦除后的数值 */
    ! z+ ^6 V8 U$ W$ `
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    " q0 @0 g8 C9 i/ G0 {
  13.     0x00000000, 0x00000000,
    ) V2 m, r: J! r4 N) I
  14. };
复制代码
$ Y5 Y- O! v4 N' v1 ]" i, _
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
* l: w) a0 e: r: c' m, e8 @% N. r! o; w, ?; h
51f220556177a36712977b8d140b627f.png

: _8 f9 \; t& T) I' s
6 u: C- X* m4 o! P9 _  F9 k) \; [85.5.6 第6步,编程文件Loader_Src.c的实现
! }) y  s/ s0 E" j! @4 d下面将变成文件中实现的几个函数为大家做个说明:) @/ y: `' }2 C- `4 \

- g1 G% K) m3 S  d  初始化函数Init
* N- X. Q6 Y4 ^1 \* F% E
  1. /*
    % S% c# w  k# }' w$ t
  2. *********************************************************************************************************
    8 G* ^! N8 ^/ ]0 G5 L0 |' }* Q$ l& c
  3. *    函 数 名: Init& a7 n# b% q6 M- O6 X
  4. *    功能说明: Flash编程初始化( h- m" K7 m% o% L* h8 I/ I6 f  h. {4 E
  5. *    形    参: 无
    + \0 o0 a" K" J! p* I  n9 v
  6. *    返 回 值: 0 表示失败, 1表示成功, K6 [0 f  o! j! o6 Y5 _) g% a& V1 K
  7. *********************************************************************************************************
    : _5 l1 }0 h9 P3 q7 J; S
  8. */
    8 H0 A" Y" d, L% t/ Z1 o+ x
  9. int Init(void)
    ' [, Z! M: @& _6 d* g; s; B
  10. {   5 A5 s, y* R  a1 h( r
  11.     int result = 0;$ K' |; c; o" C" k
  12. , y  O6 {/ N  H! k
  13.     /* 系统初始化 */* F$ U# S& v' G6 d0 N1 q: z( V
  14.     SystemInit();
    % v! d4 ^9 z( q. |! U" P% A

  15. ! ~/ o. {' D1 p3 T: D
  16.     /* 时钟初始化 */
    & P; ?# s/ t9 y  G
  17.     result = SystemClock_Config();
    7 s( p; p5 o6 v$ I* `! k$ Z  T
  18.     if (result == 1)
    ) m1 J( W0 a; j5 o- u
  19.     {  ?4 B' @, `1 n9 F! F; ]0 g8 m$ `
  20.         return 0;7 ^/ A3 G3 B9 \5 ]
  21.     }* J: T3 T$ O' P! h/ ?" |2 J4 s( T. a
  22. 2 S3 W! u! ?7 G$ c5 Z6 u0 p* U
  23.     /* SPI Flash初始化 */
    + M; U2 u+ @/ a7 C1 f* }& ?& z
  24.     bsp_InitSPIBus();
    9 W; G0 p6 x, p* y1 |
  25.     bsp_InitSFlash();! J+ [* t1 T8 i2 Z% H  v/ K. v) k) N
  26. : e# A6 A% d8 \+ w
  27.     return 1;
    6 U9 [1 q/ _4 ~* F  ?/ F6 w
  28. }
复制代码

8 V+ f9 Y' g- u4 ^- g+ V9 V# _  整个芯片擦除函数MassErase
- A: E" ~& }  P* A9 Y1 H整个芯片的擦除实现如下:: V  Q4 `' k& z+ r2 _

9 Z* M% k, U& M& g+ N9 P( x
  1. /*
    ! R7 @' o' V0 {* s) b& X
  2. *********************************************************************************************************
    3 `. w: [) Q% q/ d6 j) y9 l
  3. *    函 数 名: MassErase
    1 c4 I3 j4 m8 A5 y" y% A: g; q* B
  4. *    功能说明: 整个芯片擦除% T( K; N1 V# c5 E9 ], M* Z7 X. p
  5. *    形    参: 无
    + k3 |0 s2 x2 A$ O2 ^
  6. *    返 回 值: 1 表示成功,0表示失败# K' `, C1 X5 n' C* k+ }
  7. *********************************************************************************************************% f- _* C& S. \* D1 I  l, ]
  8. */
    , E, p0 ]9 U) A6 @
  9. int MassErase(void)
    + k; \" _. g  U# \2 O
  10. {9 H9 }1 u& ?6 ?
  11.     sf_EraseChip();  H4 I- _# m2 }& \0 r! P1 L

  12. # T  Q) C9 U6 B' l
  13.     return 1;   : w5 M* t3 m: y6 P- F' q
  14. }, d( ?! z! f" p- A+ ?* J
  15.   扇区擦除函数SectorErase, J$ B8 ?! ~* f7 M( G5 G
  16. /*
    $ M1 K4 L2 |7 H7 V
  17. *********************************************************************************************************
    1 w" c! s* l( y, X% u4 P! e. z) y
  18. *    函 数 名: SectorErase0 V" [1 O8 w7 U; o) g6 u
  19. *    功能说明: EraseStartAddress 擦除起始地址
    " t7 M  D$ Z0 q9 [
  20. *             EraseEndAddress   擦除结束地址3 h$ k# W- }. l( G
  21. *    形    参: adr 擦除地址8 ?3 x+ [1 Q! d' y
  22. *    返 回 值: 1 表示成功,0表示失败' P* \- z7 J8 C( E& I
  23. *********************************************************************************************************1 U5 j7 _3 i4 A9 t$ A0 ~' W* T& K+ }+ U
  24. */+ Q: l3 b, q' R7 O
  25. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)0 A% S) [' r: A/ G1 p- C6 o
  26. {" ?& @$ t& q& i- v
  27.     uint32_t BlockAddr;8 @1 U0 ^$ o9 b: Z& K; f

  28. / B' y0 t- w/ S- G; P0 H6 Q1 l
  29.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
    8 J) r9 q& X4 F& [3 d9 o( f
  30.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;; w+ j$ I. F& `$ m) d& B
  31.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    " s, z! D' c& c% \

  32. % s! t/ t  C) j* X
  33.     while (EraseEndAddress >= EraseStartAddress)
    5 M) ~& y1 \  Z; m, j( U! ~
  34.     {
    . V2 m+ n( W" c' w/ N+ |0 l
  35.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;/ E" ]: f* s! z4 j
  36. $ i7 Y7 v* P% z; g$ s2 ^6 a
  37.         sf_EraseSector(BlockAddr);   
    & W! M6 @* G: g6 S0 [5 Z  p1 t
  38.   w4 U4 a2 B" K7 b" w
  39.         EraseStartAddress += 0x1000;/ x8 ?: O0 k2 q# l) O* \
  40.     }
    ( o/ p6 B/ ]0 P1 T
  41. 3 g9 o, }9 [. t9 Q' V) u; s
  42.     return 1;
    5 D+ j- S- W. C
  43. }
复制代码
+ S$ B4 l4 T- V8 H, r4 ]# d
这里要注意两点:) O. K5 W+ t" w+ `
' @; w2 j8 f8 y5 }" T
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
: C) k$ T. C" u. Q8 b% _- e: i8 w% C6 P
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。
$ c( B! s  O9 y; y9 A& J" [1 X0 N7 ]7 H7 D  A
  页编程函数Write
/ o1 ~& z4 ^- M; q7 Y5 c. K页编程函数实现如下:
3 w4 i% r' d  m- o. S2 N9 i0 N
/ b' w2 G1 u  v* V* ~6 D; {! J
  1. /*
    4 [& s5 a$ G, w1 A; n
  2. *********************************************************************************************************' w* F6 O5 F  N/ u
  3. *    函 数 名: Write0 q4 G: q! |( @; ]% I& i
  4. *    功能说明: 写数据到Device
    ' e6 u6 W# K9 B2 m- ~
  5. *    形    参: Address 写入地址
    ! a7 |3 [. m9 y- S( X$ m+ i8 g# a
  6. *             Size   写入大小,单位字节, C1 \/ e6 ~' {! \4 v7 h! ^4 _" w
  7. *             buffer 要写入的数据地址
    1 S: y: P0 }7 F/ V
  8. *    返 回 值: 1 表示成功,0表示失败" E4 X9 q5 f! z
  9. *********************************************************************************************************
    * N3 V; k8 m3 s: Q! g* W) M7 _
  10. */2 P. j9 G5 w* j  o* D( M
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    & t9 c& e1 c) J. F+ g5 o
  12. {  
    : V! J3 M2 e! H. i
  13.     Address -= SPI_FLASH_MEM_ADDR;( O  ?+ w3 U7 h4 A6 Q

  14. * z% ~6 v( D5 y2 t' {  B
  15.     sf_WriteBuffer(buffer, Address, Size);
    ; a7 a  i% U! }2 q+ I& u

  16. ) G1 t( R  [' u, |
  17.     return 1; ! A3 z4 B$ r7 ]% x! K
  18. }
复制代码

, M: D8 U5 I, v: c这里注意两点:
9 [0 {5 O, ^' J4 c6 z# y$ Y* B- @& j
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。) X* w' T6 E8 I% r

- F5 q; N  Z& ?* T4 V(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
+ o3 h6 j/ z  [* U9 J* C7 a
6 W' f& c0 q1 ]5 y. z) R, o  读取函数
* {6 \0 o) w5 Y
  1. /*
    9 f4 U7 ^7 q9 t# A/ J  n
  2. *********************************************************************************************************
    ) ?# ]" `& R- g; ~& s3 O
  3. *    函 数 名: Read& e% N0 Z- M1 s7 \3 g
  4. *    功能说明: 从SPI Flash读取数据
    , W9 @/ `0 @$ Q
  5. *    形    参: Address 读取地址  I- F( o1 s( E& I8 L2 c) M! _+ M
  6. *             Size   读取大小,单位字节
    ; k( t- A" g+ B& ^$ N1 S
  7. *             buffer 读取存放的数据缓冲, M6 T8 f& B9 @2 t
  8. *    返 回 值: 1 表示成功,0表示失败
    8 `% h  r0 f0 H* ?4 ^" L% h
  9. *********************************************************************************************************1 D6 L: M) w) e1 u
  10. */
    0 H8 S8 m! e) Y; U3 W, D4 t% b
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    1 o1 J$ O1 _; i% x
  12. {
    & U) s# w8 o' s/ J. \
  13.     Address -= SPI_FLASH_MEM_ADDR;
    ; K' C9 q2 z3 {8 H2 c* @
  14.     sf_ReadBuffer(Buffer, Address, Size);
    4 [0 Q* J" h, H) v1 n, r

  15. : t: X: F- B1 |" g# H$ M  m( J" H: ?
  16.     return 1;
    ( |$ q' \* `* }4 K. t; x9 N
  17. }
复制代码
9 z7 `$ y- T  g: |/ A% s0 `0 v$ ?/ _
  读取和校验函数
3 e: u5 Q  N$ ~, z' L' n" C% Z我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。8 W  J& K2 ^6 {) I

" p$ Q7 V/ g3 X85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
% Y  r$ I, O4 {3 h5 Z; ]1 C最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:. |) U' N" G8 p7 \1 T
4 C! P/ O* K+ `& s1 \: E4 o- J
  1. /*
    8 l% b$ E# ]# |, b
  2. *********************************************************************************************************8 z8 |) E* J* p! ~* n
  3. *                                时钟,引脚,DMA,中断等宏定义
    % N. J, V6 c  C2 S" d
  4. *********************************************************************************************************
    ' U; ~' Q; ?1 S7 D% }  u
  5. */
      N" V% O$ M% L3 }' v. b
  6. #define SPIx                            SPI1
    $ N4 `. s* L/ a( ~
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    : i) ]# h( ^, _/ e& r6 Q
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    ' _) b3 j1 h# P  D8 t
  9. + P! D$ ?' H! Q' X' Y1 b/ ^
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    1 F8 ~7 e. ]! G- l- S$ s7 x
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()$ p! E4 x0 N( K7 q8 L4 r3 [

  12. 1 n3 ]1 i$ U1 G5 J
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()5 K, c- p* y9 k: C
  14. #define SPIx_SCK_GPIO                    GPIOB! b4 L% v0 Y- ?: }% A8 S+ ]
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3( ]# X- y! L' u6 `
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1; w5 Q! F* P1 w# L  C; F# c  r

  17. 6 q/ B+ E6 L; s8 R, A' A1 `6 G8 v
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE(). I+ S: q. _/ e; j
  19. #define SPIx_MISO_GPIO                    GPIOB+ D& X% r% F4 }
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4) S) h% {$ @+ R( X
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    ) R. ?* J$ V2 [  n
  22. 3 Z2 w1 h+ ~& P. w" x! m; R( }
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()+ S# I( ]9 g7 k5 O% v5 j/ c- A# M
  24. #define SPIx_MOSI_GPIO                    GPIOB
    - N! q1 K, t$ ^- h6 ]. u
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5% G9 `0 C4 v1 U# E, V4 Q
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1' S  J( t; T+ q5 e; E: j
  27. 硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
复制代码

6 j( @- z4 u0 ^: p- }; j
! h2 c+ F1 t0 {% n% X& I主要是下面这几个:
) l9 \3 L; x# K  w5 b4 ^/ q4 N! m! O1 `( b2 A1 L
  1. /* 串行Flash的片选GPIO端口, PD13  */
    - n. H% F/ ]8 |: {
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE(). ~% r* R( T& U* t* z. s
  3. #define SF_CS_GPIO                    GPIOD" ~$ _( K* d9 ?. G5 O3 V3 h3 `# Q
  4. #define SF_CS_PIN                    GPIO_PIN_13
    * P& o5 o0 f. T, n) A1 o7 R
  5. : o" p! M) F5 J5 s) [1 I
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    ' a- |4 r8 j: ?3 h
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN. O1 q# v" N1 N$ \, X7 \
  8. " ?3 Z: Q/ H2 A' A( a
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) *// k7 }' V( Y- f
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */: p3 A5 C& s& Y. q
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */5 u' L9 y' J: w  |: p
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */: C. E. Y! l2 Q& t' e% z
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    2 F* {8 M# m# e6 \
  14. #define CMD_READ      0x03      /* 读数据区命令 */. U- ?: C/ q  d8 T5 u( h& h
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */+ n' I8 p; o7 ]7 K! D$ \
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */# C5 ^& Z# h$ }& C
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    5 ?) c' H* I( c) i. T3 Z
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */* W* i3 f" d! W; n3 E( O( T& D
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */& e4 f+ M& M+ w7 U/ F
  20.   H! K' U( M. l# h4 S" B, D" b7 P
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码
  c* ^& G8 r4 O
85.6 QSPI Flash的STM32CubeProg下载算法使用方法7 C* U0 `" M$ W5 a
编译本章教程配套的例子,生成的算法文件位于此路径下:1 A" {0 {# f& W- V! |4 U; ~

/ X1 d- T  e4 E" N0 {1 Q
5ec6d402a7646232a55f4834cd00ac0c.png
( n6 M* y+ v5 @* s; K1 W! P3 N

& f% L3 B0 f( @. H85.6.1 下载算法存放位置
+ D1 L6 R, [% Y3 d, t生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
* M2 W& L- N9 ]0 A5 B6 m( }1 c9 R, \7 N: w* d( ?
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader( {7 |( t) e) @1 x' P" `9 f$ l
8 h) [' j1 ]/ F$ n, l2 g
2609073778af5845a869911ab36624f4.png

' e9 [7 c9 ?/ D7 |  h! m; {" s" @9 D, a4 A4 C3 l
85.6.2 STM32CubeProg下载配置
% x6 E7 Z. h) }我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
" |' n9 Z) ^" ]- G7 y( k  r/ z9 l' n5 Y
27a88335ceb606bd021c3ebc65d88e21.png

7 x: c7 C$ |# r  P0 [. X9 z* H# x; g. V4 Z0 @: Q$ B
点击Connect后效果如下:% x& X& R( E5 f' g
( |+ @( |* A7 g/ z) H6 Y
d4e256633eafdbf4bcd8cce6e3b5909f.png

; i0 U" l7 \6 o# v
$ }$ x2 E/ ]- S2 A  S0 C: ^在这里选择我们制作的下载算法:& ?& \. a9 `5 n% ]" J% O* J. ~

: o6 U, Q% o* h: q6 l  e
d4fa28f64be2de98e04f3188eb2fe09e.png

* k0 \9 t# x. S0 j# B  o5 n$ U( @4 l( U
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming, Z, o0 p+ u4 h

. A4 N! @# P: d8 X; I: L
842518a7cc1dc569598d372132b2ee58.png

& w& {/ Q* j0 p7 N) f
7 r1 R3 c. q7 P( z) V' `/ }下载完成后的效果如下:2 S; t- ~# r3 n4 g

7 j) p4 |( p" W# c
68a92f267829ca77f125db982d6c0479.png

9 \5 h4 `, m8 n+ Q) U3 t# b9 s: k( ]
85.6.3 验证算法文件是否可以正常使用! T; P5 |' P5 ]8 c+ P+ S5 B* F
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:! K! f, A: [$ m2 x7 [
: h8 P/ _( p3 z
566d2fba421c710da70e5656c6934466.png

4 T. H, p0 ?* g3 Q
0 ~' R6 B2 P. g85.7 实验例程说明$ S7 T2 J+ C0 [
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。- B; h9 K; c4 {
1 D; x- O/ a( H0 n  Y( \) ?
编译本章教程配套的例子,生成的算法文件位于此路径下:/ L; f1 R: t; o; f5 l3 o" `
& J  _2 j2 B+ n2 w  d
6108d1ff7e349f2aa268ee31804c57ca.png
/ C1 R* d6 w. g% V% u8 O8 z
" |+ \, e1 D/ V. N
85.8 总结3 g6 H: o4 h4 K4 I6 W3 ~) |: k% }+ @
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。: r2 I/ i! [8 Q0 C

: }( S4 _8 h6 O9 Y4 a  l( e8 p6 g7 {2 e; b3 t3 K6 _
赞 收藏 评论0 发布时间:2021-11-6 23:35

举报

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