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