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

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

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示
7 ~6 s; _1 w( E% q. l# I  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。) ]1 o* A' g- R' \3 R" \( e/ n
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
# P2 z. P  [! `  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
: z3 X5 j; t( s' M% M# _2 U  i& T* _' }85.2 STM32CubeProg简介
. J( k0 c( b& Z6 xSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。3 s. c6 H, b2 L; G
. l, C. [1 }) M4 e9 p6 S0 M8 |
d80578d217eb2a83520c6f3d797218f4.png
% Y6 P/ J# f+ H* q1 Z* _
+ e) {# S' N' s* r
85.3 STM32CubeProg下载算法基础知识
' i0 z: e1 |! `7 m9 Y8 j# @
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
+ C" U+ b  O  M3 j0 m
/ w+ _8 E9 H2 j3 L5 w85.3.1 程序能够通过下载算法下载到芯片的核心思想
4 q/ J9 a2 Q! `6 R! y0 Z认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。7 W: ~) ^2 U6 C. L" q
2 i  {( a0 d+ q! s4 @
85.3.2 算法程序中擦除操作执行流程' R/ B, q  l' x7 a: O
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。4 {% V& A( p  I# S9 z/ o, b; T

5 ?. n( N$ @  z+ g: e擦除操作大致流程:$ X. }! X4 ?# r
) y7 B8 q$ n' O1 s
d75670256363bb50203bed0bbe4f9fd2.png

" q5 P% r7 w# t8 I: s% R- T( t' m5 X) l7 K
  加载算法到芯片RAM。' ]( X; H4 N4 M! F0 K
  执行初始化函数Init。
" c1 P2 V8 g, X  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。/ y1 ]9 U8 y0 D9 R7 s, ~! Z
  执行Uinit函数。( h9 t& Z. I% g" B% L$ A2 X2 \
  操作完毕。0 t: V4 F7 z- [$ T, V0 x

) o8 Y0 e) S1 ~! J% D2 _& \2 s9 Y  C) L85.3.3 算法程序中编程操作执行流程
, T( u9 D, i. U: e% ^) V注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
6 N: k" _$ e3 p6 f" g
% x1 K! u4 E, T) S编程操作大致流程:* K: r% H2 H4 u% M$ P! r
9 }: u& K- q0 d4 {
4fa96e9e33d700081f2cdbd312afb31e.png

3 a9 L4 ?* i9 O! d/ B/ @  \0 F# v
  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。* h0 g* P2 W# U) \% y
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
7 N% I8 Z- b, A9 ~  加载算法到RAM。
0 z5 l0 I1 L5 M  执行Init函数。
4 D7 H2 v$ t0 |( a, V* D" |, Q" }  加载用户到RAM缓冲。# I) |5 O4 s3 ^/ \+ N. R& p) c
  执行Program Page页编程函数。
& r) r5 I& N- Y  执行Uninit函数。
0 ]- @$ v9 a7 i) o' j! q' ^  操作完毕。
3 F; q; V! a8 V2 T
! j$ {9 C/ k, A. F, V85.3.4 算法程序中校验操作执行流程$ s* g/ i' A& K5 O  B4 {
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。  R& ]' n& p1 g- S8 J
# F% C+ _7 D3 n0 e$ y
校验操作大致流程:
- ?/ v9 @+ f# f6 F* l! r" k- ~* [( N$ b
7deeae4b650bd82c457492af14fc26b7.png
& z- y' ~2 S" F( B4 Q# H

. \) _+ Q: f/ ~% j" T  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
$ b1 b- d2 G4 V+ v0 x: t4 ~7 g  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:+ W& t' {. s$ r% k4 r0 Q3 T4 O
加载算法到RAM。9 ~& z2 ]& L) W; [! x& v1 u
执行Init函数。
. R: d( z) m* U. ~7 m( b* ~ 查看校验算法是否存在% E% b1 Q. |% W+ F
  如果有,加载应用程序到RAM并执行校验。
! v6 b' T; o, M+ W9 H  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。% \" J, }' H8 O" T
  执行Uninit函数。
. ^/ g5 {2 D! v# w+ V' X# m! {  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。0 J# G# {- S) s. B4 I3 Q7 x, {% q: e
  执行RecoverySupportStop,回复支持停止。
' {- @; L" {. a% t; C# B  执行DebugCoreStop,调试内核停止。
% X* k: t: K9 C+ ?  运行应用:
" b8 Z8 ]. m/ c: h7 U6 b+ ?2 _  执行失败
# i1 n1 X7 u# E* R1 H5 K  执行成功,再执行硬件复位。% h, m3 l9 [7 c& X5 N' W# f
  操作完毕,停止调试端口。; t+ X3 M% W! T; J0 s8 a8 `0 f" Y
( _: {% f; O7 t$ t
85.4 创建STM32CubeProg下载算法通用流程
6 u6 l! g* _# t+ H下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。9 g, B- H/ g$ v0 J5 C) C$ z, z: v

# v/ D1 y" I2 H0 {+ Q" n$ a85.4.1 第1步,使用STM32CubeProg提供好的程序模板; i8 q5 o; I$ Y' S3 y
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
; B3 {$ q4 C: H& D3 A# l% |! Q( Q, t! @6 J) h6 i
97f385e3b62a055bac9b5c0214b877e5.png
6 @; O: [1 D$ N1 q: F- ]

. i# j% p2 R. e5 ?6 }2 N+ e以M25P64为例(注,其余步骤就以这个为例子进行说明):
: H6 `9 ^* C2 @8 O
" L0 L3 A" O9 C* S7 y% v
43b298f996937fe35e89697fc6479cb9.png
* ~9 w0 d: q- p) \
+ \! |# n( ^* U: @
85.4.2 第2步,修改工程名# b4 U" F+ U$ I' o! l% q
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。' x- t' F0 d1 r2 @9 J( |0 _8 S/ n
  x0 B# c  u1 m* u
85.4.3 第3步,修改使用的器件

0 ^) k, _, d+ ]8 D: j% p/ N$ t在MDK的Option选项里面设置使用的器件。' {3 ]. S, g& f4 o( B0 i
' D) A3 D; H8 l: G
36026b9a6d60249610d0246eb725b246.png
3 h1 m7 x4 f4 ^& K  ^
- X9 t* G: ^' L% x: K$ w
85.4.4 第4步,修改输出算法文件的名字$ H- r' a6 y1 w# v4 e/ s
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
3 d- V- o( v" H0 |+ O& |' D0 w" j( [
52c72b3993f28a366a22eac56f8fd043.png

; j% Z' g( `/ U- @( y6 A6 L& e
' M; s4 W0 @* I1 J& K注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
) M- R( d/ E% {4 X( O' s
5 I! T9 ]8 B3 {. D0 `4 y6 [8 G
7b48153027a44de33926c2da008716a0.png
- d' ^% I7 |$ {: u- W
, B9 t+ c4 W) R* ^. c* ~
85.4.5 第5步,修改使用的库文件和头文件路径* m; z1 [' t( f( R5 g- w
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
9 R/ j) |9 G5 [2 @) t  S
* Z' M1 ]% q; C& U85.4.6 第5步,修改编程算法文件Loader_Src.c
6 q3 X# U8 u8 B8 o" G. a8 G模板工程里面提供的是M25P64,部分代码如下:
4 c' k2 b. ~+ T- C9 m9 Y. P. O8 M
* x' g3 {  |2 e5 t# _
  1. /**5 X1 g! x3 r# k# b4 O/ }  @
  2.   * Description :
    " A! c9 |2 s) O+ J* O# R
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    * v& e7 F+ s" |  u; k! |) J9 \
  4.   * device and initilize the FSMC with the chosen configuration ( L7 q* s+ I( l0 G2 C2 k
  5.   * Inputs    :
    9 j" k/ Z5 A; ]
  6.   *      None
    8 W) i  R5 r# H% X# x
  7.   * outputs   :
    5 Y" }+ S& h2 r- ~3 m2 G
  8.   *      R0             : "1"             : Operation succeeded- B" s2 r9 w9 |* D$ H& m) ?
  9.   *               "0"             : Operation failure- a6 w& x4 p6 {0 E- M8 \+ K
  10.   * Note: Mandatory for all types of device 5 I1 B4 T& F7 M0 a) M
  11.   */
    3 K3 J0 q, h* D5 M- B
  12. int Init (void)
    / Z9 M  R% r, E' L! l- _
  13. {  
    ) x$ R+ k: p9 ~8 c. q3 y5 \1 g
  14.   SystemInit();
    % I' E* ]% K  Q5 F' p  ~
  15.   sFLASH_Init();" I( Y- r3 A# U( @5 X8 J( ^
  16.   return 1;
    4 F- {7 x& O/ X3 r7 j
  17. }  }" b( K; Q0 h9 Y0 M' f0 _

  18. % h7 U: q' @6 F0 P# G
  19. 6 x3 l$ P1 H* Q2 a8 B" N
  20. /**
    ( V& ?& W8 H$ y  I, C4 J9 N/ N
  21.   * Description :8 L) q. D) d3 u. L
  22.   * Read data from the device ) t) T  Q, V( U% J) G
  23.   * Inputs    :
    9 P6 ^1 ]: o1 l/ k; C1 H
  24.   *      Address       : Write location
    ) V$ `7 f0 t: _8 I# D
  25.   *      Size          : Length in bytes  8 o- p5 |+ L* @* l( j
  26.   *      buffer        : Address where to get the data to write9 A: t$ {% B- Y$ k4 k7 o
  27.   * outputs   :/ `) G% ^  {# A- y: |+ n0 s$ W
  28.   *      R0             : "1"             : Operation succeeded
    , `: x6 w5 t1 c) V2 Z( L0 i6 u
  29.   *               "0"             : Operation failure) Y4 h. F, y- I: r7 U
  30.   * Note: Mandatory for all types except SRAM and PSRAM    9 i5 H, Z6 |5 X3 n
  31.   */; D& S3 ^, g5 I& {$ Z) l, f
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)2 q" y2 H8 @0 `* g! k
  33. {
    2 @* U. R0 c8 g6 y4 H; d* h
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    : ^' N. Q' g. s; u6 a" F
  35.   return 1;+ |9 {7 J/ {  K! v7 |$ W+ l2 Y
  36. }
    4 Y# R1 L$ [. K

  37. $ L8 q9 e: l4 _" ~) G

  38. * R$ |6 _  c- N" K& S( Y/ \
  39. /**
    1 U: D% \. ]( n8 v$ U$ G& s0 @. g' p
  40.   * Description :
    $ U- P# T* c* G$ B; }: t
  41.   * Write data from the device & D; b3 b& V8 R" H- K! v3 o: ?
  42.   * Inputs    :
    . g" x% m7 m/ k/ M5 t7 M
  43.   *      Address       : Write location
    6 @5 E0 c$ ?3 S: x* M# r
  44.   *      Size          : Length in bytes  8 Z4 O5 D& U7 d3 w9 T
  45.   *      buffer        : Address where to get the data to write7 N6 F2 H' @8 i0 J- t2 o7 {* \
  46.   * outputs   :
    ) {' ~0 G) O# P. \. |( d: T
  47.   *      R0           : "1"             : Operation succeeded# r& V8 @+ [2 r  a- f# |
  48.   *                     "0"             : Operation failure$ x4 s8 d* Y5 R% i2 M) y4 @
  49.   * Note: Mandatory for all types except SRAM and PSRAM   
    # i0 a8 ^; r/ d& G9 R- Z2 }% w
  50.   */
    ) w* R* m) ~! s: @9 d4 ~
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)6 ?. j) b1 [! N! F; ]& @' z
  52. {' O9 ?) U; C( I; N
  53.   sFLASH_WriteBuffer(buffer, Address, Size);0 l% u# e* ]! x8 ]+ m' |# g
  54.   return 1;
    1 D1 X1 t* V% r5 D5 @+ i
  55. } ! v3 Q2 `6 j. i/ C
  56. 8 D" T) g+ A4 z0 Q% X# b* p) n
  57. , n1 ~" G7 x4 h  s
  58. /**  ^) y" u0 A2 Q' Q% N' y
  59.   * Description :' V7 d) c! X5 a* I' s- v& r: [
  60.   * Erase a full sector in the device
    : Z( z8 W: i! k" L; a
  61.   * Inputs    :
    ! z1 _& O* i9 s+ P# T0 ?: ?
  62.   *     None
    , A( d/ G9 R1 O
  63.   * outputs   :
    ( C+ c$ I) w- |: S8 \2 H
  64.   *     R0             : "1" : Operation succeeded7 D5 `; j, l3 G7 v9 A% |, Y
  65.   *              "0" : Operation failure
    * \4 z# k+ x' e. ~
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    ; a8 m+ r4 B) t7 b; f
  67.   */  L2 J. H. c& ]! }/ A% |8 @
  68. int MassErase (void)
    ) ~+ P- c, b$ N. I6 a3 N
  69. {  
    . Z. L6 q" H+ a: V
  70.   sFLASH_EraseBulk();
    0 t7 d7 z5 a( y' E
  71.   return 1;   
    # }& {3 P) S2 G
  72. }
复制代码

- j- R0 b( M. A  R/ U: z85.4.7 第6步,修改配置文件Dev_Inf.c
7 Q5 Q6 @4 q5 v+ `模板工程里面提供简单的配置说明:+ s9 u6 D+ @$ j$ W5 g
# U" k3 v/ p4 d7 M- U. N
  1. #if defined (__ICCARM__)
    # h: p0 ^" |5 g  g4 r4 B) j& ]* _, x
  2. __root struct StorageInfo const StorageInfo  =  {0 Y0 y0 e1 L6 ?
  3. #else
    5 f3 f! c+ ^( m. h) k; d9 V
  4. struct StorageInfo const StorageInfo  =  {0 [' `; ^' L" h* Q. g) w
  5. #endif
    ; O1 c. K+ Z5 G
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    ; L7 f$ a2 a. H1 |% J: T
  7.    SPI_FLASH,                       // Device Type
    9 a" `0 N& K/ m$ Z* O( C
  8.    0x00000000,                     // Device Start Address! D0 A" T% u- B
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    $ U" R  G% M7 }% Y
  10.    0x00000100,                      // Programming Page Size 16Bytes( x3 x9 ~5 V! I; \
  11.    0xFF,                            // Initial Content of Erased Memory) a! n0 n* I# h$ q+ N! s4 j) K
  12. // Specify Size and Address of Sectors (view example below)5 B3 ], r+ H- T- h' }* M
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    4 u6 |: b; h; D9 B% n2 [4 ^5 B% N
  14.    0x00000000, 0x00000000,4 D  j$ x- {) Z8 q% s, z. w9 U
  15. };
复制代码

+ \6 q9 c% w4 p2 u6 ~注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。0 O9 u% y5 }1 i) M6 u/ \
, p$ Q1 W6 Y& U+ Y
85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。

, P9 v- d& S6 Y# `% FC和汇编的配置都勾选上:
( y7 t( s, b; v# F' y* f2 G, \! ~, m7 y+ f! s2 B5 E# ^
afddd6e839292aa2b94b43e79544d630.png

/ W, E3 z/ n3 O4 `6 Y1 b  h% c3 A* g- }3 Y8 m+ V9 G$ b( o
汇编:
- H3 L* F' Y  w% ~0 Y7 q1 m. P; `$ [& N- ?8 g; {2 h6 {5 c
f639ae5b0d3c2e0e0190ec509fbc8a4d.png

2 j) p3 C) U! L  m( W
- k/ x1 A# f- Q& y如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:* ^+ h! r* n9 h6 j3 m! y% N

1 s. r( C! B3 O' o6 _/ k(1)加载以响应运行事件。
. ]) n9 h* W, T5 T9 e3 V& y$ m. q! H
/ @( O- O% w! J# ?: u4 |+ [3 b7 Z+ r! _(2)在不同情况下使用其他例程的不同组合加载到内存中。/ i8 a( G. h# ?3 Q" J

# r) {  O* W1 A! r4 `(3)在执行期间映射到不同的地址。
# w2 p. z7 o& C8 y0 |& T  w) u5 v- O, s9 p+ W
使用Read-Write position independence同理,表示的可读可写数据段。. Y+ C3 K) x6 w# I
8 g6 ]. Y- c+ `: M" Y% U/ Y: L
85.4.9 第8步,将程序可执行文件axf修改为stldr格式
. L) G- _5 e6 \7 J% P: n通过下面的命令就可以将生成的axf可执行文件修改为stldr。* D! Z: L3 g/ N% Z
; v1 |8 F+ J$ _8 D
c683673d2481d8dce75b6fa01d624600.png
) L6 e; n0 \7 P$ G% U

; o0 q, R% ]" \. d8 N# R* @" s85.4.10   第9步,分散加载设置+ i. {# W: O8 n- \6 p
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
3 w. [; j+ p  Z; O/ c
8 H! R4 G1 z" e/ h$ t7 u" `( Q2 O, ]
b96835a7506516319002e6b2e8db9371.png

5 A- z7 C# X( o. v; h0 @3 i
5 o! t3 q7 z  h' a# ^; e分散加载文件中的内容如下:
: M! A- |& }+ g1 s& L2 n/ w( @
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    3 j& J# _5 a5 c$ y
  2. {
    5 U- U4 S. L. s  F
  3.   PrgCode +0           ; Code) c  B& s% X9 j/ k/ [7 j
  4.   {
    2 J* ~; b- k. D8 a% e' I0 s/ k* [
  5.     * (+RO)
    + _0 G- `. Y& E
  6.   }& B6 e" S* a  q3 S, A, h- x/ p
  7.   PrgData +0           ; Data$ t" T* Q" D+ M; _5 K( p: L0 C& X
  8.   {) e' ~: P" M- F8 R: T8 l" |$ f8 h
  9.     * (+RW,+ZI)
    2 q* l. T2 g9 W
  10.   }" g$ B6 o. q( _. s
  11. }
    ' K2 A. F$ W. q0 P

  12. 0 f, n# D# e$ O) w+ p8 j
  13. DEVICE_INFO +0               ; Device Info
    . k( y9 h) H$ O6 ~- ?7 }( E) H5 L
  14. {' w* s* B6 `/ T7 T
  15.   DevInfo +0           ; Info structure
    * `) [- U2 s  \% E" U  r; D
  16.   {' d- q% n" L& ]/ _1 |2 Q
  17.     dev_inf.o
    * _6 G2 {' z. }% @1 W) h
  18.   }) _: i- I# s1 u+ h; t# L& |
  19. }
复制代码

: T# U; ^2 _# V% g" K9 y+ @这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。* B. \+ A% n" y- Z
) f0 j) D6 F* X
--diag_suppress L6305用于屏蔽L6503类型警告信息。
/ r; C$ m( w9 w2 T% G+ N" v' Q( ~$ i
特别注意,设置了分散加载后,此处的配置就不再起作用了:+ G) Y1 ?8 S0 U$ I0 v2 _8 b1 h6 H

4 s/ @2 w, b( U& g
641395c374a3af02f1a92d3ff9ee4199.png

1 f, f6 [% W# C  ^$ Y7 H3 ?
: N" x5 Z3 o/ X, Y% n1 _6 \. A9 D85.5 SPI Flash的STM32CubeProg下载算法制作; T1 S* }" @9 m; N$ A" a% f" y# D7 n
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。$ d) o( b, R) d% P
) E7 I; X5 u3 B1 t: d( f/ @' A' c8 a2 ^
85.5.1 第1步,制作前重要提示

8 E7 p5 z; j( t# @这两点非常重要:
# {/ L" Y; @8 ~/ s! o% d" |' m; }- x$ q
  程序里面不要开启任何中断,全部查询方式。! Y: \4 q" B- I& X
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。7 T, u3 x; h1 U) j2 O
6 q: S# h" J7 f# M$ |, X: c  e
85.5.2 第2步,准备一个工程模板
* ^/ j0 q0 U* i+ C8 `7 t 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。; I9 F( Z& N. T7 e$ g. o

* C6 O# b! s6 e% A2 I* \/ t) u$ a
80ad5e3abdd6a993c8621230401a1313.png

, `; u: x& X+ z# m
8 T2 o8 a7 r  `/ V, D6 u" }3 I85.5.3 第3步,修改HAL库
) M( k- Z  f! f3 |这一步比较重要,主要修改了以下三个文件:
$ R/ f  Z7 T+ Y6 i% |" O, p- c+ U' u$ @. g( l$ ]& D
33bf89f25487547d4008f43b7cf10e03.png

, u( R- q. R" Y# g5 [6 ~
7 T3 R3 q1 H1 o7 J主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。  r) `4 A- {& O& n9 I

  A' P0 `- ]* m" ?7 r85.5.4 第4步,时钟初始化
; l4 }# k' E, J! ~1 o; O& R9 z' |我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
. N+ G% \" G. ~# l7 p2 a: m  Q1 K2 P  G
  1. /*
    * u. M+ j/ K4 v9 y
  2. *********************************************************************************************************8 `! b9 @0 E' |+ M
  3. *    函 数 名: HAL_InitTick9 H' @9 W5 I  q( x, r
  4. *    功能说明: 重定向,不使用3 z0 E' g4 R( i
  5. *    形    参: TickPriority4 J; W2 r/ u7 [8 P. q+ s. ~
  6. *    返 回 值: 无
    9 g8 a% ?" ^: a) x, x3 Z; _
  7. *********************************************************************************************************$ W8 a8 w6 A. F; X+ q
  8. */: c) T4 \* A" J" Y6 K
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)0 }: o% a, H& |  p
  10. {
    1 F$ Y. s6 W) F* T- U- v
  11.     return HAL_OK;+ N9 G" P, X5 w: Q' y1 K: P
  12. }
