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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示
3 l4 W! G' n+ B( s8 E, ^2 I' h  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。8 t. Z: s2 A! k9 x; A! z: `5 s
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
6 f1 c6 U3 E9 [- g* t  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。* C/ [2 b, c+ `) }3 }
85.2 STM32CubeProg简介
' _1 d# {; K( x+ ASTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。! r- [- d! f1 A( G/ q5 q* G+ P

& t) k7 _7 |; i; x8 o
d80578d217eb2a83520c6f3d797218f4.png
! N3 i" r+ P0 k! S8 v& Z6 {* k) z

" n% r( g2 q: H9 x% F! s85.3 STM32CubeProg下载算法基础知识
( I* q8 |. L- |! C$ @% L
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。! x3 D; T$ u; ^$ u
0 _+ z) s* F( A9 H3 T
85.3.1 程序能够通过下载算法下载到芯片的核心思想
& j9 A& O1 W+ X认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。" `! r; ?; [4 q! e

$ v! g' E+ e5 ]3 A. W2 P85.3.2 算法程序中擦除操作执行流程0 f" S# m9 O% m, b  z* k
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。3 O% t7 \. ]! {$ H9 Z7 Y' T( m# b

! c6 \- l( F/ n6 {, r' L% `! \; Z擦除操作大致流程:- L, A/ B$ J/ Z7 S
; }' V$ `- \+ K# z' b. t( G
d75670256363bb50203bed0bbe4f9fd2.png

" F. \6 `  o$ w# N6 X$ V
: V8 ^: H/ m2 g$ ^  加载算法到芯片RAM。6 C& v$ x' T0 S) W: V) e: R
  执行初始化函数Init。8 r4 i" X) o5 g  c; ]1 t
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
% p. D( r" v. w$ V- B/ E" D  执行Uinit函数。* g* c( S/ ]9 }5 v. W
  操作完毕。- O" }+ c  W$ P. p. O
8 _8 r& V; B# z1 f
85.3.3 算法程序中编程操作执行流程2 o1 Q2 i; h6 G( ~  |( C
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。9 t6 O) |6 t+ P! _
; x9 |1 c6 [/ s" \) y
编程操作大致流程:* j% U" V8 V: @

* G6 E7 W( }4 j" E, S4 b
4fa96e9e33d700081f2cdbd312afb31e.png

& T3 F$ y# M; J& L; Y" ?) d( S. j7 I; P2 B( O5 [
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。, R9 J7 D( t' u: c/ l5 E4 t
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:. y5 T0 l$ ]7 G% i5 j- v
  加载算法到RAM。
* b6 E; l4 h! b  执行Init函数。
( C2 ?& L: ~# k, ]  加载用户到RAM缓冲。* Q. g' Y) ^" I. B8 h' Z
  执行Program Page页编程函数。
4 S( K. i* ]2 T8 `  执行Uninit函数。
/ l6 B4 }2 T- w# }# M: t  操作完毕。( f+ x1 W! c: [/ }) A8 |

% d9 m( u% J( J; @. q85.3.4 算法程序中校验操作执行流程
" T+ l) E7 C) m# V注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。' E) ]" U! \; G2 F, {4 \0 l
3 V6 `% V4 {" k# d
校验操作大致流程:
( h' M6 G9 e, i3 w8 ]6 S% d6 d+ M& g) m7 A& x9 [/ |
7deeae4b650bd82c457492af14fc26b7.png

5 J0 o+ v' h, i1 m* x  F" E/ O% i6 O" M, x5 V
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。+ M* X5 H+ H, `! @
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:! a5 C+ x. d7 {8 p1 f$ P  I! B
加载算法到RAM。  \  v: _" ]  f9 }) K% g
执行Init函数。
, r1 y* Z$ x0 V( M$ \; g 查看校验算法是否存在# N3 s) M% X: L$ K# {4 x( m6 k
  如果有,加载应用程序到RAM并执行校验。
, F8 A3 B! d9 w/ c  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
) e: U! c, t8 h% |& q) ]6 M  执行Uninit函数。
, }) L. o8 J3 ~- _/ H  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。# T* d' I7 j5 O- s$ b
  执行RecoverySupportStop,回复支持停止。5 w2 j; j; i% P9 M! [( D* C; X* J5 h
  执行DebugCoreStop,调试内核停止。
% E% k4 D7 j. z0 ^+ P* S! @  运行应用:- B  t9 t' ?- p" j: C
  执行失败
. h" w7 E7 Q  V) A  执行成功,再执行硬件复位。2 {7 h) s- G! Y. m8 F0 i
  操作完毕,停止调试端口。
  {* q( }. w' G8 l( w5 G- P4 N+ t  W1 s! _1 y1 ^" U
85.4 创建STM32CubeProg下载算法通用流程; \2 j! i, o% f& Z# y* g- A
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。* Z4 K/ p" U4 `) [

" X. H8 q- r6 Z/ R85.4.1 第1步,使用STM32CubeProg提供好的程序模板
. a+ v. I! i5 {$ W* [位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader5 C' m% F' o9 `; o
; S8 G5 [( B7 T2 J9 Z, |
97f385e3b62a055bac9b5c0214b877e5.png

# P" K. `8 ~# T) y4 c: q# T& i3 M8 d
) B" [& E' N' m( {; Y以M25P64为例(注,其余步骤就以这个为例子进行说明):
* U* F) }( d) @5 U9 C
/ ]; _" ~5 y1 u0 v9 i
43b298f996937fe35e89697fc6479cb9.png
3 L/ u" E8 `" U  O5 K& p4 L

! q& X1 Y1 f! X& h* M& {85.4.2 第2步,修改工程名
% h5 N8 j- U; X1 nSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
7 G: }2 d) i: E  r( ^6 Z/ a( F
! H9 T0 ]' F5 d; }$ k3 Q  n3 I; s85.4.3 第3步,修改使用的器件
. l  o3 ^' H7 }" h, l7 R
在MDK的Option选项里面设置使用的器件。
3 j9 G( y/ x& J" y: c* k* e
+ I( W& {- Q% M  p. X
36026b9a6d60249610d0246eb725b246.png

( m( y$ J: b" p% ]# u$ y( X
( K" O9 @* s* m& m$ Q9 W85.4.4 第4步,修改输出算法文件的名字
3 J2 d: J4 S& u9 d! J& h这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。4 n. \7 n: _! J2 h& v% D
4 V9 c2 t/ Z; E
52c72b3993f28a366a22eac56f8fd043.png

4 ~5 n  e2 o$ Z" ~% }8 `
6 m; I- r$ i0 W5 y注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
, n' k0 `, m9 h% x0 t. X2 n+ m& |# H3 Y& r; p( J9 c
7b48153027a44de33926c2da008716a0.png

; T; V/ Y9 H, m% @+ m! c
' z* Z! X/ ^9 N5 @9 ~, X* u85.4.5 第5步,修改使用的库文件和头文件路径  O$ }/ N4 B# `1 A  x/ j4 ^0 T
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
  H4 s% O. h5 {( I: N3 h9 s' k( ~# l
85.4.6 第5步,修改编程算法文件Loader_Src.c
9 k, Q: ^" C7 b* g模板工程里面提供的是M25P64,部分代码如下:( G# r$ B/ O/ J9 t* m. |& _

" Y$ O" r" A' `' z% S8 V$ C
  1. /**6 D5 ]3 z( {9 n# v
  2.   * Description :
    + G7 T* [) ^6 j
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    . }' N; Z) q7 i1 v7 x
  4.   * device and initilize the FSMC with the chosen configuration ; C4 o5 x1 U8 N( i5 {# L' s% _
  5.   * Inputs    :0 k  n& j% l9 ~  m% K; M# N
  6.   *      None
    2 h3 |! Z# W7 N1 |- {4 X
  7.   * outputs   :
    3 j, b$ g6 N+ ]' p
  8.   *      R0             : "1"             : Operation succeeded4 m0 y" k2 z! }9 ]
  9.   *               "0"             : Operation failure! \4 p# X: Y+ d: f
  10.   * Note: Mandatory for all types of device ( w( R& F9 F1 o. K
  11.   */) ?) q3 m% @% z% B- W& a% i, U
  12. int Init (void)
    ! n/ [% U& t: R
  13. {  ' n5 `) T- f  j9 t  \( }
  14.   SystemInit();
    ( {& K8 y9 I0 p9 @5 a' A4 Q
  15.   sFLASH_Init();! L2 j* E2 ^$ r: M) X* f2 L
  16.   return 1;
    $ \: `0 a; O  Z; y
  17. }, _# X. z. p5 y6 P2 x1 ?

  18. ) _2 {7 x9 X' |! ^2 p# L

  19. 9 r4 B( c. \$ s
  20. /**
    ) k& g1 ~- ?* I! B/ A& u2 \; X
  21.   * Description :
    3 ]% f# |* I' u
  22.   * Read data from the device 3 V8 R/ N$ s5 i* Z0 C0 q
  23.   * Inputs    :7 a) Q8 I8 c4 t8 ?
  24.   *      Address       : Write location
    3 E1 ~; v, b/ G0 l4 t/ U
  25.   *      Size          : Length in bytes  - T- b2 H  u7 K' P9 {4 l
  26.   *      buffer        : Address where to get the data to write
    * D2 L( O) N- C- a6 G
  27.   * outputs   :( D. y$ R( L/ G$ q! I, D( E& M
  28.   *      R0             : "1"             : Operation succeeded' y1 V, f7 U6 F/ x1 g- A
  29.   *               "0"             : Operation failure
    $ q) y) B1 |$ O0 y0 F" @' z8 u+ r- [
  30.   * Note: Mandatory for all types except SRAM and PSRAM    $ _- |7 o  Q' Z8 |! d' [
  31.   */$ a/ P/ W% V2 ?. y
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    , P, \/ v) ?" J+ s+ D% \
  33. { 2 Q2 W% k  M+ u
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    6 J! T* k$ X" M2 [% f+ K7 y
  35.   return 1;6 B0 x, r) M* }6 W3 d5 Y, ?/ K
  36. } 5 R: ]) Z+ S, I- \
  37. 6 |8 X/ e# R7 D/ n

  38. ( h8 ~  y' w& t' K' F& s5 U
  39. /**+ N. C" r$ @0 G+ A" ?9 k
  40.   * Description :- J6 x% E; y4 L  a' P* t0 G
  41.   * Write data from the device 9 k2 ?' t# h4 S6 P
  42.   * Inputs    :
    & Q1 Y/ Z7 Z1 y8 T8 M
  43.   *      Address       : Write location
    ; A2 Q5 a3 d* s: D8 A& d
  44.   *      Size          : Length in bytes  
    ! ~: P" v% X) G9 O6 \) Z, b
  45.   *      buffer        : Address where to get the data to write1 T9 ~9 a! X0 B% l! N1 n
  46.   * outputs   :
    " S( G' C$ q# K% L
  47.   *      R0           : "1"             : Operation succeeded& k  |% _" g( }# d1 G
  48.   *                     "0"             : Operation failure0 V* E3 U! T% N% i/ }# W( O
  49.   * Note: Mandatory for all types except SRAM and PSRAM    & u! d5 L8 P  K
  50.   */
    ( r: E/ a+ M4 _1 U' x' Y
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)7 j5 Y$ t4 n/ j, o8 T! U+ K% _! c
  52. {
    5 Z" |1 \* P  m9 ^: E
  53.   sFLASH_WriteBuffer(buffer, Address, Size);( a5 J" r0 C, X0 S1 Q5 o
  54.   return 1;0 [5 c" C! n6 v1 v* f1 l5 B4 Y
  55. } % i( u# `3 [9 v7 a& N

  56. * I( U+ I6 J* I8 P; A1 ~# u& D) v
  57. 4 @. ~/ g/ Q* o1 i
  58. /**3 b. H! x! Y8 m/ `4 L
  59.   * Description :- q6 C4 v1 E2 U# _; O2 q
  60.   * Erase a full sector in the device6 p4 y3 Y" m" O$ f/ v
  61.   * Inputs    :9 O7 L1 j* A! W8 L9 W3 I% `" ?
  62.   *     None* C' G+ V1 e8 S: P  t1 }
  63.   * outputs   :
    - R* N2 e- D/ {# w
  64.   *     R0             : "1" : Operation succeeded
    * \8 D! a6 C5 B' |
  65.   *              "0" : Operation failure
    " x% e, M" A1 x+ i7 u+ f9 n
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH: z9 j8 n6 N& j# U4 U6 }: G/ A2 r
  67.   */
    7 Z; @( F7 c; m: C
  68. int MassErase (void)8 G+ _5 @% y7 `- }. F4 i  V) @
  69. {  6 L7 C; E6 K& ^0 v
  70.   sFLASH_EraseBulk();
    ' F5 r  E! g' l
  71.   return 1;   
    / e* Z6 Z4 K' J8 F
  72. }
复制代码
# m4 a0 ?1 e% V! r0 d+ ^, ]
85.4.7 第6步,修改配置文件Dev_Inf.c/ B6 [/ R# ~0 H0 o8 t. g: C
模板工程里面提供简单的配置说明:
; p$ T4 Y; S( i: z8 M9 `
1 @7 R1 \% G6 n3 Y6 O
  1. #if defined (__ICCARM__)$ I% l  ?2 ?3 {6 h
  2. __root struct StorageInfo const StorageInfo  =  {# S: |7 Z" x6 ]4 H( O( D8 }! o
  3. #else
    ' M) B$ r" Z; e2 z
  4. struct StorageInfo const StorageInfo  =  {" o. M/ r) y; w' S; g) h+ }
  5. #endif# [* p6 {9 A' ^& C' s" G4 Q
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    ; ?, E4 M9 S1 V. A: i  K. P
  7.    SPI_FLASH,                       // Device Type
    7 U# ^8 O7 B1 j: J
  8.    0x00000000,                     // Device Start Address
    ' D- p: Z$ T+ f. ~) s8 Z0 ~
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    # e' e6 h9 z/ V1 [2 J; y
  10.    0x00000100,                      // Programming Page Size 16Bytes9 i) f* C- ~+ h& i3 B4 }* T7 z
  11.    0xFF,                            // Initial Content of Erased Memory
    5 h" G+ M& m8 {3 k3 W. P
  12. // Specify Size and Address of Sectors (view example below)
      p7 W# V- [; x0 }6 |  o: b
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    8 N& [4 z$ s4 o; U0 j0 A7 b
  14.    0x00000000, 0x00000000,
    / J$ @" @2 ^$ c1 T, {
  15. };
复制代码

. W6 [( A% H  Z6 z2 D/ q注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
6 ?" C& w5 R; j$ V7 V* U% G6 B8 ]3 ~3 j! z6 E( R
85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。

: t* a: H# J$ y! J! E1 ~C和汇编的配置都勾选上:4 e$ c  T. D: K7 Q3 f

0 t( P0 K) l) Q4 n# S
afddd6e839292aa2b94b43e79544d630.png

2 p6 z1 t* K6 c9 c% }" r2 p
- m0 M8 V, M5 z' `2 _3 z6 s  i% d) R汇编:
9 B+ q1 E+ b$ n) ^" G+ |5 ^$ p9 X  H6 y; v  n( a/ }
f639ae5b0d3c2e0e0190ec509fbc8a4d.png

0 Y  K# l  p( [% N( s' K" R- {% _! E% V% E% K
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
! E6 {# Z0 [+ o
1 i+ d1 A$ G1 r) ]7 c2 S(1)加载以响应运行事件。
# z& Z/ E( z( G3 z+ I8 ?
- N( E- k$ \; O! G% T! \(2)在不同情况下使用其他例程的不同组合加载到内存中。/ ?* N' x/ r9 _( Q# P9 G. y! e' Y

& T) Q5 [2 u9 @) Q6 E(3)在执行期间映射到不同的地址。
5 X) w. M4 O+ A! G# |9 f' P5 H5 \2 g) F6 |
使用Read-Write position independence同理,表示的可读可写数据段。
2 V$ W$ p" f4 x8 H
. J  J2 N) l+ t$ ~& P; U: g) F85.4.9 第8步,将程序可执行文件axf修改为stldr格式
# h# Q; p9 g- u4 F) w4 U通过下面的命令就可以将生成的axf可执行文件修改为stldr。: K8 _+ s8 m& R+ K9 S2 l
/ Q  v1 b* B* h: ]* y- K$ d
c683673d2481d8dce75b6fa01d624600.png

6 V* [' r( o' p6 T: @" q: t+ @" `0 E" \1 W
85.4.10   第9步,分散加载设置: j* `% C  M! u
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。& e7 f" r8 F& {, b. T& J0 b
+ y. [$ I1 D  V. Z7 t' F; h
b96835a7506516319002e6b2e8db9371.png
5 Y1 q0 T/ r6 @5 T8 ^5 q7 @

) S" O0 ]/ P: P; y% B- t- ~; D; A分散加载文件中的内容如下:5 s! I6 o! \/ X, }6 G: H

+ {) I! z3 f$ Z2 [2 \. `
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    $ K* p# ^" x4 h& H$ I- w
  2. {
    / r8 H3 ^7 ?+ X4 Z
  3.   PrgCode +0           ; Code
    * S, `7 ^8 q1 `; Z% A4 p
  4.   {
    ! k4 ]' J" e6 V3 J6 Y/ w/ R
  5.     * (+RO)
    0 t. Y9 _7 @" r, _0 u2 T4 z- L
  6.   }
      h) u$ ~; C. h% L$ ^
  7.   PrgData +0           ; Data
    $ _  k! f. z) K4 O
  8.   {
    ) T+ n. ]: i  c( Q( ]
  9.     * (+RW,+ZI)
    ( {+ k! S( X/ R" a" R1 n2 {4 Y
  10.   }4 C4 J9 s) t6 E* p& ]
  11. }
    + g- B# d( d+ \( A( W: a

  12. 9 K9 O& F/ V# u, a6 Q2 w5 ]% S
  13. DEVICE_INFO +0               ; Device Info7 P3 a+ ?# X% U
  14. {
    ; K  x5 `8 h* K; I
  15.   DevInfo +0           ; Info structure/ T/ q: v: @7 o( G. \
  16.   {1 n' i) ?9 {: t7 ~& X
  17.     dev_inf.o
    2 `! I! d. U. ~+ u
  18.   }
    . k, x% b7 f9 \+ C' ?, {
  19. }
复制代码

# u' u) P/ V$ s4 h2 q/ ~这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
+ d7 A; v0 A" A, v/ D% t; \- B3 s0 l
--diag_suppress L6305用于屏蔽L6503类型警告信息。& D# y5 Z* k$ [

# D, H+ k+ b; M* t6 O  G- F特别注意,设置了分散加载后,此处的配置就不再起作用了:
3 K5 H! c+ k0 ~. P& s" u( O; I. M8 _" Y( k% f
641395c374a3af02f1a92d3ff9ee4199.png

$ M: X$ B* v/ E  U: o
7 |& i; Y6 e% e( T5 P% Y4 R85.5 SPI Flash的STM32CubeProg下载算法制作
: d2 \! J4 `7 _1 C' _! n4 Y' n下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。0 x1 v; w' I9 O

: k( U7 s8 g+ z! A/ X2 ^; \1 F3 T85.5.1 第1步,制作前重要提示

" o5 M, o5 a$ P/ d这两点非常重要:# ~3 C3 X" M3 B) o! H4 h
# t& H- [* [% i* e* M' `1 f6 c
  程序里面不要开启任何中断,全部查询方式。
/ v' m6 m) b' p; j% @  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
  c$ ^8 b# C/ ]% `* V3 g* E3 g1 o# f7 y9 d4 h2 `0 }) M% b7 J8 S4 {9 V, }- ]
85.5.2 第2步,准备一个工程模板
& a3 s, Q! O$ H 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
: ^' \0 K3 O" c+ _2 P  F
) I* _9 b3 u0 x' G. _( I* L( I/ S
80ad5e3abdd6a993c8621230401a1313.png

9 i& A% B2 @. d: \" l  Z( `
3 O/ F; n/ b: ?0 u4 h85.5.3 第3步,修改HAL库, s, @) B4 c8 w0 X
这一步比较重要,主要修改了以下三个文件:
- y5 _' t0 L$ m& H" l2 h9 ?' i- P/ K$ k6 Y2 \% f7 h
33bf89f25487547d4008f43b7cf10e03.png

) X/ _6 q2 N. d
7 c2 E7 F4 Y9 S+ f7 g9 t主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
+ h. ^' [8 H  y3 l5 E2 ?& O) m( b% ?! A8 v
85.5.4 第4步,时钟初始化
$ s' u) N' l  M  C我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:9 n; r9 \* W" j6 B) u! b3 p
3 ?( @' [. q, y
  1. /*/ x/ ?: s! s9 v* G3 }% Y! M; ?4 ]
  2. *********************************************************************************************************8 X. B" h) ~) I0 v7 ^1 v3 M7 I" \# Y
  3. *    函 数 名: HAL_InitTick
    9 o! c0 X- H- F3 p
  4. *    功能说明: 重定向,不使用
    3 X( k; O( Q) R9 f( l! k
  5. *    形    参: TickPriority
    / @9 m% I' N# v$ ?
  6. *    返 回 值: 无1 c& R  W6 F9 ~5 F$ a
  7. *********************************************************************************************************
    % Z) V8 l. y9 d' V- ], I$ N% }- w
  8. */
    ! ?9 N3 q7 @1 J) |: I
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    3 m: I* Z& r& o
  10. {( h7 T* W5 {' m! g1 [% ~8 S
  11.     return HAL_OK;
    3 s( F( e1 F' W0 U0 B
  12. }
复制代码
: c/ X  A8 i: A, h- z0 ^2 ^+ R8 O
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:/ I$ t0 y& t8 f: }5 @

8 K& R. z  s/ v" {! n/ C3 v
  1. #if !defined  (HSE_VALUE)
    5 y: A; i$ G. }4 }6 p' c3 n# A
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */7 c1 e& T4 Z- V. V
  3. #endif /* HSE_VALUE */
复制代码

# _7 s% v1 `; c$ b0 D0 {最后修改PLL:
% B. E+ k! d/ o+ ?2 o" ?6 _4 H6 i& V0 e$ U
  1. /*
    8 y9 m: v& u" f1 ?9 [
  2. *********************************************************************************************************5 }5 |: m9 X% K
  3. *    函 数 名: SystemClock_Config
    + h- H! z$ x& h5 M' s  k+ C5 V* M9 N; k
  4. *    功能说明: 初始化系统时钟
    + q) w) S4 u1 x/ k
  5. *                System Clock source            = PLL (HSE)
    . j/ S" M0 j/ g/ t
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    , ~! l5 k0 X- i, Y3 @2 w% p
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)! b/ ]0 O. c, t& ?0 ?0 n
  8. *                AHB Prescaler                  = 2
    % M; v3 c+ w1 Q3 r( e; P2 Y" x) l) J4 ?
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    8 E+ G6 R  B2 S# L+ b
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    1 x# n2 h$ m! X! B) w
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    ) l; |8 n9 v+ ]
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)0 N* I" ?5 S) v) t$ c
  13. *                HSE Frequency(Hz)              = 25000000
    9 w" s; a- }. ~+ E. J' n
  14. *               PLL_M                          = 5, C+ _' [/ K- v$ v
  15. *                PLL_N                          = 160
    ; L+ q& z/ H3 a' @) V8 X9 t
  16. *                PLL_P                          = 2/ z6 h$ ]( n+ w5 b, a
  17. *                PLL_Q                          = 4
    & \  O/ X  |1 V8 m. p
  18. *                PLL_R                          = 2
    " p5 S: V: i5 _1 R# V( N2 S
  19. *                VDD(V)                         = 3.3
    9 x2 D" L: N+ b7 d3 d  I
  20. *                Flash Latency(WS)              = 4
    + g5 x  n: T# T
  21. *    形    参: 无7 b9 R+ B% w  ]  f4 E& d
  22. *    返 回 值: 1 表示失败,0 表示成功4 K" t+ F$ Z: E- d( {7 U
  23. *********************************************************************************************************2 ~$ E  @  r7 o" H% Y
  24. */
    % W4 r: ]( b3 J
  25. int SystemClock_Config(void)
    : r  a5 I: P$ \3 d6 E( E
  26. {
      C  r3 R) P3 S$ u. f
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    + Q6 R4 s5 W9 Z7 ]4 ^
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};) m* u& ~/ l9 ^
  29.     HAL_StatusTypeDef ret = HAL_OK;
    3 c/ H! w  m; s

  30. 7 Y& Q$ k. ?* ?: O5 R, C
  31.     /* 锁住SCU(Supply configuration update) */* l( o" S2 y" T3 B( X
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    + i% L2 y8 u( [' @7 O

  33. 4 h1 W, ~! p& g# {5 ~
  34.     /* / H- P4 C: l* y
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    8 m/ U0 W- G8 n& ~) i5 `
  36.          详情看参考手册的Table 12的表格。
    4 y! [! q  y$ h" r6 c( V5 w0 j
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。3 q8 u# G' Q  ~4 E3 X5 \
  38.     */
    8 v) o) D3 ]$ _1 B! b
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);( K' T& b  }- L  A  G
  40. - N* v# D( f" }4 [( {# S5 Q
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}4 K# b( k# w, l

  42. * U; i) I1 ^4 \) L* D
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    0 k# x; z6 K5 Z6 C: ~& W
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;5 C' }+ v: c# l+ a3 n/ M9 c+ `
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    . g# K9 T) ^$ _
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;) F# u$ H: S' a& g1 j
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;* ?" D5 b6 O! F4 r" ]" Q. X
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;( r/ P2 O5 `) Y$ \
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    + [% @& W- Q; _; I5 V5 \& G+ \! U! Y
  50. 0 C, Q  V) J1 a8 k" Q5 a
  51.     RCC_OscInitStruct.PLL.PLLM = 5;) D- G( r/ |* V
  52.     RCC_OscInitStruct.PLL.PLLN = 160;+ T6 n7 T0 q/ ?
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    1 `$ @% S# m) b4 p) P4 n
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    8 _% B$ \" n2 a. l2 K
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        % t' A+ u* Q5 o
  56. 0 m( e! `% P) S! \
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;! w0 V* w" T& `3 w. Q& E, \; F5 h
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    , Z' o8 K7 a) }3 \! @4 S# K: @
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);% ~4 f# C7 h, X+ U
  60.     if(ret != HAL_OK)- `* W# s, }' w% }9 Z1 b# L
  61.     {
    6 C# @: d6 v  r# D0 q) f) F. ~0 h
  62.         return 1;        
    / a0 L0 n9 z' c) P; [$ T. O+ c
  63.     }3 J# I( z! ^" K3 e3 Z+ U! S( l
  64. " U" X" {' J3 r9 p, D) @+ u
  65.     /* ' q# g& Q, v/ n# q" Q, `7 Z3 _
  66.        选择PLL的输出作为系统时钟
    : J% ]9 h3 Y  s$ ]7 U$ X7 Q
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    6 |- D* D7 \+ K5 V- y
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    9 [. J" P% p  a8 X
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线8 b/ Y' M% e, K3 }. s" V8 C
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线7 K) L' D- F) o6 |
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    9 |" ~" \$ Q4 R' ?1 a/ N
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     $ p' @9 M# [8 \* T) M# ^7 k" v  y
  73.     */
    2 W' j8 {3 g; L6 `; g# S% A
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \& P3 t; a" _) i+ p3 ]3 I' v' l
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);5 E" x- U# @/ ~+ i5 h& p7 D$ A

  76. ) ^- t2 h7 o6 _$ r: M8 v
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    8 L2 r1 @; @/ E  p/ J
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    ' V" X, m- v* N6 F! \
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    . b- F; S5 U: F' G; [0 [1 }$ Z
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  , i% w6 x) v( x4 f& [7 e
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    " w0 [  L# Q! ~% G
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; % R- k/ p( ^" _+ m7 t! ]
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    : u8 q/ V3 |+ \" D* [

  84. . _5 }* j( [0 j. I% o* @' N
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    ; M' b! ?0 k$ a6 S
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);; f, n1 U* `: B
  87.     if(ret != HAL_OK)
    ; [! z6 a" W% [2 F% Y
  88.     {
    , y- w$ H: T8 F# W8 v' j
  89.         return 1;
    8 h- l6 e5 I+ ^+ ]3 b0 i, l  X2 q! Z( @
  90.     }5 L8 ]: d& R/ Y# w
  91. " ?0 B. B' R. Q
  92.     /*& ~4 E3 ^  k& T6 {! P" m
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 3 }4 m. n- q7 `. I' f' d
  94.       (1)使能CSI clock, ?, j6 ^- P, e& f+ s" `
  95.       (2)使能SYSCFG clock* a2 t: M, n9 Z% t1 K6 E: p
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0; e" K; J: K& K; ^) `$ _" y2 k; w
  97.     */
    8 a1 _* r% M+ h! [) [( x
  98.     __HAL_RCC_CSI_ENABLE() ;
    % d2 s* [8 q7 E. M- o
  99. - q% p7 j) K: T) B8 q: M) b/ G
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    ) c8 Z% l9 X0 P! B  D! t8 D& O
  101. , N' _) r  e# r" j
  102.     HAL_EnableCompensationCell();
    3 I+ ^9 \; T+ I

  103.   I5 p3 j3 r; F1 C
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    ( e) d; ]; e9 d9 l2 [
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
    / c" h) g( [+ S/ p7 `
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();( `% w' V. T$ M6 X# S

  107.   b* }2 K, d: V  A6 D; P$ S
  108.     return 0;
    . n# ~2 A+ [+ d
  109. }
复制代码
. ^; o# }0 e" y! D, B0 G. w
85.5.5 第5步,配置文件Dev_Inf.c的实现/ q9 r9 i: A1 f; K
配置如下:
9 P- E5 C% ^& \$ Q4 `' A: a3 y" ~4 l0 H& r" T4 Q+ E  \% l1 }/ j& s
  1. #if defined (__ICCARM__)4 y& j  l/ x1 ^. S) A7 T/ w4 s% A
  2. __root struct StorageInfo const StorageInfo  =  {9 x* ]5 s# M. h3 b8 i/ @9 ^- M3 h  H
  3. #else
    ) h- Q! N* e& B8 V- D9 m
  4. struct StorageInfo const StorageInfo =  {
    / y: Q2 y2 e# H( y  n3 |* ]/ N
  5. #endif
    . _# j9 X" D5 P) s1 z* J: ~
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    ) a0 J9 S3 S6 F( J! d
  7.     NOR_FLASH,                      /* 设备类型 */
    & q. |/ l& ^: @# ~; w$ Z$ [9 |
  8.     0xC0000000,                     /* Flash起始地址 */
    ; T8 ]% I  r5 A# F; O( ^1 ?
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
    + |8 S7 ]: [+ H8 t
  10.     4096,                           /* 编程页大小 */
    , ~. P- S! b7 I# @# s* z" O& m; Q/ x
  11.     0xFF,                           /* 擦除后的数值 */
    $ X* k  ?$ m- s/ x+ T
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */; V3 R3 K. l$ M6 m
  13.     0x00000000, 0x00000000,
    " N, V' g/ L1 t" t* [% p3 y4 j% L
  14. };
复制代码
" m/ w2 I; }4 B: b% i; N" r8 l
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
! f  g) x2 Z# _. F' y9 J5 K& |/ z/ ]3 a
  f$ k( E1 }/ {+ ~
' h8 k! y7 L$ L) i
85.5.6 第6步,编程文件Loader_Src.c的实现
4 U7 x1 k2 s& U3 Q+ r. |下面将变成文件中实现的几个函数为大家做个说明:
) T; X8 V+ \! r( ?% u/ Z3 X( w" [- B3 l: I1 q6 g* [9 t+ ]8 O: I( b
  初始化函数Init
5 C/ `3 {: a; {; P) v
  1. /*- o" g+ X8 e) [: n
  2. *********************************************************************************************************8 ?. C; F- I4 b6 G9 S7 ^2 y
  3. *    函 数 名: Init
    " }- q3 J2 g$ G! }
  4. *    功能说明: Flash编程初始化, n. T7 l' C" R% f3 q% \
  5. *    形    参: 无% L2 z3 G6 [5 u: H" p
  6. *    返 回 值: 0 表示失败, 1表示成功
    % x3 ~$ k5 `6 Q$ Q! V* t
  7. ********************************************************************************************************** F" @+ o7 o: o9 S- t
  8. */
    0 q9 |* V" z7 V9 U
  9. int Init(void)  Z: R" K7 N% P& V3 r4 C& V3 v
  10. {   
    6 ]* t8 W' n+ j
  11.     int result = 0;8 K4 T! G( B& Z/ h/ k  p8 |

  12. ' h  c% T" _% D+ [; E& _
  13.     /* 系统初始化 */
    ! R& a3 W6 I& |/ s/ y
  14.     SystemInit(); 7 _9 b7 I: v9 s3 f4 f

  15. * h; L- i6 c5 v& N
  16.     /* 时钟初始化 */+ z( l+ x1 [# w* L1 g
  17.     result = SystemClock_Config();
    % o5 f7 w- U" u; h9 L( m* I  h) s
  18.     if (result == 1)
    4 Z/ t: ~- S& l) g+ w$ P+ `% b2 V
  19.     {
    ! k5 u* o6 p8 @$ \  j, w
  20.         return 0;
    8 `$ N! l7 y: L1 J# G/ L/ u4 L
  21.     }
    6 ^- I9 S+ N6 s5 H, c$ r( m5 C
  22. 2 w1 e' w0 D* t
  23.     /* SPI Flash初始化 */7 l& `& N0 Y  a& O1 X% ^0 q
  24.     bsp_InitSPIBus();
    1 V) C; ^4 q3 I8 p' r2 }! z
  25.     bsp_InitSFlash();
    ; J/ [; w; Q# P6 T. l7 z7 F

  26. " I* H: H2 w8 a4 e
  27.     return 1;5 M4 N' |$ |' K/ a+ f
  28. }
复制代码

1 n" m( U/ W6 B, w) O( h9 O, f  整个芯片擦除函数MassErase
8 b% B  i. A1 _/ o9 C) g整个芯片的擦除实现如下:) w# H, j* _' ?

1 D+ k0 Y" w  B* n
  1. /*
    + @! ]2 q* ^" T  x. L; f) z% h
  2. *********************************************************************************************************! a2 J0 H0 b8 q, F, O
  3. *    函 数 名: MassErase
    9 i+ j6 ]: `  Q
  4. *    功能说明: 整个芯片擦除
    & l; I4 Y  m( {
  5. *    形    参: 无: e9 C7 @* C/ K$ N$ {/ o: a& h
  6. *    返 回 值: 1 表示成功,0表示失败
    : H- U1 o* u* H. y" J+ i! i
  7. *********************************************************************************************************
    2 _. z1 b; E  `* o- r3 q
  8. */5 S# q( s0 B7 R7 E/ B
  9. int MassErase(void)
    ! M1 G1 c4 S/ ]
  10. {  }, b- e$ J$ R7 o# }3 \. N
  11.     sf_EraseChip();+ W' ^7 n0 V; ?2 \5 y
  12. - a- e" H, m+ l6 c! p; O0 g
  13.     return 1;   ' {' G- z) z1 w% I4 H6 Q/ o* X
  14. }
复制代码

) G5 a+ m, k4 C; e+ w2 {& r$ T  扇区擦除函数SectorErase
4 k3 m) |: Y" f8 }1 J
  1. /*
    " i6 d# R" P7 N- K7 w- K
  2. *********************************************************************************************************# |* x1 B( g  @' x
  3. *    函 数 名: SectorErase
    " ]8 m3 Q3 g0 V  B- p9 ~* p! l
  4. *    功能说明: EraseStartAddress 擦除起始地址- R8 S( u% Y+ w( i( g0 j0 H+ \* a
  5. *             EraseEndAddress   擦除结束地址
    9 [9 c9 I$ R% }; n" }9 q
  6. *    形    参: adr 擦除地址
      y7 S4 b' {- z
  7. *    返 回 值: 1 表示成功,0表示失败
    - G( x* `1 _. v# w5 r$ O
  8. *********************************************************************************************************
    - i  N& w3 y+ }+ a# E, I: w
  9. */, |/ B9 S/ H8 P1 _6 {1 q. V, S
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)5 v& y' c; A/ x0 i7 M9 x% G; P0 J( @
  11. {0 m* O2 p. o0 l8 _; q
  12.     uint32_t BlockAddr;
    ( }$ Q: s% S. U: G& Z. P  s7 Z8 L+ |. }( T
  13. $ \/ n! h% \# w7 S: B2 w
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
    5 S! u3 _5 }2 r7 K4 Q
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;  U6 I  g. D8 _2 k) b! o2 y7 B
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    " G6 o0 T1 t' ~. o
  17. 9 v# O& }0 `# e; A4 _: v
  18.     while (EraseEndAddress >= EraseStartAddress)
    ) l" U0 s, D. [& J- Q( N
  19.     {" O3 Z4 h+ f* C: l2 ?
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    , @% X* ~- i7 G6 ^. g% |

  21. 3 {& T7 E% @3 U$ W
  22.         sf_EraseSector(BlockAddr);    $ P- O  Y: I1 ]) b4 [4 x
  23. * d0 h/ Z' O. x# R7 y. T
  24.         EraseStartAddress += 0x1000;
    # s( l$ k/ q+ O9 K7 z% W; J
  25.     }
    ' l( \9 \9 J6 o( j9 Z) Y

  26. + k; k8 n& K) C" W" M4 x4 Y
  27.     return 1;
    ' l( [( K3 f: m* j+ W- p1 b
  28. }
复制代码
* f/ r8 E5 b, ?+ J' D, D
这里要注意两点:# i9 Y* V( @% E: R3 S

+ Z& {& B- p! N1 \: v2 m1 X$ A(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
+ y6 `3 u7 u$ p5 p) C8 T8 W) z4 B$ K$ r' g0 e, X, R$ V
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。
4 G$ K" v2 i$ Q9 M( v! A* R8 p. \7 D1 }7 G
  页编程函数Write  O3 t) f- w8 N( D/ K% d3 _- [
页编程函数实现如下:
5 |' ?  N% D- y9 _7 Y+ e( ~
8 R8 I8 ]4 K* x7 r% T) J
  1. /*! V6 j5 D3 @% m: f, ~# y
  2. *********************************************************************************************************
    2 R. a- |. C$ r! J
  3. *    函 数 名: Write
    / A$ |8 u1 I7 l
  4. *    功能说明: 写数据到Device
    * ~8 T! Z' }$ G: u1 ?" k
  5. *    形    参: Address 写入地址
    7 P5 X) |# }. @+ k6 n
  6. *             Size   写入大小,单位字节
    + x) }- ]9 U7 P9 T0 `. n( R
  7. *             buffer 要写入的数据地址
    9 K5 ?% {( C: l) t
  8. *    返 回 值: 1 表示成功,0表示失败
    $ V, r; W; |6 V& Z
  9. *********************************************************************************************************
    ( _3 O# P. X9 p5 F' r9 O  l$ |
  10. */- t+ z8 E2 J& [( L: N
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)3 s" ]% }7 @2 f) E) [: O% V
  12. {  
    ' M& L, q& C* f$ p. G
  13.     Address -= SPI_FLASH_MEM_ADDR;
    & e- J. k4 U2 I3 o( l7 u: O" f
  14. - w( d+ `" a3 W, z2 K6 W2 h2 O# m
  15.     sf_WriteBuffer(buffer, Address, Size);
    ) {9 i& q0 l0 e6 s1 B
  16. " F) I7 u0 b* \. }
  17.     return 1; 3 a$ t3 ^1 h' B+ C  k5 ?- T
  18. }
复制代码
8 W+ R3 u$ H3 d0 m
这里注意两点:- u" Z; B$ s5 X  q
4 x3 f  n' _. V! g2 L, q, {
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。$ y3 n7 A& \8 T
1 c8 B4 x( \& Z& |/ c, ^. A, U
(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。! d" ]& v+ j0 _0 G5 A2 K

, N0 D4 }8 |# E  y5 e( B  读取函数
4 k5 L; g4 p( x* {. t% k& L  X
  1. /*
    ! k  [; {8 F" `( z3 n
  2. *********************************************************************************************************: `2 v; G; G/ _
  3. *    函 数 名: Read9 z* D  B' R: |  c0 m( w# [( p
  4. *    功能说明: 从SPI Flash读取数据% U6 B7 k3 C: g0 o$ u+ L( m' P
  5. *    形    参: Address 读取地址5 X: ~8 `+ K5 i7 I8 P
  6. *             Size   读取大小,单位字节4 {9 v7 h' m3 q# A! H8 @5 E
  7. *             buffer 读取存放的数据缓冲
    % w+ ?+ U$ A5 z- u% T
  8. *    返 回 值: 1 表示成功,0表示失败7 W! [" n+ l* U1 {  k, B$ f1 k, n
  9. *********************************************************************************************************
    / n& @) X. c" _3 ~
  10. */1 w7 E$ u7 L; t* D& B4 ^
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)7 C) r/ l3 ]0 D; g% m
  12. {
    ( c  C9 `3 G* h7 U% k9 [# I/ n
  13.     Address -= SPI_FLASH_MEM_ADDR;
    1 @( ^! h- T4 P
  14.     sf_ReadBuffer(Buffer, Address, Size);; f6 N0 D& @9 e4 Y# H
  15. 9 M1 \% {/ W4 V# |
  16.     return 1;, Y1 j  U' ~8 ^5 [* X
  17. }
复制代码
) Y: y+ t: n2 H; _3 Y
  读取和校验函数
  U7 s3 q5 L' `* S9 W: m我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。7 @, _% J5 x+ [2 ^- e
1 a; t+ l0 O: M  ?: X
85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
3 P5 F, d4 K$ `* S; j- g* u6 ?最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
' H4 l; r& ~# l
6 U. y& l. P+ t5 e2 y" ]3 d/ ^
  1. /*; I8 t7 }2 {$ I
  2. *********************************************************************************************************2 g- Y7 c  z8 T: N8 g9 M' J
  3. *                                时钟,引脚,DMA,中断等宏定义
    # ?9 X1 Z- I% I( h8 p5 q5 [4 [
  4. *********************************************************************************************************7 ?8 a9 s$ f( ^+ l6 I
  5. */$ w, {- L% M" s5 `+ n7 {
  6. #define SPIx                            SPI1  V4 b) c9 |# S  S! W
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE(), I" D! T5 [% g* \! t7 f
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    . G) k) }. X0 ]: a& _7 N9 F- d
  9. ' L  B9 G2 Y' c  R8 p0 e5 Y
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()4 A' R) K+ x1 E" W
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()0 p( ^) Q" ^8 H$ O$ n
  12. ' a+ J& r$ W, _& m% `
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()+ n; T; ?! `: [* Y
  14. #define SPIx_SCK_GPIO                    GPIOB/ H. `7 w; n" g: j; M
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3. l! M+ z2 f% ?, Q7 `" Z
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    6 b& T8 B' N# J# e0 j6 O
  17. 3 i, E7 Y2 W3 X; ~+ ]* _% d! X/ [
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()* |( `& f% z! C& N5 U- T: K
  19. #define SPIx_MISO_GPIO                    GPIOB
    4 Y, E/ W7 I5 J3 C$ u7 Z! _7 v/ N
  20. #define SPIx_MISO_PIN                     GPIO_PIN_48 l" M' M6 I. o5 Y
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1$ ]3 f* H$ |9 r7 P9 Q  C

  22. # l" H( o0 ~# q$ O- M( E! g. L: w
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    3 z: I' a0 I/ t
  24. #define SPIx_MOSI_GPIO                    GPIOB9 W" H! n  I* w, C$ U! M" h
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    " T0 S3 U' O: a5 m/ @/ Z% }5 i
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码
7 w+ J2 C% w* z2 R5 [
硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
& P" ]; E. F- g! s
) v5 \/ ^6 x+ S0 i5 z/ h主要是下面这几个:7 F! G8 ~% w, E1 C1 ~- [

* T2 S, F' \. o3 c
  1. /* 串行Flash的片选GPIO端口, PD13  */
    * V8 R6 ^9 }( T# T& c' U% {' ?, J
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()4 c4 L3 O% m( X1 m
  3. #define SF_CS_GPIO                    GPIOD
    2 V" i3 C7 J" `! {# f
  4. #define SF_CS_PIN                    GPIO_PIN_13
    " O8 m" K9 x0 ]9 y% F* K0 x) x
  5. . M% A) e8 h0 [
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) - b" P; K: X: s5 J
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN, }$ H- O. x4 [  U2 ^) {1 g

  8. ) t# T& n2 i6 x
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) *// k9 R7 N2 `" c
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
      y$ m2 J7 c+ x# L$ L
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    . I% z! T9 b- Q* ~! I
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */: f' y+ @! z+ w. J9 l
  13. #define CMD_WREN      0x06        /* 写使能命令 */
    ' e& \: k3 i4 I( f* I
  14. #define CMD_READ      0x03      /* 读数据区命令 */
    $ h: M, x3 v4 D' a4 h! A
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
    ( M3 r6 N6 c" E2 n6 u
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */" j) B( r, t  g
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */5 {* y0 K( f) J% t
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */  G, @: Q- i$ p% ?) b
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */) m/ E/ u8 o# s& ^6 H1 {
  20. % _5 x; y$ w$ I# ], d4 A
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

1 }6 u; I, ~0 V, f' a& }85.6 QSPI Flash的STM32CubeProg下载算法使用方法
( ^& t% k: b$ N% {: V编译本章教程配套的例子,生成的算法文件位于此路径下:
! B( k8 ~. F5 b* w# k: p0 o  s/ A) V3 R: |' Y
5ec6d402a7646232a55f4834cd00ac0c.png
7 `5 {' s7 \3 }3 T% c

$ H. |0 ^$ z6 N8 I0 u; W* j85.6.1 下载算法存放位置
( f" Q* c1 }" D6 P. Q6 R3 k! r生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
3 Y9 M7 r' ]4 k/ r- Q, y( o% F/ O- D' g' G- w
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader/ R2 V0 f1 ?" G: _; G5 x

5 J: @! H1 x6 ?5 J
2609073778af5845a869911ab36624f4.png

+ d+ S: ], _7 H1 S- q/ |# g
! k: j/ g$ O" l$ G9 s85.6.2 STM32CubeProg下载配置% I8 D7 W8 A* j; p" o- J2 y, z+ C
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。/ R) L0 {$ O3 b& l1 L

1 k9 Q) Q9 c9 j6 Y, P+ b, Z5 A- K3 @
d4e256633eafdbf4bcd8cce6e3b5909f.png
  h" u+ `* f* ]3 c/ |

' o0 \& F3 v; i: m点击Connect后效果如下:
9 W1 B; {* |4 M5 W; e* k  Z8 {: Z" n* p/ V
: c6 s& p# w! [1 H9 d$ ^; [- I$ Z, D
0 ~6 g5 d, d3 ]2 Q3 f
在这里选择我们制作的下载算法:
# T' _  H$ F) f& H& B2 L, h6 g4 y/ V
d4fa28f64be2de98e04f3188eb2fe09e.png

% ?+ _; F4 S' X4 N7 c; f2 O$ Z5 g8 o# j4 t& A: ~5 I
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming5 b& k2 T9 u8 q9 ^
4 N, O$ Z( R; N5 r
842518a7cc1dc569598d372132b2ee58.png

) q" \4 y! S! T
8 X( o' y& x% l5 u1 y9 j下载完成后的效果如下:( z6 q+ t. _1 b( [; b
# U# f/ a! y: @3 R) C4 [2 A
68a92f267829ca77f125db982d6c0479.png

/ L, a3 B, r7 P& I& \5 ]5 d1 s4 _. t" V- @' ~2 J
85.6.3 验证算法文件是否可以正常使用5 q* j8 E( k: d! u1 O" o
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
% _# I0 _' v  M& M1 k3 z3 N6 G- Z/ t
566d2fba421c710da70e5656c6934466.png
# `4 b0 e# j5 P/ a

2 X( x1 F  h6 i# Z% ~8 y3 u2 c85.7 实验例程说明

, F9 p& x9 x' w- a本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。  s; O# L. y6 u$ N

- v2 t5 N9 }; K0 K$ E1 ]& j* k编译本章教程配套的例子,生成的算法文件位于此路径下:
, V/ K% s5 M) r1 Y2 c
6 p  |* A6 M; T
6108d1ff7e349f2aa268ee31804c57ca.png

/ X: C: M6 N5 f: W) t+ d
: M9 I. E9 {% i& p85.8 总结' d4 _+ Z+ m, U+ x) V9 k! S
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
8 m2 {2 o7 d; S% ?/ Z3 [, H) E4 Y9 I& p, N5 O  P7 ~5 F& t5 ?
' d9 x& N, B$ H% @

. V. l0 E8 m3 S5 w% i
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
收藏 评论0 发布时间:2021-12-19 15:00

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版