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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:35
85.1 初学者重要提示1 r3 U( Y! i" V) T6 U' e
  QSPI Flash的相关知识点可以看第78章和79章。  \1 t5 `6 ]) S9 Q
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
  |& w5 u( S" b. ^9 b$ h8 O  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
. u) \2 Q/ Y/ u) ~: |$ P  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
1 [4 L; Q% q# D3 w' m85.2 STM32CubeProg简介
: t5 m! V/ M  ]6 a2 |+ xSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
& t/ m* |! S' {
, h- |9 o% _; y) j7 ?9 c: _
d80578d217eb2a83520c6f3d797218f4.png
  Q  V/ z( n: N( z

2 G7 ?, L+ Y; E# {+ d: F85.3 STM32CubeProg下载算法基础知识
3 Q( z+ E' t+ U) L0 R* k, @: bSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
- S8 k. Q5 C9 b% u1 A+ M) Y5 d9 |1 S9 F+ V, \" G5 o, E
85.3.1 程序能够通过下载算法下载到芯片的核心思想5 [! k( u8 l4 T+ K8 @# @0 g" a9 E
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。2 w$ e8 w- l' \- Q: }9 j$ e
3 E% H- y1 q, Q# ^: o; N8 U
85.3.2 算法程序中擦除操作执行流程6 k1 Z+ {+ k  t
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
3 \7 N: ?6 l' P; w4 L; ?
( I$ E' q4 k- f" L) e1 k$ Q4 [9 ?擦除操作大致流程:
5 h0 B* F, _2 [1 |  k% a
5 E* C" V! j3 Y* M
d75670256363bb50203bed0bbe4f9fd2.png

7 _: p. w  I) q! f  ~
  c- Q; j$ }# m- i1 ^: ?' U9 G2 _  加载算法到芯片RAM。& w7 r. l$ |% {% Z& a) y
  执行初始化函数Init。
4 V8 [3 l/ h4 n. d  p" K  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。" f5 m3 U8 \# r7 s' G; z1 P  U
  执行Uinit函数。8 U, b, d9 F+ @- j! a& M$ G& z
  操作完毕。
- `; z( F4 D2 R/ M9 v, V$ \" \3 ?85.3.3 算法程序中编程操作执行流程
- [6 Z+ t$ `9 U注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。# s' R2 c6 v* J- |
6 [; v& R  c- }9 W% F6 d8 X
编程操作大致流程:5 q# w0 y, F6 T# i/ k4 Q

: {9 l( G& S9 ?9 G$ [) R, ]
4fa96e9e33d700081f2cdbd312afb31e.png
( m6 {: r- A" @; }

! S- Y+ W  B3 [) ?6 C  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。, b0 D6 O! o$ d3 U& C6 q
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:9 t) c5 K+ F5 Q  P
  加载算法到RAM。
& }5 b. T6 a5 H! R& f0 h  o  执行Init函数。5 g3 {6 t  ~5 P# }, x/ [
  加载用户到RAM缓冲。5 p5 U/ K4 @: P5 Q, ^
  执行Program Page页编程函数。  O. K& f, x/ E/ T5 ~6 D
  执行Uninit函数。* w; W0 o5 B* a, L- c7 S
  操作完毕。9 Z& C2 W- u+ k) `3 _% D
85.3.4 算法程序中校验操作执行流程8 }! q7 X9 A* {5 O5 E! `4 r
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。' O) v1 L, a6 e# R6 H
& a# t& C$ a8 K8 M- y+ ~  ~( @
校验操作大致流程:
7 R) z- L5 U; R/ U
- ~  ]% c/ l1 j  J+ i
7deeae4b650bd82c457492af14fc26b7.png

. S0 m9 S( N0 k! k7 A! P( X1 b1 w+ h$ y" f2 i
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。1 q- V8 j' l7 y: t* J
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:8 S. @5 h  e. I: R  k6 w' X
加载算法到RAM。: ~, ^5 `7 ?0 d* V, L' Z$ {
执行Init函数。/ V: f* i" q8 T' t* d8 M
查看校验算法是否存在0 N; Y; W, ]9 p' b
  如果有,加载应用程序到RAM并执行校验。% ]) N$ V  Z7 E6 ~  d
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。3 v9 F5 x1 |! p7 {
  执行Uninit函数。
& B/ h+ `( M4 S$ s( p' `. J  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
( k0 Z* c  M7 G; u+ s  执行RecoverySupportStop,回复支持停止。( I$ _+ w9 a  h4 g& c( C0 C1 D1 p
  执行DebugCoreStop,调试内核停止。
; D4 L0 H6 r! `) ]. ~- F  运行应用:4 z7 V* C% |# Y* N
  执行失败1 s2 T# O( U3 Y* v  z2 @5 s) ?
  执行成功,再执行硬件复位。" ?# `, o* V; R
  操作完毕,停止调试端口。& o& x- q& G4 [  u
85.4 创建STM32CubeProg下载算法通用流程2 x7 g9 J0 E+ B9 V
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。& I, g9 c! F" T: Z7 K( o
) \+ `" n$ Z. [. J2 e3 Y
85.4.1 第1步,使用STM32CubeProg提供好的程序模板
+ E; Z' L# K( M9 A5 `位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader  W: s4 ~' a7 t5 ]' K) b
4 A/ O" ~2 w6 _; O4 \3 ^+ u- ^4 b; s
97f385e3b62a055bac9b5c0214b877e5.png

# O" m/ D2 }: |6 a: X3 w+ n4 o6 e: t$ |- i0 C
以M25P64为例(注,其余步骤就以这个为例子进行说明):( }1 k- C! x( x" ?$ M; E7 _0 }
6 J. X5 i  T& l: M/ W* {
43b298f996937fe35e89697fc6479cb9.png

: |, _, ?2 K7 g5 \" X, B# t4 Z3 N; |& }, c& b  ~
85.4.2 第2步,修改工程名
- s0 l' F5 K; q6 I8 VSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。" N& H, O# E  `& D6 T! a; q3 X3 D

' }; E! r/ R# D0 m. ~3 i8 C85.4.3 第3步,修改使用的器件; f1 @/ y# r! n* }
在MDK的Option选项里面设置使用的器件。
, J! G  Z5 Z4 B* f: V3 l: D- {' K3 e2 \+ D6 u3 D
36026b9a6d60249610d0246eb725b246.png
# J" _$ A" s9 D: \* D6 P* {
& f+ d+ u# [# q, Y% b6 l
85.4.4 第4步,修改输出算法文件的名字8 G" H! R) D3 x& {3 B! r8 a* d
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
2 t. t. P9 k+ Q" @( ^4 x
5 U6 G; z$ [8 q; [5 a
52c72b3993f28a366a22eac56f8fd043.png

5 N+ P4 d# o$ n$ q# U- ~* ~$ v
& J0 q7 v# h2 ?5 g# y! w4 L. X注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
" n  _( g+ e+ d
9 m$ u1 y& e+ D( P; f2 p
7b48153027a44de33926c2da008716a0.png

. X$ \; D3 k& [; C' d; i  D2 K  I  L4 [8 X+ q
85.4.5 第5步,修改使用的库文件和头文件路径7 j4 g4 u* C0 ]# c6 d1 m  d9 d/ G
根据大家使用的主控芯片,添加相应的库文件和头文件路径。9 Y9 f# F( x5 x! p* @

1 w/ n* s! Z% H* g- y% d# v85.4.6 第5步,修改编程算法文件Loader_Src.c
, E) N6 o+ E6 A3 N1 _  M% \模板工程里面提供的是M25P64,部分代码如下:5 L1 f$ B8 [4 h( `* T: t

2 T$ D" H0 W+ F+ w$ i! H1 N
  1. /**  q) j( o4 j* S* Y
  2.   * Description :3 \1 h8 M; W+ X. x" ?
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    , q$ z& a2 ]0 B$ D+ Z# r2 U8 B9 F* s# [
  4.   * device and initilize the FSMC with the chosen configuration $ \5 C5 U$ x9 m; U# F& Z0 |! `4 m2 O
  5.   * Inputs    :9 l# O! [- [2 x0 L1 D- X8 L
  6.   *      None
    3 A4 p+ n, r6 x) R5 w
  7.   * outputs   :
    ( [- {6 m" [! e) n+ i
  8.   *      R0             : "1"             : Operation succeeded) U. N8 ^* ]. D" a
  9.   *               "0"             : Operation failure
    : Y; |( I+ @& ]+ \3 r" V* h1 n" z
  10.   * Note: Mandatory for all types of device 3 _5 T* R# ^# [* X7 e
  11.   */3 o, S8 P( r7 I6 G  K9 |
  12. int Init (void)
    : i4 d! Q3 T; s. p0 B# H
  13. {  6 `  M! W: T& O/ s' h1 \
  14.   SystemInit();. O. Y9 [4 H; ]
  15.   sFLASH_Init();0 a; [5 b) ]5 x4 _+ P
  16.   return 1;  W- @* r. L7 j% d
  17. }
    " ]5 u8 d$ M7 y1 u0 R6 p
  18. , H' r% i' {" m' p/ v4 y& `! D, f- A; y6 u

  19. ! Y$ u. H: {& }* |1 {
  20. /**5 b) P3 j5 }( u# A
  21.   * Description :
    0 b& \4 l8 x  Z2 L5 I$ x2 t
  22.   * Read data from the device   `$ w% U! Z5 e' x$ `
  23.   * Inputs    :
      X8 ], e$ F8 m) ?" Q
  24.   *      Address       : Write location
    $ b3 z3 l8 d* o8 R$ c8 A# P4 I
  25.   *      Size          : Length in bytes  
    7 R2 V3 {4 @- J
  26.   *      buffer        : Address where to get the data to write
    0 r6 a# l8 K  E1 A* n" w; `7 E# I
  27.   * outputs   :
    % S7 D; Q7 ~6 P' j2 H3 f/ ?
  28.   *      R0             : "1"             : Operation succeeded
    6 \, [5 e0 q% O# W8 o
  29.   *               "0"             : Operation failure
    : G6 r- y* N0 n7 l+ U$ `$ v; n' p8 `5 t
  30.   * Note: Mandatory for all types except SRAM and PSRAM    5 w( c0 }# c% _1 [1 C  a
  31.   */
    8 ?, k! q% v! ?1 G. T- p0 B9 D4 {& C
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)0 B6 v7 a! T0 F& E
  33. {
    + {3 n. r7 l& w7 s  s  j
  34.   sFLASH_ReadBuffer(buffer, Address, Size);. U8 N6 ~9 Q" \" h+ x
  35.   return 1;
    . @8 ~% G" I: L8 \
  36. }
    5 D/ l  ~% U0 M, [! A- D

  37. 5 U" m7 [4 s6 r" ~
  38. ) Y/ d3 C& U# Q4 N
  39. /**4 F6 ~+ U2 V* F4 L$ X% H
  40.   * Description :
    ( `5 @! M/ B# v
  41.   * Write data from the device
    + p  h' Y0 F# @2 i' {0 x
  42.   * Inputs    :
      k2 q. h* j1 M+ O( c* L4 p9 q+ M( l
  43.   *      Address       : Write location
    0 P" ^3 b+ B5 {# ]
  44.   *      Size          : Length in bytes  
    ) e' L  k5 h5 z6 z5 k1 c! ^) h
  45.   *      buffer        : Address where to get the data to write
    + n/ ]2 `, ]9 i* H
  46.   * outputs   :
    " n, c5 s8 X; v) ~, d! y1 Q
  47.   *      R0           : "1"             : Operation succeeded* m5 W4 J+ d3 A0 {2 O4 ]
  48.   *                     "0"             : Operation failure
    4 B/ ~) A- ?: C$ R/ Y
  49.   * Note: Mandatory for all types except SRAM and PSRAM    ' Y/ O4 a8 i& p7 h
  50.   */
    2 d* t- C# C4 [4 b8 @, A: s/ m
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)8 n2 K- N8 Y5 A& a* {
  52. {4 L9 ]$ M6 q- H, l- E& ^. b/ p2 Y
  53.   sFLASH_WriteBuffer(buffer, Address, Size);- b4 C/ b' x( j) l+ y& @& ]6 y* y
  54.   return 1;
    : u; |. i9 G9 n) j5 M
  55. } - |- w9 y% R/ O) [7 I& x1 B
  56. 5 y8 ?/ L% `, q! N; }
  57. 0 `( @0 c" \+ I  z+ d
  58. /**$ y( ~- T8 z' y# _5 ~$ ]8 F
  59.   * Description :. A5 g4 `  P, Q; J* {
  60.   * Erase a full sector in the device
    ( g( X+ {% `) v4 L! Q3 l
  61.   * Inputs    :
    . o, j+ f; i* g1 l% y
  62.   *     None  _) [2 d# M+ n- A. ^5 {, j0 m  ]
  63.   * outputs   :
    % D  G1 e# G; t1 u5 v% R
  64.   *     R0             : "1" : Operation succeeded
    : }; y2 R- J5 q: Q
  65.   *              "0" : Operation failure
    9 \6 a, ~, x+ ]$ ]0 }9 {. x
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH9 I4 ^3 E' [( Z3 n, J
  67.   */, u6 \. _, O6 l3 G0 Q0 `' o
  68. int MassErase (void)
    & j8 V7 `' v" e  E; f7 T7 o
  69. {  
    : t4 R' B' x6 \' e  {% r* b
  70.   sFLASH_EraseBulk();
      w& b: O/ \1 D: c! h
  71.   return 1;   
    * g  Q! U( d+ f- d% j
  72. }
复制代码
4 t$ b" |5 ^: s6 v$ @4 ?
85.4.7 第6步,修改配置文件Dev_Inf.c
7 G' f: J# P7 ^: b- k' D模板工程里面提供简单的配置说明:
4 e/ J) b+ b( x$ i) }; x$ E
5 ?7 O- R6 J5 N! S+ e
  1. #if defined (__ICCARM__)7 q! ?) r0 i& X- a  r. O2 i; d
  2. __root struct StorageInfo const StorageInfo  =  {, z: w1 o2 c% ]1 P8 d
  3. #else/ Z" }7 Y. R1 s* q
  4. struct StorageInfo const StorageInfo  =  {
    / }' J5 m1 t% e0 F% H+ o
  5. #endif
    7 d2 m5 ^8 M. @$ j$ l
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number! P* T7 g$ G& f* v% h! w/ L# v
  7.    SPI_FLASH,                       // Device Type
    + l8 \" W1 {4 F5 m
  8.    0x00000000,                     // Device Start Address# M% |) W% H1 q- B" u. ~; E
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)4 d  `  Z2 {+ }: x
  10.    0x00000100,                      // Programming Page Size 16Bytes! o5 Z4 N5 z- ~" i/ V2 a
  11.    0xFF,                            // Initial Content of Erased Memory
    & Q& s1 N0 d4 a' e$ @. l8 _) D4 c
  12. // Specify Size and Address of Sectors (view example below)
    ! n# e0 m5 [: q; |( T6 U1 H: K
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes / P' O' d9 R6 y3 L
  14.    0x00000000, 0x00000000,
    8 b/ {1 A  R. j0 \
  15. };
复制代码
8 Q4 J, q( U3 Z1 X  B# o
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
; f( d  H. l! M' k/ N  e* ?, T5 G: `: B" ?2 V& H
85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。/ d; A! Q. e( ~* d4 Z# A
C和汇编的配置都勾选上:9 q( ~  h0 P. i1 r6 X, [! o6 o
: P: o6 o9 z2 ?. ?. T
afddd6e839292aa2b94b43e79544d630.png
7 G1 {! \- |$ J* Q% ?

+ F/ u& N$ G( B+ a7 L9 C3 N汇编:# b5 ~4 t- x. E5 H

. I/ r4 u+ A  U/ r5 ]
f639ae5b0d3c2e0e0190ec509fbc8a4d.png

2 n, M/ [( i( U3 L  b+ n2 D, a- u" z8 y( m1 @
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
; G) ?( l# e  |. s2 E2 L- U6 Z& h
! X/ k, T: ]6 H/ ^$ B(1)加载以响应运行事件。
4 o5 o' C2 t( y0 m7 v6 j/ e3 H* S6 a
, O  s) Z0 w/ m(2)在不同情况下使用其他例程的不同组合加载到内存中。9 D! H! g  k/ n; r
* n: m& H0 D( s. p5 j) ^
(3)在执行期间映射到不同的地址。
- H7 G( P* b! p: C& v6 ?& t* J% H$ V% E) j
使用Read-Write position independence同理,表示的可读可写数据段。
$ S7 Y, [1 E5 J* r0 i  I+ ^5 L/ r: M2 }  G! J1 f* N, s3 f
85.4.9 第8步,将程序可执行文件axf修改为stldr格式
& Z) h  t7 Q4 w. r) J, D) C通过下面的命令就可以将生成的axf可执行文件修改为stldr。$ \$ P& D8 V2 A- e* U; @6 i, w" i

) @0 B. X) x7 e- h
c683673d2481d8dce75b6fa01d624600.png

" G8 a/ K6 M8 N$ i: Z3 f& Q$ o  D: B& |& e7 Z" f* O# U6 a
85.4.10   第9步,分散加载设置
# X4 v  Q% i% i( t" S3 {0 l3 X我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。' y+ N9 f0 M8 O! I( t# r0 V
- `* F: x) B) c
b96835a7506516319002e6b2e8db9371.png 4 ?; T  T5 _, y6 S0 B6 Z: a

! R! T0 n9 T6 H6 s分散加载文件中的内容如下:& o3 H" O9 @" a
, L& O, V# I* ?5 [* u5 J# [  b
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    4 W& e/ X2 \0 e1 I0 z/ r
  2. {6 G- Q( h8 x' c$ u+ `" y
  3.   PrgCode +0           ; Code5 Z4 t  j3 l; e4 O
  4.   {
    / E) [# ]9 k. c9 `7 x& R
  5.     * (+RO)1 H6 E$ J8 {+ M6 Y& V4 L
  6.   }1 d  ^! e2 s, M& ?7 ?
  7.   PrgData +0           ; Data7 S8 P8 f' T. f) L& a0 s8 m5 R' _
  8.   {
    ) V. s' `4 X) G% H( Z0 @6 o, }
  9.     * (+RW,+ZI)/ q# y3 Q9 M- t3 ]6 l  D8 `
  10.   }; f, P6 i) G" v9 g9 H0 e$ r( ^
  11. }5 W9 [) y8 D" q5 G
  12. 6 D0 d- z9 }6 f1 `0 _
  13. DEVICE_INFO +0               ; Device Info
    ) i6 V, S3 @# _- n
  14. {; J- `/ `1 V5 D0 Q
  15.   DevInfo +0           ; Info structure. e0 x8 _5 \9 K7 K. B  @
  16.   {, g; N7 b* X9 w
  17.     dev_inf.o
    1 I, B4 o4 d* C. ?0 e
  18.   }
    / X: b; e2 r1 s% Z  t
  19. }
复制代码
* H9 I3 s: k$ Z, [* B, U5 [
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
* o/ |7 ?) ^- x% P9 b+ k7 m% X3 e7 L& {/ K# R
--diag_suppress L6305用于屏蔽L6503类型警告信息。
# n* `9 ?8 P4 U6 D4 x7 P
2 s0 O9 Q6 X' C/ z$ ?特别注意,设置了分散加载后,此处的配置就不再起作用了:# g4 |& P+ p9 l$ s
' p. H9 O* m+ o# T+ J) Y
641395c374a3af02f1a92d3ff9ee4199.png

: |5 D( ?! z! s8 M
* E8 R# L; g' ^5 G( t/ q85.5 SPI Flash的STM32CubeProg下载算法制作4 L* X" m6 C  t4 v
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
) w' C0 A* f# Y$ @$ S
  [* O* {' l- Q) X( Z9 ^85.5.1 第1步,制作前重要提示: C6 J$ c' J0 t. z3 ~" ^" G
这两点非常重要:
' v4 ~6 X- U9 Q9 ~1 M4 a; @+ Z
  x& }' c) v/ c1 P! E& P* f  程序里面不要开启任何中断,全部查询方式。, Z2 y( M$ V" F7 D  B8 n
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
, j/ i3 t: K2 ^% s8 _85.5.2 第2步,准备一个工程模板7 D+ M/ |5 t8 v+ h+ K5 Z
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。4 y0 o0 k! }/ b0 r! e

+ E/ `" D. Q  g. @: `; Z% T
80ad5e3abdd6a993c8621230401a1313.png

  U' L2 U1 \! Z' M3 k/ E2 J& k' o0 v' `% W' f
85.5.3 第3步,修改HAL库
- F4 P0 u& x7 E/ N; y& X7 [这一步比较重要,主要修改了以下三个文件:
! `5 B' m  u6 W7 i  W3 \1 z8 m4 Q$ q% C8 _
33bf89f25487547d4008f43b7cf10e03.png
  H3 ]' [" Q# z7 ^. i" Y0 d
' L9 ~) t0 D8 \) s  n. c$ |' a
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
  l& q' R! I# u6 a
8 V$ C+ X5 o1 m, F- X85.5.4 第4步,时钟初始化  x& F& I' j( D
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
" h) K* F3 L3 Z" T2 g- X8 q0 d) t; ~
  1. /*5 f; W6 P/ r( x8 v5 z% ^
  2. *********************************************************************************************************' ?* v) `1 e3 g+ A
  3. *    函 数 名: HAL_InitTick" y9 D+ q4 }' ~* d- S4 g
  4. *    功能说明: 重定向,不使用
    6 H9 ?. \' O* Z( v. L4 `
  5. *    形    参: TickPriority
    + O) ^1 f) E: H# p7 J
  6. *    返 回 值: 无( C5 N  z0 g1 Z6 u* b
  7. *********************************************************************************************************/ ?) M$ k3 s% C; A# Q5 P6 s6 b$ W
  8. */: `3 f' X- F# `5 R: v7 Q. @
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    4 \& Y) k4 W0 k( a2 q# D" a
  10. {5 H: t) k1 {" c0 m5 i
  11.     return HAL_OK;* K3 J) o# z& G8 K1 B, O
  12. }
复制代码
8 P6 l3 _9 @, r3 T# L% x
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:3 Y- b+ ]2 C) _$ y7 Q2 d  \2 a) N" ]
% z) g0 a8 Q" K; s1 ?- L+ z) y
  1. #if !defined  (HSE_VALUE)
    ' O8 {7 Y' h4 J8 ?& s
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */, [- x, t' d% n
  3. #endif /* HSE_VALUE */
复制代码
2 K- d" {: ~5 s3 Q0 Q3 I
最后修改PLL:+ i  n6 g& V& l; e) v! [
* c( U4 C4 t! t- L. l3 T9 ^
  1. /*7 z  F3 k% T7 @2 m  V+ o
  2. *********************************************************************************************************
    5 p5 T5 q- y# k
  3. *    函 数 名: SystemClock_Config+ j* s: w$ ]' h2 @+ l
  4. *    功能说明: 初始化系统时钟
    . b/ V( C6 k: V+ h/ Z
  5. *                System Clock source            = PLL (HSE)
    7 j) ~  F. ?$ r4 y2 r
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)7 `  s1 \7 Z: x+ W; e: O0 Q
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)& }, g7 t$ l% a
  8. *                AHB Prescaler                  = 26 L! Q- h  n+ w+ P  L0 A; b
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    ' Y3 f: p6 m$ \
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    3 M$ W" l2 N- \: a1 y& r9 F: X6 v$ {
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz); O3 R/ Y/ U) `8 c6 f
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)' |$ b, _: P5 }; G9 v, C/ h* x9 o# m
  13. *                HSE Frequency(Hz)              = 25000000' ~- }/ ]7 G7 m( @% s
  14. *               PLL_M                          = 5) c; I$ E% p2 {# H* s
  15. *                PLL_N                          = 160
    : _% n" L1 i/ V; {4 d( c
  16. *                PLL_P                          = 2- Q- `) u( \4 t3 }7 u/ P
  17. *                PLL_Q                          = 49 X& H5 K! e( w& U  I* q# D
  18. *                PLL_R                          = 2, b3 E9 g7 w# \
  19. *                VDD(V)                         = 3.3
    - F! P6 ]' v2 L% ^! D6 r! m
  20. *                Flash Latency(WS)              = 48 L8 v6 N6 R& J1 ^8 x$ @
  21. *    形    参: 无
    ) {8 e" u9 u# M" ^( Z% A1 V- g4 U' q
  22. *    返 回 值: 1 表示失败,0 表示成功
    9 d1 Q  ~7 t! X' V
  23. *********************************************************************************************************
    ( r2 Q  o! w* U7 _/ v( T5 g
  24. */2 k' q4 J7 P8 `6 G% `
  25. int SystemClock_Config(void)1 j/ j( W& W' g# X9 t# I6 k
  26. {
    0 v+ N0 P+ V2 G, k8 w; b" |$ }
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    + n: O) C, t4 j: m, `
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};/ i+ t; U& p  b$ P+ _3 M1 e
  29.     HAL_StatusTypeDef ret = HAL_OK;
    # S% Y5 ]1 Q! b5 t/ I  z$ N

  30. $ @  Q/ L4 D5 V9 |/ V) @
  31.     /* 锁住SCU(Supply configuration update) */7 b5 b+ }8 o% L0 `- K
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);5 V. n7 u! Y, G' L# @5 Z9 ]

  33. 4 ]2 l3 ^" A3 i2 W& L$ D
  34.     /* ( {, K+ n+ [2 V" D0 y$ g% n
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,6 L+ M# x. p: R+ f6 M' M0 ]. b% }
  36.          详情看参考手册的Table 12的表格。
    % v  F: n1 Q, x% G6 W
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。) m3 [4 q+ S3 Z6 `: v; G" s
  38.     */( @1 z/ ?. T( {6 |3 x4 D
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    $ r: G0 l; l( B6 F1 N
  40. : ~' p$ l& |9 r% S, D& a
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}% h0 _* h' {+ S0 O# h

  42. # f& D9 {1 f  z
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    ; m: }. ~' Y# `- {7 x6 c! V+ Y4 k
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;( A: m7 l) G; o: ?0 O  ^
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    7 C  @& v; w* \6 Z: U4 b' o4 w' T
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;! R$ H% w: q) A# }, C  B. P: [/ j
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    6 L/ ~4 D& g+ p) ?8 [
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;" u# p- K: H* {) M$ b
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    % N- _2 `) f' @- k+ _0 c# y( I
  50. ' l' h7 x. E$ ]" B" y4 F4 Y! p0 t
  51.     RCC_OscInitStruct.PLL.PLLM = 5;
    ) N% L0 n1 w+ q8 I( l  A  W
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    4 ^0 N* _. T* G
  53.     RCC_OscInitStruct.PLL.PLLP = 2;; R2 v" \" E* _4 i; v
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    & m: V6 x# |9 `% c" T, f
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    3 N! W4 L2 D' m1 J
  56. 3 U; |7 A4 Y- b
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    0 ?6 N, c* x1 t2 [* H4 O8 f! H
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    6 v/ G9 T  L7 {/ z) j# Z6 x; C
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    ( }/ _  S7 l* @; |- Z6 O; S5 l% e4 _
  60.     if(ret != HAL_OK)3 j9 r. s$ G- J# c
  61.     {
    9 A" C$ o8 P0 d  I
  62.         return 1;        " C; w* `, b5 e5 r2 t6 x, c! ?
  63.     }
    ! W8 X# |( O4 r3 `! D5 V
  64. 4 }5 i5 [1 P6 l
  65.     /*
    # e, X, S+ C1 T
  66.        选择PLL的输出作为系统时钟% W! S8 N( `+ i$ F/ H) O/ h
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    ' F' c! O$ B* m9 v4 p; k
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    7 F/ ?8 F$ t4 D# R
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线0 Y8 R: s9 c' g* F- Y
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    ' E+ ?% Q  Y5 w- `
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线, w% U, f$ L  k0 D
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    % `- K! l6 s5 `; E/ v5 ~9 w
  73.     */
    * {, B( Z( {% v% Y" w
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \, }' v: N8 Q8 j* P$ Y( V1 ^2 c9 C
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);8 Z& s8 I0 n" Z3 ]8 I
  76. ( u6 o7 `" P2 O8 m! S) z! {
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    7 H' ^, Z: x/ d
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;8 h  z" d6 C+ j4 Q/ r9 L4 W
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    " R: o0 j2 B; i" d* Q1 [
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  ' I  a* r% ]4 x4 H9 O4 N
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; 3 O) C8 T2 r. ^7 h) q" a* m
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 7 k! M3 a. e$ o. G" E7 G
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    * T9 v1 h6 U" @" ?" j
  84. : e/ b$ E; ^/ `( s1 `4 Y
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */$ @# \' C1 P, ~2 G& U, ?& U  ]
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    , v+ y2 c  R: }3 C9 l1 T$ T$ h
  87.     if(ret != HAL_OK)
    " L* H! n% N% g' G% ^
  88.     {4 ]; ^% O5 N$ B3 c# j* A$ @( K
  89.         return 1;9 S8 a( w: H. _; P
  90.     }0 m. ?. N# v$ _/ _' c/ U
  91.   s. A/ Y  F, d# `6 X! \1 ]4 h
  92.     /*! i1 v2 a, F* ^
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    " N/ e; ~1 g2 s8 e- b! _
  94.       (1)使能CSI clock
    $ Y8 ^/ K& T& a* m6 `0 d1 x0 P
  95.       (2)使能SYSCFG clock2 J( E! T+ p8 y& X* m* L
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0' y# H/ t+ {  X  P
  97.     */
    5 g6 X$ h3 j) c: ]& O
  98.     __HAL_RCC_CSI_ENABLE() ;
    ' u+ Q9 ]7 D1 Y& I. c& ~

  99. / O/ ]3 W$ E% x3 l' a5 X
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    / p$ j. P& |$ i

  101. . u. {' I% t3 w% Z
  102.     HAL_EnableCompensationCell();! T& k3 O( t  ^% F8 g' I, w1 n# x
  103. " n* n7 e# {3 b- m. P  u* p" Q
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();+ B% U6 w! G+ x( b- c
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();3 W* F( k  L- K. ~3 _3 R
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    # z" w8 F: `. F$ ?$ v
  107. / v8 J& ~: D- `+ o' y$ o0 k
  108.     return 0;7 v+ d3 G" L( c0 p
  109. }
复制代码

) E- z) s$ [" ]7 d* O85.5.5 第5步,配置文件Dev_Inf.c的实现
4 s1 L& ?3 U- i; U( |配置如下:
( P$ E& j; y1 I& K, z* l1 u+ Z
( o; v5 _0 L% n6 t
  1. #if defined (__ICCARM__)+ T& i& y3 {- Z. N/ c4 E
  2. __root struct StorageInfo const StorageInfo  =  {! b$ M) e) g6 _  N' r
  3. #else
    ( r5 |" J' Y& k. }; M
  4. struct StorageInfo const StorageInfo =  {( E: a' \! b; E4 s- c
  5. #endif
    ! F$ z* g5 U" d. r
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */; V9 P  Z% ~* Q8 G0 j: ?% J7 p& H
  7.     NOR_FLASH,                      /* 设备类型 */
    1 q; ^3 W9 D6 f+ J" c
  8.     0xC0000000,                     /* Flash起始地址 */) z5 ?% Y3 y6 t7 V' v4 ]7 b
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
    7 `+ y/ Q0 a) U, |4 D, r% u
  10.     4096,                           /* 编程页大小 */
    ) B* s3 Q7 V$ v+ _- @) @1 R0 D7 J: W
  11.     0xFF,                           /* 擦除后的数值 */, n  A) o. K3 ?( f
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
      W$ Z" u5 q( V. g: ~
  13.     0x00000000, 0x00000000,
    & G! o2 s' L9 R
  14. };
复制代码

8 O2 s- U+ }9 R/ t注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:& c7 G; w6 |: m& J6 S) x

/ x8 p6 V% c7 `2 {2 j2 k) L
51f220556177a36712977b8d140b627f.png

& c# g& U6 c$ z
) s5 K8 |& S6 P9 ^2 y2 V85.5.6 第6步,编程文件Loader_Src.c的实现
: S' U3 B9 C- u- e  o6 A3 [; @下面将变成文件中实现的几个函数为大家做个说明:
( M. a( m) x* U/ J# {
5 M6 U: S3 w) d9 e5 k/ V& {  初始化函数Init+ p1 i- T; H! @9 ]1 N$ n$ x
  1. /*
    8 r- i% S  t' y6 n  ^# {1 |- O; g
  2. *********************************************************************************************************! s/ }: V4 ~! z; N7 b
  3. *    函 数 名: Init
    $ i  r( d7 V5 g
  4. *    功能说明: Flash编程初始化
    ) ^* g1 l3 p: D6 O
  5. *    形    参: 无
    , `/ A3 O  Y* d- f2 x2 a  z' B
  6. *    返 回 值: 0 表示失败, 1表示成功6 g% _3 Y! |( J. H
  7. ********************************************************************************************************** l# }0 L. ]  @* v
  8. */
    8 Y5 k0 ?; q. O3 P) j6 ~
  9. int Init(void)" z& S" z3 o1 O* i% j& r2 h
  10. {   
    , `' ~: o2 A  }9 t( N1 C+ C
  11.     int result = 0;
    " A0 k4 A( _' }2 b& a
  12. * T0 n# R# ~! l# E
  13.     /* 系统初始化 */3 g9 A8 c& M  z0 y: R3 F
  14.     SystemInit(); / q6 q" w5 ~. P: X
  15. ) f* [2 W, }$ x& N3 R0 t; P
  16.     /* 时钟初始化 */2 n  \- V+ c7 O$ ]
  17.     result = SystemClock_Config();
    * `# o" w0 f& n
  18.     if (result == 1)9 {+ n1 U# W  b  p
  19.     {# Q; R# s% g- {8 t. u9 M
  20.         return 0;0 @5 f- C3 }; }. f) Y5 ]
  21.     }
    9 g1 l7 ^6 v2 F6 k. F. T8 c2 h
  22. ' c0 M( _! {1 s7 I; P
  23.     /* SPI Flash初始化 */
      n) n+ Z, @7 p
  24.     bsp_InitSPIBus();1 h, I. `2 I* F7 r
  25.     bsp_InitSFlash();$ ?3 p* |& T5 g* v7 ?6 l

  26. 5 g- I1 R1 w0 x/ e
  27.     return 1;) x+ I) Z5 O: W( D/ C0 B
  28. }
复制代码
' f9 a3 N& h; H$ D
  整个芯片擦除函数MassErase
. Y7 K* ]  x# E8 s9 S整个芯片的擦除实现如下:
& Y, L% }( P0 K* e$ h7 ^6 _+ g8 Z6 Y* e8 s) K* l5 A& _
  1. /*
    + M/ y& i& I- i$ c4 D
  2. *********************************************************************************************************, X5 \3 e0 n0 c
  3. *    函 数 名: MassErase
    3 }% e  q) |* J! x
  4. *    功能说明: 整个芯片擦除5 ^, y5 {# n! T, M3 K8 C
  5. *    形    参: 无" Z* N- E# D- I" |1 v
  6. *    返 回 值: 1 表示成功,0表示失败( P7 ], v- o6 |% T
  7. *********************************************************************************************************
    : h7 t1 S9 {& I& ^& W  O  d
  8. */2 W$ u1 `6 V( d. `8 w1 v% j
  9. int MassErase(void)9 c  m, m( S/ ]$ |9 ?+ Z
  10. {4 J9 @6 q* w0 Z, ^7 f
  11.     sf_EraseChip();
    8 E" {; Q/ L; |, I! \! t1 S# {

  12. ) p  j, t6 v5 _. r( b1 b6 g$ E
  13.     return 1;   
    ) F  A6 m% f. Q5 Z; v$ Y7 `" [
  14. }
    . E. H7 j+ [6 L1 \* i! }4 h
  15.   扇区擦除函数SectorErase
    ( e4 @+ p7 u, Z" _$ K1 T! c
  16. /*
    ! x; [  `- j8 ^2 X( {& ^
  17. *********************************************************************************************************
    + S' x6 x5 i  Z. B. ^" D
  18. *    函 数 名: SectorErase
    9 e  Z: d+ S0 P+ z4 H. P
  19. *    功能说明: EraseStartAddress 擦除起始地址6 w4 U6 @+ g& z! i* j9 x
  20. *             EraseEndAddress   擦除结束地址4 X6 L/ g& f) j  |' t3 Q
  21. *    形    参: adr 擦除地址
    4 `9 J  q5 d* U- d, d; P/ p
  22. *    返 回 值: 1 表示成功,0表示失败
    ; ~+ _2 {" y$ V& r
  23. *********************************************************************************************************% P' l3 g+ {0 W* }8 b5 T6 p! o, M1 v1 U
  24. */
    " D; o5 k: d: X* B
  25. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    5 D+ o& J. P/ K9 z
  26. {
    ( k2 u8 o; @5 w: P) v
  27.     uint32_t BlockAddr;
    % Y  k6 X; W/ [, a! y1 s7 I) l

  28. ! w8 X4 N0 }% Z+ [
  29.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;* P% s, x4 q1 k( v# n  q) ~$ D
  30.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;& R' e; E# ]% T+ e
  31.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    ; I% o, e. _  d, n
  32. ' D( [. y% U8 r
  33.     while (EraseEndAddress >= EraseStartAddress)
    ' @3 _4 e& R7 f$ K2 p: Y
  34.     {' l# }2 Q. N4 g+ J! R
  35.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;- M4 Q6 ~" h  Q/ J! d' a+ b) V6 p
  36. , b/ `" n! P5 H+ V7 g9 `- c
  37.         sf_EraseSector(BlockAddr);   
    8 u! C' d+ F/ E" w& B1 E$ m

  38. / q" v% B6 u# z
  39.         EraseStartAddress += 0x1000;* l. F* j$ \: m3 i7 n$ s
  40.     }2 E' ?( V9 }( f: v6 Y5 \0 K
  41. - X# J6 m: G0 @0 l+ n0 R5 M
  42.     return 1;! |5 {/ N9 c5 {7 E
  43. }
复制代码
9 p: z5 y/ `. `# H3 a2 M/ H7 p
这里要注意两点:
' z! @; N0 c+ s) q, x' Q& q- D8 J% M
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
) H" K, u9 k; K4 K
7 b2 N% N2 ^: z' s( x. M(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。
# r7 Y6 L# b/ `: x( Y8 t% T0 m- O; D+ a- i
  页编程函数Write
4 R2 k! z5 p/ x- z; g6 X" h! j页编程函数实现如下:+ ~0 ]& r# T0 D0 _- G
3 y7 m2 h" d7 n0 @2 b% t
  1. /*
    ! G$ U+ [, ~1 K* h4 F8 W
  2. *********************************************************************************************************
    & q9 H' Q% ~/ T6 V* D( w
  3. *    函 数 名: Write
    5 L4 Z5 i3 @0 _8 T' I
  4. *    功能说明: 写数据到Device% J2 S: _( \. h6 _4 Y3 H- M" @
  5. *    形    参: Address 写入地址  ?3 Q) @; i$ e. D3 _
  6. *             Size   写入大小,单位字节
    * m  U1 @$ c) B: ]. L+ K( O$ e
  7. *             buffer 要写入的数据地址
    ! n  b! D8 F$ J' R* G! r! A( S
  8. *    返 回 值: 1 表示成功,0表示失败
    $ I2 N& e: y, x2 b# v
  9. *********************************************************************************************************$ n( r5 `6 b/ J1 c! Q
  10. */$ G  j: I& N5 K  r
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)4 _# p% C. j* V# s3 ^5 I1 b- o
  12. {  2 g, ?5 @# j7 P) S
  13.     Address -= SPI_FLASH_MEM_ADDR;/ J% h4 C' ~/ B8 L- F0 u% \" d# P

  14. * d5 E0 _. o9 D- u& D/ J6 K
  15.     sf_WriteBuffer(buffer, Address, Size);! o+ T6 P# Y2 Q& p- x2 i7 Z8 ]/ t

  16. % m8 F; g7 ?+ S/ b6 v
  17.     return 1;
    * B) c( F- ~- q3 ~: J9 b* A: I
  18. }
复制代码
6 S2 r. B, Q. q0 s$ \2 j8 ^
这里注意两点:
1 u6 I6 c0 P8 c  W7 I/ T$ a$ ]% L" N# N; N* }# L# U
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。
1 A# ]5 `: l& I. M# g1 r+ [% [/ y3 |  m5 s8 v, i4 x, P9 P/ b+ n
(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
- \! ^. G3 |; i/ |  Q6 ^* J! u# h0 J6 I: D! U8 G7 ?3 G
  读取函数$ v; k/ O" o% x4 l
  1. /*
    1 N# s7 P0 h- A& V( w
  2. *********************************************************************************************************, r- O( j" Z! A# ~+ O
  3. *    函 数 名: Read
    " u2 D& @( N' @
  4. *    功能说明: 从SPI Flash读取数据
    9 o4 h8 H$ G+ Q  {6 b
  5. *    形    参: Address 读取地址6 c! @' I4 M: S1 D8 [8 H
  6. *             Size   读取大小,单位字节* P9 M( C5 h) G; n+ m
  7. *             buffer 读取存放的数据缓冲5 v3 J5 \  a5 Z* A
  8. *    返 回 值: 1 表示成功,0表示失败
    * _! J1 n. t9 ]7 s* m7 L- r8 w
  9. *********************************************************************************************************
    7 p( j3 V$ i' z. M+ m' Q# Q, _
  10. */
    : _6 \8 g$ g5 f. n+ C
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)4 {) v4 s6 S4 B1 F* n4 O6 q- Q
  12. { 2 e# q" H2 U: n. U1 t
  13.     Address -= SPI_FLASH_MEM_ADDR;/ U) g: [8 u. F$ g; n
  14.     sf_ReadBuffer(Buffer, Address, Size);
      k" o6 D+ w: [( s" Z8 i5 I

  15. ( C4 |+ P) F2 h5 {$ {8 [
  16.     return 1;7 D: {$ u8 V2 H- l
  17. }
复制代码

4 k! E, H& V+ p0 f* B7 C  G% s5 x  读取和校验函数& h+ O' S( u! i4 n; T7 J0 Q$ Q9 M
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
$ @# d! w2 U- ?- K8 P6 w# c7 q1 X3 K
+ Y0 }+ M4 `7 U: N7 p) F# G85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
2 y! d9 u8 v! a+ J6 p. W  F最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
5 L7 X0 i1 r0 D8 X1 v/ A- n) D, _, x' K7 U* `
  1. /*% t/ f- d7 X6 I% D
  2. *********************************************************************************************************
    0 W/ W( Z& B; h2 J0 Z) h
  3. *                                时钟,引脚,DMA,中断等宏定义
    2 ]/ w8 Z' s$ S" l4 s) ]9 W
  4. *********************************************************************************************************
    & v" d( x% t/ [/ L0 }
  5. */% L: d' e6 @" W2 ?( }$ |+ H
  6. #define SPIx                            SPI10 s* L9 Q- X1 S
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()) x. h7 t" `5 J/ N* j& b
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()3 I( ^7 ^/ w2 |+ f7 }; O% U9 ]
  9. 0 t! m4 Q& `4 N  u2 F& c. H* x
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()9 A" r* W* v4 E
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()2 N' _2 v6 e* v4 A% T! v. Q* @7 S
  12. 2 R! f% N/ @+ \  K; n; t
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    ) O1 J$ b$ C- t# ]
  14. #define SPIx_SCK_GPIO                    GPIOB
    , p3 a+ X5 I0 C6 c  T
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    7 e7 s0 I) f0 ]* t4 a: E
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI12 Q* r" z5 @9 [- Q

  17. ; H2 R6 Z7 y& `8 e
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()& X+ `. E$ {: G5 d5 I0 R
  19. #define SPIx_MISO_GPIO                    GPIOB
    " F8 f6 U" z5 M1 s* d2 W6 Y
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4- y. t, ^% X  W' G% ^9 F; t! c
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI14 F4 [9 O3 r  t# K/ @+ ^& [+ A

  22. 5 P% M6 g/ A; z+ ^" U7 W
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    , P. Z7 |. O+ z+ G! n; c  R
  24. #define SPIx_MOSI_GPIO                    GPIOB
    / H4 Z, I; U3 J9 ]4 R. N  O
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5+ o; F" z9 Q( `# q, [' B
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1, T* B  ^1 `, }4 [. H
  27. 硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
复制代码
* f2 F# j- S2 b* t1 \* s

) ]2 m% h- m2 ?- S! ^) f主要是下面这几个:
; r9 Z. |# G1 j1 M  o" ]
/ Q  U) K/ P' d" B* H4 U# t
  1. /* 串行Flash的片选GPIO端口, PD13  */5 E( S/ \) o1 F! i% `2 K0 C( u. a
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()7 o2 v( R" \3 F/ J
  3. #define SF_CS_GPIO                    GPIOD
    / Q8 A& Q7 n9 _/ i  Q' h
  4. #define SF_CS_PIN                    GPIO_PIN_13
    , D4 O+ v  x$ v( D7 B" m' X
  5. 3 ^0 ~, R4 D' Q; A6 n. u) e. E
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    % B) V# U6 o8 j# J; |
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
    " W. o' D3 U  [8 o/ ?& s

  8. ( v/ C! f$ `$ Z* Y
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */# W& i. S0 X, V; M1 C* N8 a
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */5 b' j* t- m  O
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    - K$ L3 M# C. ^8 z
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */9 Q! v, M- p/ R' i. n+ M1 K
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    , P& A) f5 l7 R9 ^6 q
  14. #define CMD_READ      0x03      /* 读数据区命令 */+ f; W% e9 T; u( i+ I6 y7 X( H0 o
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */' h# h# i. n" I* t) q! c
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */) L) ^: f' V: C4 P
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    . T5 ^2 w9 |* a
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    " A" T+ Y- @5 @8 a, `2 X
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */6 J: }4 T  E6 T8 Y% r8 I

  20. ; R9 v! [, j; p, _2 v. R
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

  w  X  D/ y- R6 ?85.6 QSPI Flash的STM32CubeProg下载算法使用方法3 M3 }2 J9 F. a/ f
编译本章教程配套的例子,生成的算法文件位于此路径下:
. Q- Z+ E  |6 b6 S0 ?6 o! u+ Q8 s6 I: z3 p
5ec6d402a7646232a55f4834cd00ac0c.png
7 d8 X3 v3 {3 B; X& ^; R- H( \

( Y6 c3 `$ d  \0 r' @8 C% `8 M# Y85.6.1 下载算法存放位置9 n' u; G6 h& x& y6 a; N( S
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
/ q; q: m/ T% S: C
  D# u5 f! K4 p7 H6 y- P) {\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
$ _% M4 L" l# j, c7 s  W$ a- A. _0 g
4 ?" x% I( y1 Y8 ~% B9 [1 o$ W- G
2609073778af5845a869911ab36624f4.png
9 v. {$ O5 L1 @! K! A; Q) r

6 ^) S3 V7 k4 q/ _1 h2 h85.6.2 STM32CubeProg下载配置
# m7 _' Z, g  `- t- y! ~8 b我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。# \$ l, ]. x  K' S5 B

$ J$ R. k$ W! y% p
27a88335ceb606bd021c3ebc65d88e21.png
3 `2 C! a0 a( u/ a$ V( o
: f7 S/ D7 w& e1 d
点击Connect后效果如下:9 y; x* z- }2 N5 B

- ?9 u& \, l' d2 z
d4e256633eafdbf4bcd8cce6e3b5909f.png
  V9 E8 y0 ~" ^' ?+ a, ~9 n
5 n) X8 q1 X6 s$ I
在这里选择我们制作的下载算法:
, W& L" R  n/ m2 }) r8 {- p- C8 D! O# D1 [2 q
d4fa28f64be2de98e04f3188eb2fe09e.png

: z7 C/ f" U$ p5 |' E7 ]) N! l
+ C2 B% `: ], Z' P5 f1 c任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming8 k7 S2 |! f3 A! |2 a+ ?4 |
/ C; e' x! V( Y8 m
842518a7cc1dc569598d372132b2ee58.png
& b0 [8 A2 Q: p
( }9 F% |3 m. s
下载完成后的效果如下:  x5 ?, a/ I2 i0 \9 I7 [- f

: q; o8 V" |7 v" `( L
68a92f267829ca77f125db982d6c0479.png

8 u2 D7 B# {, E% C4 b) H; {7 \" r) O; i5 [; L
85.6.3 验证算法文件是否可以正常使用- b9 F/ v! @! l1 t3 @" [' T# y* S# A
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
: ?+ r; N+ q$ q3 f1 z; R# n3 G8 s4 Z8 Q! |
566d2fba421c710da70e5656c6934466.png
5 m" K1 l6 b* y, T7 u
. i* \6 q- t9 W6 D! A" ^
85.7 实验例程说明
" z( u, G) _$ B6 n3 w本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。/ }( _* L( o0 y* i9 |8 j' [$ k& y
/ c5 j' o/ c; D  ^/ u
编译本章教程配套的例子,生成的算法文件位于此路径下:
. e0 B' f( M( a% F3 u# B% C
# ?' l5 c6 ]  S: e  b
6108d1ff7e349f2aa268ee31804c57ca.png
# e9 S9 n- D9 `2 x1 A
2 j1 c3 `1 l+ f8 P0 g
85.8 总结$ h5 w! ]. l9 }. r# b. c* y
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。& X6 ?) f2 _+ N3 T& f: ?( ^
5 g$ n$ |1 F5 v% ?9 |  |: x/ m3 E5 a
' K7 q, T4 u1 v6 T5 v8 S
收藏 评论0 发布时间:2021-11-6 23:35

举报

0个回答

所属标签

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