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