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