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