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