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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:35
85.1 初学者重要提示7 o3 k1 e- a. C/ ~7 ^; Z
  QSPI Flash的相关知识点可以看第78章和79章。3 ~) |: S8 n- }$ U
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
+ S- }$ |1 A# n9 k  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
$ ~# M' q# r0 X/ N2 K3 E  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
: F; K+ z& F3 t* i( e% d% V85.2 STM32CubeProg简介  y% g+ W7 ]7 _# s; w. u
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
4 a6 T/ y/ M8 N9 p, D% ~
: l7 L! m3 M' k( i8 F4 I$ ]
d80578d217eb2a83520c6f3d797218f4.png

4 r8 G. n1 `/ @$ ~- D
0 J+ v; R! ]6 V$ [2 |, Z  j, M85.3 STM32CubeProg下载算法基础知识4 o/ e) o. h2 C7 B8 a; {7 v. p9 s
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。8 C3 K  B6 ?/ M" T% L

5 k5 ^9 Z2 }/ _: s% e! P0 t0 m+ O85.3.1 程序能够通过下载算法下载到芯片的核心思想+ D; b$ M# N0 p& R6 s' f: ]
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。
% V! d- k3 P/ O$ x  L2 |9 U, Z/ @  o; H$ I+ ^
85.3.2 算法程序中擦除操作执行流程4 b, m( v7 T: Q' R( [
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。1 `& w+ y: I. T1 r" l