复制代码

; R4 D/ L* B: j& {" {然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:; i% d. Q, D/ `
8 h) L9 q: d. B/ w
  1. #if !defined  (HSE_VALUE)
    $ s) f. N3 ?; d. X# Y; Q: Q  W% _
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    ( I7 O8 x$ C3 l% d- v2 N
  3. #endif /* HSE_VALUE */
复制代码

+ I" ]8 G* a; {+ q' i- ?" F5 p最后修改PLL:4 d) H' q+ R8 O7 y1 ]4 ]
. r8 }2 q7 m: J; L, f  o
  1. /*
    4 f/ ~7 n5 ?, I$ c! q- k
  2. *********************************************************************************************************; Y+ K' }) Y) W  o1 e; R8 e
  3. *    函 数 名: SystemClock_Config
    - |$ E+ y$ z% h6 V
  4. *    功能说明: 初始化系统时钟
    : q0 u% m$ A4 {4 [: O7 P
  5. *                System Clock source            = PLL (HSE)
    7 K5 X4 `7 |4 \0 f- w- d
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
    & `$ R3 K6 F6 ?  t3 a
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock): ]) U) ]1 H) O( a8 H8 y* ^& O
  8. *                AHB Prescaler                  = 2) \( @* M4 a% Q' p4 i4 I
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    ' F& m4 D2 R6 p- h
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)7 F' O- l! x: }0 j7 T$ o
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    / |" x7 g8 @6 S
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    1 J" g, h6 ~* q
  13. *                HSE Frequency(Hz)              = 25000000
    & a8 R. W* L+ v! T: e% m
  14. *               PLL_M                          = 5; \8 n, |# c; E2 v, d+ I
  15. *                PLL_N                          = 160/ p+ n# c% I" h
  16. *                PLL_P                          = 2- Q$ ~; H, ^- v/ B1 }: A
  17. *                PLL_Q                          = 4  T1 b1 i7 n& W
  18. *                PLL_R                          = 2
    5 ?! \; [  w# ^' T/ {- Q  c
  19. *                VDD(V)                         = 3.3( w' ^- m+ g9 ?7 G3 S! @" W9 @2 {
  20. *                Flash Latency(WS)              = 44 V! L+ C, S  d: j# l
  21. *    形    参: 无2 M# _0 M! ~" Y( l* A
  22. *    返 回 值: 1 表示失败,0 表示成功2 d! D0 Q3 v( k
  23. *********************************************************************************************************
    3 ]& ~" ?- x+ x$ X# Y+ `: J( H- N
  24. */
    ' ~  q! V9 S7 o
  25. int SystemClock_Config(void)" a- b% J8 {; G8 z; K/ ^5 M
  26. {
    + y5 K& i; Q) K) ~1 y% g. O* T
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};' e0 k$ T& K+ M
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};2 `' C# l2 \1 `* b( r
  29.     HAL_StatusTypeDef ret = HAL_OK;
    & y% Y/ z; A1 }) L6 R4 F- w4 ^+ c
  30. 9 ?3 t# r5 \4 Z
  31.     /* 锁住SCU(Supply configuration update) */6 H/ v" _( |7 B2 {$ W9 q! I
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    9 `* p( W( ~  h1 X7 [
  33. : ^# H: e8 y) A4 }
  34.     /*
    3 @+ |/ c# j0 \  r: F% o" p0 \" G8 d
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    9 K2 q9 t% u8 i6 k3 j" v3 ^$ F
  36.          详情看参考手册的Table 12的表格。
      d! P# G" t; i6 D; ^2 P
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    4 }/ Q3 i( B- W' l
  38.     */
    , p% c  z7 \$ K+ S+ y/ C
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);( ^  s$ l  J4 L5 Z' c
  40. 0 f6 e) u. {: y% b
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}0 a! Q9 p1 f: l6 g8 ~2 ^/ @' U
  42. : u" M# f, C& ~" I' z: r8 y$ l
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */, X2 W6 [: v& o* q. |& p( Q2 m  Z
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    * v  a$ L: Y! W% }5 ^
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    / [1 Q. l' R; G1 k& ]
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;9 e4 Z, I, h, D4 u6 l
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    - v1 X6 N2 p- n5 f- S1 h
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;5 s" a0 t: V2 q. q1 w+ n
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;2 c* x' ~/ J& _0 x: c# F: q

  50. & A, l. d; `3 c
  51.     RCC_OscInitStruct.PLL.PLLM = 5;$ ]; Z( C3 X) u4 J4 E& d" y  V; V
  52.     RCC_OscInitStruct.PLL.PLLN = 160;9 D! S+ V# x4 \6 I8 `" M
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    5 x# _! Q9 E. A  t* A! }
  54.     RCC_OscInitStruct.PLL.PLLR = 2;( |) Z% }7 l8 \& {
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    5 Y% |; F& o) o4 U8 D

  56. 7 F2 x* X- f/ J7 J& p6 h) i$ b
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    % m* c% H( k4 P+ D/ f: i
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    3 L2 ?2 K: \" e- v; W) A1 n8 e
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
    9 i' }2 E5 X5 S  ~+ ^' K, \
  60.     if(ret != HAL_OK)( D! s# j; o" k1 i) ^1 B/ {
  61.     {- @) [" a) L0 T. S) S3 r( W1 ]
  62.         return 1;        * q8 w6 X' u0 F
  63.     }) s) y% d1 h; l  M1 U
  64. - V+ t8 D0 k( K9 M
  65.     /* 5 \( y" h! [) \  o" ?
  66.        选择PLL的输出作为系统时钟
    $ f  Y1 g/ t: R; {
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟5 I  U' [8 S; @8 A) g, U# D( p6 ?
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    & O$ o0 w2 z, ]3 u) u4 @3 U
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线1 J# s% i; C- M3 _2 [8 K) ?: U
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线+ ^* @# r3 I3 c; l
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    4 s/ u* a8 p6 c# h
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    0 e: k. h. _0 y7 A
  73.     */0 o, x* V3 h0 @& E
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    ' B- V9 I' s! |& i" e; ]3 @, u
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
      [- B, n' q% w

  76. 1 j, q7 L3 L- Q% s& x. i
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    1 O6 k1 ?4 w7 K) Y3 w! W0 u
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;- x5 B. m* G, ?9 e
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;4 [, u2 [) Z( c. ~# Q& ?( b  U. J
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  & x8 N) [% u' Y- a7 E& s
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    9 Z8 f( A* @0 z: E7 b) ]( ?/ Q
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    9 t+ M0 g$ G# q& H7 {: M4 t; M
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    $ O% E7 s& X& X5 n
  84. + a/ u5 }  I" [4 q! t! S4 m( v( A* [
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */; W) Z( r' D9 \5 H
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    2 y; ~5 h# `  v) u
  87.     if(ret != HAL_OK)
    5 W* }% g* X$ Z
  88.     {# T( L3 m" |- c5 u; t# \, X, q
  89.         return 1;5 g: k  M# Y9 X) j7 m8 Q* K
  90.     }: |/ ~/ `. V" Y# l7 c* u- H9 U
  91. 1 i7 r7 \/ a1 b1 f
  92.     /*
    ' X3 Q; q! N1 r6 P6 H; d
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    0 t# p$ w' M: E, C. J
  94.       (1)使能CSI clock( q& S# D5 `; \
  95.       (2)使能SYSCFG clock
    ) P  P# ?5 v2 `9 ?
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0. }" D  r& d; u% T! Z! N
  97.     */
    + `) R9 r& J; s+ l% d
  98.     __HAL_RCC_CSI_ENABLE() ;5 f( F: ]4 S4 f9 E7 L; n* O) ^' R

  99. $ @3 R+ ^0 d) v5 q
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    4 ?9 n8 l( w  Y. k  {9 l

  101. " Q$ |6 E3 c  V6 q* s0 d1 T
  102.     HAL_EnableCompensationCell();; G% g0 T; }! f0 O" J1 S2 X

  103. 5 d2 ]1 Q+ a. \3 U
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();; [$ N6 R% y7 b2 g
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();- ?& s1 O. i  o; E' E" j
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    % p& M& b6 B9 t, ~2 ?1 s

  107. 7 F. i, x3 C* v& @9 `4 s! a. o
  108.     return 0;
    3 ~4 P) B* l$ W
  109. }
