85.1 初学者重要提示
; }3 q7 J2 R9 @0 i# K QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
, X& T( t5 l% h- K& b( E& L STM32CubeProg下载算法制作和MDK下载算法制作基本是一样2 S. L! T: v, E7 E% F" S9 P
本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。6 ^1 ]7 N% ~$ @7 l' [
85.2 STM32CubeProg简介
7 U) X% q" @- w) JSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
0 F+ `1 ^. D/ W0 E4 H. N6 L) Q
' O5 Z6 R& X7 B. Z. |
5 s* G; N4 Q- X1 p9 t4 c: t
$ B; K9 E. y$ `4 F2 b$ w8 K85.3 STM32CubeProg下载算法基础知识
7 q, e/ W' m. ^+ uSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。# m0 Z6 U7 v( v6 q4 P0 w
# C) E+ y- E: d3 K6 V
85.3.1 程序能够通过下载算法下载到芯片的核心思想
8 B2 s* I/ g) J认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。# H& _; U1 p) G9 K K
& O3 V2 ^& Q' ~% U
85.3.2 算法程序中擦除操作执行流程) b. [ b6 h" b. |* H
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
" W% N1 w) X3 V/ F' |8 h/ r& `6 @! }* ]2 [; T6 t- f
擦除操作大致流程:0 Q' `+ |% y1 Q5 s) ^5 C/ n
9 ~- ^! Q+ `; }( O" d4 C
$ a* P$ B6 K7 @" B/ M5 x
! s7 D* T- ^5 K5 a- } 加载算法到芯片RAM。
0 l, A5 x+ l4 x8 r5 ~ 执行初始化函数Init。; M, X4 e- T, R1 o
执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
9 Y! p2 F) s+ r! S 执行Uinit函数。
' `2 }! [5 V, l0 A) k4 T h+ C 操作完毕。: s; `& n* y8 J
5 B8 z8 a2 t1 y% Q* F85.3.3 算法程序中编程操作执行流程
8 w& d7 E" f \# j$ S: U注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。6 j8 _4 { W9 U( G3 Q
- l, A- a$ E$ e9 }7 c编程操作大致流程:$ Z: [- g- B1 l' z% q8 o H0 P
" {9 U" ]( e7 Y6 Y6 t# V) O
& Q+ k( H7 z) U, i
/ @) Q, G/ e5 D/ s4 _ 针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。3 U# u7 r- Y# t$ M2 r
查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:; e p0 \0 o6 Z7 N0 `/ \
加载算法到RAM。
3 r9 I a9 v# j+ M% A4 p1 k% F$ I 执行Init函数。
( m9 U4 v% e) z# H* S 加载用户到RAM缓冲。
% B P2 ]1 ?: q; j 执行Program Page页编程函数。
: e! N7 b3 m: a& z1 @6 @ 执行Uninit函数。
1 P. [% b# w( ]6 l7 ~& T 操作完毕。, T# r- \% L, J' W) h5 {
4 `9 G+ t) ^0 p* s- a85.3.4 算法程序中校验操作执行流程
1 B7 W* `2 R; e) X) [; ?/ ~注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。( u" }* C% L T% x+ W0 G
/ R4 ?' o6 b! n# E
校验操作大致流程:6 j% {! T1 K8 o! H
" K& d* J; O' T2 i" l7 Y. z' K/ I Y
# x2 Y! M8 i" I; i+ B
0 y6 s/ _/ \6 W! q0 r 校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
) X, G( s# r$ L* ?) Y# D- Z; U4 W 查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:# L! e/ W) {+ e0 a3 j8 G
加载算法到RAM。; v1 j1 f* k5 ]1 s6 ], f1 F9 `
执行Init函数。
a0 v+ F" U- H8 I+ c G 查看校验算法是否存在* r+ J" I- ?! y& p" c
如果有,加载应用程序到RAM并执行校验。7 B2 h# E2 F5 e) l1 y' V9 N8 T$ z
如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。4 e4 c+ l- @* g& T9 N/ |4 j
执行Uninit函数。 e6 D1 Z1 v& |3 b9 s: J
替换BKPT(BreakPoint断点指令)为 B. 死循环指令。 v2 y) p# m: d
执行RecoverySupportStop,回复支持停止。
9 F4 T$ X! X3 m' ~ 执行DebugCoreStop,调试内核停止。
+ B. ~. u4 c- S( y 运行应用:) B" f1 ~4 m4 L. x4 [& j! ~
执行失败
! ?4 z+ V3 D) X& q2 b6 c 执行成功,再执行硬件复位。
6 Y0 i/ R& _2 S 操作完毕,停止调试端口。
2 Y, k' T# g+ B# {! U$ @& o, g6 U9 q3 Q8 d, A
85.4 创建STM32CubeProg下载算法通用流程
2 |" U V5 \* |; z/ F5 g' r$ p下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
) k% S" S1 g$ f4 ^
* C- \+ Y; W5 J4 I+ B7 X5 i! `8 \- n85.4.1 第1步,使用STM32CubeProg提供好的程序模板
( J# x# @" y! L: o7 K位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader& J2 Z F9 r2 T+ e3 m
2 C1 K" u) A; ]
* w- i9 f# \4 g( @- w
2 m3 w$ a5 d. ^( ]3 H* y3 e! i以M25P64为例(注,其余步骤就以这个为例子进行说明):$ k& F3 A, k" G0 _
) t9 P8 x' g) b/ E+ z
9 q7 m" s5 H, I$ S C) _ d P
0 r# I# u7 o' p2 u85.4.2 第2步,修改工程名
1 i1 a. l. j: z) s% @% }; t! l) `STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。0 \: U/ }4 z' ?3 J8 T
, m4 C. W+ n t2 S' p7 O85.4.3 第3步,修改使用的器件( B9 s O1 s$ H5 V4 B4 @' Y
在MDK的Option选项里面设置使用的器件。* x) W# ]$ Z6 g; c% D% I" H8 z
" e! C! r7 U$ P; t j3 A5 q. a2 o) L7 p/ \
& _! A9 g: W" _85.4.4 第4步,修改输出算法文件的名字
. ]0 _- m/ J) ?2 P k4 {7 F这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
1 A) K6 l" |1 Z6 ?
5 S7 C3 J! W7 H: [) g# V8 C6 y+ T3 ]9 U4 g3 t3 h; O; k
. y& @* }( S& {" b注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:% }& x) k* x/ {# x
' h" d: {$ o! ~! j0 b# A: e
! s4 q- Q) L) a" j' O0 d1 @- o* d' W
85.4.5 第5步,修改使用的库文件和头文件路径
! g7 t. B+ i3 {8 b* Z# c4 i4 S根据大家使用的主控芯片,添加相应的库文件和头文件路径。
$ I" t1 p" v$ E; }! o T$ W- F; ?4 Q/ c/ ^
85.4.6 第5步,修改编程算法文件Loader_Src.c. g% r" d* E! o
模板工程里面提供的是M25P64,部分代码如下:
6 ]# X" L3 O* q0 h7 m8 F) g3 x& @
% X. L) S0 }! F# h4 v# g& [- /**
& _$ ^6 _" [$ f' j; O - * Description :
6 V @+ s" e# }8 D: L$ U1 f+ ` - * Initilize the MCU Clock, the GPIO Pins corresponding to the8 i# ?4 a$ m6 c0 E1 H: b6 r( d
- * device and initilize the FSMC with the chosen configuration . V8 H- i$ {9 ]* ~0 d0 ]
- * Inputs :3 }5 [* p2 n+ H% N/ N
- * None
7 z# A0 a1 }$ U - * outputs :) z' g9 M7 g$ `! r# N
- * R0 : "1" : Operation succeeded
) [' L/ Z9 q9 u( j1 }) _ - * "0" : Operation failure
X' j4 t/ X# w$ M: @ - * Note: Mandatory for all types of device
- e# M/ U6 ]) y - */
" B7 V$ b7 W" J9 ?6 V - int Init (void)7 z8 K* ?0 N: L9 {/ F: N
- {
0 J- Y# d$ u( R6 U6 q - SystemInit();
* n& P) |2 ?- E$ }1 C% c5 y - sFLASH_Init();
& j1 i3 N7 g6 ]- f* w: z - return 1;
' T! K1 u+ J4 r! A7 s* O7 c - }
2 |# E$ E4 R3 T7 `. \2 _ - 1 V0 j7 [! \5 q( o' b7 v+ `
- ' ^6 s- @8 j& b. Z+ b$ d
- /**- t5 _% A3 b7 H( U/ s
- * Description :
. J9 R" y' M9 V4 r$ j+ j - * Read data from the device
3 o; M9 Z4 U7 F# t- m I - * Inputs :
, N! Q7 K8 C* [3 v - * Address : Write location, @! f; k y% e: N* M
- * Size : Length in bytes / Z* n, D+ ]# N0 \- @
- * buffer : Address where to get the data to write( ~9 @- w0 W0 u8 j9 v' S6 P4 D
- * outputs :. ^* u: L# g/ X
- * R0 : "1" : Operation succeeded# V' Y- _$ t- R' C: W; M
- * "0" : Operation failure
: e5 {" M7 _- h4 @/ T6 _ f - * Note: Mandatory for all types except SRAM and PSRAM # T% v* G: b s/ L
- */
% N4 p- C0 w+ G/ T" M- o' ~$ @ - int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
0 m- o8 k- o; Z4 r - {
; n2 F6 T6 c7 L# f% z' b - sFLASH_ReadBuffer(buffer, Address, Size);! X: s+ w. r- z( E6 w2 [8 r7 B6 Q
- return 1;
5 _' G4 e7 z3 G1 f; @+ g8 X - } 7 B3 ]4 k0 E$ ]9 P/ _
- 2 d, Q/ F# j. W5 d0 T
/ D$ {) D& Q4 p l- H+ s- /**
) \" N! G# u3 U0 Q - * Description : g( t$ M. p4 G
- * Write data from the device
3 s) p) v v$ `9 `6 \( j' r4 @ - * Inputs :
* H a+ J. z1 r: g- \2 V; \ - * Address : Write location& U( Z6 I& k8 V! N, X8 h* H7 U
- * Size : Length in bytes
. u" l: l/ a4 a( _) ` s: s$ b Q. } - * buffer : Address where to get the data to write
1 X* L5 B4 `8 S5 o - * outputs :
) D% ^4 s* V$ ^' v - * R0 : "1" : Operation succeeded; Z9 e3 I2 ]8 V9 K
- * "0" : Operation failure$ |1 ` ~; h3 @/ V N# N5 }* p0 t
- * Note: Mandatory for all types except SRAM and PSRAM
( o* I' m4 J' y5 s: n G) { - */
7 M% L& A4 D+ `$ Z7 c7 I - int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
0 M. ]$ M A7 Y$ T: z2 P: V2 k - {9 u# p3 k- \# Q& L5 y/ {4 j- [
- sFLASH_WriteBuffer(buffer, Address, Size);! b& p3 u, L% ^' F7 X
- return 1;
5 x/ w9 a) f$ B9 o- z3 m( J - } * i0 E5 W: J% z9 F- `& g
: G( x5 C' |# F8 o; k: [* R E- $ \9 V8 Y& _' V( ~
- /**) M$ g! p' z: I
- * Description : ]" o+ T m* U* j
- * Erase a full sector in the device. ~. }0 M5 T( M
- * Inputs :& X2 _9 t0 p) X/ b& `! ?3 k
- * None
1 h! m2 [# U) p/ q" O, P$ ] - * outputs :! B! U" r& _# b: o, _' `7 h s+ n
- * R0 : "1" : Operation succeeded
/ i+ u. F' s- r( G: S( v8 A - * "0" : Operation failure, D4 b6 n" d5 k9 c# o8 T% O
- * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH' e1 ?0 L# ~8 ~& u$ v7 P
- */3 ?4 ]# f0 L9 F: C' H5 F* Y( g5 K
- int MassErase (void)
1 U( f0 E$ J; r [. y5 m - {
* J' C9 O+ Z7 q Z - sFLASH_EraseBulk();
; S( O8 U5 l7 Y6 s' X; A7 u, r - return 1;
7 `$ O. H# e+ r - }
复制代码 ' ^2 T. L; N7 ]9 R
85.4.7 第6步,修改配置文件Dev_Inf.c: |. ?5 i( c: B- l) R
模板工程里面提供简单的配置说明:
" b: c* c" I6 a" f; ~5 W
4 [" [# A1 R/ e2 `% B) r- #if defined (__ICCARM__)3 d$ n1 f S& Q1 @+ N2 u! x2 d8 z
- __root struct StorageInfo const StorageInfo = {
. s' g: {- T8 g: s8 q - #else' M& H g/ Y+ h
- struct StorageInfo const StorageInfo = { l/ ^/ j0 t0 _# q8 f" N$ Q
- #endif: }6 [% j& H' y8 a
- "M25P64_STM3210E-EVAL", // Device Name + version number: z+ }4 G) {5 f' t* L; O* b6 L
- SPI_FLASH, // Device Type
% @( z7 ^6 Y K" P - 0x00000000, // Device Start Address
. i! ^4 x4 w& v) P - 0x00800000, // Device Size in Bytes (8MBytes/64Mbits)
u6 I# i' g. W4 G0 h% f6 [: O - 0x00000100, // Programming Page Size 16Bytes
6 G( n: f/ L: u/ l% u: Z" o - 0xFF, // Initial Content of Erased Memory' H- p6 {5 t, \) S
- // Specify Size and Address of Sectors (view example below)
1 E3 k0 ]7 A. A8 ^8 ~$ r2 y' B - 0x00000080, 0x00010000, // Sector Num : 128 ,Sector Size: 64KBytes
7 e9 k! g/ Y) m! L) ^/ {, B - 0x00000000, 0x00000000,
1 l ~9 L, A1 b2 j& t3 N) f - };
复制代码
! e* }, F- x f3 q, e+ Y注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
: @0 n/ y5 j5 `& M6 x' N& S$ c, k' x U+ h- O+ f2 S( {) z3 O4 B
85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
. |, L8 x7 i( `% k$ @C和汇编的配置都勾选上:
8 t* ^: \+ j& w9 l5 b' T
. E' z- |6 Q% b' d
- e8 a! N# I ^, [ q$ J/ a
* o* g0 Q% z3 R& i* x3 [7 x汇编:. ~8 M; _- ] B- h9 Q% i: i8 b7 I
# X! F( t, \3 E1 `$ h( B% B( e
( U! `8 E! R* j/ @& y% ?- `7 C
( k- n/ F" Y5 o: ~3 F1 h* P6 n8 t0 V: z
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:# q4 E, j* A% _" x" L: E
5 N: O/ s+ r% G4 V( T, N X$ U
(1)加载以响应运行事件。7 G' W5 K0 L: E
! O. |' c1 Y: r(2)在不同情况下使用其他例程的不同组合加载到内存中。
" |) Y c9 R, V+ u- B: L' m1 @: K4 w
(3)在执行期间映射到不同的地址。
/ i% {+ @$ X' G/ O+ t- c! f. S- y( C! ]) y2 ~
使用Read-Write position independence同理,表示的可读可写数据段。) x8 D6 |; i/ f1 _8 T1 R
7 g k8 b9 u; W/ m& m. X4 \* K( ?, I
85.4.9 第8步,将程序可执行文件axf修改为stldr格式4 ]: [$ E, a1 P. L2 K3 W5 U
通过下面的命令就可以将生成的axf可执行文件修改为stldr。" T* A8 \5 C: {# {% z
4 @- t% Q) ^/ g. C6 `$ a
: r8 B* p d' r
9 |: g0 }0 ~: m6 u( A85.4.10 第9步,分散加载设置9 a9 U& n1 b" k! a: U' Y. n
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
2 w6 r/ D5 q' \5 B( M. O' }* V, \: P' F$ z& y4 B; Z3 V
" z0 T3 ^% W/ U8 X
" d. \ ^% p0 a& l
分散加载文件中的内容如下:
: r4 m& r4 K& z$ t# F1 k9 R0 u/ W& B& m
- FLASH_LOADER 0x20000004 PI ; FlashLoader Functions; J" W3 t, S6 V3 t
- {- ~, f* ?: i* c
- PrgCode +0 ; Code( p8 w$ m: `8 s) | o8 m
- {! D- E9 R, Z M# q* P" b' C5 [0 o
- * (+RO); z; f. l7 K9 T7 s) j
- }: V4 |6 H$ B6 {. m9 s) E
- PrgData +0 ; Data0 ]- {& F; h7 v
- {
# p _0 M0 w# J& `7 J6 x: t; Y - * (+RW,+ZI); d* }9 o/ I( L7 j: G K0 Z% m
- }
& m1 u6 P9 K. m0 h - }) B: V4 s( o# P" c$ Q4 }% C
- / O! F. O4 _, y) i( z/ w
- DEVICE_INFO +0 ; Device Info+ Z ]0 F" n+ c, X! y' i
- {( L, S1 I. ~0 R" |5 @0 t k
- DevInfo +0 ; Info structure1 I4 n9 H6 z5 a
- {' R `3 G5 Q2 e' y$ w8 d
- dev_inf.o
9 E! }: w* S% j6 o$ R- x- W! v - }
; K/ f1 e- q1 ~ - }
复制代码
3 p4 E9 u3 B% O7 ?. ?+ F+ W这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。* S% M* O% n$ i3 o
* X. s; h% O+ M# G, H--diag_suppress L6305用于屏蔽L6503类型警告信息。
' h$ a; R+ q* ^/ E% B7 l2 I1 ]* ~) G# o3 r3 _9 h9 ?
特别注意,设置了分散加载后,此处的配置就不再起作用了:: U9 b2 `' `) R, C" J
7 X+ n8 _0 a. v. h L# b4 y/ w% K8 F! @2 X
6 ?" Y0 U/ e8 {* |0 ]6 u3 s0 R85.5 SPI Flash的STM32CubeProg下载算法制作3 i0 a/ g4 ]) B5 Z$ j6 z" x9 s1 t
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
4 S% M$ Q/ X9 W# i
5 ]' x) a2 _% M9 X/ N85.5.1 第1步,制作前重要提示. A5 D' T* w% {# V
这两点非常重要:" ~( Z) k' L! X! F' N% U
5 o$ ?$ W& K4 Q' Q; m# _
程序里面不要开启任何中断,全部查询方式。
" G! E, e: e7 e" J9 D$ c1 I HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
" V$ _6 L* U4 ^( ? R, v5 _# I1 X/ ~ l
85.5.2 第2步,准备一个工程模板, n6 C0 T" [. ` U1 \- O n) V% E" s
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
) g7 z' M( M& o, J. m9 @
& Q6 M( N9 {7 C3 D# r1 g, A! B2 D1 g; z3 i
/ P# r" f2 T8 b6 r6 n85.5.3 第3步,修改HAL库 ^8 L: |' J/ L$ i$ ^1 e* J q
这一步比较重要,主要修改了以下三个文件:/ g0 a g. e- q0 M
( {, t* D: G7 {9 o! k b7 W$ `- Z" p1 A8 Y1 l4 h4 T A( k T+ ~
2 D$ s! ?' e* o主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。+ Z& S! [ E) O" d
8 i1 J8 x i- e( z5 M, m R4 q1 F85.5.4 第4步,时钟初始化
8 W: Y2 y$ `% }- g- `7 W% j我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:, X+ R4 X9 R5 \7 z8 D! K8 N0 Q
) r7 r, h' p) E9 z& n
- /*/ ^* Z( {8 g3 B5 |& Q
- *********************************************************************************************************
! J& ~/ ?5 D8 v2 o, Y - * 函 数 名: HAL_InitTick2 T: X: a1 R0 R* X: _6 {
- * 功能说明: 重定向,不使用% t% c: s& X6 X- V0 i8 } c
- * 形 参: TickPriority/ P6 c' l: E* y' P
- * 返 回 值: 无
: t$ r S* ?0 [; [" ]4 ?1 A7 H. [ - *********************************************************************************************************- I- F% J: R. y: j' w. v
- */' O' k$ y2 ]) y* b& m+ G# O) x9 n
- HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
' [; n% z) B% s1 i/ N; ~- H2 q - {" C% K- o* u! V
- return HAL_OK;
, z2 |3 L. I# J2 |+ u+ ? - }
复制代码
@( R& ^" j; q6 \* @- g T5 c% ]然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:) i s- B$ }/ [- Y- H {& L
F2 x8 h( ]3 A- #if !defined (HSE_VALUE)
/ Q: q" T% M/ a' [1 V - #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
! P$ z; H1 s, b% Z2 {* g8 M - #endif /* HSE_VALUE */
复制代码
8 S# m" v9 w+ N' D% m x最后修改PLL:
! }7 Q! S- C% ~5 D# d" f u2 V3 \5 Z3 S' k; ]8 [9 \; L
- /*7 M* r6 f6 D& @9 f3 ~$ q
- *********************************************************************************************************) V V* U9 D& `0 u* P. O! J
- * 函 数 名: SystemClock_Config
8 K% P# A$ K' J% o. t - * 功能说明: 初始化系统时钟
( I, F' j& H0 N8 A/ K - * System Clock source = PLL (HSE)9 ~0 J+ w- z) [" w2 b3 E' u
- * SYSCLK(Hz) = 400000000 (CPU Clock)7 ~) W. y8 r$ \5 z' s
- * HCLK(Hz) = 200000000 (AXI and AHBs Clock)
9 g5 h M" }, g s - * AHB Prescaler = 2
, n7 R$ l# s% l6 p$ n9 ?& x - * D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)
- ^; V# V9 _7 ]3 G5 r - * D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)
) k F- c: f% v7 X) e" ~1 W - * D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
# l v; Q x1 S2 q" j& p - * D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)- ^: N' k3 Y- g+ d/ i: [4 z
- * HSE Frequency(Hz) = 25000000; V b& i5 e0 {
- * PLL_M = 5; c3 ~ s2 b. x4 i1 h
- * PLL_N = 160/ [! Z: k( h: N! e
- * PLL_P = 27 ^5 s6 ~/ b- a ]. r1 [
- * PLL_Q = 4* r( S* U. T5 A$ U- y9 f
- * PLL_R = 2* S2 q0 R" s. J; ^' j4 V
- * VDD(V) = 3.3+ a6 d c) c+ m
- * Flash Latency(WS) = 4
" f* E$ ~" p. A: n3 M - * 形 参: 无
. H n$ L: e# f9 m3 Q - * 返 回 值: 1 表示失败,0 表示成功7 j) \! h* R2 ^/ C0 u: v
- *********************************************************************************************************
4 M% L- F- \" |# x# S0 @) s O! b) [ - */* m: y- u5 F, R4 a- s
- int SystemClock_Config(void)
8 x+ g6 l4 D9 m" ~! ^ u' P - { F) j& r% F& _" j9 z- D" D1 R6 B
- RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/ |2 I( Q) b4 E6 P* C5 W/ I) o - RCC_OscInitTypeDef RCC_OscInitStruct = {0};# |# ?5 w6 T! }$ U
- HAL_StatusTypeDef ret = HAL_OK;2 l" L& Y, M* m y+ y* D
- 7 t( z8 X) L/ p7 G0 c
- /* 锁住SCU(Supply configuration update) */
" `( O" T% W8 k) f3 ?, T/ a1 [ j - MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
# F, D* F3 s' S - " E+ x5 m0 l/ T1 j: Q6 q
- /* . U3 g7 b5 l2 t2 I: X9 r
- 1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
7 w* d8 Z0 O, u9 q% \: a - 详情看参考手册的Table 12的表格。
' w; r% {) h9 T - 2、这里选择使用VOS1,电压范围1.15V - 1.26V。2 z- }9 C6 }& }% c$ T. s
- */+ w. @& C' h$ F4 S: g% z
- __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
6 q N% K* \0 Z
6 d( o4 h' T5 [9 G; R: p; o% }3 t* T- while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}/ c& [- n& N8 t7 @) d O
8 Q# ^5 h" M: c3 y2 F1 O- /* 使能HSE,并选择HSE作为PLL时钟源 */
1 n% T! o7 e/ L - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;& o2 k) [1 _+ Y# U; K
- RCC_OscInitStruct.HSEState = RCC_HSE_ON;; h1 C' l$ d$ h& X9 r
- RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
% I' B1 e$ B/ a7 a0 b4 V& e3 ~ - RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
5 S9 N s: I! H# i1 x' r/ y1 V& N" v - RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;" b, a* X8 P2 V. s
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;0 L! `' B- G! i2 g0 X
- 6 A/ k' {7 w: H3 j* C# v# D
- RCC_OscInitStruct.PLL.PLLM = 5;
2 ?" q0 X: D3 I; g' l Z& K/ } - RCC_OscInitStruct.PLL.PLLN = 160;; h/ C) G4 s k; y0 u4 P
- RCC_OscInitStruct.PLL.PLLP = 2;/ Y: f+ j5 O. N9 n9 N2 R6 K3 t
- RCC_OscInitStruct.PLL.PLLR = 2;
( j/ h4 }8 t% f - RCC_OscInitStruct.PLL.PLLQ = 4; 7 C- F. l1 S6 D) m+ q4 F
- " u$ w9 u4 \5 n. s7 S
- RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;* V2 G; p w# H3 s" e8 N+ E! {
- RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2; # m" F0 v1 W( `6 f6 u4 i+ G# h
- ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);0 S( q. Y6 r# l) j* R1 [; _
- if(ret != HAL_OK)
3 J+ i. P3 A" h- l" p0 [ - {
% L6 \* C+ u# ]. G - return 1; 1 z6 ~0 q3 T p* ]. v; b
- }3 ~. \% X: Z* n, F, J* N( `) r
- $ R( W7 I% ]+ _" s6 z" ~
- /*
" Z9 ? \5 J3 {+ B4 z0 c H- u, X - 选择PLL的输出作为系统时钟
1 e1 c9 Y; W6 E/ ^9 A7 ]( v - 配置RCC_CLOCKTYPE_SYSCLK系统时钟
' E$ v I/ _2 F% J5 E& k0 B - 配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线% ^5 E( a% A$ r& z, y, X6 h, b/ k
- 配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
& C* L2 w; g1 |1 ~2 h3 H# V1 s - 配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线! n; M4 ]5 h7 f* @# R1 q
- 配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
4 } t7 O7 [, Q6 O% g1 i9 n! P - 配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线 : G- Z# q8 G. _& E% U4 ]: C8 _! x$ m
- */+ E S* f# r$ t! T& C
- RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \. G+ [( r$ _& U, g/ q# x) T
- RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1);
1 f6 X7 }7 |! G
2 N5 p: i& d6 X" d. [5 ? r5 O: \2 y% l- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;! n$ Y6 y6 O. L0 Z% K
- RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;4 |/ o& w+ L c" W, s5 S; N
- RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;7 J% ]6 B; E8 E- O W
- RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; 3 P6 c/ ~, D1 c) q1 d
- RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
) }( [, x+ O" l4 r( [0 X - RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 0 Z- c$ G- Z6 U/ Y4 h* U5 D3 f
- RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; + c. O. O/ I1 ~' N
/ k. q* G8 q$ U: M- /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */) N% q a( M+ b# l, }: W8 T5 n
- ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);; H. \/ r6 Z5 o' A* t5 \
- if(ret != HAL_OK)- }& f5 S6 _2 q, ~+ ^' @
- {
2 ]- l9 v# j- a: u5 T2 Z - return 1;% _* ?3 ^; o, s/ D" J6 I
- }
$ ^- x( ^; ?" m6 X - 4 c0 z' I9 v/ R' e5 P% t
- /* B/ E$ D& C1 t/ ]' {" v) l
- 使用IO的高速模式,要使能IO补偿,即调用下面三个函数 4 k& K+ z9 B, }, y5 q
- (1)使能CSI clock$ W8 r1 `0 J: k/ ^0 l4 z+ d1 k
- (2)使能SYSCFG clock, s6 V" r+ b4 O" V4 V, g# @- e) r0 V
- (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0" B4 [. k- A& W5 u* |# K/ U; R
- */
; I, ^( b" Q( I7 H x$ n - __HAL_RCC_CSI_ENABLE() ;
1 |" q- X, j- h' m4 y b
. m3 P+ O" o5 ]2 x# Y! \, k7 K- __HAL_RCC_SYSCFG_CLK_ENABLE() ;
B S* X# s7 j - , e$ ?) }7 m* _
- HAL_EnableCompensationCell();8 b/ g0 p4 Z: L% A% O' h5 h' n
- 2 l" S+ f s, o
- __HAL_RCC_D2SRAM1_CLK_ENABLE();" r/ c' T' ]( O+ N! r1 }8 p% B
- __HAL_RCC_D2SRAM2_CLK_ENABLE();* A% A3 Y" n& T# w% F y5 I4 l
- __HAL_RCC_D2SRAM3_CLK_ENABLE();
! o& u0 W2 o/ _' x8 M; Y: _+ J- q; I
: _3 A5 ~2 M/ V3 q, W V- return 0;& t+ Q7 [) d, Q5 n, H! _
- }
复制代码 4 J, X( y, r( S+ \/ f. Y6 z% Q. g
85.5.5 第5步,配置文件Dev_Inf.c的实现% y* [1 M, Z6 d
配置如下:/ x5 k4 m" W* B9 J( g& C
' e: d9 A0 @5 N4 |# V% ~/ {- #if defined (__ICCARM__)
; X8 v5 k5 R, x1 G - __root struct StorageInfo const StorageInfo = {/ S8 ]0 w1 [& |4 g
- #else
$ `- H# m8 J9 v' R% n1 u# { - struct StorageInfo const StorageInfo = {6 f. t- {' b0 ?& e, r, i
- #endif
0 b: M) U8 o6 W3 g# T b( H% ? - "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */8 ?+ B: Z" N0 V' a# c0 D4 j
- NOR_FLASH, /* 设备类型 */
8 S) x. C) B' B. f( W9 W& _7 Q - 0xC0000000, /* Flash起始地址 */2 }1 J* V/ b/ q b$ }2 o- o. N
- 8 * 1024 * 1024, /* Flash大小,8MB */7 d/ [$ u6 q7 l3 P# I% g6 u4 ^- W
- 4096, /* 编程页大小 */& P! B! q2 g8 ]5 a3 H
- 0xFF, /* 擦除后的数值 */" ?& x4 B: c+ a
- 2048 , 4 * 1024, /* 块个数和块大小 */
& p/ q0 S' ]+ Y C" K - 0x00000000, 0x00000000,
6 M4 H0 i5 W$ m: r& e - };
复制代码 : ?, l7 [; A: z( O
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:
: v) |9 y, D/ V3 Z# \* _8 v/ r3 t3 f
( m. v6 w2 ?% m
9 w/ Y7 {- u( @' F5 u; ?85.5.6 第6步,编程文件Loader_Src.c的实现% \5 t7 S) D: a+ i& B
下面将变成文件中实现的几个函数为大家做个说明:6 c8 O5 ~- P* p$ Q6 v0 g) W% ?
0 W1 v. [5 w5 ^" { 初始化函数Init
' t2 e* ]# n+ h4 }8 p8 F, P, J- /** v0 Y2 q# O0 r
- *********************************************************************************************************2 _: c/ g7 `2 T) X
- * 函 数 名: Init$ ?5 C) F! }7 x4 Q/ _- C- q
- * 功能说明: Flash编程初始化
' a# ? G) S1 Y) V: [ - * 形 参: 无
/ ` |$ \! B" n. B - * 返 回 值: 0 表示失败, 1表示成功
3 J. B6 z: G4 D8 u - *********************************************************************************************************
" K) V# V$ w+ `" H+ i - */
& N2 z" o$ y" u) E! Y - int Init(void)
" |6 `' T1 e e+ ^' ~4 } - {
: ~* }. Y, H! S! n - int result = 0;
, ~9 M8 I' z0 I9 D A
4 _7 c; H5 ^) h9 G7 A- /* 系统初始化 */
5 E+ V* G( j2 Q4 C, k - SystemInit();
0 y* [- ?/ u4 z - ( x1 P @8 k" ~3 h) E( O
- /* 时钟初始化 */
) Z1 X: t2 T, V( m# L - result = SystemClock_Config();$ d! f, x: d8 c- l% N
- if (result == 1)0 f$ R# G4 f3 R2 w
- {$ d" Y! c; T" E2 Q+ d4 S9 x
- return 0;
, y. r6 x' M" V& `/ Z0 i - }3 T/ f8 | E" U& n* g4 J
- 6 d0 R+ c4 W) V$ P" F8 a. T7 X
- /* SPI Flash初始化 */" k$ [5 c5 U8 _* M9 k
- bsp_InitSPIBus();+ h9 i+ `- m' g4 q6 l4 ?
- bsp_InitSFlash();3 p4 t$ w1 g# V) x, z
+ z7 l2 j; |: }& I |6 w! r: S# {- return 1;, B9 k g, E) p. o$ I N; N, k' m
- }
复制代码
& f! b- o# J$ n. m% K& r: V 整个芯片擦除函数MassErase# K7 |; q% I" ?2 V2 ?7 ?4 n! a
整个芯片的擦除实现如下:; a4 H3 V6 }7 z3 {) x. l
7 b- S7 _$ Z7 Z+ s. f1 l5 k0 U9 N
- /*
b- S# \" j1 Y9 W* _ - *********************************************************************************************************% W+ ]) s% D9 c. Z' x- e. S9 I
- * 函 数 名: MassErase1 j4 c" W; p6 J6 j0 _
- * 功能说明: 整个芯片擦除
; {( a( U* N" c& B; n$ s' z - * 形 参: 无
3 s8 r0 H. P9 N5 w0 k1 { - * 返 回 值: 1 表示成功,0表示失败
# \8 n7 O7 n3 f2 v" i8 K) M - *********************************************************************************************************
' ~( x% d# n1 s1 w6 f - */) q6 P! o! y6 T: u- G; J
- int MassErase(void)
" f3 e: D7 L, Q. y+ | - {4 I" B$ T# b# i$ o
- sf_EraseChip();
j. R' E8 }" z* k
+ {' ^, {- D# @4 d- return 1;
. N9 G% X1 \/ ?) I - }
复制代码 " [* m N" x9 N
扇区擦除函数SectorErase! \" N0 J# z5 O( a1 l
- /** b* E8 C( c8 G* u
- *********************************************************************************************************
" E; L& B# m; P* `# v' c - * 函 数 名: SectorErase" R1 z8 u" N7 b' M
- * 功能说明: EraseStartAddress 擦除起始地址* k/ k, C5 R- b5 ~+ v) _- g4 W
- * EraseEndAddress 擦除结束地址! _$ }4 Q5 R- q9 `
- * 形 参: adr 擦除地址8 Q; }6 k! j; T
- * 返 回 值: 1 表示成功,0表示失败" n5 {! c" {3 |$ ^, t
- *********************************************************************************************************
# I+ p4 g6 D4 t. } o/ J. a- @& A - */8 X( z) C& l4 k- r' a1 t* [( T
- int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)0 X' C- |. I$ z/ U) D% k; @0 M: m
- {
8 J0 ~+ _9 a9 [4 R - uint32_t BlockAddr;
- p! L4 o1 Z# g f3 k+ u - 9 l y) k8 {; J
- EraseStartAddress -= SPI_FLASH_MEM_ADDR;
- F0 P1 a" E4 n: w; [$ H# ^9 E) S - EraseEndAddress -= SPI_FLASH_MEM_ADDR;2 i" d, q, `9 R
- EraseStartAddress = EraseStartAddress - EraseStartAddress % 0x1000; /* 4KB首地址 */
1 u, Y# G. {* @0 V& E5 e+ M3 j6 t - + ~$ F8 ~* r( j2 P$ X( e8 _
- while (EraseEndAddress >= EraseStartAddress)/ M( j% u: O1 X- c$ c. r# i
- {
5 i/ Z1 \0 F3 r - BlockAddr = EraseStartAddress & 0x0FFFFFFF;
- H, ^% @" B5 r% x$ J5 D - ; h$ Y5 A( G( F0 r
- sf_EraseSector(BlockAddr); 8 N( f. Z [0 ~0 u* P! G. Y- T# U: {
- $ ?& D" w V4 R2 Z
- EraseStartAddress += 0x1000;9 E) }9 P+ Y1 B+ x0 X
- }
9 K4 I ^+ B# Q6 `" @! `% W8 R9 d - % o# I# \) g+ z% P6 h9 H1 y
- return 1;
/ W6 u& W! P% A2 G5 C: } - }
复制代码 ' K9 K' Z$ _. D$ B) `7 a' ]7 ]
这里要注意两点:% g3 e7 T) a/ W' F! l' G! n) c* r
/ i# o, Q O, ~(1) 程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
; h9 X3 s0 N( v9 ]/ b. q$ o
2 z3 J) O1 ]; M+ P(2) 这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。2 s2 Z* T: K+ Q# L7 Q
7 x; `3 {- L4 e; Y
页编程函数Write: ^* |& J0 O! U# L" P
页编程函数实现如下:( T0 V1 G6 K+ c- q
3 ~- H/ ^0 Z1 T7 }9 S4 l: N- /*, z, Q$ V+ g" p: `
- *********************************************************************************************************2 Y% ~) r6 e4 J$ q/ J, P0 M' U
- * 函 数 名: Write
9 M" C" p( \& c# c+ P' _5 f w* F - * 功能说明: 写数据到Device
5 s! e! B/ I9 J- X6 g/ V' { - * 形 参: Address 写入地址) ] ^+ Q. f$ @3 s3 O
- * Size 写入大小,单位字节/ p" p! [- N6 m+ j( R3 C; R
- * buffer 要写入的数据地址
# v$ t* l: n" z9 K8 m1 g& o - * 返 回 值: 1 表示成功,0表示失败
+ j2 W3 G% S% @6 _: r - *********************************************************************************************************6 \, B- C. b- H. I
- */' i2 w& V/ @- z/ r! g
- int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
9 j- p0 e# v- v+ [% D, ^6 i - {
$ ?( Y) Z) |. Z5 s# I - Address -= SPI_FLASH_MEM_ADDR;9 U8 j3 D7 H: a
" {" i% R. J4 j- sf_WriteBuffer(buffer, Address, Size);
3 C' r7 N* S* @* E( U. X" P - . i8 I; x8 u O+ b' ~3 l
- return 1;
" [; E2 p3 K. J( [1 [) o. Z0 g - }
复制代码 % D; n) o8 s7 C. e; P
这里注意两点:2 q" W; ~" B+ T# I. w; v' u* T d
" a4 ^: A: u1 N1 {
(1) W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。2 i% j, A4 E; W0 t
7 P, T5 V3 Q0 ~9 e9 S6 p(2) 程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。$ H) [, ~+ J2 t# c
6 R5 s& a# b* D/ }8 u( {, H 读取函数# W$ x. u7 H6 F% o
- /*
9 x+ p, \5 ]% {' L% M y2 W6 _4 o - *********************************************************************************************************
6 H4 r& ]% U0 e, N: A - * 函 数 名: Read
4 `. h# c$ u2 o/ h: b - * 功能说明: 从SPI Flash读取数据( _7 `) B4 m; V0 \ \- a
- * 形 参: Address 读取地址+ w. W+ o# Z9 S9 j
- * Size 读取大小,单位字节
2 T T3 X* |/ U - * buffer 读取存放的数据缓冲
+ T# l8 c. Z0 b. z2 {! h7 v - * 返 回 值: 1 表示成功,0表示失败
$ k6 X' Z/ h0 [! P6 e0 j, a - *********************************************************************************************************9 f+ ^0 B/ K& O; [
- */: @* h$ R8 n- P Z2 }$ w8 c, J
- int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)7 L1 {: b$ Q5 t! ]5 A5 ^
- {
; n2 `0 G" t0 h - Address -= SPI_FLASH_MEM_ADDR;1 o+ p8 F' E* G8 L4 Q2 L0 W: M
- sf_ReadBuffer(Buffer, Address, Size);( q1 b: q8 H& n. @
- ( z4 a+ Z1 Z/ }
- return 1;+ g- D8 V( v# Y9 g' w6 m) l( n3 g
- }
复制代码
2 k* I9 E2 H; N 读取和校验函数) a* |" r- e0 Y! N
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
" V! A2 g. I/ t* i+ x# u- I
" S! g! Y$ S$ ^/ S85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)% o. {( R% q; H" c6 I( Y7 U5 i6 a
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
8 @$ d3 Y$ u; O( N; X
; u& J+ v. p8 d9 }( {5 |6 e8 p- /*9 r, b' Z5 T6 r5 X
- *********************************************************************************************************4 P0 k, Q! L" N
- * 时钟,引脚,DMA,中断等宏定义
4 o' J' M2 t9 M/ @ - *********************************************************************************************************) p) |) x# [! k- j
- *// ], I/ l( d3 Y! ~6 M
- #define SPIx SPI1! v, ], W8 N; U! \# ]* w
- #define SPIx_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE()
* |' b {; n0 F; R* I9 \ - #define DMAx_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE()
0 J4 V8 i. r1 N# Y; @3 r - # M5 @% r a# p+ f9 O4 h7 q5 K5 ]
- #define SPIx_FORCE_RESET() __HAL_RCC_SPI1_FORCE_RESET()
0 ^9 v6 K% @# B6 s# g2 a - #define SPIx_RELEASE_RESET() __HAL_RCC_SPI1_RELEASE_RESET()7 J; c$ i' Z$ B; ]- L, b
8 s0 r! W1 x! V# A- #define SPIx_SCK_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
4 H8 P: _# e$ n+ V - #define SPIx_SCK_GPIO GPIOB2 T: H- B4 v$ t4 k, ?% W
- #define SPIx_SCK_PIN GPIO_PIN_3
: ^3 M) h. ` f6 N! x) y+ k - #define SPIx_SCK_AF GPIO_AF5_SPI1
9 p& w0 L$ s6 g0 O8 {* m7 q - 8 W/ a& v' L( V" N1 O' E
- #define SPIx_MISO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
7 z9 O2 H0 t: s1 O) O$ T - #define SPIx_MISO_GPIO GPIOB
1 [! W0 |/ L$ z) ?4 f/ t - #define SPIx_MISO_PIN GPIO_PIN_46 i$ e7 G+ }) J4 ~& i
- #define SPIx_MISO_AF GPIO_AF5_SPI1
* h4 v' G7 U; b, F! |- N
/ q y8 \, p m9 D I- R- #define SPIx_MOSI_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()9 p8 {& v L$ o7 I
- #define SPIx_MOSI_GPIO GPIOB3 [* ~% ^9 _6 N" }, q' Z
- #define SPIx_MOSI_PIN GPIO_PIN_5
! j; w5 d+ R, q+ U% k$ y! t. Q3 N - #define SPIx_MOSI_AF GPIO_AF5_SPI1
复制代码
# L( _( w3 f, [) H0 C硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
, ~1 B& r( s. _* M1 h" [: ]/ |+ i
5 Z: C" I; d9 u' r7 J( T% L! j" m主要是下面这几个:1 a/ `1 p, d4 E" d8 k/ `: G8 z
/ U( K* H% J$ I0 w7 W
- /* 串行Flash的片选GPIO端口, PD13 */
' a. S& e3 o! P" o" r2 s9 j. C4 e4 X- l - #define SF_CS_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()/ e5 l! z, J4 q" _3 K5 B2 {6 A& [
- #define SF_CS_GPIO GPIOD
7 y: w7 s7 b4 O) c4 c! N - #define SF_CS_PIN GPIO_PIN_13, W8 Y, p. A& a0 f j1 ], K4 A& Y8 j
- % R: G: s# y9 s2 `) H4 B
- #define SF_CS_0() SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
- g$ y( u/ U1 Q, } - #define SF_CS_1() SF_CS_GPIO->BSRR = SF_CS_PIN( r' F, t0 u+ g. ^$ Z2 t- q
8 z; E5 V& f* b j! u- #define CMD_AAI 0xAD /* AAI 连续编程指令(FOR SST25VF016B) */+ U+ ~' Z1 Z3 W( f1 j6 V
- #define CMD_DISWR 0x04 /* 禁止写, 退出AAI状态 */$ n( J; Q1 T8 G* y0 ?
- #define CMD_EWRSR 0x50 /* 允许写状态寄存器的命令 */: {. {! \7 b# M3 w
- #define CMD_WRSR 0x01 /* 写状态寄存器命令 */
, K8 I. ?. j) p& a - #define CMD_WREN 0x06 /* 写使能命令 */
' a; m# B5 j0 W' w9 Q& F - #define CMD_READ 0x03 /* 读数据区命令 */
; F3 u) j' q g. I - #define CMD_RDSR 0x05 /* 读状态寄存器命令 */
( m0 D% h+ ^4 A/ f- O, Q8 F; |& f, N - #define CMD_RDID 0x9F /* 读器件ID命令 */3 a3 T! C. M& H- J7 P
- #define CMD_SE 0x20 /* 擦除扇区命令 */2 v- n, r; R2 J
- #define CMD_BE 0xC7 /* 批量擦除命令 */
" `; H- i6 v- v% m8 a - #define DUMMY_BYTE 0xA5 /* 哑命令,可以为任意值,用于读操作 */
" R P5 n6 a; G h0 m5 K% d0 s
_! F9 `% A; V7 z- #define WIP_FLAG 0x01 /* 状态寄存器中的正在编程标志(WIP) */
复制代码 & R `% p8 C5 V5 L( X7 K- L# H
85.6 QSPI Flash的STM32CubeProg下载算法使用方法3 D' d, w% i9 n; K* x3 A# Q& I; j- n
编译本章教程配套的例子,生成的算法文件位于此路径下:+ R/ a- q- u+ J' `6 m8 m& X( E
+ p6 y2 R/ }9 j% Y
6 o8 i3 M* y% G! J x& v2 | n
2 B$ _: }. Z, @* s1 e f% T85.6.1 下载算法存放位置
7 n# Q3 {# `5 ~生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:+ q9 z9 {" ?% o) F
% W% g& j* I( S7 z' G1 \
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader, [) o( J9 F3 e+ e+ Y
; K! l* V" ~1 X/ l
2 \& J% F' {4 W/ k5 g) C0 [. }$ [' C; a' ^+ G
85.6.2 STM32CubeProg下载配置5 c. e9 o" i0 B l, l5 [
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。* i/ z1 k6 v' m3 f
0 F7 b- P; X, Y! \: O6 n# j- W* K+ C# S' n
2 F; k0 k8 {( Q0 b
点击Connect后效果如下:
0 [4 z i2 Y' ]; e: M: K" j, `" a- f4 _5 X7 R* Q. B/ U
6 o Z% Z* f! y2 i9 }
5 L2 H3 |: Z4 `/ _5 l5 N
在这里选择我们制作的下载算法:
4 z& Z, Y; Q3 z
" ~6 A3 r2 U0 D* e8 I
" X; r, V T& v) M0 a8 x3 ^$ S
9 W/ ^5 j" X( G. Y4 [1 L任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming0 M9 }4 p; z$ u" U, z
5 u3 J6 l+ @# R4 H) g3 A
7 \- f2 D! [# P4 a; f6 Z7 F- [, Z k* s) r' ^
下载完成后的效果如下:& c) Z, e! r) c* O1 M
1 N- G5 f8 g) z) T! s9 d+ r6 t
% W, s8 a+ ` \% u5 f6 @- _
3 B, D" s% l; d4 B/ k: M* i+ e85.6.3 验证算法文件是否可以正常使用, C ^+ v$ @( }! U* o# f# E, s4 {
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:
$ J' W+ H4 i6 e4 {. t+ e, ^+ \6 d9 F8 @
4 Y) P, A: `+ n# G( t
0 D, q# I5 p0 S! l+ p9 g9 l" o
85.7 实验例程说明
* N: O8 f( S6 S9 b4 ^5 I/ ^本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。1 k0 A( U" T" R
/ T' ~) E0 z, m. u, q# n
编译本章教程配套的例子,生成的算法文件位于此路径下:8 C* L3 b ]0 O
8 c# p6 b7 E5 U5 r
, p+ U$ b6 w) d% H
' [# J, @+ Z' p9 y' K2 l' a5 @85.8 总结
0 s, t! [. U7 k4 G) A本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。+ c6 |, k9 t% C% k# d4 {' \
! `. d# p- W/ [" r
1 h8 U( m j, t5 ]6 v2 s1 f% P. L+ U( _& q& \+ S; V/ p/ g+ B6 z& F
|