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