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