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