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