复制代码

% d! G; y6 ^3 L0 i: k. u: ~+ v% H85.5.5 第5步,配置文件Dev_Inf.c的实现" p$ H! Y1 h6 T6 E
配置如下:
, u- b+ Q. X- b$ p
% S2 R2 C) q* D/ V6 C
  1. #if defined (__ICCARM__)
    * m2 `7 N& M: d; }4 H
  2. __root struct StorageInfo const StorageInfo  =  {* ]& L4 |, h: z# V! B
  3. #else
    & d1 M& b6 `; X' Z/ C1 ~+ @7 S
  4. struct StorageInfo const StorageInfo =  {) O* ?( {6 m: e2 H  j. ]
  5. #endif
    1 n* ^& [8 w/ l4 _# t" w) g
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */3 S) Y8 v5 V/ @9 N( w# ~6 a
  7.     NOR_FLASH,                      /* 设备类型 */# C; g% [7 Y( X: p
  8.     0xC0000000,                     /* Flash起始地址 */+ O7 U9 i: X+ l' m
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
    2 u; M* W1 x8 n- p2 j
  10.     4096,                           /* 编程页大小 */
    ) g" R; U2 J% h! ]9 W1 E; ?' `2 a
  11.     0xFF,                           /* 擦除后的数值 */" k8 q. Q% j: ]3 ^* K
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */1 `2 \; `# k- j) p* ~: T
  13.     0x00000000, 0x00000000,+ F7 I. Z6 o; G  U$ L+ p
  14. };