% I1 G# P) I( t6 ?0 `2 E0 ^% p8 j擦除操作大致流程:
4 L# Z  d2 k% M( o% C! ^) O, t& z, ?
d75670256363bb50203bed0bbe4f9fd2.png
2 q4 M% S( f4 Y4 \7 F# F, j$ v
7 X5 c9 v8 g3 r7 H+ B' |
  加载算法到芯片RAM。
1 Y, b7 u! S/ b! E7 V8 N7 i- s  执行初始化函数Init。4 t0 {6 ~" g7 @. d0 D, j% l! e" W
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
7 ~0 u8 f  Q( V6 `$ O  执行Uinit函数。5 u! Q& x# H0 n) {
  操作完毕。
9 d" @; a0 ~! k85.3.3 算法程序中编程操作执行流程1 j# K1 A. y' @7 }
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。' c* t$ u7 @3 U/ e' c: b9 L
' a5 C- {1 h: m
编程操作大致流程:, Q  y5 l' W* ~, p4 e
9 p4 f: L0 O. x  `
4fa96e9e33d700081f2cdbd312afb31e.png
" B6 l9 f) @# C- L. a0 m, y
$ K, v* T  p' T% G' d" m/ p
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。1 Z2 K: r8 i8 I2 g' s6 s1 o
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:4 [" |3 z2 @" k7 p2 {
  加载算法到RAM。4 P" r( G% x' Y% U! ~5 a7 ?! @
  执行Init函数。
( ~% R9 f. {+ A+ I6 v  加载用户到RAM缓冲。
8 |% \& Y6 r& I& ?8 a  执行Program Page页编程函数。
5 b0 ~$ K( n  l6 }& D$ R  执行Uninit函数。$ H; }9 B- P3 n  V9 H6 O
  操作完毕。
- d/ F! i6 x7 n! d" M85.3.4 算法程序中校验操作执行流程3 P9 H1 O0 b( f  t
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。/ f3 j1 @1 j# z" R8 D
, Q- e) D" w& l7 A4 O
校验操作大致流程:. Q; {" N. E% [' \

/ p: m: Y7 x* e7 X3 n
7deeae4b650bd82c457492af14fc26b7.png
! E) q' J4 o' j: o& T# E/ \
8 D) w6 f' y& M
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
2 j6 I+ ]) X; }" D+ K$ e! p* x  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:2 ~0 D1 e+ k! |$ d
加载算法到RAM。
, J9 c/ s: E3 D3 M( M! ?7 r# V& A 执行Init函数。9 t, f" I( C2 ^4 J; [' Q
查看校验算法是否存在
% U' d0 M0 ^7 u4 `  i% }  如果有,加载应用程序到RAM并执行校验。4 u; E3 v! E$ R) A1 W  o4 h" c
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
7 S" Z" D4 {0 J  执行Uninit函数。
) I7 V8 ]5 h% o% X1 r  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。1 N$ G0 |+ X( ^. s8 f* Z
  执行RecoverySupportStop,回复支持停止。
1 P; L6 V' t) n! ?* i) ~3 b  执行DebugCoreStop,调试内核停止。
" d4 B$ S, ]! C7 h% {  运行应用:
( |$ h  c& t# D' G% V* {7 c& D  执行失败0 I& z+ F% p& g+ W$ ?
  执行成功,再执行硬件复位。3 n0 ?8 B  W& Y2 L" _( P* a3 E
  操作完毕,停止调试端口。
0 u1 ^: L( ^3 P8 v# |4 c85.4 创建STM32CubeProg下载算法通用流程- x) q9 f9 a* V6 e
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
& p9 E$ `* X1 p
0 M: v8 P, g9 }8 u85.4.1 第1步,使用STM32CubeProg提供好的程序模板" ]$ g7 ?0 s2 ~! J* E6 w; ~6 G; x
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader/ \5 R0 Q4 Q, N4 @$ z; ~

2 v# a" }$ k& a
97f385e3b62a055bac9b5c0214b877e5.png

" X8 q+ f1 q6 o+ n% L
& s/ f& B9 M& M. ~1 [& [" y以M25P64为例(注,其余步骤就以这个为例子进行说明):  R, t/ b, Z: E& }
% R! f  j4 r* u: K8 k5 `7 j% G, o
43b298f996937fe35e89697fc6479cb9.png
' V2 @4 J6 w, k1 ^' l
- t# c: {* T: D  o, H) F
85.4.2 第2步,修改工程名! @1 o0 H1 c; l4 @8 H( u
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。# h) A% s7 K! G8 H9 q- ?4 b
1 [! J1 h! o& s# U7 N
85.4.3 第3步,修改使用的器件
2 r  b9 P3 Q/ {+ _在MDK的Option选项里面设置使用的器件。+ |0 Y) g1 j7 e
3 R. O6 y/ V0 h) w; r: B
36026b9a6d60249610d0246eb725b246.png
. E  [' _* ^" P8 o6 ^) x. b2 A
$ i9 H' r: A- k5 a9 j( J, l7 N
85.4.4 第4步,修改输出算法文件的名字
/ \( ^+ G& J( U0 _+ P. K这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。0 w" p$ A2 r6 b. R1 P) M

8 {* K" |, Q7 f3 \$ ~
52c72b3993f28a366a22eac56f8fd043.png
/ q5 Y! W/ t9 O5 Y1 b$ w3 r2 g

* u6 m* i: [7 j5 t& ?9 w注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:" m' M* }& q: E. @  @; f- B* r

1 S! x6 E# t: @. A0 q
7b48153027a44de33926c2da008716a0.png
' g% J8 G* B1 o0 s/ L& B
& g) l" q2 i, }& d
85.4.5 第5步,修改使用的库文件和头文件路径
' z- Y( w# N' A" N8 Z' A( Y. Y& B根据大家使用的主控芯片,添加相应的库文件和头文件路径。
' C; O( g& `* y) w( x6 Y6 |. i1 [% @+ ~, W
85.4.6 第5步,修改编程算法文件Loader_Src.c- E2 c- s2 x; E! s
模板工程里面提供的是M25P64,部分代码如下:
" v3 O& C" C9 D9 Q- H1 g# v# R  t
; \- ~) t8 P$ Z5 j4 T# {1 Q
  1. /*** O. ^! W6 A9 L. q" x" s
  2.   * Description :! H+ m3 p& o- l& P
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    4 F& ~5 B+ t9 j% f
  4.   * device and initilize the FSMC with the chosen configuration
    5 Q' _  D9 p  g+ k; [, A8 n
  5.   * Inputs    :1 J  Y: D! [" i$ |4 g
  6.   *      None0 }5 z/ @! r, V5 E4 S2 m
  7.   * outputs   :& q1 w8 ~" @5 J. K: m' L
  8.   *      R0             : "1"             : Operation succeeded
    2 a% s8 U- k9 ^5 D
  9.   *               "0"             : Operation failure1 i0 T9 I. x, \; Y1 F. T
  10.   * Note: Mandatory for all types of device
    5 \, o* r$ S2 e4 u$ U) [/ h' p9 L
  11.   */7 L# g+ q& @7 {  \; ?
  12. int Init (void)
    / V0 W% R# g7 C2 p3 d! R- E9 v
  13. {  
    / r, X8 {: Z; m
  14.   SystemInit();
    , T* t9 m( M, F
  15.   sFLASH_Init();8 K4 K2 d( J2 N4 N) q
  16.   return 1;$ o6 [0 y" N+ z5 Q8 H& X
  17. }
    9 P$ I# m0 X/ U2 ?# u
  18.   _$ P3 K8 v3 Q' _

  19. ! a: M& c, t$ \7 L) s" ?8 {  K
  20. /**
    1 A) m: T. |* u9 x  {
  21.   * Description :
    3 E2 X' Q7 X8 E" V7 y% v8 l
  22.   * Read data from the device
    4 G0 J) C# J- `' ?; Z, }8 H0 D
  23.   * Inputs    :  ~0 c! |! z7 J3 \" M" K
  24.   *      Address       : Write location
    7 @0 L+ p: a1 _4 G; w
  25.   *      Size          : Length in bytes  9 D4 E1 ?6 i' i% P8 [
  26.   *      buffer        : Address where to get the data to write
    6 y+ X" p" H+ J
  27.   * outputs   :. q1 j8 a$ u( I' O% x# \
  28.   *      R0             : "1"             : Operation succeeded2 _# A' D: z/ c$ r) S* k- Y& U9 h
  29.   *               "0"             : Operation failure8 i) }; A# ^8 w/ Y% p7 c
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    / G6 B+ E# ~5 x! t6 L# Q1 M
  31.   */$ |% l" p% B  O% g7 i7 C  h% z  T9 p
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    & `. t# U# R, C% G' Q) ^& D
  33. {
    3 y: E8 o; |/ {, F" [
  34.   sFLASH_ReadBuffer(buffer, Address, Size);. K8 W, n4 p9 g! ^
  35.   return 1;
    ) S3 I. t' w; ]% R% m& V' j* M
  36. } 2 g% u/ C5 D+ b" Y0 L# k

  37. % `4 u6 N/ B& Q* p' ?6 V( j3 G1 P
  38. % l7 _8 k  R0 p
  39. /**, Q/ E, l/ @2 V+ n" ]5 m; i
  40.   * Description :
    ! W( T9 A! ~: e5 t. S, X
  41.   * Write data from the device
    ; ^% `7 k5 \7 B% `5 @
  42.   * Inputs    :- }/ F: l! j$ G: q6 M
  43.   *      Address       : Write location' U5 H! [; I, I5 Y- {
  44.   *      Size          : Length in bytes  2 W5 `* H# m# w/ o# @7 h
  45.   *      buffer        : Address where to get the data to write. L2 O/ Y- z0 n7 M1 j# l; ]2 _6 ?0 ~
  46.   * outputs   :/ {: q& ], r- ~. y; t3 W! k
  47.   *      R0           : "1"             : Operation succeeded
      Q0 L. ~" O' i. i* ]- c
  48.   *                     "0"             : Operation failure
    & M. R% W2 A5 Y
  49.   * Note: Mandatory for all types except SRAM and PSRAM    8 k: I4 [1 Y# Z  g3 N- Z
  50.   */
    + c% \' n- K# @
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)/ R7 L; w9 u3 R$ B" f. ?
  52. {
    5 E: }- U' t% \( d9 F5 T7 W
  53.   sFLASH_WriteBuffer(buffer, Address, Size);4 R5 g9 V9 g+ b6 i1 p" s
  54.   return 1;4 K; ~5 B8 y8 H! u5 ]
  55. } 2 f" Y4 \! G# \% V) F# V

  56. . J! [+ k6 W7 a- R. z- X

  57.   P: `" d. l$ V7 n
  58. /**
    / j1 \% i6 l7 ]+ F' x3 k
  59.   * Description :
    " \$ Y% {/ J! F9 {* p
  60.   * Erase a full sector in the device
    . R. h0 }- A2 i0 I8 m+ f8 C
  61.   * Inputs    :
    / ^: a7 \  t) G3 C
  62.   *     None
    & c+ _; }8 t8 D- J. ~; u
  63.   * outputs   :2 {7 A/ {: v6 D
  64.   *     R0             : "1" : Operation succeeded2 f5 N' E8 W% N7 w: g6 E2 R5 ]+ N
  65.   *              "0" : Operation failure& H# l; M' z$ B, e$ h
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH% ?  V# P+ K2 a4 G
  67.   */
    5 v6 m9 Z* d' x% @0 H3 K+ O
  68. int MassErase (void)
    % q  A9 x# q- A5 f; f/ z' Q2 D+ M
  69. {  
    ) m# z+ \- m  R: P  j7 }1 L4 z
  70.   sFLASH_EraseBulk();
    - q: x/ [1 s: O8 O7 ~7 u; i
  71.   return 1;   
    5 ~# p8 J8 K; y1 F1 F* q9 P
  72. }
复制代码
: a# `: e3 V- ]! c$ N& k+ w, p
85.4.7 第6步,修改配置文件Dev_Inf.c
" b/ U" D" ?7 L7 c) c模板工程里面提供简单的配置说明:
" a) C0 X# t1 }4 ^/ s: X# O1 q7 i5 k, h, a5 G
  1. #if defined (__ICCARM__)6 h2 m2 c+ a0 R
  2. __root struct StorageInfo const StorageInfo  =  {7 S: H# R1 [9 K2 A! A* N
  3. #else
    $ A* r4 A* d) N. E
  4. struct StorageInfo const StorageInfo  =  {- E- f1 E# K& q' C* v' z' X
  5. #endif
    " F7 _1 D! A4 ~+ \+ P
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number# x( J+ u& O( {( r+ M6 M) S5 ]
  7.    SPI_FLASH,                       // Device Type
    7 @+ [2 v0 V! y7 H: x  O4 H8 x+ Z
  8.    0x00000000,                     // Device Start Address
    2 F* B. f8 G+ s
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    ' e' j" r( O) B7 F
  10.    0x00000100,                      // Programming Page Size 16Bytes: T" Y+ P6 l( D7 v4 d- m  K
  11.    0xFF,                            // Initial Content of Erased Memory& ]* X! A1 A/ a' G# V/ u0 i
  12. // Specify Size and Address of Sectors (view example below)( a% x, P9 M, L9 k9 U/ }8 K
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes ! J/ b* c8 y( h. g+ B& i% B
  14.    0x00000000, 0x00000000,* [0 W! {  n( a4 }% U; n
  15. };
复制代码

& T" i" m1 t5 L8 y. ^注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。$ d% Z! W( H% K8 l; H! ~! E

$ {6 n, ]  d; |85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。2 z  c2 L7 P2 n. p; W( d. v
C和汇编的配置都勾选上:
+ ]8 `6 N. ?, l, F2 }( X) G( q5 K" R. z6 |  H9 Q2 ~' s" w
afddd6e839292aa2b94b43e79544d630.png

! X; x1 z8 N6 G& r. x
9 x' a% C0 [+ B' e6 r1 W" `2 ?汇编:
' L! E# S, |& b' Z; ^# l# _  h: f5 U* m* {# ?1 R, A
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
3 B9 l7 Z4 W6 ?+ x$ u

# ]  O$ W7 H3 K- z) E8 s如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
$ p1 B0 ?( i2 x% y; |1 g) I1 K) X9 O$ t
(1)加载以响应运行事件。
; @* Z0 b' Q- x, q$ o# n1 S; \& M' |6 J4 P, [5 b
(2)在不同情况下使用其他例程的不同组合加载到内存中。. o3 ^0 p( x8 h7 v3 f9 J

( F# C' h- ?. x" A0 R(3)在执行期间映射到不同的地址。1 t, C7 H: E0 B) M5 J( V$ J
8 c, @' Q4 Q- \2 q) ?" d
使用Read-Write position independence同理,表示的可读可写数据段。" M4 C2 H3 O7 a$ j& f5 p7 a
7 c* o# S8 P) \
85.4.9 第8步,将程序可执行文件axf修改为stldr格式
0 J+ }! f, {4 O通过下面的命令就可以将生成的axf可执行文件修改为stldr。
$ \( b$ H+ t6 o# k! v3 `
5 e" D; L$ i# l& t! K3 D
c683673d2481d8dce75b6fa01d624600.png
" R& |( m) k+ s6 q% M

& e4 N& ]: c5 u: N6 Y4 U9 z8 \3 K7 p85.4.10   第9步,分散加载设置
" b8 R, w5 d, W我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
0 [  j: b- {  i2 \/ d# d
1 `7 T: _& H+ y8 M% ~# b b96835a7506516319002e6b2e8db9371.png # Z4 I" ]1 l+ c! `% T6 r

' j1 f" @: {2 c( y分散加载文件中的内容如下:
( q+ i) A) e: m
& T1 K% r2 k0 j
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    & h/ h+ ^5 G3 t/ Y# D
  2. {
    " W' N& x( _. q% K4 q
  3.   PrgCode +0           ; Code
    + l' o9 `4 s: o7 q; o( d* r% N
  4.   {
    + `' D( P5 u/ k$ }
  5.     * (+RO)
    1 \( v3 D1 g0 c, _
  6.   }% a( L5 P( t/ b1 e& X
  7.   PrgData +0           ; Data
    3 p4 k. ?- g/ s: h+ \
  8.   {$ k7 |; q# w, @  U  k/ X
  9.     * (+RW,+ZI)
    # {) p, O  t( }  a; K  K
  10.   }& N- k% l2 t7 M  ?& t3 d
  11. }
    2 z4 h# A/ O: v1 ?
  12. ' S+ n0 b! C+ b+ z
  13. DEVICE_INFO +0               ; Device Info
    / i+ |8 m, l1 T" `, R
  14. {
    ! A1 D& s2 H2 ?3 F
  15.   DevInfo +0           ; Info structure
    * n1 m* m% f! ?( o
  16.   {: W3 Y5 g+ @+ B4 D/ C
  17.     dev_inf.o
    ( Z: H) j. o4 @/ I% j/ [+ E9 {  e; w
  18.   }$ a9 n; U) ~; w* R2 I, B( e- K
  19. }
复制代码
) I/ Z- _, G' X5 v9 b7 |
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
; b- \7 Z- B# Q' C3 o) S. c* {8 `1 D5 }5 a3 ]
--diag_suppress L6305用于屏蔽L6503类型警告信息。
) W9 M  P0 x3 Y; |) m% W0 E
; y/ C8 \5 i' Z. `' w+ [' y* c特别注意,设置了分散加载后,此处的配置就不再起作用了:8 r6 C8 x: @7 J, x" Y1 z3 Y
! Z+ v5 w) x3 G  U
641395c374a3af02f1a92d3ff9ee4199.png
4 l. G& ?4 H6 J- |3 r4 B6 c) G

$ j+ R- y6 C8 \, c7 g2 O0 g85.5 SPI Flash的STM32CubeProg下载算法制作
8 o; c% h0 \5 i, ]/ U0 G* t6 |下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
) t3 }+ L: Y) n, R, i1 g
4 g8 o5 v: Q4 C85.5.1 第1步,制作前重要提示% a1 H; n# `  t; X" j$ X  K/ c1 i7 f+ K: `
这两点非常重要:
8 K8 s7 X! B) ~% `
9 e6 ?3 f2 m8 ^# c- {6 K1 }  程序里面不要开启任何中断,全部查询方式。
  Q, v$ _5 }) R9 I. e* c. A  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
3 L* M. D/ z2 n# s' B85.5.2 第2步,准备一个工程模板
# A' o; ^3 ~+ @ 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
  _  o$ Z6 P5 o* D, R5 q- V" B% V1 ^* X) o/ f/ V" p; A: b
80ad5e3abdd6a993c8621230401a1313.png

8 t' H7 p9 |8 z1 Q, g* |- y6 p" p! f0 X; ?9 |! F
85.5.3 第3步,修改HAL库
. e  ?8 d: n! }: i$ W) ^4 A( ?这一步比较重要,主要修改了以下三个文件:
# O8 G( P8 B# @  l; w. t9 Z* A9 V$ H8 N! c
33bf89f25487547d4008f43b7cf10e03.png
5 V1 M% b3 `9 k; }0 ?3 V$ w# T
1 G. |& M1 G  ?$ C1 e
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
8 f  p# L1 \) m; ?% k* M: ~8 a  a5 g9 X
85.5.4 第4步,时钟初始化0 n) D2 s: P7 ]4 Y7 U6 ?
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
- k6 E6 Y0 v/ Y' x" u( J! ^+ H8 r8 `- R  T- U
  1. /*& H, D( F' D. ?' h' U1 {
  2. *********************************************************************************************************
    ; O- f' _% a/ ^4 u
  3. *    函 数 名: HAL_InitTick
    ) y. ]9 _. m' j- e% G- T
  4. *    功能说明: 重定向,不使用
    " H0 A# k* g" n" v9 j3 h$ ~7 P
  5. *    形    参: TickPriority% C- N. ^- B; ~$ B0 M$ P
  6. *    返 回 值: 无
    1 K, d/ b2 k1 f* l3 L# W
  7. *********************************************************************************************************
    2 ]- A. }% D  p
  8. */+ O$ b2 n* a$ @$ Z& ?% ?& _/ P
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority); g1 O% P, _  Q+ q/ {6 A
  10. {
    5 y$ T, S8 Y$ @! X: R. _( {# C
  11.     return HAL_OK;
    / L* c. v, Y# a2 N
  12. }
复制代码
  n  n% e7 x' J
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:) s  Z& m0 w) w& B! c4 Y6 R

, J" I- k: r( P2 f/ u# X
  1. #if !defined  (HSE_VALUE)
    8 I7 F1 Q6 B# G, h7 r5 S3 E
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */) ]( V/ V9 j$ t
  3. #endif /* HSE_VALUE */
复制代码

5 S6 l% B- J- N3 |最后修改PLL:
) V1 |4 R$ v3 {1 h8 ~1 M. a" |! f3 N$ B" P! x7 _2 Y: K' S9 F
  1. /*! L6 Z! O% t  A$ a5 P3 ~0 p
  2. *********************************************************************************************************
    " b! {; o! [  i$ ^' o" w
  3. *    函 数 名: SystemClock_Config
    % q) E  ^2 Z$ b) M6 u& H9 S
  4. *    功能说明: 初始化系统时钟
    6 M; r7 r- N# v# [( [1 C
  5. *                System Clock source            = PLL (HSE)
    - s2 R( ?9 ~) `3 A5 M6 p- x& n/ G1 I
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    ! W3 A$ P* N* e, M$ l$ z7 G
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)+ s1 n* v! |- L7 J3 e
  8. *                AHB Prescaler                  = 28 N9 D1 {0 |! G: V% C" n: @, d
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)7 p2 ~: U8 F' c" k
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    + B/ g# `5 T  h" N
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)0 T# A5 r2 K4 X" Q* N8 ~- V/ x
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)! L9 s4 i7 r3 c/ `, o. e2 R- Z/ v' H  e
  13. *                HSE Frequency(Hz)              = 25000000
    ! G  r! d/ n* ~) f
  14. *               PLL_M                          = 5
    ( U8 s2 N+ N: @, }. j! E; _
  15. *                PLL_N                          = 160
    . L5 J* c& [$ Y/ F! f
  16. *                PLL_P                          = 2
    0 {# N( n# O* }& A9 G8 e8 ^
  17. *                PLL_Q                          = 4
    ! g: l* h, n5 d; `5 M- q5 O
  18. *                PLL_R                          = 2
    / ^" {% z7 R, U/ |1 q+ \+ i
  19. *                VDD(V)                         = 3.31 N, R) f$ j& J, e$ D$ o% Q: ~2 M
  20. *                Flash Latency(WS)              = 4
    : L5 n: T! v1 w
  21. *    形    参: 无
    ! L" q6 r8 l, u3 K
  22. *    返 回 值: 1 表示失败,0 表示成功! L3 r0 C6 l5 P% \# I: u& u
  23. *********************************************************************************************************
    3 `# j* P2 D, j* v( R
  24. */, R3 r& G5 _; t( y" Y. n* F
  25. int SystemClock_Config(void)
    * T! y; y  J1 {; j& K8 ?9 ?
  26. {
    $ ~- Q/ W8 D$ ?" s. K0 }9 W
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    " Q6 Y' ~" y8 P7 m6 ~$ k% L1 ]
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};5 O4 b+ ^) m  _7 ~7 F
  29.     HAL_StatusTypeDef ret = HAL_OK;
    8 F2 Z: r/ z2 K- A

  30. * n2 Y/ O  F4 ]( e6 s
  31.     /* 锁住SCU(Supply configuration update) */
    1 X. G  [! `0 p6 E- n
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    4 F1 s& K; X5 e9 X4 ]# s* I' v( Z

  33. ) _( ^! x/ R+ W- e+ W; m+ `5 D! a
  34.     /* - X1 Y/ Y* q+ H1 u$ j5 U
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,0 @7 t5 E6 Q0 e' ^8 R2 X8 C
  36.          详情看参考手册的Table 12的表格。- e5 v* }! w0 u* |& ~9 ^" H( ~5 N5 W
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。  p5 s% n& J* s7 i/ h
  38.     */
    ; X8 j& `0 c4 m5 a, ~/ Y4 H. t
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);' e  w/ Y! o7 Q" U; h! {
  40. 4 D1 R3 @( G3 e8 b* k7 N" Q
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}) x+ f: }9 B* _. q
  42. - X( L+ I' r2 w
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    0 K- y) b' s+ I/ @8 J
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    1 r- A+ W) K- w0 ~
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    ; u& S  Q7 L8 I$ T/ q8 q' b, R
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    + i. G+ ?) i9 u
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    1 d4 H" F0 ?9 ~- j
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    8 g0 z1 C( A3 N. N: {
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;$ L5 U5 {1 c9 q# g* F
  50. ! o0 b( q( U2 v) W( |) y0 R. L  R0 K
  51.     RCC_OscInitStruct.PLL.PLLM = 5;; ?7 j2 m( s/ }+ n
  52.     RCC_OscInitStruct.PLL.PLLN = 160;- N; d" M5 p7 l+ d  V. c6 e. a7 k
  53.     RCC_OscInitStruct.PLL.PLLP = 2;6 l  M  U3 V& _+ g' N7 v
  54.     RCC_OscInitStruct.PLL.PLLR = 2;6 T3 c3 {, B1 A# U$ [0 @, V
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;          m* |3 ?. g6 g; W' h/ |1 Q+ B
  56. + H* G/ \+ a9 }3 \% U
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    6 V) m$ e6 A4 S0 V% V8 r
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    4 S! {/ X% u, x( H& A
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);6 p+ N* Y* o* h# i  h/ i
  60.     if(ret != HAL_OK)4 N/ m. t  W% X1 F2 D
  61.     {2 {& `/ y- ?; a. `
  62.         return 1;        3 f- y$ r+ O' o! U- A. o0 G% i
  63.     }9 ], W$ X! X* A9 Z( T
  64. $ W" {+ F4 H5 _' m
  65.     /* ' {$ ~6 N% z* \% X( k$ D' {2 P2 s- m
  66.        选择PLL的输出作为系统时钟
    * S* O0 G/ z5 n: ]" v! `
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟5 j) `6 ?2 f: R* p
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线  a+ k0 o1 r5 S
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线* l0 q- B4 H  o2 [  O1 v
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    4 m! Q* r0 q% S+ R: i  E- p* \
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    . s& p! _0 s! ~" E
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    8 O! G  @9 h4 u, u2 x
  73.     */
    / x; H& ~% {* c0 K; ~) {
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    ; L- B( Q( S7 C, u) r: ~
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    5 r' ^4 Y# M" x; r1 I. G( g

  76. 7 x8 h0 n, Q" f$ R
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    . U, L; [, [9 i5 }
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
      R- u8 ?; e; W1 `+ d& C$ S4 K1 s
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;9 S$ Y0 D- e5 G. f$ R
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  % \( h% U. ?7 \4 U1 J* T' D
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    . Y! y  z3 c+ Y( I2 o# N& y) ]3 f
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 8 ?- j9 m4 z7 a% W3 n8 F* Z. b
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    6 t2 v6 Y, D  s3 @8 a2 t
  84. : \* x: g; M: `' h" ^
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */9 m6 O! f. R. I1 F
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);( D! h0 p' B4 J8 a% z* G+ N
  87.     if(ret != HAL_OK)1 |! ^/ p( B- V# [( g
  88.     {( T! G- i' r  f6 E$ R2 {0 k& K
  89.         return 1;
    * _) M% W) m# q# S
  90.     }
    2 J- L  ?% g& d8 z
  91. " E9 y% T7 F' Q( N, S) t/ o
  92.     /*& F5 M* E" ^5 ]9 a/ \8 i" d. k% g
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 5 s% _3 v1 X$ e8 U5 B) @/ i4 x9 k
  94.       (1)使能CSI clock$ z# s9 D" R+ W- e+ f" K) p
  95.       (2)使能SYSCFG clock/ \( Y8 h3 C- t5 ~( \0 @/ z
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit08 `$ E) _7 B$ C8 K$ D
  97.     */% e* b, s) y: V+ u3 E/ |
  98.     __HAL_RCC_CSI_ENABLE() ;
    6 H! o8 @7 @$ G

  99. + o8 G! d, z* `& [( c
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    7 }- t" H% `0 y

  101. ( v7 t5 h: z# _3 @6 a
  102.     HAL_EnableCompensationCell();
    ! W% Q0 x( l' y4 C0 L

  103.   A3 i% K+ y1 ?" i8 d
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    4 R* d2 p- f% j3 e- u) E* d8 Y
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();/ ]8 h$ r) U+ ]- V+ S; E. z: |
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    6 |2 y$ {' N! A1 X

  107. 9 I- m, f& C, ]. N! k- P
  108.     return 0;3 m) {$ N! x; C/ l  t
  109. }
复制代码
8 g) u+ x4 D! h9 q8 f6 k
85.5.5 第5步,配置文件Dev_Inf.c的实现. w# c( |* t' ^& }
配置如下:* U5 F+ r7 b* I" t+ W7 A

! c/ l$ J" e/ w" g: d
  1. #if defined (__ICCARM__)( n* X9 \; Q9 Q1 G: [% n9 p. ?
  2. __root struct StorageInfo const StorageInfo  =  {6 h# g% Q4 h0 B
  3. #else
    $ m0 _3 N8 p( T( L# ?
  4. struct StorageInfo const StorageInfo =  {
    , ]+ m8 b2 I6 _1 V3 Y7 L
  5. #endif3 H/ s) |( N/ \; ~4 ]# j( m
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    2 ~% b; ?1 Y2 h! w# t1 R
  7.     NOR_FLASH,                      /* 设备类型 */
    9 t+ D% h: J7 f; D
  8.     0xC0000000,                     /* Flash起始地址 */5 s* M  M! _9 R, g
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
    % P1 x, K: Y. B/ S3 h+ z6 n2 y
  10.     4096,                           /* 编程页大小 */
    / }+ t  O6 `, N3 R: M% A5 s
  11.     0xFF,                           /* 擦除后的数值 */
    8 D1 O2 `: Q0 i9 t0 s
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */: m" j/ y. q; _2 s
  13.     0x00000000, 0x00000000,
    6 ^& X: G) t# _
  14. };
复制代码
' b7 N! |; Y2 ?0 F5 R4 \3 n
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:( q  z7 X) _& U) y* z" X7 l

7 i, t) B. \; i- q
51f220556177a36712977b8d140b627f.png
) s1 T' d* Y0 W1 o: z3 _1 K" \

