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