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