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