4 I7 \/ W% H8 c& F: u) H% A7 O85.5.6 第6步,编程文件Loader_Src.c的实现) w( d+ i/ |/ X! Z
下面将变成文件中实现的几个函数为大家做个说明:/ T0 U. c1 W. r6 N* o
* [  e- O" k/ t: ~7 d$ Z
  初始化函数Init. {; Z3 D* B5 r& l! D2 I
  1. /*
    ! Q( I/ ~( ]4 m6 m5 A" E7 q
  2. *********************************************************************************************************
    2 V: x) m/ a( |( z7 _
  3. *    函 数 名: Init, A3 \0 n- Y1 E& ]
  4. *    功能说明: Flash编程初始化2 k" L* x! a' ]& j9 `
  5. *    形    参: 无+ Y. t0 ?; b0 M: `$ t! ]% b
  6. *    返 回 值: 0 表示失败, 1表示成功" g# [4 B! n+ D1 n( Y- [1 e
  7. *********************************************************************************************************
    . \. x! X2 A8 T
  8. */
    6 E- V% E+ P& m" K4 [" N" M
  9. int Init(void)
    ) [! B: J/ m) G7 c$ r
  10. {   ( C& L6 \* m  _8 U' Z! m- r0 T
  11.     int result = 0;8 {: g. K/ T& M7 t1 L$ z

  12. ' q& f; i2 }1 A" Q! Y# ]
  13.     /* 系统初始化 */% i8 l+ x; v5 ]
  14.     SystemInit(); % P- o9 D8 x' Q1 C5 c7 Q; I7 s

  15. ; O6 h9 [% W0 H' g9 E: P- R- \
  16.     /* 时钟初始化 */3 F' c5 n; c- g6 {* o- U- Y
  17.     result = SystemClock_Config();
      x7 D) A) l8 ?1 h9 K! Z
  18.     if (result == 1)* j5 ?3 i1 w  |/ I
  19.     {6 K! Q* D5 P: l' g  {
  20.         return 0;
    . E9 H1 a6 O! r1 \# r; v6 c
  21.     }& M2 x4 w: l9 R9 U5 Q3 V
  22. + f! g; C7 A9 `; S; V# b- j' z
  23.     /* SPI Flash初始化 */
    , ^  z0 g+ N: V% Q: u# g$ |* ]1 ]
  24.     bsp_InitSPIBus();
    . l0 }2 J  o. e1 T9 a* ^% ~
  25.     bsp_InitSFlash();
    % c% z" U6 E& y) ^; }
  26. " U1 O$ m( i1 z6 o
  27.     return 1;& R% [  V! }5 B( s/ n7 R& _$ V
  28. }
复制代码
% c2 f, A4 F4 O) n
  整个芯片擦除函数MassErase
/ A& F0 y* i( k8 D# W整个芯片的擦除实现如下:# m" R, V2 T+ n' z6 [3 a

% D2 S  `7 A; k+ X( S+ ?
  1. /*
    + a6 A' C/ ]; T8 C: @6 R
  2. *********************************************************************************************************+ w! |4 c8 \2 Y( x# n0 o  h' B6 ?
  3. *    函 数 名: MassErase
    ; M- a5 F" j. V( H
  4. *    功能说明: 整个芯片擦除
    . n7 }7 V6 ~, M2 [4 y
  5. *    形    参: 无
    . `9 o( d' L; n% }
  6. *    返 回 值: 1 表示成功,0表示失败
    4 y( \+ i% l  j/ a% k
  7. ********************************************************************************************************** c* \4 F( }- C# M
  8. */. o$ q- a" w1 f1 s$ Y% C- \- Y
  9. int MassErase(void)
    ( B) t; D* [: S  M& R
  10. {
    ! L1 r3 d3 ^2 ], ~
  11.     sf_EraseChip();
    & G3 X4 c! Y, t
  12. 6 m$ u5 v& \/ f0 {" ~+ a) K
  13.     return 1;   
    ) [, w+ S% Q% S
  14. }% v6 o" C5 s0 e7 J3 h; }3 A4 O
  15.   扇区擦除函数SectorErase/ y1 L; l! }& Y/ Y9 s. k- A
  16. /*
    7 K: D2 F8 f# V3 a, ?
  17. *********************************************************************************************************
    # R: }& t% h& r# _7 K- k9 l: B6 N
  18. *    函 数 名: SectorErase
    # E9 e" S) w5 _% O
  19. *    功能说明: EraseStartAddress 擦除起始地址5 s- n% M! R* K/ C" t/ U: l
  20. *             EraseEndAddress   擦除结束地址8 v4 k  l" U, e9 Y$ F8 g. e$ N
  21. *    形    参: adr 擦除地址# m9 [& m' Q2 p, u9 T
  22. *    返 回 值: 1 表示成功,0表示失败
    + s/ d/ X9 o" K- P" M6 U5 Z/ d
  23. *********************************************************************************************************" P* K1 J& p0 q2 l4 k9 O( j# t: @
  24. */
    0 g3 B  u6 f/ }2 ]# R6 Q! E0 S+ u' T
  25. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress): I. f4 a- ?! P; m$ z. F
  26. {. D, p1 ~  c* s9 u
  27.     uint32_t BlockAddr;
    , l( `% {) n3 n- D9 q

  28. 5 x+ w, V& ?7 v1 [0 J# {) ~9 U
  29.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
    ) D0 U8 ?( F) Z
  30.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;
    % \" g8 J2 d3 D; t
  31.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */. T6 V" T8 a( ^
  32. 9 W  p# x1 ^8 H* F' s
  33.     while (EraseEndAddress >= EraseStartAddress)# \$ C! q" I& K2 v! Z3 a5 r% t
  34.     {
    ! z4 d3 ~5 F4 H
  35.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    - W1 K4 X: [5 a

  36. 8 o5 f9 {2 L- |0 E5 U1 X) F8 c1 R% b
  37.         sf_EraseSector(BlockAddr);    " w# b* O% ?. f

  38. * U' U' P! v/ ^8 V/ c
  39.         EraseStartAddress += 0x1000;& Z6 [: z& I) r) _
  40.     }
    + _7 C3 s! F' p1 |6 ~
  41. ( U. |) G) Q; t0 o6 D( H
  42.     return 1;
    5 D: F/ J: i# z2 R- ~
  43. }
复制代码
6 k( Y1 S% P; j6 N
这里要注意两点:
, h1 X7 X/ c9 \
8 x. P5 p+ v! l7 \2 t(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。5 n  O5 F2 G7 O+ q! R" Z) v5 p) }
- W3 k3 K0 `% k$ s( M1 U3 H
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。
, k: p$ x: m; z, C6 \# l% i5 x! T7 L2 P8 ], d
  页编程函数Write+ i0 A- Z- B! I" ^9 t" l8 ?' O5 D
页编程函数实现如下:
' X6 C; D+ x$ P( i8 v% }8 D  V. K* {; g) j  K
  1. /*
    - K8 l: }8 J# `: v: O8 y
  2. *********************************************************************************************************/ r" F9 a( k/ o; e
  3. *    函 数 名: Write6 J( d3 X) r1 h3 g3 t) h
  4. *    功能说明: 写数据到Device
    ' b' H8 H, B" t/ A
  5. *    形    参: Address 写入地址
    ) }% f  ?+ h7 I8 K
  6. *             Size   写入大小,单位字节
    # A6 ?# {* [+ Y& i
  7. *             buffer 要写入的数据地址
    8 U/ l& |- h+ ~
  8. *    返 回 值: 1 表示成功,0表示失败
    7 p- x' M7 W2 f3 b% Y- q; D0 v. S
  9. *********************************************************************************************************! y4 t9 I+ T1 V# p$ E
  10. */7 s& A* b3 [3 G+ R8 r: A" ~; V: \
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    - F$ j% n$ Z6 I1 T, ^* O
  12. {  ' W7 z1 J& K: U3 _2 A
  13.     Address -= SPI_FLASH_MEM_ADDR;, B6 u7 t( M4 s+ R4 s
  14. 6 v+ Y: }" T/ M9 l4 J: t. @
  15.     sf_WriteBuffer(buffer, Address, Size);2 c1 j0 ~* t0 F- a

  16. # d, G" e, j( q5 \; f1 x
  17.     return 1; - O+ o6 {! C8 o8 n4 F/ ~1 |# m
  18. }
复制代码
" L4 @0 [- |1 i2 s- Y& e" S% O
这里注意两点:, a. i- M- t7 t0 y) s. _

# S6 _; O1 [/ q& N- c5 Z$ m# C(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。% x0 ]: a! W, f8 r! E

6 K0 c7 s2 ~2 Z2 r(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
' H4 X" u# O! i0 j9 i0 s# o9 I, {. f( z* k' ^) A" J% a
  读取函数. A4 G; @/ p% z* ^
  1. /*, x9 Z/ t: V3 `/ R: Q9 P
  2. *********************************************************************************************************
    - Z* F: E$ q1 ?/ F
  3. *    函 数 名: Read# @% Q9 ?, _8 N, d& q: E0 J
  4. *    功能说明: 从SPI Flash读取数据% x/ h/ N2 J& L  m# C
  5. *    形    参: Address 读取地址
    . s5 n, E  Q7 D$ N3 s
  6. *             Size   读取大小,单位字节
    1 ~9 F9 X8 R6 Q) X
  7. *             buffer 读取存放的数据缓冲4 }; E6 U. \3 p; {9 u$ {. j: j& ]
  8. *    返 回 值: 1 表示成功,0表示失败
    + |# ^1 H5 P. P0 b, U
  9. *********************************************************************************************************
    8 f3 P. Z& l0 b8 ~( q' }
  10. */
    . w+ v* Q; c  C- n9 }( V# E
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    ( |* @4 D' [- q, \
  12. {
    * K1 u% ?7 e% c- e- t
  13.     Address -= SPI_FLASH_MEM_ADDR;
    ( g! X5 B4 X3 b% k' k  X8 E
  14.     sf_ReadBuffer(Buffer, Address, Size);1 {$ X+ ~. _1 `! l& e

  15. ( T: \: Z* j1 u
  16.     return 1;" Z6 H* ~, I: b5 I$ {1 ]8 i
  17. }
复制代码
1 G3 D+ H1 f  M9 @+ s/ D& F
  读取和校验函数: N# G4 U' w! r3 L
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。* i0 m5 l( D9 F

. |. i9 O7 v3 d3 h; A85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
  y! r' O* [. d) w最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:( V0 F1 C( R1 y) D; I3 c

  H- n" |3 L2 C* t
  1. /*  y: D& G8 s6 S5 i
  2. *********************************************************************************************************
    % f+ H" ?% m  Z) L, l: q
  3. *                                时钟,引脚,DMA,中断等宏定义8 Z) k$ J1 F* G1 E+ S) x7 l
  4. *********************************************************************************************************
      o5 O% v! N  Q: ~
  5. */
      L2 Z1 i2 W2 q
  6. #define SPIx                            SPI1
    ; B6 j+ v% u/ Z) Z, o
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE(), c3 P5 ?- d! p7 X
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    ! D+ K6 M: s* D+ _/ S' Q

  9. ) n( T/ o; O) n) ~% \/ v
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    2 q7 |# b! w& B9 M6 T
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()0 @. x. {( D- I& C/ M
  12. * f3 Y" z) P$ G
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()- ?' x9 S7 z$ p7 u
  14. #define SPIx_SCK_GPIO                    GPIOB  H- P2 }  ~+ G* g7 O% j6 c1 E% m
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
      u) g8 v4 d( x6 L3 |+ A# J& r
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    9 c& N+ y; A8 F

  17. 3 d6 x; S6 F/ t/ r: t
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    ' z9 l" }6 ]! q, H
  19. #define SPIx_MISO_GPIO                    GPIOB
    4 j# G2 X2 s; R+ i+ O, r9 C0 P
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4
    . m2 r: B( M7 U4 S  A9 G
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1) R: |" c. v- Z' Z! ?1 v* a' r
  22. 3 d5 y* N4 y: i4 ~$ W
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    ' O: G$ h; S4 u$ T6 R4 n5 ?
  24. #define SPIx_MOSI_GPIO                    GPIOB
    1 F' ]4 i) Z/ V; o/ ]7 }* R0 ]. A( P
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    ) B# |, h' g9 b6 q6 Y
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
    ; x) w* y. R9 c% q4 N1 s" E
  27. 硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
复制代码

8 q" p& U5 B4 k8 I$ ?# `/ M: ^  u+ A/ ^
主要是下面这几个:8 c" T/ U/ z6 w# }/ B0 M

- P# h1 e" B2 w* P# l% X
  1. /* 串行Flash的片选GPIO端口, PD13  */' [2 y  {) D. ?: }+ g4 v$ x5 [5 H
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()- _1 G( m8 L/ ?/ d+ ~
  3. #define SF_CS_GPIO                    GPIOD2 G- c( j# T# L2 Q5 l5 R3 R
  4. #define SF_CS_PIN                    GPIO_PIN_13
      i7 ?! ~  r8 r
  5. ; m0 e' X; N( L  \5 P9 C3 `% T( U0 Z
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    1 j5 P3 T" e% _6 P; q# e% h
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
    + W& R0 B5 m5 y3 y2 _  [

  8. / f2 d0 e9 d+ N2 f3 U" e  p
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */
    7 v5 e/ M0 X8 W* y' ]' d8 q
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
    / w3 Q# z  E) \% [# U( X
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    ; Z& U) u6 J8 b& u! J/ e" U
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    . c$ Y6 V" ~: p& ?. p. p
  13. #define CMD_WREN      0x06        /* 写使能命令 */3 K0 s1 T% k! \( @! ~1 l$ X
  14. #define CMD_READ      0x03      /* 读数据区命令 */1 z- j0 Z9 E4 i. _7 N9 }
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
    * A" |0 O) o! R" L  R; z
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */8 R7 p, c- h! N( _  A
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    % _5 Q7 h) v: w5 O
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    + t1 [" ]5 g& u# F( g
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */  C. W6 r* g( ^# S: J: _+ q" {

  20. 3 B* }* X4 b! o+ D
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

  V$ ]7 d2 u0 J, ~# k. I85.6 QSPI Flash的STM32CubeProg下载算法使用方法) Q2 v3 {* q0 O* u2 L
编译本章教程配套的例子,生成的算法文件位于此路径下:/ B+ G' z* P2 e& }* {2 b

/ U# r& Y$ E2 R1 @. D0 e
5ec6d402a7646232a55f4834cd00ac0c.png
* l2 X) }' d0 F0 t3 L
' `3 u& Y. T3 ]# |& l1 h
85.6.1 下载算法存放位置
/ x7 [/ ]0 y6 x% A- S生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:; I' i$ i% ~4 @2 O
7 t  B% f3 |  t! g
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
* O2 f; @$ e8 S- @) `0 e
4 S- _9 `% Z# e" |
2609073778af5845a869911ab36624f4.png
' d$ z4 s& V+ c! d8 R5 t' g

1 u2 m) A& ^+ o85.6.2 STM32CubeProg下载配置
4 ?4 P6 k" }( y$ n% p5 H我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。6 T: x& m" M- }. Y; S6 F

& @8 d: G$ `# N. r8 G/ a7 B7 y
27a88335ceb606bd021c3ebc65d88e21.png
  V$ k( R; N: m! K2 s+ k
3 A& y: n% g$ c+ E7 w% r$ h  u; k
点击Connect后效果如下:
8 `2 g4 F/ l) D0 g4 {  l/ a
% T  h) r3 n% b: b* C- V6 s* M
d4e256633eafdbf4bcd8cce6e3b5909f.png
$ W. t5 g* V( m# z2 _
. h3 D2 `. y- X) m. a) ~- v6 z
在这里选择我们制作的下载算法:
# p* B% y& o) e3 z9 `& H/ D: U6 |$ [3 C6 ]
d4fa28f64be2de98e04f3188eb2fe09e.png
) l2 A5 h* M5 z3 E1 j

& F1 F2 p' u, U3 G1 Z任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
( d- E+ X. x/ {# ?4 G( ~) Y2 R0 I: a+ t6 s
3 Y" v9 |# ^: a3 Q9 H- K% C8 L
842518a7cc1dc569598d372132b2ee58.png
2 @/ [' t2 V( ^1 _  w
+ @/ I6 x) w9 d/ G# X* S
下载完成后的效果如下:$ G8 {$ o8 r% f6 T
7 U! c, G% F/ \3 Y
68a92f267829ca77f125db982d6c0479.png

+ `: u1 d* T3 K' j# q0 j( N, c& z. m5 T+ Z, o/ v- ~
85.6.3 验证算法文件是否可以正常使用* M# E. u4 A% K7 {: K
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
! a/ U6 d. N" d5 Y1 n, l9 _
% h, c5 C( {5 {6 P. {# c
566d2fba421c710da70e5656c6934466.png

- ^* t& R7 A. R# h" j/ p' P# m, a$ u; P( `( h" @. X0 R( C
85.7 实验例程说明, A4 @  a9 ]  y$ Z- m
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。
7 _/ y' p) M& K, f+ h( K" {6 @; U0 F4 p( H- g$ t+ X
编译本章教程配套的例子,生成的算法文件位于此路径下:
6 S7 Z+ H, a# n6 K
! X' `2 U9 a* e- g1 e* j
6108d1ff7e349f2aa268ee31804c57ca.png
9 f; G  S0 K) @) X* O
, [: k8 i2 D7 m
85.8 总结
4 \! w4 w3 ]/ H* q& V# a本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
; j) n# B1 p, B' l0 R8 u& z" J0 H6 o0 v; h2 Y# o' {# g4 l) I

# s$ C1 Z" {' m- k" {
收藏 评论0 发布时间:2021-11-6 23:35

举报

0个回答

所属标签

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