81.1 初学者重要提示6 G. T/ F. b6 |4 d
QSPI Flash的相关知识点可以看第78章和79章。0 b3 C5 u% M: \( X: ~ G
QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
$ k+ d2 K% Q7 N: ?' d: L/ g+ u STM32CubeProg下载算法制作和MDK下载算法制作基本是一样" K$ h0 Q0 Q, x
本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。+ @: F2 V" x5 S+ H f; I
81.2 STM32CubeProg简介
* ]) s/ \, R! M) ?3 w6 ^STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
* Y% a: i0 G" ]6 k" r' R' s8 U
~: [4 p' }) {# S: }8 A+ z3 b$ ]" N9 o }) ~: o4 Y; E" }' S6 U. l
+ h+ T* }! u7 h' s, L81.3 STM32CubeProg下载算法基础知识/ p! y/ u& j; a- P
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
8 _' F7 m# {- X+ X) M7 I9 U$ R+ C8 q. Q
81.3.1 程序能够通过下载算法下载到芯片的核心思想
6 L7 d* ?* f; }0 ?! X! O* O6 @1 F认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。2 W0 O9 g1 T5 [/ n V) ?; N( d
, T8 \$ a4 O U, g9 @81.3.2 算法程序中擦除操作执行流程
4 d! }, e4 n$ p1 m! @9 p% w9 x+ y注:下面是MDK的算法执行流程,STM32CubeProg是类似的。8 M' _' b. s& f5 a" M
: m6 o5 C, ^" b" y( y$ @擦除操作大致流程:
. z5 f/ I# M- |, L, e r- s$ }
7 |# j W: m% b4 K! \" q+ O- A
1 A' G- A# q" c D% x
, a% v- M7 N" U1 M8 q 加载算法到芯片RAM。, r3 Z# a, Y8 ]% G3 Z
执行初始化函数Init。: Z5 y* Y+ A7 b1 B- k# U
执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
# n2 k+ v" t; X 执行Uinit函数。
% Q! C6 i/ Q' t$ `2 ?" D2 k; V 操作完毕。
D3 p" B( f2 k1 _+ o% i3 P81.3.3 算法程序中编程操作执行流程
9 U7 { _5 B, g注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
) m( A% S6 M2 k3 t- Q" U- O* g* j# Y9 Q& W5 y, f& c
编程操作大致流程:
' k$ e( {6 X( D- k0 l9 B# i
9 @9 z# A: l4 H2 N$ n* ~2 V: z; m# Z! j: f5 O
1 [ u2 h# i' U
针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。4 a8 {3 Q: m" X" b3 N0 Q
查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:2 g% g& ?" G/ G) n' C( E
加载算法到RAM。- y- Y1 J& t( Y
执行Init函数。
: F8 u( R& q0 }) \/ a- j 加载用户到RAM缓冲。
/ M% c5 K& s! H- D 执行Program Page页编程函数。
# n6 m& G7 X" q% K1 d 执行Uninit函数。* i( l! ?' X3 y. n# U; U8 ?9 t
操作完毕。
! p4 j, d! W9 Q4 O: R* k81.3.4 算法程序中校验操作执行流程
- q4 e; G8 f# k" j% O, D5 G2 [注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
; y3 m2 [+ H6 \
7 M% `5 O8 j0 z, O! T校验操作大致流程:3 ^& W- H, w- W
$ o$ c( v# m' I2 K2 _* x2 p
! L2 i t0 L* [' K4 S
( P- w5 U) [- K. M, F 校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。. I& W; V" q: R5 A8 L
查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:5 t/ H1 Z. X( ~' H& x7 D5 s& `# Z: G+ B
加载算法到RAM。
* x ^& x- q' K4 P 执行Init函数。7 I( p) a0 V1 O. ^ v6 d0 i
查看校验算法是否存在
6 R/ v; g; e/ P 如果有,加载应用程序到RAM并执行校验。
* V. Z6 M3 f: `2 r' d. c5 R- G 如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
$ h! Z3 }- @ \& y: d 执行Uninit函数。
. x6 K$ i5 q( t L 替换BKPT(BreakPoint断点指令)为 B. 死循环指令。- g* L0 ?( [- e
执行RecoverySupportStop,回复支持停止。% k* \* V- H$ A, }/ |9 b+ u
执行DebugCoreStop,调试内核停止。
) e4 n/ O! M0 i" B8 R5 m 运行应用:
( z' q) X# A6 q0 ~$ a5 V 执行失败
( l. K0 k, U; S9 N! T9 I! U" a) k; { 执行成功,再执行硬件复位。& w0 I+ c( p2 \8 @2 {" o
操作完毕,停止调试端口。
. X8 H% H9 e7 `! y% i6 Z$ L/ v81.4 创建STM32CubeProg下载算法通用流程( H2 G. ~# \' {' P: ?
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
( V) R0 q) P$ M" w' ?; f/ j, F2 N* C- c/ q& S: k2 l7 m/ J
81.4.1 第1步,使用STM32CubeProg提供好的程序模板4 `. H, S: Y# M, u
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader1 c8 p; c+ Z) w# e9 ?' u1 }+ [4 B
! j/ @; c6 J- a" ?$ n- C/ c: T* ^
5 W3 m* f. g8 b. o4 @2 s& @- L2 U' L: \$ b4 E
以M25P64为例(注,其余步骤就以这个为例子进行说明):) G( P* I. ]6 l9 e0 }
) M( l8 C9 D5 K8 y. n
8 }2 Z* w; J" [# a" w
9 V ^) I( _' j
81.4.2 第2步,修改工程名& [3 ?( J& i& p- L1 v1 H; Y k
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
/ e1 a% ^* b$ @8 d1 ?. \: ?1 f+ k. c- D$ T% n
81.4.3 第3步,修改使用的器件
: h7 `' S( z! B0 |* B在MDK的Option选项里面设置使用的器件。9 S2 k/ I# N7 {: f# `, d, E
8 M6 Z+ q+ [! h7 ~! Q0 U: q3 K% t! N+ Y0 b9 F
) r- K8 O% x) W# m+ |8 Y
81.4.4 第4步,修改输出算法文件的名字( x! i$ @' `$ Z0 m
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
[( ^2 {1 D0 m
" D* U1 }2 k, W1 x1 g4 o, O4 S; ^% k. N6 ` e1 M$ H9 q- D/ [- F
/ ?- r8 L" g% y3 u7 P6 Q
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
3 n% i3 i; ~2 l5 U+ S
0 Z# [5 x2 f' @. _
' h* [+ U; v6 _* l7 Z7 P/ @81.4.5 第5步,修改使用的库文件和头文件路径
/ d' M8 ~$ c. q# L" x0 {+ d根据大家使用的主控芯片,添加相应的库文件和头文件路径。' v9 F# q* ~5 \7 p: a
5 a9 i9 s; }! ^# l
81.4.6 第5步,修改编程算法文件Loader_Src.c. {- h1 k0 I( S' s& g
模板工程里面提供的是M25P64,部分代码如下:
+ X+ F5 F+ W4 s* \' o' J( b" {6 N$ K$ j0 P2 W# M4 m& S+ Q
- /**' p6 f8 B) U0 ?$ H# Y A
- * Description :& b5 O1 C- P5 U
- * Initilize the MCU Clock, the GPIO Pins corresponding to the" y/ W$ R' o3 T* b7 u, V7 l0 R. P1 x
- * device and initilize the FSMC with the chosen configuration
/ L( x1 r: X- c0 [+ ~ - * Inputs :
+ R- Y# M0 F$ f; `& C$ l3 P! Q - * None
8 b( M% N! ]. v9 A - * outputs :% P `, u4 ~# A7 p5 l
- * R0 : "1" : Operation succeeded4 z \8 V I+ o% X2 b9 n; Q
- * "0" : Operation failure
/ U9 N, H% ]; | - * Note: Mandatory for all types of device
2 o9 r# R2 E6 K6 h - */
$ H0 o5 [# Y, }9 T' ^7 l: W# `; ^ - int Init (void)' O* T8 E; V" J% J# I) A% N1 v
- {
9 D! o: l+ @) b) i - SystemInit();' c0 \4 U6 R# s, ] |* w
- sFLASH_Init();
/ ]# }- ?* b1 X& @. V$ P- n- t - return 1;
- r5 j6 O! m; ` - }: W( @, ?, d. b/ l1 m r
) }) m$ D5 }( O G& ^' G+ N
* {3 G w! A, d- /**
1 `3 V: ~& W/ r4 o: ^ - * Description :
4 a$ ]! \+ o3 h% {/ _) S, K: Y- R - * Read data from the device 9 g" G$ M" ]4 i- s B
- * Inputs :- Y4 ]- p! ]0 l9 }
- * Address : Write location' P5 _0 y" b; F5 C' w y* e$ C& T
- * Size : Length in bytes
B T+ F7 j% r% t/ W - * buffer : Address where to get the data to write: s: m' k& b4 ^1 K+ B. a' _& l
- * outputs :
/ h( C5 u' L& f+ l" J" e( a2 m1 r - * R0 : "1" : Operation succeeded/ s$ `) y4 P! O* Y- i
- * "0" : Operation failure
8 f+ p9 F9 _5 A4 u# @# |# M1 ` - * Note: Mandatory for all types except SRAM and PSRAM
% z0 Y4 C+ m f, _. ~ - */, H4 w/ V( I* K
- int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
2 x( d4 T, ^. \! J3 B - {
0 D) f7 A4 x9 O) D - sFLASH_ReadBuffer(buffer, Address, Size);& j4 O, \6 ^3 T4 d- Y( z
- return 1;4 H' i& X% N1 e. Z: _4 S
- } $ i7 U5 O7 Y* N' v8 n
7 n: C- i7 | V" g5 C- ' e% h+ }( V( N ~2 v2 ^
- /**8 j: p0 ?! u" w
- * Description :3 o' ~1 M1 y1 Q" d% L; ]' O
- * Write data from the device
M/ `# | e, r3 H9 {: R: u! p9 z - * Inputs :- j* J' I+ a/ M$ R e
- * Address : Write location6 Y1 ~2 k# E T3 i: h5 a; F
- * Size : Length in bytes
0 n) J0 T# q$ C; ] - * buffer : Address where to get the data to write. ]6 V Z& C( t1 Q0 Z9 E
- * outputs :& G! E. t) Y/ L! W9 S0 X- F
- * R0 : "1" : Operation succeeded
% p1 ~" X1 i5 o& j7 W& S" W - * "0" : Operation failure0 e8 ?! J/ {! b' ~: o& V2 A
- * Note: Mandatory for all types except SRAM and PSRAM : D3 U5 k" r" d8 p: E; K
- */, k4 q: C; K. K) \6 r/ V. r
- int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)$ E. M) j3 R7 _: G3 b+ S
- {
0 C0 I/ }5 C5 x# |8 o7 z; j" Z: D - sFLASH_WriteBuffer(buffer, Address, Size);
6 n: h8 D" m* a6 I/ b: I9 b - return 1;
0 m- l5 q P, g4 N/ i - }
- T; l; E3 C) U* Q' ]1 E* y
% d* ~) d* P( x- 7 W1 R( J* O) @
- /**
8 C4 ^- }: k& g' y4 d! R8 {/ ` - * Description :/ M- v* q! p: q0 l2 h
- * Erase a full sector in the device; i! a2 r5 M; X/ _/ a0 X" B3 S
- * Inputs :7 V; h# K$ y. l
- * None
; Z: { I' @# h% u9 Z ?8 E+ Y - * outputs :. J- P0 r; t2 W1 _. l) s' ?# F* |
- * R0 : "1" : Operation succeeded* H, w, o* {: g/ @! w5 y1 [( @
- * "0" : Operation failure
7 V& \5 I7 h! ^( A; X - * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
* Y9 x) }, k& ?- m9 k - */3 l) m+ c0 A! a# l- J
- int MassErase (void)9 z& I3 N j' F- V9 B! b) U
- { 6 A/ r; q1 r& X) p
- sFLASH_EraseBulk();8 ]9 \: e9 b3 n2 N; g
- return 1; 1 x' s2 u0 {/ z
- }
复制代码 4 ]1 ?' E/ M& d( N
81.4.7 第6步,修改配置文件Dev_Inf.c
& }5 k' ]3 g- Z- ]" E5 v& f& z模板工程里面提供简单的配置说明:
, @3 `! @) T/ [- q4 i( Q3 |1 k3 h
, x$ J- \1 n9 B: }1 S+ Z- #if defined (__ICCARM__)4 p3 U7 V/ y& b1 ?$ W! U5 V
- __root struct StorageInfo const StorageInfo = {% q1 y' U. t) J9 U! Y
- #else; s8 X: A) g* p" F- l* J
- struct StorageInfo const StorageInfo = {* |7 r$ u; H% j" t: _. k
- #endif- V% ^) Y/ |2 B2 _, ~8 Q( u4 q( @" A# r
- "M25P64_STM3210E-EVAL", // Device Name + version number* Z0 r. j0 r6 W( X6 [
- SPI_FLASH, // Device Type
) b9 M2 E5 t. w" P, w) L - 0x00000000, // Device Start Address
! v2 m2 N% g- N) t - 0x00800000, // Device Size in Bytes (8MBytes/64Mbits)
1 X) X$ |) l5 |# j" T% W - 0x00000100, // Programming Page Size 16Bytes
( x' L, _9 m% j$ i* }. u5 S - 0xFF, // Initial Content of Erased Memory, ^( b( n: w! A+ V; A: i, C) a$ A
- // Specify Size and Address of Sectors (view example below)- S. ^! g" T5 Z" X I9 @! B0 ]
- 0x00000080, 0x00010000, // Sector Num : 128 ,Sector Size: 64KBytes ~) u( o* \1 `: t6 e2 C
- 0x00000000, 0x00000000,
2 q4 s9 Q- G$ \: ?2 ] - };
复制代码
: y5 r* N3 n9 a$ d注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
! t! [' [6 T9 ~( x& d. l3 d' r' H" @/ _
; B) w% M( d' o$ `
$ z0 J1 s, L3 c6 x
81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。3 K3 Z0 L' f/ ^! {# r" `2 j
C和汇编的配置都勾选上:
6 P" ~1 A3 b& w6 C3 O2 o0 M- o3 X+ L N
( H- R$ w0 V8 N! W. e8 T9 h
1 n5 }) D( ]) j2 }汇编:; T* b/ L0 {' `: `- [4 @
9 u' D0 t( N% U, ~$ X8 f, l; Z4 t( E# l& ^# G
: B" u0 b. p1 s3 |) S
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:6 n$ U/ T" Y, D6 V+ n; P$ \. o3 d
' d. q6 {4 z* E0 Q K# n/ D(1)加载以响应运行事件。
9 [. f8 A2 F) K( F8 M- g8 h4 _& X7 B7 i$ ^1 v' N) W
(2)在不同情况下使用其他例程的不同组合加载到内存中。
: s, P' Y% W" l2 O8 X- s
# r1 X, y& K% j, c(3)在执行期间映射到不同的地址。
: {; z' `3 t8 h& r; }1 F
! ~. Q- R! b3 }& D+ h" w% X使用Read-Write position independence同理,表示的可读可写数据段。
$ y( B7 ^% r. M4 x
2 h: ^' s, N" ^3 ?: S. K) F7 F81.4.9 第8步,将程序可执行文件axf修改为stldr格式
/ K' [- _! Z* }/ C% a8 Z' [通过下面的命令就可以将生成的axf可执行文件修改为stldr。' C# g" j4 J/ e) p8 c/ X/ \
7 M4 C$ ?( `$ d) p; u& Z! `( M. P, X; {
X' j# H ]- ?81.4.10 第9步,分散加载设置) C' `* r5 Q0 T c x
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。0 O: t4 U) o0 ~3 `' p7 f, I0 a5 E
1 W' `* \- X9 f7 c) G$ H/ x9 _: ^" `: Z5 C5 @
% F( K5 A# ~* B. R0 [6 C- h分散加载文件中的内容如下:- X Z: ]% }) b2 ?; Z
7 n+ ?; ] E: E6 j4 a) b- FLASH_LOADER 0x20000004 PI ; FlashLoader Functions
9 L6 m# E1 v- {9 | [; a - {
% @# q) e7 f) Z. O& t4 W& K; S - PrgCode +0 ; Code
0 P2 C1 D$ |6 z: S: g0 h3 g - {/ j- w, Q5 I8 S, b
- * (+RO)
4 h& W5 ?; Q" O - }
' Y* d* d5 z# l - PrgData +0 ; Data- n: e% z7 [: b% F( ]
- {
' [8 S" p0 y3 n9 J- M1 o* F - * (+RW,+ZI)2 Z1 S6 B9 g( k( z1 e
- }, a9 B$ v- Z5 H6 Y
- }
4 M$ Y2 Q3 ~1 Z - + ?' |: b V# J z4 q7 s
- DEVICE_INFO +0 ; Device Info
' T Q8 U$ H# V: y7 M$ Z' J - {
, t6 V5 r7 V" D! g/ _4 _1 L - DevInfo +0 ; Info structure* L* a& W$ r1 r1 F
- {
5 e. \' q% h5 t x; d9 _! L - dev_inf.o
4 q6 A9 }3 g1 h4 o" R- g - }" ~6 l5 [5 b6 j, ?9 Q6 f5 J) y& l. m
- }
复制代码 ! w9 k# z+ j# X( [5 r
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。- N8 M3 L' {% Y6 H6 J
2 V T+ H9 Q1 b0 U# X! j& x3 M
--diag_suppress L6305用于屏蔽L6503类型警告信息。* E4 M+ T1 n# X% F4 }3 \
! S! Q5 [2 f" d5 R0 K特别注意,设置了分散加载后,此处的配置就不再起作用了:6 @1 O8 J$ i) l6 E. M3 C4 M6 @
# J, ~. m) ?& `* ]
}$ ^3 I7 P+ q2 M
* t$ m! h6 c$ h. ?9 l& k: z- n0 m81.5 QSPI Flash的STM32CubeProg下载算法制作
+ Z$ H4 E r5 r下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
# [$ {2 b0 @: \- t4 A: d& X; u
' Z# `) u. _. f. V E) Z* ~81.5.1 第1步,制作前重要提示
! L$ j b, m$ _5 S5 U% n这两点非常重要:5 j4 R4 C4 e S- ]0 i! S
& F9 k" z5 R. Y1 m
程序里面不要开启任何中断,全部查询方式。' B! O O4 _8 C3 l
HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
: {4 |/ }8 E. d" }" h. I81.5.2 第2步,准备一个工程模板. ^9 j9 Y2 F, G/ Z% a% v$ d
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。0 a' C0 x) B7 Q& p% r# f
9 ~" x% ?. `/ j9 _8 \4 z# w5 F1 [0 D w2 M9 _3 ]5 k
" B, A9 u7 x# \ ` b* A81.5.3 第3步,修改HAL库
" t: W; \) ?5 v5 P这一步比较重要,主要修改了以下三个文件:5 P1 a( v3 Z" S, g& v5 C; T6 K
7 ]: ~9 I+ D6 I0 |2 E5 W9 a. k. W+ ~# k8 s% t+ W$ ^$ N, W
; |/ C* a5 b* `* {/ ]主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
' D9 K5 `' m5 ?5 c5 k& w
+ @* D& L7 ?6 {( V5 J81.5.4 第4步,时钟初始化# ]) K u6 o6 K( z- P8 t& {. n
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:- ]% C! L/ K+ \! d& J3 P, I8 ?( d
- @+ K: j" J* B0 s5 W0 {4 C. l- /*8 d8 R- k; ^+ D: _3 [( F- l1 d
- *********************************************************************************************************4 v' ] v- R* m7 l, X- p1 X
- * 函 数 名: HAL_InitTick0 @) Q2 P" F$ [3 T9 ]# o
- * 功能说明: 重定向,不使用
: s: J" C' e3 V$ E$ \8 _ - * 形 参: TickPriority
2 @; Z8 F- E' s0 E! s - * 返 回 值: 无
. x+ R* v1 i, [; \" W - *********************************************************************************************************
1 p7 O4 J2 x& Y4 u* ~) S) q - */) Y% ?' |+ l) y; K* a9 x. I
- HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)7 `* r, V8 z6 ?: d
- {
_( q; l/ Y6 g: {( v - return HAL_OK;
" f$ c9 u. e1 N- s - }
复制代码
8 z: }* u P( y Q. K然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:3 a7 W8 m! ]3 V
: i, [: |' {- S
- #if !defined (HSE_VALUE)
! f7 E- {* N) \) Q* a0 z - #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */) U# y. h- e( }+ q4 R+ U2 S
- #endif /* HSE_VALUE */
复制代码
2 V) ?. S/ m+ s5 A8 S/ {最后修改PLL:: P0 a- q: m& H0 Y: r1 A
) [$ ?/ B& y" `( z- /*9 [. t7 k4 S8 j' \+ c# h9 ^$ v
- *********************************************************************************************************
. R, `3 w" L5 A7 `' b8 S, a8 { - * 函 数 名: SystemClock_Config
; [$ u% I4 Y, u& \; ] V' _ - * 功能说明: 初始化系统时钟
& L5 C& d9 v$ D. H( Z! n& Y - * System Clock source = PLL (HSE)
& @/ S( I$ [6 W6 l - * SYSCLK(Hz) = 400000000 (CPU Clock)5 C+ G6 W3 d9 q$ _6 R
- * HCLK(Hz) = 200000000 (AXI and AHBs Clock)
- _5 ~: A' ?1 V4 L - * AHB Prescaler = 28 j" V L+ w; U& U+ G
- * D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)8 \! T* I3 u; Q& O8 k! K: o- F
- * D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)
+ M8 y! d. j" X/ b1 V4 X# O( Q V9 [ - * D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
& g6 U5 p* J4 t0 O - * D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)- y9 ~) _# y% w0 e0 [7 F, O
- * HSE Frequency(Hz) = 250000003 R* ], @) E- U4 ~/ X# t: A6 [, v, b
- * PLL_M = 5
* c; a5 Z, {- o' I: Q - * PLL_N = 160
' S6 m0 J) R- ?7 m1 n! y - * PLL_P = 2$ f U s$ ^. O) j" j- K+ m$ `7 \) N
- * PLL_Q = 4
' R1 H. V( R1 U# Z! p - * PLL_R = 28 ?' o/ N1 t' R) Y* C- i
- * VDD(V) = 3.37 ^% h! R' U2 E0 @
- * Flash Latency(WS) = 4, i$ F; b+ B9 v) _. [
- * 形 参: 无! k. C$ [2 l" @5 J7 u6 F9 P
- * 返 回 值: 1 表示失败,0 表示成功
& D% r/ K- a3 ~; K( a - *********************************************************************************************************$ J5 ~3 N, a3 l5 g. t$ b/ s8 Q
- */$ C1 p. J2 \* h' |! m* D" x
- int SystemClock_Config(void)
5 ^1 t+ c0 s( u2 g - {+ m5 F) }9 L3 Y/ D# A; j
- RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
9 a4 G" p1 O& M5 P! D1 S - RCC_OscInitTypeDef RCC_OscInitStruct = {0};
8 g6 U- p4 I: }1 ~7 `: G - HAL_StatusTypeDef ret = HAL_OK;
+ U2 {$ G0 @: t0 [
. F# A, P$ H8 g- /* 锁住SCU(Supply configuration update) */, L4 L1 B; t# w, U5 I l5 g
- MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
3 \% q; f( `% i' `: Y - 9 f6 a( L# _ F; R* O* s
- /*
复制代码
2 b& C# `# v, [6 n" }$ M& { X 1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
0 G- s5 d# s4 o" S* h3 M 详情看参考手册的Table 12的表格。 S2 b) Z3 l9 G( p4 J! j) a
2、这里选择使用VOS1,电压范围1.15V - 1.26V。
" B, j, [+ O7 D; n - */
* @$ N$ j) ~2 O; d - __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);6 ^6 u1 N' ]5 t9 I1 L. M( l
, g+ t( A8 R9 d- while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
& h5 @1 g2 B/ s! g
8 d! j0 D" x9 T; r5 o- /* 使能HSE,并选择HSE作为PLL时钟源 */
! w% T7 w6 Y. k& W" S. M( y - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
2 x4 t1 `5 H% C0 S" S - RCC_OscInitStruct.HSEState = RCC_HSE_ON;! {, m& W' K# V
- RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
3 {; u+ E: B* _/ ~, T0 } - RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
2 ?+ s$ s& v4 h2 i7 @! O" i' M5 Q - RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
) @8 I }9 z! k# a. ^/ h) f' ^5 G - RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
$ c) v$ K" q5 D4 Z/ Q9 P* `( n - 2 p& s! \, Q: a, ~1 c
- RCC_OscInitStruct.PLL.PLLM = 5;
5 U0 G& O5 t7 h9 i - RCC_OscInitStruct.PLL.PLLN = 160;$ Z& Y" E/ p6 ^+ g% V
- RCC_OscInitStruct.PLL.PLLP = 2;
w% I+ I `, Y' U2 N+ F7 w; A - RCC_OscInitStruct.PLL.PLLR = 2;# L e! d, ^) W# Q% E$ P) l' C
- RCC_OscInitStruct.PLL.PLLQ = 4; 0 z' [3 p/ m; o8 [" b
8 T( Q* O [/ F2 [! \- B8 ~+ N- RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
$ D3 r4 a: e- F! ]3 V - RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2; ) l9 p6 A/ T. l0 f) W0 i$ n
- ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); C- p. ^2 o+ A
- if(ret != HAL_OK)
' ?) Y3 [+ L q/ m5 d: V! ?! W - {
* l0 n! u( W% ]& T9 z$ J& Y/ c7 V9 X - return 1;
h c1 ]3 t% @6 z. A) i* d - }
- X% q4 `9 Y; x7 M: q8 e6 c: [6 y4 d0 @
. w6 j: T6 t: G, n- /*
7 D# v( h( d4 s) }9 I1 Q - 选择PLL的输出作为系统时钟' O" A) G8 z9 x; @
- 配置RCC_CLOCKTYPE_SYSCLK系统时钟; O. k$ n- C2 R6 h/ j, b9 E
- 配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
- f% g# l" \0 _: O5 n! u( ~ - 配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
% k, X/ w" m* E, ^+ S - 配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
: i/ L. ]7 P, O - 配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线1 j) x& M- ?) p- _% k
- 配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线 + M: B t3 ^# V0 D1 g7 M
- */
/ l% F' j5 ]8 |( C* a6 a( M - RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
0 ~7 e9 Y2 e3 B% b, r8 r1 K6 t- J - RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1);
9 M" u" D0 Y* Z8 s) b7 F$ x4 s9 D+ J# D1 F - 9 Q% ], e+ B2 s
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
8 V, g7 f$ V( c4 v - RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
6 R! M- g# J% l8 t: {6 m - RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
3 q( C/ F3 y+ @+ }( }) T& |) s - RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
0 j1 D: b2 B J/ _4 w# l - RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
! W5 O9 W2 \- v - RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
' x( m) q% {' x* p9 {$ | - RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
4 g4 f3 x- {: Y f# U# D
! G+ z( J+ h: }8 S. N+ k0 b3 I! B3 R- /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
' x5 t7 g3 Z8 P7 H2 a4 ^; o9 v5 ~% t+ n: ? - ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
. j" W, e4 p) H - if(ret != HAL_OK)
" f7 p; h# N( F+ ~9 O6 s - {3 f. m) {0 P8 C( x
- return 1;- H+ C1 E/ f0 z6 X* p
- }
, X. Y. k5 T, z7 S+ `* }% `# ]1 k - ) Z. F0 O! B/ }+ ?8 \ U
- /*9 F" K. q5 E$ T: G
- 使用IO的高速模式,要使能IO补偿,即调用下面三个函数
: t; F0 C1 J" Z" Y" |1 G3 L# [ - (1)使能CSI clock3 v1 z2 V. k2 |# r3 }' R7 ^
- (2)使能SYSCFG clock/ r1 d1 ]; u! m1 z
- (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit06 h9 l9 V6 t& a( w! q3 J
- */7 |) U( o0 j! j, Y/ G7 _
- __HAL_RCC_CSI_ENABLE() ;
2 \; V4 W& q, m" I! V
0 c8 b$ |0 t/ `0 @8 j, ^* w- __HAL_RCC_SYSCFG_CLK_ENABLE() ;
- i, m( c9 u( g( L1 N9 [4 B% o
+ _: W' L9 \( i- HAL_EnableCompensationCell();- p$ O$ |& R& F! I( x
' ?* v$ m! |+ T% [3 {0 q- __HAL_RCC_D2SRAM1_CLK_ENABLE();; u# n' l# g9 k, T
- __HAL_RCC_D2SRAM2_CLK_ENABLE();: \+ o$ l Y& k! O7 ~. r
- __HAL_RCC_D2SRAM3_CLK_ENABLE();* z5 ~ L2 E" P, [% S
- $ F ?8 l, G* H
- return 0;' K8 C/ S6 X# F- K5 v! z
- }
复制代码 + W% h. H* _; o
81.5.5 第5步,配置文件Dev_Inf.c的实现
* l! G9 Z/ M1 V' r P z4 N配置如下:/ P' g; I# v, q$ x3 F0 G
# ]0 b* S" l% Z# w) A
- #if defined (__ICCARM__)
s" [2 L& v' s' K7 G, \/ f - __root struct StorageInfo const StorageInfo = {
$ a7 i1 i4 l, t2 a( |8 }7 n) f" A - #else
1 |8 Y* u9 r0 P$ q - struct StorageInfo const StorageInfo = {
: ?" q( n5 p9 A5 E" v8 R" ` - #endif. ]7 ?* l% n, y& N
- "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
9 q+ n0 f' F5 L r3 T! s; h! @ - NOR_FLASH, /* 设备类型 */$ {0 F1 J: Q; Z' ^7 c. g. M4 S
- 0x90000000, /* Flash起始地址 */
3 x" U+ P# n! J l3 O. g - 32 * 1024 * 1024, /* Flash大小,32MB */
# {0 m& N3 e2 `/ | - 4*1024, /* 编程页大小 */
& ^( C$ q# s9 J! r4 K - 0xFF, /* 擦除后的数值 */
* r0 |8 K j. a" q' T+ Y - 512 , 64 * 1024, /* 块个数和块大小 */
/ h9 I4 ]$ s. h6 B; b: C- u - 0x00000000, 0x00000000,
7 P9 s0 N7 |. ? B6 L p3 @ - };
复制代码
) A1 N$ N$ d8 @ q" a7 N注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:% {' }- E( W9 j2 t$ u& J* h
* R) T# \9 Q: s
5 T3 o- i4 R( |) P: b, R
0 [* ^ C( y0 ] w; ~ f1 G81.5.6 第6步,编程文件Loader_Src.c的实现( g9 q- M' X: u8 p$ }
下面将变成文件中实现的几个函数为大家做个说明:
d5 V$ L w# l5 W0 J0 y4 U5 m: D: O+ p7 d+ |! n
初始化函数Init
3 P4 e, B" g1 a- /*. C# Q7 R% S; j% t! ?* T7 g! x
- ********************************************************************************************************** {9 U: o0 X# x1 y4 ^
- * 函 数 名: Init6 }$ V- k# f5 K4 X' m
- * 功能说明: Flash编程初始化. r7 ^; Q5 H) r+ Q9 }8 D
- * 形 参: 无$ X8 X9 [' x4 l* m: R z
- * 返 回 值: 0 表示失败, 1表示成功
& y1 p4 F, l- _7 n - *********************************************************************************************************
- F, ]9 e% Q: [$ F - */
/ L$ e% x) n: u. a, I" Q }/ [ - int Init(void)
! }* ~$ i- t8 ^ a7 |, T: _: y - { 4 C3 Y7 ? G: i8 l Q
- int result = 1;
' c( a1 X. |1 k& P: D# G: E$ I3 X3 E - # ~ a* _! q" A: W) n+ f
- /* 系统初始化 */
7 g* c1 ]9 Z1 ?- Q- @* g8 j9 l - SystemInit();
! g, `. H8 L- t4 h1 Y h
' C7 Q" l" W3 ~) X3 q& ]- /* 时钟初始化 */
& p M9 |$ Y# o. q8 D# \. g- O - result = SystemClock_Config();
% s3 _5 H- y& o5 P D7 I2 `# ` - if (result == 1)! |7 G; o6 F3 Q. f* F
- {
* X$ C; @7 T8 x% P$ c - return 0; + D4 V' Y/ Q4 `) a$ \
- }
3 {% E7 @2 a* f; I# [
( N' v& ~( G! a% G9 V) N& z- /* W25Q256初始化 */6 l( n# _) T/ V5 Y8 W! T
- result = bsp_InitQSPI_W25Q256();
" Q* j& [0 m& D4 E g - if (result == 1)
3 F7 ^% ~9 P3 B0 [" X. j$ X - {, N; \, w x0 a" G1 }. s& d8 e
- return 0;3 x9 D6 z. A; d8 x0 ~! v" A5 N
- }
5 v2 d f @; w; J& S2 ? - 5 U9 R1 U$ Z2 @2 B W3 M
- /* 内存映射 */
9 M; @' l9 i# q3 d; E6 q - result = QSPI_MemoryMapped();
. K. r8 R Z1 v - if (result == 1)
/ j+ l% m) |, u( M9 f5 o1 i - {
# {5 a [9 |+ C - return 0;7 N7 R$ i: n+ I5 d: Z
- }
7 \8 b# g( D3 q: ] - Z/ v% P8 T% G6 l
- return 1;
% d) A- c* o+ T8 |! V+ r$ Y - }
复制代码 . Q" \7 D/ M/ R5 R7 u
初始化完毕后将其设置为内存映射模式。- e: a) U) K0 U+ |0 q: q
3 t" \; Z& W& b 整个芯片擦除函数MassErase
- P- P8 V7 _! z. W* ~/ U. r整个芯片的擦除实现如下:4 x' u, V/ {* @ _# V
( p7 a4 L7 Y, c1 t- /*
/ Q& G b" t% u7 g! n9 I - *********************************************************************************************************
0 X1 j; c- g) w1 Y, z7 F$ w - * 函 数 名: MassErase
$ ` P6 L% U/ j - * 功能说明: 整个芯片擦除
0 c5 W7 ]/ @0 u1 N {4 W - * 形 参: 无
o$ W! V' ~: r- G7 Q/ k - * 返 回 值: 1 表示成功,0表示失败
6 V7 |6 a- h& h, N" m/ @ - *********************************************************************************************************+ N/ X# f% a' b8 p6 r3 [8 F
- */ |: Q2 H5 J- K+ z- J# h: `( Q. q
- int MassErase(void)
( \4 f z; n1 \8 ~* j - {
1 R6 c. D2 Y. o9 {( { | - int result = 1;
' {. P0 g- A. }: \4 e5 {6 ?
1 N" S! L% y& n3 j/ l- /* W25Q256初始化 */* [) @5 C: p* N E1 ^' `
- result = bsp_InitQSPI_W25Q256();
0 @0 o- W7 T9 p% q* k7 Y0 a& ?* W - if (result == 1)5 k8 x2 s; y/ H, Q3 |3 M- n' q' U, {
- {9 P, U- @% _6 U. ?
- return 0;
) Z: B* ?7 R2 l+ \" l- f- n - }0 |9 o) ]8 b8 w- [8 n
2 t8 c( g, T) _8 T- O. r- result = QSPI_EraseChip();
: b% [' b2 j) e - if (result == 1)
7 V' f! E% Y: C - {3 v. T* f! D6 U; _/ M: T$ ^1 {7 M
- return 0;
: D0 D( S1 `$ H4 K - }
& ~" h% W: r5 Z3 k3 n - : v r& Y$ V& s* |9 }; W! {
- /* 内存映射 */ . b. _& B+ L' l3 A9 K
- result = QSPI_MemoryMapped(); ) {0 ]! {3 ?+ ?* E: j* ]
- if (result == 1)4 e/ k3 \( \: o- L
- {7 r3 K$ {* q$ a* Y/ \
- return 0;
/ h- N9 _3 U7 R/ _, M) ^ - }
, i( w: A9 I" D - ; L+ O# @7 T7 I" U( @7 q* U7 m
- return result;
; {: m, Y6 i; \& Y7 C" T - }
复制代码
2 e3 A* e! K s$ ^ 扇区擦除函数SectorErase
- L& u: [+ S6 ^( @; O$ X/ m- Z扇区擦除实现:4 z' x- C0 x; \ D9 l2 a
: M4 |9 X4 y1 q- /*
7 Y* i, `6 D% a. r - *********************************************************************************************************' _+ R& P4 D5 Q- ?6 m6 C/ n9 y
- * 函 数 名: SectorErase& b* T4 K9 n- B. R
- * 功能说明: EraseStartAddress 擦除起始地址
" u6 k' ~- m0 j - * EraseEndAddress 擦除结束地址
6 O- H* }0 @' q0 M/ M1 i% r2 A - * 形 参: adr 擦除地址
: w t$ m$ h6 Z% Y/ g$ y - * 返 回 值: 1 表示成功,0表示失败
F3 j5 U! v: m - *********************************************************************************************************
3 f& a6 ]" c! ~ O - *// }1 f3 j2 c6 r# [
- int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)8 u# e' v8 ^" R2 Q% Y
- {6 a/ R5 \2 [: k* y1 \3 ]% [$ l. [9 }
- uint32_t BlockAddr, result;
) u8 X. u: N/ G* ] - 1 B# T/ X& n) b4 `5 z% t& s
- EraseStartAddress -= QSPI_FLASH_MEM_ADDR;
; Z8 \) m6 L# V3 u4 b - EraseEndAddress -= QSPI_FLASH_MEM_ADDR;) }+ R, R2 [( H- L
- EraseStartAddress = EraseStartAddress - EraseStartAddress % 0x10000; /* 64KB首地址 */# o$ k' }8 `+ A( k( C" v3 j
; y7 A* H5 Z2 c/ |# @- d- /* W25Q256初始化 *// h5 s. O- Y, N# P, {5 A8 N3 U3 g
- result = bsp_InitQSPI_W25Q256();
. `* m# F' d% ?8 H* f# d! z x K - if (result == 1)! c/ t* N" O9 A+ B$ P" b
- {. r, P7 q' G- y4 C: l u! d7 a
- return 0; " @6 \( H7 u; o% }
- }( Q4 q7 k; O; X5 I2 R7 X
- ( ~) }0 ~2 o( x( }9 k2 }: a c
- while (EraseEndAddress >= EraseStartAddress)' |. }1 G! x$ n
- {; o8 e0 F# p1 G1 _" v6 v1 C5 k
- /* 防止超出256MB空间 */
0 X0 u9 ] G( l& Z1 O - BlockAddr = EraseStartAddress & 0x0FFFFFFF;3 Q% E! V: W3 s, \+ r0 s5 u
- - g+ \$ _% T# w8 ~! f
- result = QSPI_EraseSector(BlockAddr); : r: ~% V" i( n3 a# y
- if (result == 1): O+ S* [2 w$ Q2 Q0 `6 D8 V
- {9 ?9 M6 S- x* p! w
- QSPI_MemoryMapped(); 3 |, V8 N1 p' L4 { {" b
- return 0;
& y9 h4 q, N& C2 l# Q8 }/ M - }
: Q$ l2 A2 o+ X3 k
3 V! i6 m5 M) n+ |3 a. v- EraseStartAddress += 0x10000;
/ U3 g7 P ?2 z d Q3 `7 z - }. \ v3 @, h" \" U4 D
- $ V8 l5 I3 T1 B: m: z2 _' b: S; n
- /* 内存映射 */ ) r% s3 n8 S5 Q" v
- result = QSPI_MemoryMapped(); 3 U1 O4 b0 |+ X8 J" g6 I# U
- if (result == 1). H5 f I$ a/ e/ o) g* v. v1 g
- {6 Q% S' t6 l: G/ k# L' o5 x- \. [
- return 0;
+ J+ [4 y( l0 @8 j - } l6 T' i A( o" h
% O9 G6 @! F1 P, x6 a( ^' P' a- return 1; " p8 e# J( l* v' Y* u
- }
复制代码 / A$ M% O! A* @
这里要注意两点:( y/ n% ?2 a# u5 q. }% K5 F
9 T, c/ y s& E8 R(1) 程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
- M9 H, H5 ^3 {9 ~$ J# s$ h6 }& n: i& S( b Y$ d
(2) 这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。" `1 \# R1 M2 P7 U+ N
: o7 t6 M8 y& m 页编程函数Write
- D+ Q8 }' H: D; o页编程函数实现如下:4 V& a5 `( q5 }$ a7 u
0 x. |( F8 _1 f7 w* W. L+ p2 ?
- int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)1 e3 _9 i3 O, `
- { 3 J- T0 k7 n+ [9 I
- int result = 0;
* y& r/ C2 g4 | - uint32_t end_addr, current_size, current_addr;
% v. P. R+ Y0 Z" U
}- n$ M# w: N1 }
: _/ u: z8 G7 x- if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
! [/ N: u6 p" y - {) A5 }3 j6 l9 S, [
- return 0;& O* K+ }' ?, h1 t5 K& K5 L- a9 e
- }
@4 f- W, S) g: M% g( ]
7 Y! v9 H+ X: N! n8 J5 Y" f: y5 W2 ~- Address -= QSPI_FLASH_MEM_ADDR;1 S/ t# }3 h' d( W/ F0 S
- 9 x1 f/ e4 P. U
- /* 计算写入地址和页末的字节大小 */
6 {8 m- \. O4 W! n r# s - current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);8 M$ c4 X3 M y2 p* s |( ^) M7 b" |
- ! U; L- x% j# `
- /* 检测页剩余空间是否够用 */+ Y* N6 [9 j' K1 H7 k% T; _
- if (current_size > Size)# Z: u$ K4 p9 G; V$ b9 o
- {
4 }( I2 ]: L" e, U - current_size = Size;
5 ^1 x% F9 s" D/ c9 W8 I - }
' ^/ E! C, w# Y' @" u - ; P+ d; X9 W( L2 R# W! E
- /* W25Q256初始化 */
) n( b3 G. ]& }1 Z7 A - result = bsp_InitQSPI_W25Q256();1 |$ ^" K/ H1 \# `
- if (result == 1)
. b! x6 ~% D5 L; p+ w B - {
+ `2 \3 O3 T( v2 f+ c( z4 j - return 0;
2 _- ]! O9 v. ? - }6 q+ o; h$ a7 W) ~6 z% o* L) i
5 z% d0 S9 r2 ?3 d+ N8 g" D- current_addr = Address;
8 g. Q* M/ n3 ]/ K3 v( x - end_addr = Address + Size;
7 W. a7 {3 B6 N5 y7 e; s - ) I5 F+ g. j$ v0 N5 y' W$ K
- do{7 M) b8 B% E* N/ M0 x+ u
- if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)
2 R9 W& S0 Y2 V" s+ H9 Y7 p, n - {' [* R: l4 E8 d/ V# _* ~: A3 D% c
- QSPI_MemoryMapped(); ' Y$ J% C/ [5 j
- return 0;
4 G$ h. O/ T5 t. h; r" ? - }
6 I8 A3 k" @- I0 L' c; ^3 B P - 5 `. c) R& K$ N/ b5 S6 u
- /* 更新地址 */6 Q5 i% d2 N. V3 f0 h% p
- current_addr += current_size;
" q }8 O! V5 q2 G& o/ N( s7 ]1 b# Q - buffer += current_size;( e+ m- [3 V6 C5 @6 p0 A7 V% ^
- current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;& `7 \; I' S: H s; q7 n8 M
: g' _! v. y+ [; c( r- }while(current_addr < end_addr);
0 u0 T; g' ]5 [, l% j, W2 x
1 C0 z0 w: V5 r+ @- /* 内存映射 */
% { x% z+ v, J. k! i - result = QSPI_MemoryMapped();
, M f8 }1 a5 |! p - if (result == 1)
3 y9 s$ s& C, d# Z - {
! @. s3 H3 T( h! F: Y - return 0;
6 ]3 D" D6 f9 F0 K" P - }
: r! p& M$ s% _ - ( D. a2 ]+ `2 i2 b. Z
- return (1);
. K4 D- l* m1 f u - }
复制代码 J$ |. U/ O8 q, I. m3 f: u
这里注意两点:
7 J4 Q$ g) ]) d p
) [$ ?1 `; X: Z% D(1) W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。
0 ?+ Q# N* S0 g1 h% Q2 q( m) r! ~% _9 P) K
(2) 程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。9 K% I m2 m' u
* O, m# M G- R( n5 v0 S, }
读取和校验函数/ i( b+ c! N& Z" @0 D2 z. X
我们程序中未做读取和校验函数。+ s" ~) p3 y" j" I$ m4 l g
+ \* w0 N- J# U" C, M$ [
(1) 如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
) B/ U% U9 P) W% I$ Z
9 Q8 w3 Y9 I$ K, r: ?5 C+ h(2) 如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。9 } p$ l6 w! a% C
' I: q' D$ y1 p2 M/ Q: I
81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
- C$ Z+ E+ _) T: c4 L$ b4 g' A最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):6 C Q* E6 H# [, j( R+ Y) a. Z, B
' J3 k. f, d& o- /* ! Q6 A$ ]5 _- M1 d) m5 l+ S
- STM32-V7开发板接线
$ G! X: p- X2 V4 N$ k
3 f3 f. {9 E) P1 ?- PG6/QUADSPI_BK1_NCS AF10
J) u# R) X) b7 v/ q9 C7 }& Z - PF10/QUADSPI_CLK AF96 }1 F: Z& A; t9 R. O9 v
- PF8/QUADSPI_BK1_IO0 AF10! o0 Q5 ~/ ]8 X, ]/ o" u
- PF9/QUADSPI_BK1_IO1 AF10
0 q: R; ^8 R1 o8 u: J W7 x - PF7/QUADSPI_BK1_IO2 AF9
" [* C$ j+ E" ^2 s# X" b) N0 Y: z7 d - PF6/QUADSPI_BK1_IO3 AF9+ {$ ?# R# s( e+ I# M
- . {# \% Y3 T3 I) E4 E; J; @0 b
- W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB3 q6 D! P$ {) l% H0 L2 B
- ) [. t) D! z. o& X3 @
- H7-TOOL开发板接线. \/ _. V; C4 W
- 7 U( j }% L" \% g
- PG6/QUADSPI_BK1_NCS AF108 R8 r4 N1 ^! E7 S+ X/ s
- PB2/QUADSPI_CLK AF9
/ j& S3 o) o! j" {5 P - PD11/QUADSPI_BK1_IO0 AF10
4 A( P- t8 r) E8 ]9 N: A - PD12/QUADSPI_BK1_IO1 AF106 _/ U* C, b4 W' L4 b8 E `
- PF7/QUADSPI_BK1_IO2 AF91 O. T) t1 X$ v: p
- PD13/QUADSPI_BK1_IO3 AF9
' {# E3 v+ ~1 a5 b - */0 @0 c2 q9 v# P! Z
M p* L3 C1 k, c- /* QSPI引脚和时钟相关配置宏定义 */8 ?& i3 U5 a+ @; q6 Q% ~
- #if 0. Q$ e2 X" P; e* a( p
- #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE()$ o$ c" K8 I5 K; r9 V
- #define QSPI_CLK_DISABLE() __HAL_RCC_QSPI_CLK_DISABLE()
& r4 `( p2 v1 ?# @, q. A5 o& p( h - #define QSPI_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
+ F5 r2 h% M$ e$ l0 c+ { - #define QSPI_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
( n6 S1 ?6 A! f4 J8 C1 D9 @ - #define QSPI_BK1_D0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() z. y& w: N _! u. p3 ~
- #define QSPI_BK1_D1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()
0 Y, U+ ~0 ]1 K5 I- a* D6 J - #define QSPI_BK1_D2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
$ L4 w- J( k/ Q - #define QSPI_BK1_D3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()9 P e% t2 ]- e2 Q. }& f
* s& e# G% ^2 R/ L4 Q- #define QSPI_MDMA_CLK_ENABLE() __HAL_RCC_MDMA_CLK_ENABLE()
3 m% q7 ]( L' K2 _. i( X1 \# ]8 N) X - #define QSPI_FORCE_RESET() __HAL_RCC_QSPI_FORCE_RESET()9 j" z9 W+ g+ s
- #define QSPI_RELEASE_RESET() __HAL_RCC_QSPI_RELEASE_RESET()
. j/ }) [7 E8 f- y
' H" A% G: s. |& l$ O+ r; w- #define QSPI_CS_PIN GPIO_PIN_6/ v/ x' z5 y+ K( [7 g4 M: z8 m
- #define QSPI_CS_GPIO_PORT GPIOG
' k, N, y# ?% q! y0 F% Q2 r3 h - #define QSPI_CS_GPIO_AF GPIO_AF10_QUADSPI
3 k' Z' v2 |3 |/ d n+ { - 4 \$ L {1 w0 H: x, C3 Y) Q
- #define QSPI_CLK_PIN GPIO_PIN_2: _) W7 i! b$ O1 R4 h
- #define QSPI_CLK_GPIO_PORT GPIOB
, F- q" A* |; z B, C8 ~2 c - #define QSPI_CLK_GPIO_AF GPIO_AF9_QUADSPI
! j* }( T' z, d3 X! K+ I9 `
7 {# n# h1 p5 E; T8 F* f1 ?- #define QSPI_BK1_D0_PIN GPIO_PIN_11
; w5 \1 Y& b, g0 ]5 P+ y - #define QSPI_BK1_D0_GPIO_PORT GPIOD0 S R# b" ?. M
- #define QSPI_BK1_D0_GPIO_AF GPIO_AF9_QUADSPI
8 P( t: m; c; J' j3 _
+ J5 G& a2 C3 X7 g- #define QSPI_BK1_D1_PIN GPIO_PIN_12/ L% ~& w; M6 @! w2 r7 L$ q2 ?. k
- #define QSPI_BK1_D1_GPIO_PORT GPIOD
) j2 G0 K( |0 r - #define QSPI_BK1_D1_GPIO_AF GPIO_AF9_QUADSPI
0 V+ r# ]6 Y/ K% O9 p - % |7 Q/ L! W# W/ V: |9 S
- #define QSPI_BK1_D2_PIN GPIO_PIN_7) ^( E( @+ F6 ^1 w' G
- #define QSPI_BK1_D2_GPIO_PORT GPIOF
' W2 M% [) M$ s: Q - #define QSPI_BK1_D2_GPIO_AF GPIO_AF9_QUADSPI9 l t- Z' N/ D) t% k9 l
, K- F; E# W# ?; d0 Q- #define QSPI_BK1_D3_PIN GPIO_PIN_131 j3 H2 G* X4 P, k
- #define QSPI_BK1_D3_GPIO_PORT GPIOD( o6 ]+ } w. [0 m+ G1 O: S7 b% z5 u
- #define QSPI_BK1_D3_GPIO_AF GPIO_AF9_QUADSPI2 C* Y# L8 Q y7 ]& V3 [/ ]
- #else' j# d: X' m, t
- #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE(): D- j. a8 f8 v. b( v& C) h; B
- #define QSPI_CLK_DISABLE() __HAL_RCC_QSPI_CLK_DISABLE()8 R p9 D2 R& C3 p
- #define QSPI_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
+ j0 w1 l1 ?' m2 W9 p - #define QSPI_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
9 j5 k* L3 e; o3 e9 ~3 w- p - #define QSPI_BK1_D0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE(); N$ l) J! D9 s; z7 p6 s O; e
- #define QSPI_BK1_D1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
- y+ Z/ j5 J J2 [! M, ]% w% D3 { - #define QSPI_BK1_D2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
7 r, t( J! G- I! c - #define QSPI_BK1_D3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE(): R- I* X! v8 G
& ~# [( M! Q& w8 ~- a+ i- #define QSPI_MDMA_CLK_ENABLE() __HAL_RCC_MDMA_CLK_ENABLE(); s4 B _6 e0 j! j7 U3 p. @) w
- #define QSPI_FORCE_RESET() __HAL_RCC_QSPI_FORCE_RESET()
1 S3 D7 h3 ^, S& k' h$ w - #define QSPI_RELEASE_RESET() __HAL_RCC_QSPI_RELEASE_RESET()
1 j$ `( O7 Z8 o
+ q5 W3 ^4 p, V" G+ q, ~. R- #define QSPI_CS_PIN GPIO_PIN_6
+ P- C q/ v8 m$ `) l+ `* c F! C - #define QSPI_CS_GPIO_PORT GPIOG% S9 c$ E4 {' y) m! ?- ` {
- #define QSPI_CS_GPIO_AF GPIO_AF10_QUADSPI
1 g# f+ {4 g5 S; }( K6 ]
# T. B% _' n( w; c- #define QSPI_CLK_PIN GPIO_PIN_10
0 t' N% Y+ w' V% h0 D - #define QSPI_CLK_GPIO_PORT GPIOF
! Z: ^' F, I& n+ Y" T( i# U - #define QSPI_CLK_GPIO_AF GPIO_AF9_QUADSPI
8 k8 Y9 X: ]5 u8 d
/ c& Q! c& V& V4 t$ O4 B/ z- #define QSPI_BK1_D0_PIN GPIO_PIN_89 [$ P$ a1 r* j) K5 ~5 m
- #define QSPI_BK1_D0_GPIO_PORT GPIOF
" ^( o, m1 n) J$ v; G/ M1 h - #define QSPI_BK1_D0_GPIO_AF GPIO_AF10_QUADSPI
! D/ g3 a3 ^4 N6 X+ @0 K - 7 J5 b- t& Z; y1 w1 D5 P
- #define QSPI_BK1_D1_PIN GPIO_PIN_9
0 S5 U) O- Y1 R2 W- _' M - #define QSPI_BK1_D1_GPIO_PORT GPIOF
1 W# W1 A- I% e/ J) n* C$ | - #define QSPI_BK1_D1_GPIO_AF GPIO_AF10_QUADSPI; a$ w5 b+ a& T9 z( }0 i% d
! Y1 G9 f* U8 W. K# u! w0 t- #define QSPI_BK1_D2_PIN GPIO_PIN_7# e" r$ u( }! Q, ?% i
- #define QSPI_BK1_D2_GPIO_PORT GPIOF' g0 I( x/ B' G$ O+ D7 V
- #define QSPI_BK1_D2_GPIO_AF GPIO_AF9_QUADSPI( U% q. W' F" ?; V, W! Y# I; j+ `6 U
- x; v5 r# `5 B
- #define QSPI_BK1_D3_PIN GPIO_PIN_6
# ? w) P) h& i" }& h9 R/ A8 o - #define QSPI_BK1_D3_GPIO_PORT GPIOF
& r0 ^' Y2 |* k( c% Q - #define QSPI_BK1_D3_GPIO_AF GPIO_AF9_QUADSPI
) [$ Y* g# C$ _! B - #endif
复制代码
% {8 }5 d( p+ e9 {7 _硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
L$ |3 {% I/ G+ U6 c$ }% Q* h7 L/ j0 n3 s$ z1 G U0 p5 ^& ]
主要是下面这几个:+ t" q8 _. [9 e+ a3 V6 W% k
4 D$ V' _( Q( s( f# k
- #define QSPI_FLASH_MEM_ADDR 0x90000000
$ a/ C. G" m4 h% o6 H - + T9 f8 m. `1 r+ Q1 v2 H3 w
- /* W25Q256JV基本信息 */
8 }/ K7 h' M7 V' Q$ P - #define QSPI_FLASH_SIZE 25 /* Flash大小,2^25 = 32MB*/# b; S7 v" l& H8 Q0 v, [5 d
- #define QSPI_SECTOR_SIZE (4 * 1024) /* 扇区大小,4KB */* ~/ b+ e) ~' ^+ H5 {
- #define QSPI_PAGE_SIZE 256 /* 页大小,256字节 */& ^: R/ k. D, d
- #define QSPI_END_ADDR (1 << QSPI_FLASH_SIZE) /* 末尾地址 */
" n' t9 x- _1 d' Z- f1 n# H s- X - #define QSPI_FLASH_SIZES 32 * 1024 * 1024 /* Flash大小,2^25 = 32MB*/
" ?$ O! y5 H: i
. [; c) |) u! R9 x8 k- /* W25Q256JV相关命令 *// j% M8 S( k- {! n9 K+ @1 u
- #define WRITE_ENABLE_CMD 0x06 /* 写使能指令 */: I7 t# p, l8 b* d# I
- #define READ_ID_CMD2 0x9F /* 读取ID命令 */ M3 P0 N X* k# l
- #define READ_STATUS_REG_CMD 0x05 /* 读取状态命令 */$ Z, x1 @7 i- ^4 V6 c! s* Y
- #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD 0x21 /* 32bit地址扇区擦除指令, 4KB */
$ J4 q% l; h+ ~- m& a - #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD 0x34 /* 32bit地址的4线快速写入命令 */
: H/ Y9 e) t- ?% j$ J6 y6 z/ p - #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD 0xEC /* 32bit地址的4线快速读取命令 */
/ r! k/ g3 S7 F( D - : X; Z" K% I" X! c
- #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD 0xDC /* 4字节地址,64K扇区 */3 i y$ ~& }7 T Q' s
% g. [+ W& L( ?$ `- #define BULK_ERASE_CMD 0xC7 /* 整片擦除 */
复制代码
+ \% X. g* }+ l- x" S7 M81.6 QSPI Flash的STM32CubeProg下载算法使用方法. F- z+ x& A4 z4 D
编译本章教程配套的例子,生成的算法文件位于此路径下:
/ y4 x$ d6 J8 e- B
8 w4 [: K. L/ n( v/ L0 B; ~
( @( h4 y; Y$ v; u0 ~4 O5 }
5 ?3 F) u9 H) Y$ N. R( C81.6.1 下载算法存放位置& ~. V- X5 b9 a/ Q. }/ i
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
" L2 T$ }; ?- @; k. d3 \1 t4 q6 J, D# r8 G9 _) G" `% L6 X
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader6 ]+ S6 I# m: W8 I
0 u1 [# i$ ~5 B
9 b- p1 j. V6 ~: _2 H
. }( z+ |/ F: C
81.6.2 STM32CubeProg下载配置
- d- V; y0 S, l/ B# ^: {8 F8 ]$ |我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。! _/ m( m7 C* X$ X: \/ z
7 S! C6 _3 c2 D) F9 C* N' x4 h; j& C Y g" Y% ?; W: n* N% G4 P; I5 A0 P
3 [8 N, P/ @1 r$ H# w; o: v点击Connect后效果如下:
, K" |* P. o# A; v# ` v) G( I+ Y, J2 s0 J
3 O+ b7 l' W6 A
' u- k H5 F( Y# ~/ T在这里选择我们制作的下载算法:- d+ {$ O$ t& e# @3 |1 j# D
; ]( s) Z3 Q8 l+ }. q) W
; ~- r" y4 p8 E- Z, F! ?6 S2 @8 y5 _% b$ f" N& Z% G
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming% v% X$ w& _: k2 c, p" U7 u, ?
) }2 `9 r4 X0 P
: w' A2 [. D- J1 Y Y% j4 ]
* k+ h. S& y1 T% S$ d下载完成后的效果如下:* `# E4 f6 s& Y: w- V
. _/ ?+ m; h: @
6 g" a7 y6 U' ~7 u, c: O n3 ~/ h$ y9 x8 [/ K! v9 U3 O
81.6.3 验证算法文件是否可以正常使用9 v* q8 o3 h8 |! Q+ ?. I
为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:: r/ ^9 N, ]; ]; c$ C
6 c9 m9 s7 U/ ]+ c
( \5 u: E! `# n, \
1 V E2 K2 q) p# Q- r# _81.7 实验例程说明0 M' ^! @# _ h5 ` V% w
本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。
* s6 R0 u" @/ m& `, O8 P' q R; y. N9 I0 H
编译本章教程配套的例子,生成的算法文件位于此路径下:8 v; H, i, N( l7 s1 |
0 t: i, ?+ q( M/ J, [
/ P; b0 {# y% Y0 a
1 R9 P+ J* e: m
81.8 总结
4 l0 e, ?+ a8 ]6 o6 @' M4 O本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。+ T: v) N }. G8 _: _/ P4 t% t
5 ~" S9 Z$ V8 K8 D( G
! W: S- l! V6 O( T" Q m* r% z5 F
! U# Y! D0 K$ @. C; ~6 n& F( y
|