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