复制代码

6 s! L# f# D3 R2 d注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:4 X* ~* V* l& J9 t

: f/ d3 V; x" a/ C4 E" d6 P

; X  a) y: h) x% ~. @3 ^5 x; r, N6 L1 k" b1 o$ P
85.5.6 第6步,编程文件Loader_Src.c的实现
; M+ [6 x$ m2 }( S1 n' n' O' m下面将变成文件中实现的几个函数为大家做个说明:# A1 g6 v1 I' R0 B0 d/ G/ ~9 p/ b$ f

! O, C) p  V  l' ~9 u, ?  初始化函数Init
# t1 Z$ f! j$ B0 C9 j8 D2 q
  1. /*
    4 b' k/ ^$ h* o3 q7 h( N
  2. *********************************************************************************************************9 ^6 j: }4 t; k) ?0 q
  3. *    函 数 名: Init
    3 K2 {) a: F0 {0 M4 g9 u5 h
  4. *    功能说明: Flash编程初始化
    ' B) k" O) j/ w4 p
  5. *    形    参: 无
    7 T' P; I- r4 h- J" Y6 W* z
  6. *    返 回 值: 0 表示失败, 1表示成功( M4 e! @4 Q$ f# S) w5 k
  7. *********************************************************************************************************
    % I2 H. x* p8 \8 V) O; o
  8. */
    - w  W5 \  U4 c  d  ?
  9. int Init(void)% \$ ~/ G; X7 h1 F( S4 N# m; F
  10. {     g0 f7 ~; F( z: m* E0 c" C
  11.     int result = 0;' Z5 h* h0 ?3 J0 w- |
  12. 4 l6 K! D4 H/ {+ L8 a3 O
  13.     /* 系统初始化 */: H6 J" W1 Q2 o9 L+ y( D' @- t
  14.     SystemInit(); 4 N% Q; G6 \; n7 W. n
  15. " ]) ~! u. ^) a5 h" o, _
  16.     /* 时钟初始化 */3 _# z6 f# X* u. ?2 d
  17.     result = SystemClock_Config();
    % P$ C8 a6 ?5 [: ^: Y& H9 S( m
  18.     if (result == 1)
    % s" I' d2 u8 V4 [
  19.     {2 z0 Y# {1 b2 E+ H+ V3 Z6 X
  20.         return 0;+ w. F7 I3 r% A! ?2 s0 i5 L( C# C$ c
  21.     }' ]3 R3 I" y! o8 ~0 l9 Y  H( [
  22. , K& m" K9 n+ q) U1 K. S
  23.     /* SPI Flash初始化 */
    ( j$ s6 |6 S7 C! o5 {. Z
  24.     bsp_InitSPIBus();
    ' {# N9 B. W5 W, ]! ~) h
  25.     bsp_InitSFlash();0 G4 R4 |  o' S* I% Y
  26.   s/ O9 [( {. {2 K$ P4 @8 R, l# w
  27.     return 1;; L$ J0 q( `& R
  28. }
复制代码
# @& W3 U+ T3 t% L
  整个芯片擦除函数MassErase; x4 Q+ D& Y; Q9 ^% D
整个芯片的擦除实现如下:8 Z2 r: \6 h# `
8 V6 y4 o2 B" D' o9 c4 L
  1. /*
    / e( m3 C) Z) w+ `4 ?( d8 u) X
  2. *********************************************************************************************************4 C5 _) T7 U9 ~9 i6 E
  3. *    函 数 名: MassErase& p) X7 k0 U* o3 |
  4. *    功能说明: 整个芯片擦除
    6 Q8 r7 {+ o# ]' E, H$ v: D
  5. *    形    参: 无3 v: _9 `) M0 g
  6. *    返 回 值: 1 表示成功,0表示失败& W* m3 k4 D& |( n* q( ~9 U
  7. *********************************************************************************************************; P8 n  y* o4 N
  8. */
    3 E3 G, S; U; ^0 h2 h
  9. int MassErase(void)& Y. k( ], S% l
  10. {
    . W$ }6 `) _4 l8 H3 W3 c
  11.     sf_EraseChip();. c& ?& z* E' O/ ]) f1 }
  12. ) i( }& a( p- H6 b+ R( r
  13.     return 1;   6 ~- c. b+ \% P" ?" y
  14. }
复制代码

; u1 V8 f  E5 F& H! e# i' T  扇区擦除函数SectorErase# x, H6 ?: E' h3 u8 Q6 [* W
  1. /*9 Z9 F0 L8 c" H" w. |6 U# M
  2. *********************************************************************************************************$ a- M, V4 x; i1 i# k
  3. *    函 数 名: SectorErase
    * ]- ]1 |1 [8 N0 R
  4. *    功能说明: EraseStartAddress 擦除起始地址
    ' B9 _8 r/ A" n4 p% Q
  5. *             EraseEndAddress   擦除结束地址- [' C$ G  }# e! t( _, p
  6. *    形    参: adr 擦除地址7 z0 o0 `) B- B0 T, l" n6 @0 @
  7. *    返 回 值: 1 表示成功,0表示失败2 n9 E' i, @" N# F# }/ ]% W
  8. *********************************************************************************************************
    9 `9 v5 M+ q2 {5 I. q8 ^) k0 w% `
  9. */% E& A' J  z  J2 e( p
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    " D; e% B& p2 a
  11. {
    ! h# Z1 n  w# l. f  a
  12.     uint32_t BlockAddr;
    : D) w9 \' p! @% e. s$ ^, \

  13. ; D2 q, x1 W8 C0 {* Y$ ]2 s+ {
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
    " P7 H% |1 r5 ]
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;0 C- p# D/ y; m4 @, Q( `) z4 l
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */3 j0 X. Z8 r- J' J5 U

  17. " {6 G; s) t" ?. A3 T4 s" y2 ^
  18.     while (EraseEndAddress >= EraseStartAddress)- R" N3 Y3 N3 i7 `6 N
  19.     {
    / P4 `1 ?% ]4 d; i
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;* _% R/ L" _! c6 a7 S

  21. # C; W  M% N$ n+ Z3 ]) P
  22.         sf_EraseSector(BlockAddr);   
    7 p+ D: y4 l& g) d/ }0 N& p
  23. ) S/ ?" k+ i; i. L- Q5 X
  24.         EraseStartAddress += 0x1000;; G+ R1 k, K9 ]0 Z& g/ o
  25.     }( C$ }, J& r! l1 k( Q8 t+ W

  26. & o4 s; \' A  o8 Z% c; ~
  27.     return 1;7 w/ g2 T+ l, F
  28. }
复制代码

8 Y4 i3 i; B& M8 V" S! a8 Z这里要注意两点:& y7 E; v# i, e

0 I: a5 W0 b: A/ T. O' o(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。" a8 g, e& e; s
7 Y4 n0 d/ u$ e+ d
(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。. W9 [; m9 I3 g5 \% D& M( a
( G  x, U3 i9 U" R
  页编程函数Write/ T* b  j/ q/ j$ n
页编程函数实现如下:+ y3 s, D! T; {4 y3 Y, z, S

9 t5 @' S' [5 A5 J
  1. /*
    3 d5 B/ R6 j1 y* V, G& U
  2. *********************************************************************************************************, }: X& H' X; p: X! d9 j
  3. *    函 数 名: Write/ |$ Z4 ]3 j( I( s0 B; A$ S4 G1 ~
  4. *    功能说明: 写数据到Device
    ! c! P5 q  Y1 q" m6 f" o
  5. *    形    参: Address 写入地址/ `3 ]  D& V% Z, ]2 W
  6. *             Size   写入大小,单位字节
    ; H9 ?# g- {$ }: x( K7 ?5 Q
  7. *             buffer 要写入的数据地址
    & e' A, H1 L0 t" y4 M9 r
  8. *    返 回 值: 1 表示成功,0表示失败6 r  K4 b/ E. z5 e
  9. *********************************************************************************************************
    0 d. F* j. ?. z
  10. */: P2 V$ S, n, X8 c
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)0 J7 ]6 s9 G* {$ K
  12. {  
    - M0 W( O, A5 s; Z$ z. N
  13.     Address -= SPI_FLASH_MEM_ADDR;; i+ \0 y  S' R+ m* F
  14. 9 x" K. u( w8 d& w1 _7 g# c7 h' r/ {
  15.     sf_WriteBuffer(buffer, Address, Size);4 D& S. t9 ~( I) d
  16. 8 h1 L6 T' d* p3 C3 D7 t; G$ R
  17.     return 1; 6 l' ]* {' b5 w+ D) Y( P5 U
  18. }
复制代码
" W$ k( g6 L8 M$ C( |, Y' Y: H" x
这里注意两点:
9 @3 t6 k+ X& G9 y2 h- ]* J0 M: q: w3 }/ P1 p% Z
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。7 e0 T5 M4 A3 ^1 n5 }) t$ `

1 v  F' }% I7 s(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
$ X, ~$ M" L2 G
' |; {9 X- v# W7 e6 J. k  读取函数$ k$ V" j% ?& ?3 Z
  1. /*5 }  p( X0 M& O7 r+ I! P
  2. *********************************************************************************************************
    2 `  U9 X6 _3 w8 K  Q& B
  3. *    函 数 名: Read
    $ A7 S  ]; @6 w, R0 I( q9 n
  4. *    功能说明: 从SPI Flash读取数据6 u2 W4 b2 |3 k2 z
  5. *    形    参: Address 读取地址
    6 f2 H9 V/ S% [, {5 ]; t, u) h
  6. *             Size   读取大小,单位字节- k7 H; A4 D4 d: K" Z& G+ @! e
  7. *             buffer 读取存放的数据缓冲" N) `  D6 q) D5 y$ V" e  w
  8. *    返 回 值: 1 表示成功,0表示失败
    " G1 \$ ^* |( R  e" {) ^
  9. *********************************************************************************************************
    " @. _* ]% Y* T% \) x
  10. */! N( m4 t+ G, z  V  K4 d8 x
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    6 c8 A1 y7 [3 b, m6 A" e4 ?) N$ x
  12. { + R4 }, g; e4 [% c) d
  13.     Address -= SPI_FLASH_MEM_ADDR;7 A; e( j4 n& t6 R8 }
  14.     sf_ReadBuffer(Buffer, Address, Size);
    3 P7 I7 Q; K. h$ [% l

  15. 4 W4 l/ V: x0 Z# a, \
  16.     return 1;
    " C  T8 k" C( ?, r9 r! B
  17. }
复制代码
7 R2 [0 m8 |, g# E3 K) r
  读取和校验函数
/ K1 Y3 N, j4 O  c: y; ~7 d我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
0 b; k7 x9 n: V0 h! e# ]
  _5 T- Q; U: X/ V6 m* E, t85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
) {( T: D4 l" Z4 d+ p最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
( G$ o8 O6 k' K$ n/ ?
( A; u' g7 {/ I' J
  1. /*
    " d; Z* i  S& J; V( y$ `' t" m
  2. *********************************************************************************************************' `/ R8 B7 R( [9 Y2 O  Z- w4 X
  3. *                                时钟,引脚,DMA,中断等宏定义9 g' P9 P& @; `! v# Y+ f( w- x
  4. *********************************************************************************************************; w6 @+ i. H' c, Q& B
  5. */
    4 ~7 n0 t) [( u. f
  6. #define SPIx                            SPI1
    ) r  z2 g! v# ^" R
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
      _- @8 A5 b3 `$ L# \9 X& o4 L% l7 \
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    + \9 @+ o; Z- t; y; [4 b* @

  9. / x3 _0 D8 I: \
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
      m: C! R  @! |4 c6 a) O
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    * b* P& c; @$ }

  12. $ ~8 n3 C6 L- [# f
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    8 J9 i; x6 R2 g* H) F! M/ t2 w
  14. #define SPIx_SCK_GPIO                    GPIOB2 e0 i: }4 J' g0 |
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3+ n9 |; m4 W3 I3 m6 J
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1$ F% g+ K  O2 r6 ?1 A: Z/ b

  17. # T& G+ N8 L% r+ e% C2 ^6 o, Q7 v
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    * \( y4 U* X- Y8 F& F
  19. #define SPIx_MISO_GPIO                    GPIOB
    , e! d- H, K+ k1 Y$ W
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4: h/ b" n5 v; L' A  x* X+ P5 q
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI12 |! l. }0 G5 N$ [5 S% I2 w

  22. - g* I8 n0 z! ?# Z7 N
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    " r# Z, p- Q- S& z2 }
  24. #define SPIx_MOSI_GPIO                    GPIOB' o; c% Y9 w9 d, K/ U' T# J4 T
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5# g3 a: {" X9 k% J  e1 o
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码
) P% {8 T9 I- p( b% W' G
硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:- p0 ?1 i/ Z0 D, a0 ?$ {! ]  V& j

% e7 D  S2 H+ U8 Z1 F/ ?4 u2 R主要是下面这几个:
. ^  e5 B# b1 B0 ~8 g/ N1 N) D
* H* g% {, Z; @3 d( s, I2 P1 q
  1. /* 串行Flash的片选GPIO端口, PD13  */. I! \8 j: f# l+ a9 x8 o- I
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()  E" h+ ?8 j& S, g( s: y
  3. #define SF_CS_GPIO                    GPIOD  r) {  c5 K5 k1 a+ U
  4. #define SF_CS_PIN                    GPIO_PIN_13
    # T7 N, j6 ?, l
  5. / D  o. m% g8 d) Z) [0 C$ T
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) " e) D0 O/ W. F8 S2 A% ^1 a- W
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN' z* {- M# }- z1 M2 ^2 m
  8. - B8 }5 G# @1 d2 t! ]. |
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */
    * l1 J( t. V& [) w
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */4 `, z* x8 f  t+ W" W: I
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    ' U9 b  L8 S" O( E) b7 E; Q  ^
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */0 x  @1 B( y; w; ]+ m
  13. #define CMD_WREN      0x06        /* 写使能命令 */3 h, v% Z  R  B; _7 z0 Q3 X( Y
  14. #define CMD_READ      0x03      /* 读数据区命令 */8 Z  ~6 R8 n$ K4 j0 S. P
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
    4 l' i( n3 }9 W+ N
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */( |# g6 b0 k3 r5 Q3 F
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    & A0 T' a7 v. I; q8 U
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */# E1 |0 V# G% j1 X7 p% E
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */: T' ]+ I3 c# H! Q* c# K$ s
  20. 8 E/ c3 k- i, t0 v" H6 X. M
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

& \2 J6 [9 W$ `4 R6 ]" ~85.6 QSPI Flash的STM32CubeProg下载算法使用方法
" j; e* ?) S3 S: P7 |+ u% T编译本章教程配套的例子,生成的算法文件位于此路径下:) v% g" M+ \+ S+ z
1 ]! `' k+ C: E1 U1 F" f/ D
5ec6d402a7646232a55f4834cd00ac0c.png
2 H+ r& k9 z' X7 Z3 A  Y

, P  f4 o- F7 C$ U. H85.6.1 下载算法存放位置1 @. S5 [5 Q5 }( a' h4 |+ s" f  j- u
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:5 ]3 Y1 s* B( V# q6 T
/ V6 P$ f* ?4 }& a) O0 j
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader& m# t/ u- z1 ?
3 d6 t3 q# K# E8 [- g
2609073778af5845a869911ab36624f4.png
) F/ A! t1 |/ u. t4 S
; i$ T8 q9 e7 e0 [" G
85.6.2 STM32CubeProg下载配置+ i  ]  r- s5 e; a% @' q& p# v  V& j
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。! B4 X) i) N0 T6 n
; Y* j9 E. e" z1 M& e* k' t+ S
d4e256633eafdbf4bcd8cce6e3b5909f.png
: T: e" R0 H$ |) [+ C/ q* C

) \3 x; S2 H5 g) k. p, C0 {点击Connect后效果如下:
# ~) ?1 q/ x) l+ h$ |* c& i7 e. g' y7 h3 f9 G, r& i
* U1 F& y  G9 g- H) @4 D1 G! v  D, w
2 ]  G" m3 e/ N# D" }1 M
在这里选择我们制作的下载算法:! t9 c8 @* C( f8 u

4 z' @2 w; T9 p6 X0 b
d4fa28f64be2de98e04f3188eb2fe09e.png

( R  }& w6 V5 U6 l5 s3 j. L$ d6 Q" X% j2 l
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
+ O" R7 I; `2 a
/ M; o' g* W. q/ z4 P
842518a7cc1dc569598d372132b2ee58.png

3 ?* d  b! l5 l" K
) I& P& }6 e/ Q9 ^下载完成后的效果如下:
" c% d7 ~" q  Z( `! S
( E, |% u& L; w$ z) z* ?# H4 k
68a92f267829ca77f125db982d6c0479.png

# ?1 [8 D/ \% w8 b$ l* T- y' B' M. L  |% a. |7 E" ~4 W: r
85.6.3 验证算法文件是否可以正常使用
. v2 x6 _  r. v  B9 E为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
' N) R  A: K/ a3 t# S$ o1 [, R  z% z+ J3 ~2 G6 a
566d2fba421c710da70e5656c6934466.png

$ m" i1 c% r5 }" M& z& h6 V4 H3 Z' q' K. F2 B9 ]- A, {
85.7 实验例程说明
' x. i( v; W* d5 d: b* k: {1 y
本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。' ]; Z' ~$ r6 A- \& q& Z

9 T, I* R8 w( M: J7 S9 U2 a编译本章教程配套的例子,生成的算法文件位于此路径下:: x2 j3 X: w1 i0 z  P
% f: }* b! h" g" t2 s! K
6108d1ff7e349f2aa268ee31804c57ca.png
6 A: M! e: \+ w- Y

6 Z! i( d* \" ~85.8 总结& m/ a- O! m! t2 g) b) m6 P( D
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
8 y  p0 h: A: }8 p( L8 w& [5 d- r3 R' X

- `, H1 `0 U0 |( j9 ~8 F0 `
) h, _6 \+ M9 q; J! n; C- R7 g
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
赞 收藏 评论0 发布时间:2021-12-19 15:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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