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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:35
85.1 初学者重要提示
" y  p& K) Z% A5 r  QSPI Flash的相关知识点可以看第78章和79章。. W8 k& r8 m* R  y0 C9 C  E
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。1 C6 F$ |* K' e! x
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样1 @# G! Y' a4 ^
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。, M, l# Z' d! R2 R! O
85.2 STM32CubeProg简介
3 p  G% o" B5 g+ H& RSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
# y% C% O# b/ D. ^' y7 A
# M% E4 `% M" x6 t, ]4 [7 h" _
d80578d217eb2a83520c6f3d797218f4.png
* H: L: e& m$ ?( M

  S1 S3 j' d' y( m$ T85.3 STM32CubeProg下载算法基础知识& Q& _; i) Z$ @, l7 ~, V
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。! d9 s: m0 K0 A2 f; Q) ~
0 @% k+ [* Z, ~5 P, L( H! d
85.3.1 程序能够通过下载算法下载到芯片的核心思想6 `) o% }  k, H) {) `
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。- _' |2 l! z) K
5 P6 n, a- F$ Z( @5 v
85.3.2 算法程序中擦除操作执行流程" e- b' h# Q( |/ ~, ~
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
; g  [9 j2 U- w; \$ B: a' u; v  L# T' R# m1 Y% R" P( N! q
擦除操作大致流程:
) M8 _1 g$ f* c0 @( r' `6 A8 C" r5 K! c7 {- X
d75670256363bb50203bed0bbe4f9fd2.png
' b$ }% j4 ]# e; y: C

6 W, ]0 X% S( _  v: e  加载算法到芯片RAM。- k' h% Y/ m% q" }' |
  执行初始化函数Init。0 M2 n, w+ \  S
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。9 K! {7 u4 \! x) n0 U/ M) P+ ?
  执行Uinit函数。
( G" q4 o# k! \7 T  操作完毕。9 r+ A* z$ y' x: ?1 b& f' O, X& s! }$ P
85.3.3 算法程序中编程操作执行流程
& J3 k# v" L! f; v( G* o6 p: m注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
- z/ R( a& e  k* Z, O. Z2 a7 ]
% {$ Y" f. ?8 Y- t& }, l! y编程操作大致流程:
- S$ W( ^2 |2 ]# t' i: u6 R, l0 E. U. A5 n# y. R6 S4 X! v! G
4fa96e9e33d700081f2cdbd312afb31e.png

+ M* l# p  b5 r5 |/ i& i" n. \5 j- `7 _3 Y$ g/ u1 ?) J8 L
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
0 {1 W/ q: A- x9 Z, t6 C  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
; \. n) `- q4 k; _9 q3 d  E% b  加载算法到RAM。" `$ |0 d$ D8 ?) k, E
  执行Init函数。
9 R# q; h+ f! c' {# f! B2 }( b0 {! i  加载用户到RAM缓冲。
' m+ \( o! ^/ }8 r% f  执行Program Page页编程函数。
4 ?8 r1 g7 U2 c  A  W  执行Uninit函数。6 \$ k' |9 r) e4 N; o
  操作完毕。
4 n5 B4 {& y: B. m+ W% a; a85.3.4 算法程序中校验操作执行流程
) C1 ^$ G' N( x# h注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
- d& [* o$ F* ^; B2 \2 @; e9 e$ @( _# [. k" e
校验操作大致流程:
, G# ^' v4 C3 ?% L0 N7 ?* D) ~( R. [7 G/ \/ p2 k! ]1 J
7deeae4b650bd82c457492af14fc26b7.png

! f" Z" K! b8 B6 z7 l7 F
- @. v) i3 w/ k4 }; D* I( O1 W  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。' K9 a2 y1 j1 I7 X2 q7 {, I! J
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
0 @6 r. K3 f5 m 加载算法到RAM。8 I8 m% Y" v4 D1 y5 W
执行Init函数。+ M' I5 B! g0 E2 y5 ^5 ?. h
查看校验算法是否存在" d3 B0 a" K4 E' E' ?( @' W
  如果有,加载应用程序到RAM并执行校验。( W6 y6 F1 T! y
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。- T1 Y$ x: S. X1 J
  执行Uninit函数。
; A) [9 s. A7 z1 V  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。- r  q/ \$ {# p' `* z0 ?3 ]
  执行RecoverySupportStop,回复支持停止。
8 J2 Y8 T1 U4 O/ ^6 J# E  执行DebugCoreStop,调试内核停止。( b/ T) y; G. N( I8 J! s
  运行应用:
& O5 z( B+ h) q* @6 I0 P" E- T  执行失败2 o* t. G0 H  {, m( m  W
  执行成功,再执行硬件复位。
1 g; z0 }5 K( S4 @! K& ?  操作完毕,停止调试端口。& N! B! N! x1 p  m- Y1 j
85.4 创建STM32CubeProg下载算法通用流程
2 {/ D3 Z: l1 v. J. ?下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
" o4 P% l$ R- H  k* V
: h* p! ]" v6 B8 G4 y85.4.1 第1步,使用STM32CubeProg提供好的程序模板
& r2 O$ H# G2 S; Z2 q' \  W, O位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
; p: e8 Q' g" j  r0 c5 |7 b5 J9 `3 K
97f385e3b62a055bac9b5c0214b877e5.png
2 p5 ]: o; z3 q: ]) ?0 O8 I* U

6 |/ c3 N% W8 I以M25P64为例(注,其余步骤就以这个为例子进行说明):) J. r1 o' ]' N

, s4 Q  o' }. Q* U  a6 w3 A
43b298f996937fe35e89697fc6479cb9.png
! I  @( j* D& s- B; `6 C$ J: R
- j* t; `/ `6 d& \2 |! z8 l7 ?
85.4.2 第2步,修改工程名7 x. K2 Y; `: u' [* e2 N' y
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
4 I0 y8 l0 |2 B5 k& h( K$ R+ b* `5 i) F& c
85.4.3 第3步,修改使用的器件
) T0 H8 |. q. c& C' D在MDK的Option选项里面设置使用的器件。
/ X$ a+ X/ }0 |* N5 y! A: g8 ^9 f! n' J9 K3 b2 y
36026b9a6d60249610d0246eb725b246.png
" i% j( z0 E/ @2 C

' ?' C7 O( ?3 h; x+ T85.4.4 第4步,修改输出算法文件的名字2 m, @  g' ^0 ~1 l; o$ N
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
5 _; U0 ]# {: P! H7 {0 w( f
0 V  u( R3 c$ Q: r' B$ A
52c72b3993f28a366a22eac56f8fd043.png

# _. h7 i( V3 H8 }- _1 m$ G
- v' ~7 f) c+ L" F4 F$ F" s! ^" [. h8 R注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:* Y* d* u5 w2 J8 `7 z8 Y
3 G3 }% y+ Q4 M" D# _: Z3 W" ~
7b48153027a44de33926c2da008716a0.png

2 _$ r5 Q$ \9 c2 y/ {' [) T5 I2 P; S
/ J' A" J) @) Z2 B0 [85.4.5 第5步,修改使用的库文件和头文件路径- k" T9 L; t6 @  i$ P& V$ @
根据大家使用的主控芯片,添加相应的库文件和头文件路径。5 H: J6 R: [1 o! n
  H8 R- Z! g" W# A9 f9 I0 b. H
85.4.6 第5步,修改编程算法文件Loader_Src.c, `" \# ?9 ?5 ^6 O6 z
模板工程里面提供的是M25P64,部分代码如下:3 B+ d3 {- ~/ E# t

9 a# m) c- \# x. j3 d
  1. /**
    + M* \: ~3 L6 x7 S4 B
  2.   * Description :* w0 d5 i. ~# Z- \# n
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the  |  ^; F! E8 C" P7 l4 W2 q# i
  4.   * device and initilize the FSMC with the chosen configuration
    7 U( Q# n7 |# P
  5.   * Inputs    :, o  e' o$ L) @  g; L. n1 v: S& n
  6.   *      None
    . m: w0 R" o9 `) f( q$ \, q
  7.   * outputs   :
    ; j2 @9 h# B& o2 q: D: r# r. h
  8.   *      R0             : "1"             : Operation succeeded
    ( _3 r2 ~& ~5 R' o1 Z
  9.   *               "0"             : Operation failure3 `4 y8 U6 f/ }$ x* y
  10.   * Note: Mandatory for all types of device , I& O9 Y/ L2 }
  11.   */
    4 m9 [3 N) X9 n" V! d+ r& \/ n
  12. int Init (void)
    - G) d1 s9 T$ t# k# a  C# }2 T
  13. {  . p  i7 c1 P8 ?% I6 v
  14.   SystemInit();- m8 A4 W" J+ _$ |
  15.   sFLASH_Init();+ ]( G) i% c; |( {, g2 p; N
  16.   return 1;
    " l! }7 ~+ o, {, P% E. }
  17. }
    0 O" w9 a9 G* C6 B% d* i

  18. 8 b- Y* U; P  k6 r6 h* Z

  19. ( `$ v, J" S, |( h4 X  F- O: x
  20. /**
    ) t3 }' d- ]* t% J5 k6 @! ~7 N$ d
  21.   * Description :
    5 f, y' R& F- x6 Q# o& ~" h
  22.   * Read data from the device
    & g/ p0 g+ H' F4 f
  23.   * Inputs    :
    * {, b" {9 |/ b, e- u; l
  24.   *      Address       : Write location: |2 m- T* q- k4 e
  25.   *      Size          : Length in bytes  
    $ P+ v1 V2 R5 N4 R3 A; ^
  26.   *      buffer        : Address where to get the data to write
    ; H' c: Z& h  c! J* Z/ R
  27.   * outputs   :; Y3 z7 C0 N3 ^. e# a4 I
  28.   *      R0             : "1"             : Operation succeeded9 Q) h, z$ T: D$ x
  29.   *               "0"             : Operation failure) J- I3 S! u5 U
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    0 w+ `/ ]! S, D# |) {
  31.   */: f7 C, A3 R7 `, l1 T5 C
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    ' P  k1 B0 A& g. M
  33. { $ V* M2 d3 y6 c. m
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    1 |) Q& v2 A8 O, h; o0 x: ?
  35.   return 1;) _# T( s9 j$ L- ~4 V* t
  36. } 9 A7 N' Q6 D" _
  37.   r, E# v% W( g6 a* X  [6 t
  38. " {; N; t4 i% b+ v
  39. /**
    , x3 |( ]( _* c) N0 F7 A
  40.   * Description :# X; B- W  j: g
  41.   * Write data from the device , S9 T7 u* L% o* O9 V4 P; A
  42.   * Inputs    :
    # d( C! W) `- h( U
  43.   *      Address       : Write location6 R, t8 J8 S7 y: y, K
  44.   *      Size          : Length in bytes  ) i# {: p% G( Q3 \
  45.   *      buffer        : Address where to get the data to write% H& J  {3 H. _# ]7 ^, ^% ]
  46.   * outputs   :% h' d2 v; n9 }1 W; F: G
  47.   *      R0           : "1"             : Operation succeeded
    , A8 U! Y, W- b) i& d, |
  48.   *                     "0"             : Operation failure4 w* c. ?6 {  G. H
  49.   * Note: Mandatory for all types except SRAM and PSRAM    ( c8 i% [) U( [  V& u
  50.   */, Q) G/ r# j) o* m
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)  o$ [% ^, f( z4 m6 v' J
  52. {
    9 O9 H& X+ P! `% m9 o) Q
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    . M$ F2 N# |( r( P% r2 @9 D
  54.   return 1;$ M; g# {  n% n; f2 B
  55. }
    & m8 f5 T. }/ W% i/ d6 R
  56. * T- X* E7 J7 p, m- J1 R0 O- M5 y

  57. * p4 E+ a2 _8 c) n
  58. /**
    $ \1 @  T2 ]4 z1 W6 W
  59.   * Description :# |8 H( L; d3 `/ ?; `* t
  60.   * Erase a full sector in the device: r2 c" L* b% U/ V
  61.   * Inputs    :
    + h/ g9 y5 _" l8 s1 n) ^9 ]2 Z" T
  62.   *     None
    ( v4 D" O, C$ W
  63.   * outputs   :- s) L' ]7 B7 C+ y4 d  x
  64.   *     R0             : "1" : Operation succeeded
    8 L" e# j4 }& @! s
  65.   *              "0" : Operation failure6 R7 \) e6 \( ^- u. t& w2 B
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    / W! G% @4 Q$ C
  67.   */  d# F1 q+ H/ F) G% }2 U
  68. int MassErase (void)2 n. p% y8 s1 ?: z+ Y
  69. {  . I7 M" S- P4 k9 s  x2 ]! `# e
  70.   sFLASH_EraseBulk();
    + A6 C( L" }8 ^5 ~' X( G1 m
  71.   return 1;    5 U' J& H6 S) `% ?) y8 o. Y) `
  72. }
复制代码

5 [+ ]3 G4 Q6 a: c0 g2 C8 |85.4.7 第6步,修改配置文件Dev_Inf.c: C  z/ z- Y" o+ O5 Z
模板工程里面提供简单的配置说明:
' W& {: d( C( }2 G1 c) }' o* m( ]* d/ ?, ?+ W
  1. #if defined (__ICCARM__)( g8 f7 {1 P+ z, c: \) |* p
  2. __root struct StorageInfo const StorageInfo  =  {
    4 p  o! N. D( _" t  Y
  3. #else6 d, Z4 L- t2 |0 C% Z
  4. struct StorageInfo const StorageInfo  =  {
    1 r, X5 l. _& U. u. h) r, j- c
  5. #endif' T7 Y+ {& C0 B; x3 A
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number% d8 q+ s; r2 @( T) Z: T
  7.    SPI_FLASH,                       // Device Type
    3 q. Z8 l$ o4 ]2 l) }3 f2 y
  8.    0x00000000,                     // Device Start Address
    " \: U; a) g6 W- f4 U. x
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)0 C+ \+ C" h$ m
  10.    0x00000100,                      // Programming Page Size 16Bytes8 A9 V$ l( f8 }- A) \8 y
  11.    0xFF,                            // Initial Content of Erased Memory& h" ]7 q( z% x* a9 c  d
  12. // Specify Size and Address of Sectors (view example below)
    % s! O9 V* Q, A
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes 1 a5 b5 ?# i1 ^' Z
  14.    0x00000000, 0x00000000,
    ( o+ h( s# c% e8 z4 K  |3 d
  15. };
复制代码

0 E! ^& l( A$ ~- A2 D注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。$ @  D, s% _6 q% ]. h

$ F" v% E$ j4 p- p1 {% O85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。: E% b; x9 l+ e6 o( q  \5 ~
C和汇编的配置都勾选上:
' P' r. m& H  B" x  c% D7 d! Y' c9 S" K5 h3 p( {2 J' R
afddd6e839292aa2b94b43e79544d630.png
3 F6 B2 ?9 m# P- a' r
7 P: m- C/ Y) V/ R& I/ c
汇编:8 Q4 U. O0 Z$ R! Y( ?7 o% `

! L/ z! Z8 B" V" E+ R+ P
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
5 a" E7 W" L" V# q7 G5 Y4 d
6 [8 s6 f# C3 {2 A! u! D1 m
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:3 X$ z" P0 n2 @
. k( f" M$ q9 {
(1)加载以响应运行事件。
# q: j; B0 A9 d: ^3 P
/ ^; @- k0 q, [3 i(2)在不同情况下使用其他例程的不同组合加载到内存中。
# Y- f% S( f. |* I" X# o7 D& J2 {1 U) n! f* y
(3)在执行期间映射到不同的地址。
' L$ e6 ^+ U) I$ w* V
/ I. Q3 v6 [0 n2 V; i' \7 h. p使用Read-Write position independence同理,表示的可读可写数据段。
2 w! T* w) `# J# x) b  c  V6 f) k+ ^* l/ Z9 W6 ?6 Y( z
85.4.9 第8步,将程序可执行文件axf修改为stldr格式2 T: |- L: q( _2 M
通过下面的命令就可以将生成的axf可执行文件修改为stldr。
* J' n5 K6 C: |+ r" m6 w& u$ L  o( i- ]( k; N
c683673d2481d8dce75b6fa01d624600.png
$ L9 c3 u8 E$ {! M$ l
7 l" K! g5 f/ z0 U
85.4.10   第9步,分散加载设置
7 [! w- i9 Q1 _! \' a* f* k) ~. E$ R% H我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
1 ]; I* I: h3 Y1 G9 s, A9 l1 F
7 W0 I1 N5 d$ c6 i4 G. v. `& `1 P b96835a7506516319002e6b2e8db9371.png - p  p; M; ?1 N! X0 \+ T
+ A! r8 L, t& T$ y: ^+ v( `
分散加载文件中的内容如下:" e* }( [9 b7 a3 z7 J1 J
( W8 g8 ?" X4 G/ Q) \
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions) Y$ C. c% Z2 U: m
  2. {
    $ w6 m0 X# s- T) W& s
  3.   PrgCode +0           ; Code
    ; h5 x* B5 r3 F/ p  {/ o$ Z1 d
  4.   {" n9 q5 W3 T; p( o' H, J2 z$ Y
  5.     * (+RO)" K' m3 x& X  C
  6.   }$ h( m, n+ k1 d* P- i& X
  7.   PrgData +0           ; Data
    & f* B, O" _* u* Y8 X. P
  8.   {
    ( q7 `* _; S6 D  T7 B9 M
  9.     * (+RW,+ZI)
    6 N5 x/ b* I7 A7 P8 I8 K6 e' l
  10.   }
    ) q! ^. r$ I0 A: @7 B
  11. }
    1 j" G5 R; Q, [( B2 @+ Z
  12. " Q3 y4 O5 }5 \1 H& \
  13. DEVICE_INFO +0               ; Device Info
    ; z# K# U5 k/ V4 U1 E% y
  14. {
    % k. M* K! [" V% Z
  15.   DevInfo +0           ; Info structure
    & L* x7 q. L* {& z4 Y
  16.   {
    : m& }4 }& G6 y
  17.     dev_inf.o
    - C. i7 M. u4 M) R; ~
  18.   }1 @# i: `7 K* c  [2 O) `
  19. }
复制代码
; b: T  A7 p9 ?$ J
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。# X4 S6 d' E; y* k) ]3 ^: M

" [$ F5 Z. P4 g3 a- Y--diag_suppress L6305用于屏蔽L6503类型警告信息。; |7 _. T- z: ~6 n3 ?( i0 B0 c
' Z$ @& w, k5 c* U; A1 Q
特别注意,设置了分散加载后,此处的配置就不再起作用了:! z! f# M; S& g, K
) {+ M  O& X) U$ S/ T" b  q, L% a
641395c374a3af02f1a92d3ff9ee4199.png
7 E1 h$ i& ~# b! J
; y0 O/ k! R! Y* O
85.5 SPI Flash的STM32CubeProg下载算法制作/ s) j; c7 d9 X! Q5 |* W
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。7 }% E& v2 [! u* F
8 g$ Y* l% p* A' C& r# H
85.5.1 第1步,制作前重要提示
; {* y3 ~) U: a9 C2 _. N# D这两点非常重要:( \  x, n: W, c' L6 c* g% V  L1 ]
: z7 A" u  H0 F  U9 s1 G7 X
  程序里面不要开启任何中断,全部查询方式。7 }; A: {0 \5 S4 W2 W
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。/ G8 g- u% E: m* u2 ^  Y- S
85.5.2 第2步,准备一个工程模板* J5 U* L5 p$ {! f- C
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。; B* @! @! z/ I' T. l; y1 D+ U
) G0 @2 S9 e+ v3 ^. d
80ad5e3abdd6a993c8621230401a1313.png

0 ], J1 y4 b8 }. j! n
, {6 V0 y( L2 W# K6 R2 C85.5.3 第3步,修改HAL库
+ s9 N) T$ z) M: {3 b  ~  t. G这一步比较重要,主要修改了以下三个文件:
8 S. m- j5 l  C+ r$ B9 h% W0 {% N) U& M; J
33bf89f25487547d4008f43b7cf10e03.png
7 P6 _; N# E& i) V' F) t, p0 D
0 R8 W! \* T; S6 E8 S1 O: ?
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
* S: y# M) Q' N  |0 v4 l1 o3 Z$ L- r0 I( F: i
85.5.4 第4步,时钟初始化
1 g& v7 x6 |  c, g5 {. h/ {) E我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
1 a' g5 ?) _. v4 O5 a  q5 g
7 T" Q; O4 E, y/ p1 l* c+ S
  1. /*
    # }& f+ _% W! @  R7 P6 U. K5 s
  2. *********************************************************************************************************" w1 S1 i3 n( H9 t4 ^( c/ Y% F
  3. *    函 数 名: HAL_InitTick
      @0 S: _& B) P' w, `9 V' x
  4. *    功能说明: 重定向,不使用* {$ I, D) Q1 A. J9 i, O  F
  5. *    形    参: TickPriority
    & K7 t0 A; q$ s, L  Z" C9 }; }
  6. *    返 回 值: 无
    * }: A6 D: C5 j. c' d4 J: O7 G- k& M
  7. *********************************************************************************************************
    ( J/ x1 v$ A# B6 u2 Z
  8. */
    . e* h% a3 X. V8 y- B) |
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    : z4 A# \+ G2 t3 Z) \5 w+ \
  10. {( ?" y- Y# ]" M
  11.     return HAL_OK;: h6 u( ?1 j5 H
  12. }
复制代码

9 E3 C* r: e' M) X6 A然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:) f! C; a2 @6 e3 q& @2 g

3 `$ u9 a! Y( h1 u
  1. #if !defined  (HSE_VALUE) 4 {8 K! b/ K/ l
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */7 `% G5 T2 L5 [! G# |# |
  3. #endif /* HSE_VALUE */
复制代码
# W  E' a  D; {1 p3 R5 Y4 V4 M
最后修改PLL:4 f/ o# z/ i9 n1 f: ~: A

' @0 Y7 N* P: C
  1. /*
    - s! B" o8 w' \  T% B
  2. *********************************************************************************************************
    : \. ~, P! A6 e! d: v6 R" o
  3. *    函 数 名: SystemClock_Config1 u0 ~, b, @  t
  4. *    功能说明: 初始化系统时钟+ I6 T- ?/ K$ H
  5. *                System Clock source            = PLL (HSE)5 l. B% w+ o' Z6 b
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)% l6 S8 X4 r; g3 m; |8 L4 _% x# w
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)! F' @+ X" u0 z  `  i3 g
  8. *                AHB Prescaler                  = 2
    1 `9 G. q, p; |3 {# l! j+ p
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    1 L: P+ k1 ?0 k
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    " z) c$ [1 e( }  g
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    & ]$ @, b( |( X) a# i
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz): R4 l0 a1 F* E
  13. *                HSE Frequency(Hz)              = 25000000. ^) O' e2 q- c
  14. *               PLL_M                          = 5& {  E: \/ P- z& z
  15. *                PLL_N                          = 1601 J# F0 c7 e  Z6 P4 O" S7 q6 W
  16. *                PLL_P                          = 2+ ~& C( B3 [! r* _" X
  17. *                PLL_Q                          = 4
      \, h# P& w  n
  18. *                PLL_R                          = 2
    + w8 `. S6 g1 z" w& M# F( g& r1 A
  19. *                VDD(V)                         = 3.3
    9 T8 X  V# r9 |7 V' z4 q
  20. *                Flash Latency(WS)              = 4# O% E. P( m# c4 b+ p/ m
  21. *    形    参: 无
    4 d' k1 l* _% n' C! p9 T
  22. *    返 回 值: 1 表示失败,0 表示成功
    % k! G* Q. r* E2 N- R( k
  23. *********************************************************************************************************+ m" _. i# I8 ]* Q- _  a5 ?- }
  24. */; ?; s- s, i) j
  25. int SystemClock_Config(void)9 f- C' ?: r! K; ]* F" o+ \
  26. {/ _! o$ b* x" Y' `1 W
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    8 e& a' P, p7 P' w
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    ) E& U3 X  E. Q5 d
  29.     HAL_StatusTypeDef ret = HAL_OK;
    3 _3 s( G1 u. W9 [
  30.   x, {5 D& O3 k2 m* o# Y
  31.     /* 锁住SCU(Supply configuration update) */' u3 r/ a3 v7 T2 o. z
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    6 d7 ?, o' W  i# h' r$ a! Q1 R

  33. # M  m9 ^( W4 k  v5 p
  34.     /* ' n5 m: v0 a$ @  M- T
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,! D; w2 b6 U3 X  M9 @
  36.          详情看参考手册的Table 12的表格。
    : D: R; X$ L+ K1 N3 E/ i
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。# I" ]/ S  c+ s3 Q, P
  38.     */9 |$ Z' M  j7 n/ R
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);7 C/ }; a2 h2 p+ A1 t! N

  40. 8 |% ~  u  N& |, N) u4 T4 _+ h! [4 P7 C
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    : Q; k" o4 S- ]$ S- @. h5 k
  42. ( e; g6 J) E3 j/ C6 p. z) Z% {
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    2 A7 ?" D4 x4 }! |4 t
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;; [7 u1 w( B5 \
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    - _2 }. c3 T- o7 o8 z9 P
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;/ q2 m) |' o$ S& u1 P$ s2 V
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    ) j' \$ \* C0 v: Z) b# d
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;5 X4 X9 o! j7 N, `4 f
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;3 v: J$ [0 m' `* H" A  W

  50. 3 ^' b3 W2 O) m$ Z
  51.     RCC_OscInitStruct.PLL.PLLM = 5;! X" I2 p5 s4 Z- Y
  52.     RCC_OscInitStruct.PLL.PLLN = 160;: @+ W3 y/ d( m( U$ q$ q
  53.     RCC_OscInitStruct.PLL.PLLP = 2;( L/ y2 }4 s' |% p
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    " }" s. \4 k# ]( F' q* P
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        5 H2 q7 `% J2 S3 @
  56. $ o3 D$ V/ T- A& V0 `
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    * V0 K# k" ^7 j$ n) T
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    - |: G( h( t: ~! c8 A$ s( ?0 W
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);" x7 s+ ^: a- f. F4 i. ?
  60.     if(ret != HAL_OK)  \3 m2 g( u9 M% W6 j/ m9 }
  61.     {3 x$ L* W' o, ?" ^. T2 y3 E
  62.         return 1;        8 n8 ]/ A; C. @# P" o% E) P. e4 R
  63.     }
      y8 |& c# ]7 P
  64. # l5 E1 @' E- b+ e3 @0 l
  65.     /* ) r+ T$ I4 ~4 F2 ~! e( r8 P$ x, z
  66.        选择PLL的输出作为系统时钟" _3 _" r7 C7 J+ ^$ t. C
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    ) H- }- I0 D- o
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    ; T8 T* F1 O& s' ~
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
      S8 k' `, H) C' b; O
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    , ~0 c$ g8 l$ p) ?' U+ A; |
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线3 W* x- E+ p! I( E1 A
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    # R; I: \+ V8 [+ E; t
  73.     */
    1 S  Y( `+ D/ \0 L
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    0 K$ I( F0 w" X& B4 x; ~1 i- f
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    3 j. t- D! R+ l; M/ y
  76. 7 X/ t( |3 K- d% |/ C* z
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;! e2 x) d* E/ y6 V6 }
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;) v* [  Y8 l4 G+ r3 C- y
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;  p; F: g  o& [0 g5 X4 L
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    : U! i/ B/ [0 s; s' Y
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    / U' f8 N5 N& i
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    * X& u9 ?6 h5 u/ m3 T" i$ a
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    # t  |: Z6 A2 V' W( h# z
  84. 7 l3 L* s: V$ s: y' p
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    2 P* L2 P6 L- x' h) k; }
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);+ s1 y% M" g0 z% |; D, Y$ C7 e
  87.     if(ret != HAL_OK)
    ) [: L* R2 j' L0 O% R( o6 k2 I
  88.     {3 s4 a  Z! q/ w8 m, \* z8 y
  89.         return 1;( b3 a& }2 D/ H$ {5 Q+ @
  90.     }
    ; _- C6 i: L/ X' ^% i* H2 w
  91. 2 }# j$ E7 P2 x
  92.     /*) e" f- S* @) n
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 9 z5 p% O7 W' P4 P- K2 `8 Z% G2 x
  94.       (1)使能CSI clock/ n. s9 `% }3 K! }
  95.       (2)使能SYSCFG clock- X/ k1 ?" i0 R& f* C& X
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit04 E8 Q7 {. l- E) i
  97.     */
    $ k* z+ Y/ A2 z
  98.     __HAL_RCC_CSI_ENABLE() ;
    * P$ \& L3 y+ U" d/ p4 B

  99. 1 T: L% W. {' e- h1 O
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;1 E) d) G' y" G% S6 `

  101. 2 [- C) H8 ~7 N/ X* Y  D7 ?$ v
  102.     HAL_EnableCompensationCell();
    3 w. [3 f5 X6 a1 j! u, q) Z) q

  103. . C0 L5 x3 r$ W% t* V
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    6 d; m% J. m. ^# r
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();: f0 k( C7 N$ R
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();8 }2 p* {+ ?/ x4 ~% A& K. g" D
  107. * i& c" z9 g" L8 b1 A
  108.     return 0;
    0 @: R, z# S) d, q9 t
  109. }
复制代码
7 z! Z# I6 W; N4 d' c" W
85.5.5 第5步,配置文件Dev_Inf.c的实现
3 u. B: ^) J* a1 ]2 y1 i& Z  f配置如下:$ h' `) Z( C* z7 d8 Y
* T1 V! F3 {) U
  1. #if defined (__ICCARM__)
    " C( V6 D. q2 \  @
  2. __root struct StorageInfo const StorageInfo  =  {
    ! |5 U* L; H" _3 m
  3. #else4 u1 D0 L4 L# H
  4. struct StorageInfo const StorageInfo =  {
    , M; ]/ f% Q3 J! K
  5. #endif3 {8 U' V( {- ?7 X
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    & q) c9 U, }4 [' |. C0 ^
  7.     NOR_FLASH,                      /* 设备类型 */6 }3 T/ }' H- S: F( j
  8.     0xC0000000,                     /* Flash起始地址 */
    ; @, k* F) ?1 a! ]  s! j8 Q
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
      h0 l& A1 U4 [2 ~: K8 Y6 m& k
  10.     4096,                           /* 编程页大小 */
      ^$ Z) f% \5 V- M5 f
  11.     0xFF,                           /* 擦除后的数值 */# K# I0 Z. e. P" u% y
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */  w+ J2 a( Z. S1 T
  13.     0x00000000, 0x00000000,
    4 @6 L5 d5 O/ C9 \: x* y2 U. U
  14. };
复制代码
% U3 C- r* E! s0 _2 s) M. F2 Y( @
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
* O7 w9 U' K: T, i+ F( b: Z  {6 w3 }. k8 r3 P$ k* B6 I
51f220556177a36712977b8d140b627f.png

( x1 L. q3 z# A0 h. ]
# T/ H, S4 @1 p9 E85.5.6 第6步,编程文件Loader_Src.c的实现
8 q6 b7 T; s# {6 O! G8 B下面将变成文件中实现的几个函数为大家做个说明:
0 b- }1 J  c5 S) U2 I1 C" W) o* \2 N3 m5 u4 S8 s2 O5 t
  初始化函数Init  I6 ]9 q: Y: ^4 ~- f, x
  1. /*9 O$ m" i  P4 O& f
  2. *********************************************************************************************************
    * y8 j. K2 ?' \! r2 A1 R( l) N
  3. *    函 数 名: Init
    7 Y. q' i- K4 u, t" T/ I
  4. *    功能说明: Flash编程初始化: W2 @8 c3 Y- t5 H/ n, ~
  5. *    形    参: 无
    , q. @5 X  T0 Y7 s
  6. *    返 回 值: 0 表示失败, 1表示成功% U( Q" A* n1 z
  7. *********************************************************************************************************
    & V+ h/ D1 P$ C- B9 F
  8. */1 u& L2 `8 P1 W7 f% s. Y( l
  9. int Init(void)# r+ C/ X* q0 j4 W7 R! Q
  10. {   0 u. P" d4 L7 ^: e: @  I3 D1 e
  11.     int result = 0;
    , B$ `4 Q' q' x' {6 {: N

  12. # m5 @7 \' g1 w' b7 E
  13.     /* 系统初始化 */
    $ {+ T) [0 k% c3 K+ Z) k  P
  14.     SystemInit();
    2 A$ l. F$ H; a2 D
  15. 4 W  \- Q8 U% E7 ~2 P
  16.     /* 时钟初始化 */5 o* i6 @: u% d
  17.     result = SystemClock_Config();
    ( _% m/ `) D* u3 n7 `1 D( }
  18.     if (result == 1). ?* B6 t- M9 q+ |0 t2 H
  19.     {
    & K* ^9 z2 D; E: W
  20.         return 0;1 w8 I  H: g5 n
  21.     }: q8 N: N; ?+ A7 ]9 `' S6 c

  22. # {' `+ k9 \/ k
  23.     /* SPI Flash初始化 */" u- ^% n6 l; [# [/ Z" |9 Q
  24.     bsp_InitSPIBus();
    8 i% a) x% z5 I6 G
  25.     bsp_InitSFlash();- X/ m  c5 y' f9 x' C* o6 Q5 x
  26. 6 l& F" T$ N: u6 Q; D5 z. T
  27.     return 1;% A$ L2 h0 w- N+ T
  28. }
复制代码

" l- ^0 j! \% q& \0 o* i5 ?  整个芯片擦除函数MassErase! x# K# f! |% w: @0 Z) W
整个芯片的擦除实现如下:, p( U6 P# {. X# ]
* z4 \. W- p5 o. \
  1. /*5 B+ l9 ~+ t/ b# `4 |( w! I
  2. *********************************************************************************************************
    ( t0 J3 M% O" p
  3. *    函 数 名: MassErase
      p, g) W+ g, {. u  x' T; F
  4. *    功能说明: 整个芯片擦除
    8 s) u% e2 _& v4 v: e: u
  5. *    形    参: 无
    - G. u- ]3 @8 z2 r9 i
  6. *    返 回 值: 1 表示成功,0表示失败
    8 Y; o( V. C# z) ]5 H% A' `% @/ C
  7. *********************************************************************************************************4 o: ?. v. c6 V6 d" y0 X" J( z
  8. */4 I" z& H3 \; A7 U! F  `
  9. int MassErase(void)- W. M) ]% _' p9 v0 E1 O! j( L! |
  10. {' k; S' `# _, q/ S: E" J5 w2 x
  11.     sf_EraseChip();) I+ \, n  R2 B! f1 D) K! y
  12. $ J3 ~6 |0 ]- [! r9 r
  13.     return 1;   
    $ B7 h* e5 j6 l3 w8 Z8 X, i
  14. }
    2 m% l8 {' F) M# Z8 W' Q
  15.   扇区擦除函数SectorErase1 ]9 H! B5 K; T6 o4 t; O
  16. /** i( x- M, `7 U0 [
  17. *********************************************************************************************************$ M7 b' D& S" A/ H0 r4 B" d
  18. *    函 数 名: SectorErase
    5 x# L" `0 \5 _6 K7 @3 m
  19. *    功能说明: EraseStartAddress 擦除起始地址0 D: Q5 M/ i* D- [8 M5 j1 s3 g
  20. *             EraseEndAddress   擦除结束地址# I. |$ U, H4 d$ U1 _
  21. *    形    参: adr 擦除地址7 s2 v3 D* m) r& u+ m9 z9 M0 b
  22. *    返 回 值: 1 表示成功,0表示失败
    - K8 K0 O! s6 X8 E4 o2 d
  23. *********************************************************************************************************# c1 ~8 V8 F9 v/ v
  24. */
    : l# H4 x+ P8 \& S0 a6 T* F1 }
  25. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    : g0 L! u$ h5 |( j- }
  26. {
    , E8 o" X" n- |* h& K: n! {
  27.     uint32_t BlockAddr;$ ?+ T+ |& N+ L6 f1 Q2 i7 h- o
  28. ) _' h8 [7 g" \$ u* i0 X' y
  29.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;+ m* ?% I7 i( [8 M' Z4 t; H
  30.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;; l) P2 }6 f/ _) S
  31.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */6 T( _) A  T6 {% o) X" X
  32. ; ^( b6 S. x" u1 A7 }' B' i
  33.     while (EraseEndAddress >= EraseStartAddress)
    & O& e9 M. U+ c2 b! o% b5 t$ y6 N
  34.     {; [2 M9 x' _# }4 {
  35.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;+ B/ h$ t2 S: X) i6 g: H

  36. 6 I" e# x5 m% c6 G
  37.         sf_EraseSector(BlockAddr);   
    " @# C) l; Y" H2 Q2 H5 P6 Y: l
  38. 0 d( L$ ?; r, V; Q7 z5 Y7 ?8 T5 s5 P
  39.         EraseStartAddress += 0x1000;
    9 [/ ]4 X- F+ j9 k
  40.     }
    9 d7 F; }- R  _4 q3 ?( V

  41. . S5 s- `8 O2 V5 t  ^. U' ?
  42.     return 1;
    ; j" {6 ]+ ~) J( L0 d0 f
  43. }
复制代码
/ v3 z: e+ s) ?3 y: r! v
这里要注意两点:# [+ [+ n1 b6 U
  n) _) {, V" y0 _1 A
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。) @2 N- |6 d  F% f( d
5 \3 ]  E& L0 l5 S0 Z
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。
; ?' }4 D. \: g- Z' }9 t$ Q! o" [/ I7 d9 `( J. _
  页编程函数Write
% [) J2 n! l  r1 u- Q页编程函数实现如下:
6 z* N, y5 Q! e$ ^
% e7 R  D/ F: D' O* k) G6 z
  1. /*
    : {# u6 ^& s4 k( Z6 s8 d
  2. *********************************************************************************************************
    # \0 u8 D: \* x: |( q
  3. *    函 数 名: Write
    ! ]' P) q/ U% g7 T1 Q& C, q
  4. *    功能说明: 写数据到Device
    ) S: h% d+ `! _% u. n: ^
  5. *    形    参: Address 写入地址
    & D. A1 U' ~/ t- u; P. M
  6. *             Size   写入大小,单位字节
    / @. U4 W; }% r! e0 Z5 x. t
  7. *             buffer 要写入的数据地址
    & S( X4 e6 f0 y( x# u
  8. *    返 回 值: 1 表示成功,0表示失败
    " s5 H$ ~+ g. @' E! O/ `5 b& \
  9. *********************************************************************************************************, D1 H6 a  K8 t9 z. G
  10. */  X7 \/ e& g! \5 e" o" x
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
      A, L5 x" L! n( s0 \6 G" [
  12. {  9 L8 U0 E7 O; g% W, }8 O
  13.     Address -= SPI_FLASH_MEM_ADDR;
    & }' R6 q  C7 q1 @5 w: m' ~
  14. , d, T( I9 U  Y* r+ N! Q: B8 m
  15.     sf_WriteBuffer(buffer, Address, Size);* F5 r) c8 b2 \/ Z4 T/ x* e

  16. * L( q4 h( `3 B+ ^& I
  17.     return 1;
    & O. o6 D3 C* N# b# a1 N
  18. }
复制代码
/ J! t8 t) t% g) w' d" u8 b
这里注意两点:
6 h# G8 C% E+ m  d
0 {' O; G$ d8 l4 ?(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。! L9 v% m! k) \

$ z/ u4 D0 ^5 D! X(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
: {2 q: h; }( ^7 G) x) G2 _4 ^5 A$ \* @, C/ H
  读取函数
" k0 H) [! [0 l3 e
  1. /*
    0 d- y+ b: J8 G, V0 r+ T
  2. *********************************************************************************************************
    " x  N- t6 W4 W1 o/ u' v
  3. *    函 数 名: Read
    2 i9 A0 H: e# S, ^* `+ v* @
  4. *    功能说明: 从SPI Flash读取数据. R* j& ~  S! \
  5. *    形    参: Address 读取地址
    7 c9 |, X! m2 w) U7 S
  6. *             Size   读取大小,单位字节
    . j/ e; T9 S# k' M; P
  7. *             buffer 读取存放的数据缓冲
    / h: l& u5 B* q0 S0 F4 N8 H3 ]
  8. *    返 回 值: 1 表示成功,0表示失败
    ) [8 v5 k! z  r- t5 S7 a% p) e- _
  9. *********************************************************************************************************
    / C8 G' ]: u: A+ t
  10. */
    3 u# C2 E4 a7 g, R( p8 b( V
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    - {' a) ^& s8 l* a0 z! e
  12. {
      F/ ]6 F* y! r+ |# Y! m
  13.     Address -= SPI_FLASH_MEM_ADDR;
    ) ]& j1 U3 |! q3 ]5 F7 r
  14.     sf_ReadBuffer(Buffer, Address, Size);
    % l: [( `0 w% q
  15. & f/ g. y6 B" d# [# {+ Y( n  O9 M& A1 I
  16.     return 1;) _. j+ K. O4 N
  17. }
复制代码

, r1 P# v" O) J2 I- G% K  x3 D  读取和校验函数# m5 h4 G7 ]* u; q( I9 B
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
+ C3 }6 _6 X+ P; [) j/ k/ |2 }! b& j2 T3 i& D
85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
3 G& N4 o$ V$ Z9 _最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:5 H0 k/ Z8 X4 L" s. N

$ T& _9 x" r$ w
  1. /*
    3 U1 I" V/ }& X
  2. *********************************************************************************************************
    9 ~. x) C8 I* _7 T6 v# n" x, P
  3. *                                时钟,引脚,DMA,中断等宏定义& y! r; d5 y9 c: Q
  4. *********************************************************************************************************. Y$ o3 A* q. s1 A1 S9 c/ v. g/ \
  5. */
    % x; o; e* W' H' [1 p
  6. #define SPIx                            SPI1. X: }# K6 o1 v- X1 `
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    1 m  V" |+ f# l9 a4 p
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE(): e2 F6 d9 W& o6 _0 M7 P! Z

  9. 2 \" i3 s' z* G2 M) p) l( U- [5 _
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()6 P1 `, F# Y  j# g- A
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    : p8 e# h8 \# I  q! S* c1 N, l
  12. ! [. B( i  ^+ r0 E
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()* b+ P. `9 @3 Q0 v0 n# z  r
  14. #define SPIx_SCK_GPIO                    GPIOB7 i' m7 V% z: E! K6 R: ~0 Q( T6 \
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    5 B" J; V4 f* A3 X+ {' H1 _
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1/ ~# ?$ u+ r8 j: \8 E& ^" Q
  17. ! O- `: V) b0 u5 g) X
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()' R6 a$ c. n1 _0 c1 G4 G) F$ \% h
  19. #define SPIx_MISO_GPIO                    GPIOB+ [# S5 d) [4 n. z
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4
    % O& X1 x* p5 J4 }  ?
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    ( A8 X* L3 b3 \5 E

  22. " J8 J* @- B  o- x! V+ @
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    - U( w3 J" L0 k
  24. #define SPIx_MOSI_GPIO                    GPIOB
    : \. y$ v1 R. \# p$ ?  p6 P
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5! x. [, T  j8 u/ k& U! L
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
    2 G, Z4 t: ]) a# O
  27. 硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
复制代码
2 `7 `; v! N9 o
" K3 z( C8 ^) `, {3 o
主要是下面这几个:
, a% h& t( y6 B9 ~
7 o7 j$ O# ]( ^5 E9 U! U
  1. /* 串行Flash的片选GPIO端口, PD13  */
    5 h; k6 w7 q6 G* c' w* N
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()8 Q, w% \8 l+ Y
  3. #define SF_CS_GPIO                    GPIOD
    ) ]" z, h6 t) C
  4. #define SF_CS_PIN                    GPIO_PIN_13
    4 ^/ ^, |- z; D+ `
  5. 2 F  u7 a# l! Y9 O
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) 8 U6 }" P3 s* W" x4 S! o0 |
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN! p/ F2 ^. L6 h$ r( V4 C; w8 H
  8. 9 E6 v* e. a, R# e/ A; |
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */
    ( x9 z' I8 X/ K/ x. B5 I5 e6 _
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
    ; D* ^7 W* _0 g: F# ~6 m( O3 f
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */5 {5 m5 @+ V  y% y% ^1 e& @
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */# k" ~. i2 [# G( z9 p* ?. q. t
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    : K5 F1 r2 x$ H
  14. #define CMD_READ      0x03      /* 读数据区命令 */
    # ^* o- u: h& ^+ [. O9 ]" e
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */: q* {  O3 j# c  B2 Y% [6 Z
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */
    8 Z  A/ w. r" T9 B$ C& T
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */4 |. M0 @. X, @2 F" a
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    6 P: N# z: A% |2 x  ~# R0 O" a. O
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */4 j- p) ?- I6 s! h& ^0 D

  20. 0 B$ J7 O+ M1 J' x
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

$ k1 f2 m# l" `& Q$ j( c  ^85.6 QSPI Flash的STM32CubeProg下载算法使用方法% \: s7 ?0 |0 Q) t$ p* Z/ v
编译本章教程配套的例子,生成的算法文件位于此路径下:' r1 p2 k/ I: ]2 b7 {
4 ^+ [' Y2 F( ~- U8 J8 O2 _. l. B" `
5ec6d402a7646232a55f4834cd00ac0c.png
1 y  A; l% c! E5 O7 J9 r

0 S* A8 j& ?0 J85.6.1 下载算法存放位置( P- E7 B9 S1 y$ Z" y% v9 [/ \
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:9 f7 b7 o' H0 n  c; r% L

6 x1 \* I  V8 G\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader; Q; s) c0 [9 E0 w3 o% `3 ^7 Q) `# o

# m& }  j0 S; x  a- x* H$ t8 A
2609073778af5845a869911ab36624f4.png
0 s, L( |1 A! r2 q
* g1 i; i% P$ K, ~6 S
85.6.2 STM32CubeProg下载配置$ w1 R) {( P" e! h: G7 h
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。. k/ Q) [& o1 I2 n5 e$ F3 t- B" O" t; A
# q8 u+ @: G6 t4 J
27a88335ceb606bd021c3ebc65d88e21.png
6 [! V8 ^) S3 U1 H% P

  A9 A! W/ j) L1 t点击Connect后效果如下:, u8 B( ^/ w1 z$ N+ K
5 S, Y) K( z* Z0 Y
d4e256633eafdbf4bcd8cce6e3b5909f.png
7 }! `4 V7 e. l" j

2 W2 h  Z" y7 G& z5 D4 k在这里选择我们制作的下载算法:1 r& ^* s: F. J

& \" @) `$ c/ S5 \0 r; |& F8 m* O
d4fa28f64be2de98e04f3188eb2fe09e.png

4 U* R- K) o- e, b* d1 V9 s, Y4 t; x2 J' E
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming7 D' n, N- N# H/ `7 v
7 l+ t2 ^4 Z; P( c
842518a7cc1dc569598d372132b2ee58.png

$ r8 X) j0 Z3 ~! @3 H( }) q8 f
5 ^  L3 u1 |( F. V, s# K  a下载完成后的效果如下:. p; M9 U0 F' u  J

6 b/ v  m# R5 x9 J( x7 K# ^9 ^0 H
68a92f267829ca77f125db982d6c0479.png
5 f0 K. `) @. [# X5 N

9 L% O9 V) n9 g1 C85.6.3 验证算法文件是否可以正常使用  {# _4 j( @1 s, x
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
: v$ a+ Z* C- b5 f7 i# [2 J5 }, G$ `; \% _: L" Q6 M/ F
566d2fba421c710da70e5656c6934466.png

4 B" l3 Z9 a, v
" F. {: s- A  M85.7 实验例程说明/ R+ A. S6 I% v& Y
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。
0 W/ t9 P! V$ e0 S. ^5 ?+ C, j  L2 ]7 l$ e
编译本章教程配套的例子,生成的算法文件位于此路径下:
; S0 I/ m- z5 t( S5 C" o# r
* |( _& f! J1 h6 E: F
6108d1ff7e349f2aa268ee31804c57ca.png

: j7 y6 W5 D# y0 l. _% t7 C& p
/ @3 x( ^, ~' P7 R" Z85.8 总结
% K. l& ]& R) q7 u" l& i9 N本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。5 a4 t6 u# q* v# k8 x3 ?1 Q; w7 S; M

9 j+ R( J* F' V) Y2 W1 x0 `
; \/ W( ~7 A* N4 O( \/ u! E/ Q
收藏 评论0 发布时间:2021-11-6 23:35

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版