78.1 初学者重要提示: `5 ?. B" F2 L5 T
QSPI接口,可以做1线,2线或者4线使用。
( \* n/ q5 b; o4 C0 r 注意,QSPI接口不分主从,QSPI仅用于主控。
2 C r5 n& t$ g) e; `- H) A 可以单独使用BANK1外接一个Flash,也可以单独使用BANK2外接一个Flash,不可以BANK1和BANK2同时独立使用。但可以两个BANK合起来做双BANK(也称双Flash,即dual flash)使用,即8线模式。, O. A- a' U; X$ o4 Z$ o V
STM32H7可用的单线,双线,四线和八线SPI QSPI OctaFlash等涵盖各大厂商
% y" L0 C% \ t1 Y78.2 QSPI总线基础知识 _+ A% |/ n. E) S
78.2.1 QSPI总线的硬件框图
% f* g8 U, l! Y1 Q$ c G, H" I认识一个外设,最好的方式就是看它的框图,方便我们快速的了解QSPI的基本功能,然后再看手册了解细节。& I/ s% D" u1 S5 Q+ i3 f& u
L7 Q0 n e6 @, O( A$ x$ [BANK1外接Flash! Y3 n% V2 a# Y% |! q8 e! ^
0 j4 Z3 w: f* i. H5 Q- L2 r, a! [
. h8 |1 e+ f' P# T+ r' t6 P0 ^4 D- H/ ?# y( B$ }; l, _
BANK1和BANK2都外接Flash:# R( E. Q) `( v0 }" X5 c: F& Y
/ l" N% [$ C4 C% \. o* d1 Q7 a* _+ P2 q0 k) y0 Z$ \
& V1 U: a) v- n; E ?通过这个框图,我们可以得到如下信息:
V: v$ h: h) L7 p: F1 Z3 e" {1 ~( I+ G: ~4 O
quadspi_ker_ck时钟输入
- S4 _2 h2 z4 \7 x: n) U: W内核时钟。2 k: s3 t# x) @, P- F/ Y
. w2 l9 X; U% B2 J6 b" J
quadspi_hclk时钟输入
: X* J) O4 O# \% b为寄存器提供时钟。0 V1 j2 V! z* M; m+ F8 c- @
9 _! }0 R g/ x8 ^+ g# T# o/ F+ F quadspi_it输出信号. Z% l! a7 _. c% i8 Q8 T& P
QSPI全局中断。
5 r }( r O6 L! `! E: }
7 q$ J6 A \1 Q( Q- q6 J quadspi_ft_trg输出信号
4 [2 a0 @; r) H1 b P% sMDMA的QSPI FIFO阀值触发信号! }7 v3 D( F1 N. }% p3 E
T0 p2 w) m* f
quadspi_tc_trg输出信号
6 c$ b& ]* b9 M/ o. lMDMA的QSPI传输完成触发信号% {, Q7 { |# C, H
" U: H9 m& L$ z( t
CLK* K# p% Q+ |! u0 S& b( J
为外接Flash提供的时钟。为外接的两块Flash同时提供时钟。
, Y! r/ T/ _) e2 F( ^9 I7 K
8 z+ Q( } g* N$ k: C BK1_IO0/SO+ Y3 K+ _9 Y B# \3 b
在2线或者4线模式中作为双向IO,1线模式作为单向输出,供Flash1使用。' @9 c) g" G1 M J( n/ \# w v
/ u, K( q* H4 H( J5 G5 B* H
BK1_IO1/SI3 o% z1 a2 q- A
在2线或者4线模式中作为双向IO,1线模式作为单向输入,供Flash1使用。: G7 B0 r/ q) s$ {3 r* C" X
( `$ N) o6 n: ~0 K
BK1_IO29 V2 r0 F3 y, ]0 ^3 Y1 S* m) V. ]- ^+ M ~
在4线模式中作为双向IO,供Flash1使用。3 p# O) l$ ]2 X- m2 G- x8 x
( a: r1 D. @$ z2 t
BK1_IO3
8 B$ E3 L: W$ s. T7 Z% [在4线模式中作为双向IO,供Flash1使用。1 J3 B; m& ?! o. `7 g
- Z) X; v3 h% \" R8 m
BK12_IO0/SO' z% g( H1 j5 H( o* u7 L
在2线或者4线模式中作为双向IO,1线模式作为单向输出,供Flash2使用。
$ f$ o6 K$ S# a( m' ?' w' I
8 p0 x) d4 @' @* L BK2_IO1/SI
( [+ r. d- ^. \- x+ R在2线或者4线模式中作为双向IO,1线模式作为单向输入,供Flash2使用。
& p) e& j* Q* k, B6 [ Z4 g6 W3 P* f3 `3 ^" b- n
BK2_IO2
' Z# O/ c- l# ]2 l在4线模式中作为双向IO,供Flash2使用。1 L5 Z* f# e% y# S$ Q) p
1 I0 g7 n f1 o/ b' O
BK2_IO3% z/ o4 z! i$ _$ c z0 [- d
在4线模式中作为双向IO,供Flash2使用。
1 l; n7 c1 ]8 {3 g) ~
y3 m* D' _ \3 f1 y2 ^ BK1_nCS
$ _4 f) i' x8 t: Q# z2 L# p5 ]" ~片选信号,低电平有效,供Flash1使用。如果工作在双bank模式下,也可用于Flash2。
! I9 c: f! \6 e g3 {/ x& m7 [7 R9 e8 L
BK2_nCS' \; }% o' M; G+ @
片选信号,低电平有效,供Flash1使用。如果工作在双bank模式下,也可用于Flash1。& |7 \( r$ a/ t" G7 l
- F& L3 l8 O+ ~& M/ N1 Y* V78.2.2 QSPI的时钟源 B4 |0 h H7 Q% T' e
这个知识点在初学的时候容易忽视,所以我们这里整理下。) p- w" q- d! A. N5 V6 o
' E5 f+ b) B# u @2 R
STM32H7主频在400MHz下,QSPI的最高时钟是200MHz,可以选择的时钟源如下:
! q1 {$ J1 L9 K2 i; F. Z3 f! X* b4 p7 o. c2 `2 i- h
9 y6 q4 ^8 S: M9 Q$ ^ L
) {" |& y+ ~# H. R+ Z
) Y3 b4 p( e( V$ @8 P5 R3 c( J
& R! E3 q0 P8 y; j3 `0 u) S' \; ~: h0 u4 C2 W1 S2 |
我们的程序中默认是采用的HCLK3作为QSPI时钟。; D. K9 K. w2 U, k! W- b
2 Q4 V1 W% H8 ^
78.2.3 QSPI 命令序列
# y1 _2 B' M! |0 kQSPI的命令序列主要包括以下几个阶段:指令阶段、地址阶段、交替字节阶段、空指令阶段和数据这五个阶段,任一阶段均可跳过,但至少要包含指令、地址、交替字节或数据阶段之一。. z# v* O, x1 i0 ~( @$ H
+ x6 B# ~, v: n0 v" t! ^7 Q- X
3 o w8 r6 n) g5 S3 ]
4 ]1 O+ V& k, s6 o 指令阶段
: v. B) P" ~# m: l. t在此阶段,将命令(8位指令)发送到Flash,允许发送任何值。用户可以简单地将所需的命令写在指令字段中。根据软件和硬件配置,可以1线,2线或者4线方式发送。在某些只发送地址的案例中,指令阶段可以跳过。
: s1 u. c' f6 o
7 g$ j6 f) s( Q! V6 v+ Z1 S 地址阶段
. ?/ v8 u4 b$ d$ H在此阶段,将地址发送到Flash,从指定的地址读取或写入数据。 地址阶段是完全可配置的,允许发送1、2、3或4个字节的地址。在间接模式和自动轮询模式下,用户可以简单地将所需的地址写入QUADSPI_AR寄存器。据软件和硬件配置,可以1线,2线或者4线方式方式发送。 在一些不需要地址的情况下,可以跳过地址阶段。! a* J" u5 x$ p# Q: i
1 P9 k# \' A: Q2 o
交替字节阶段
# p4 c) h. Z o" N* UQSPI接口支持的一个额外阶段,具有更大的灵活性。它是通常用于控制操作模式。交替字节阶段是完全可配置的,并允许发送一,二,三或四字节。
# F$ B5 Z3 W; j z. Z
$ ?2 M9 q Z9 ? j& r 空周期阶段
0 {$ M5 u, ^( Z9 [4 E在高速时钟下运行时,此阶段可以确保有足够的“周转”时间从输出模式切换到输入模式。
" ^5 O0 G7 s; j* y+ E
# V% E/ a L1 h5 N }9 u5 g/ ] 数据阶段
; Q! z! V5 l/ {这个阶段实现数据的收发。2 {1 d# ^% O, k2 r) B2 N" S
! z& {! a6 `1 J; l2 m) y# v2 n
78.2.4 QSPI的1线,2线,4线,SDR和DDR模式; }, S6 G9 i0 F" ^
这里所说的线是指通信阶段使用的数据线个数。
' t& b( v* L' V% w J% f6 I, c, s5 u2 f6 u* |. l' y9 e
8 h9 [0 M3 u6 ]* U
5 A) P. _1 e5 w; i! K1线(SingleSPI,虽然是一发一收,但属于1线方式)
: w7 W) S" R; ^7 W/ ~发送用的数据线BK1_IO0/SO(BK2_IO0/SO),接收用的数据线BK1_IO1/SI(BK2_IO1/SI)
$ l' S' q* [ ?2 ?/ z. P* d3 C
; _5 {5 T" N2 v, M1线模式下,所有线处于的状态:
6 _( o3 f; ?: ^- o- J! N; |" T$ X* M0 R% D, A* z
(1) BK1_IO0 / SO(BK2_IO0 / SO)处于输出模式。, t) T, ^' x0 Y: I1 ^5 y' A
' d' [) ^! S( e9 Q& T( [ (2) BK1_IO1 / SI(BK2_IO1 / SI)处于输入模式(高阻抗)。
& ], L( o- s% b1 R2 A7 L2 M+ U7 Z! }0 V
(3) BK1_IO2(BK2_IO2)处于输出模式并强制置0。; T' m4 j! M- i5 @1 z
5 I! c5 N; O" P1 e/ O' M
(4) BK1_IO3(BK2_IO3)处于输出模式并强制置1。0 ~3 I# L# c, m. d/ O5 S) y
$ r3 S4 h. ^+ w: a1 s$ J, ?0 E# R% x6 ~/ ?
& ^ ?4 h1 a( ^1 `
* [, V1 b6 F% ^9 C4 r& L; K' n6 H 2线(Dual-SPI)% W6 U; g c5 d, q! K) \
同时使用BK1_IO0(BK2_IO0),BK1_IO1(BK2_IO1)做输入输出。7 Y0 p+ O) @; V$ `2 [6 D2 m2 f
z, {- A2 r! ~4 \! F7 [% m
2线模式下,所有线处于的状态:8 l, g5 H4 V2 z! H6 n6 D% a% w0 ]
+ f2 k/ U7 F3 B, d( J7 U6 b; t+ W (1) BK1_IO0 (BK2_IO0 )和BK1_IO1(BK2_IO1)读取时处于输入(高阻)。其它情况下为输出。6 q5 x) ^$ E4 w2 y
5 C+ @3 }' n" E. I; X; K
(2) BK1_IO2(BK2_IO2)处于输出模式并强制置0。
! b ~& }; E9 W9 ^% b( ^3 F; B% g; v" p
(3) BK1_IO3(BK2_IO3)处于输出模式并强制置1! w4 r$ i, _6 A8 Z! X$ ]( T
4 d( k& ^0 D! ^8 p/ @# w. K$ p+ f% u: W
5 E ~6 H* A8 K4 f* v4线(Quad-SPI)
: U8 s: b( }: ^) \同时使用BK1_IO0(BK2_IO0),BK1_IO1(BK2_IO1),BK1_IO2(BK2_IO2),BK1_IO3(BK2_IO3)做输入输出。
% X: V/ W! O* ^- O" s" z0 ^. L( O7 h+ P3 d! @+ ?. O& }2 I9 B/ S/ ~
4线模式下,当读取数据时,所有线处于输入(高阻),其它情况作为输出。/ c( G7 f# M8 ]- u8 K( y g2 R
- U, N. {* a# d. f$ q/ h: O9 i
" B B P- @ W0 x* ?2 ~4 F4 S; M. S0 C" b& m4 _! n f
SDR! w' S' M" }. B! m- B' I8 b# S$ I
在 SDR 模式下,当QSPI驱动BK1_IO0(BK2_IO0),BK1_IO1(BK2_IO1),BK1_IO2(BK2_IO2),BK1_IO3(BK2_IO3)信号时,这些信号仅在 CLK的下降沿发生转变。
" {( J7 h% W1 k/ ]& c" }1 L% C& E" D! k
DDR
2 b: c/ Q! @3 P# Y F. ]在 SDR 模式下,当QSPI驱动BK1_IO0(BK2_IO0),BK1_IO1(BK2_IO1),BK1_IO2(BK2_IO2),BK1_IO3(BK2_IO3)信号时,这些信号在CLK的上升沿和下降沿发生转变。' c& x2 d" ?4 x( _4 y
9 r" B$ C7 }1 `$ y. { 双BANK(双Flash)
( H8 S2 X, O/ _, R, J- }1 ` Z双闪存就是将QSPI的两个BANK分别接一个QSPI Flash,然后时钟公用,片选公用(也可以不公用),从而实现8线模式。4 E( H" X# Z# G% U, r& N9 D8 v% j
" i+ d! o1 F R* O& i
- m6 F- p2 ~& u) g4 P0 W/ d* A
. i; \2 i5 O$ q
78.2.5 QSPI间接模式,查询模式和内存映射
6 o4 P$ z2 K/ @5 uQSPI支持在以下三种模式下工作:7 ]2 c7 Y6 V+ c( m) ^/ S
1 L2 Z- H/ x3 Y2 P& F8 P) J
1、 间接模式:
6 `$ W( e! [4 Q; b7 c/ Q0 w7 O( Z q$ g3 n3 w3 T% a: G
这里所谓的间接模式是指寄存器方式访问外设,就跟我们操作串口外设一样。间接模式主要用于以下场合:' [ f- n/ k. x
3 M0 d. K' \8 F
用于读取,写入,擦除和配置QSPI Flash。; g: A# x8 g! U9 ~% i1 ~* E( E3 G: q# n
如果不需要AHB总线访问 QSPI Flash(在内存映射模式用)。
8 {( z5 I- z9 \ CPU或者DMA通过QSPI数据寄存器执行所有操作。# J+ y( w0 V; v
o9 y4 C, ~ x) w' v% z$ h1 j+ G) S
在间接模式下,所有操作均通过QSPI寄存器执行,含读取和写入操作都由软件管理。 QSPI接口类似于经典的SPI接口。传输的数据通过数据寄存器与FIFO。在在此模式下,可以从大容量的外部Flash读取数据或向外部Flash写入数据,可以支持到4GB容量。
* x0 V. t8 U3 Q1 y$ y z1 t2 y3 m1 O: m7 R( d' K* |
如果进行擦除或编程操作,则必须使用间接模式,并且所有操作必须由软件处理。在这种情况下,建议使用状态轮询模式,然后轮询闪存内部的状态寄存器以了解何时编程或擦除操作完成。
# m" j7 y5 x) L% W/ Y8 p# l5 J3 H1 {6 O
6 w) P. X& g! r; W4 d1 z9 C( k: v- n& l4 F/ b; P
2、 状态轮询模式' ]4 L: e3 V9 o/ ~4 O
9 ^1 `- e0 s# I% T H; S+ @/ j2 j
在以下情况下使用状态轮询模式:
5 s2 w$ d) \2 z6 n4 p0 q8 V0 A* d, t5 W6 W! B( V# H
读取QSPI Flash状态寄存器。, t9 ~; Y! h- N* A" Y1 P
在操作结束时自动轮询状态寄存器。' c% e8 Z& t, h4 @/ K y
可以自动轮询内存中的指定寄存器并减轻CPU负担,例如检查擦除操作何时完成。QSPI也支持定期查询QSPI Flash,并且可以屏蔽返回的数据位,将所选位与所需值进行比较,结果比较可以通过下面两种方式处理:
3 ]* k G3 z# |& D( X# Q6 M, y
9 t1 P8 c [/ N( P7 J, X, C: n AND与操作模式:如果所有选定位都匹配,则产生中断。
2 b H0 X, P# M w OR或操作模式:如果所选位之一匹配,则产生中断* T5 c8 G) e7 T: ~3 s
[, K0 I3 H3 s
. _5 @6 L% n% D- Y5 k
发生匹配时,QSPI可以自动停止。4 B3 A0 S. f" m. }7 E2 \, Q e+ ]
8 _ m- ^) P( J4 t; g
; \) t* y8 U5 b6 G- o+ I& L) X" H# K" J
3、 内存映射模式( k3 N6 D" j( {% G( w3 D7 Z/ W
( r' U2 v* g5 g/ R' W! X
在以下情况下使用内存映射模式:
/ d/ s8 L7 A9 S" d3 _- w* N! c% Z4 d+ ]( f6 f0 f
用于阅读操作。/ c8 K/ l( b. D8 F+ c
像使用内部Flash一样使用外部QSPI Flash, 任何AHB总线主控都可以自主读取数据。8 j- s' j/ \7 |' b
用于从外部QSPI Flash执行代码。
: x- m) j) i2 J4 n* R1 s2 A/ s; I9 ^$ J
+ a3 _* C) ~# H6 ?* g
QSPI接口能够管理多达256 MB的内存,在内存映射模式下地址范围是0x9000 0000到0x9FFF FFFF。# G8 e3 l4 V" e9 t; n- F" j% g
r( N; _ b% v; K% y
78.3 QSPI总线的HAL库用法& g s5 v0 \# z. }( p8 Y2 ~
78.3.1 QSPI总线结构体QUADSPI_TypeDef3 o( S! d5 o/ r- z0 a* ^
QSPI总线相关的寄存器是通过HAL库中的结构体QUADSPI_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:
6 E2 U9 O: {2 t2 W$ O
R, K( r( j% [; j4 k- typedef struct
* K- P' F/ \9 |9 ?/ C - {1 o3 Z# M8 y# C0 w- K& `
- __IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */$ E8 w/ i M. `9 w V
- __IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */' A& a2 ?5 O4 a# |- C$ A
- __IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */
( E& L) d$ ^( {. S7 h. C% Y - __IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */
- }& E3 n+ f! k" M - __IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */
) a! x, a& D: s9 G) O& w - __IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */! d6 e* `7 V/ ?6 T( c5 f H' d
- __IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */
0 h2 b6 u2 B5 Q' T - __IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */% y+ N( ~% b6 a/ G* P G: c: g+ x
- __IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */" B" C1 M9 Q8 O
- __IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */
1 B9 J. t/ [( A6 ? - __IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */; a9 S" c4 p. _4 ~6 |# C
- __IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */
) C* l9 n- r6 t: g6 L - __IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */
% b2 {+ C6 b( d - } QUADSPI_TypeDef;
复制代码
. ?# p9 X, o0 u% Z T& Q这个结构体的成员名称和排列次序和CPU的寄存器是一 一对应的。; i' c$ \1 U& ?$ K+ w. L; z, v
( s1 n/ C8 N! @7 }- F2 K4 Z
__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:$ g/ F M! j; e9 E- @" l+ v: r4 t
- #define __O volatile /*!< Defines 'write only' permissions */; e$ A+ p h) q; u2 @ t% }
- #define __IO volatile /*!< Defines 'read / write' permissions */
复制代码 & v% J- Z @2 K* S0 h
下面我们看下QSPI的定义,在stm32h743xx.h文件。2 L. x; z/ D- _$ c1 H
3 M E [7 M' ~ Y) Y4 C6 x+ E4 ^1 C
- #define QSPI_BASE ((uint32_t)0x90000000) /*!< Base address of : QSPI memories accessible over AXI */
8 Z9 y, a w4 J+ e- E - & j/ O6 ~8 U- `, r9 i
- #define PERIPH_BASE (0x40000000UL)
7 \' X! [" d, W, X( i! h - #define D1_AHB1PERIPH_BASE (PERIPH_BASE + 0x12000000)5 e$ @" K& ?# V0 K! V# U$ j! e
- - S& l* t2 _8 r3 G/ @. n7 R$ Y& a8 w
- #define QSPI_R_BASE (D1_AHB1PERIPH_BASE + 0x5000)% L+ D, `, `, A$ J
- #define DLYB_QSPI_BASE (D1_AHB1PERIPH_BASE + 0x6000)
8 J# R3 {. r- n# F) r7 d8 S; U( e$ S) h - 2 y0 `$ v& g U
- #define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) <----- 展开这个宏,(FLASH_TypeDef *)0x52005000
4 w ^) y+ x6 y) N9 v - #define DLYB_QUADSPI ((DLYB_TypeDef *) DLYB_QSPI_BASE)
复制代码
( Y) K2 D1 \1 a1 q0 ^- h9 q我们访问QSPI的CR寄存器可以采用这种形式:QUADSPI->CR= 0。
( o5 h- g& R1 ~: p
( |) @% G1 t+ U! z; @$ W, ]" n* X$ M78.3.2 QSPI总线初始化结构体QSPI_InitTypeDef
1 C( r2 y$ X- w下面是QSPI总线的初始化结构体:9 T. e, q. W3 X% s! Y+ \1 f: S# K
. R4 W) ^7 Z# j" v% s" m( r6 `
- typedef struct
0 ^+ s3 W# n& H$ v - {
2 H5 B8 ~* ^3 M. c O. R6 U - uint32_t ClockPrescaler; ; ]- H4 o P m; ~% q9 O7 M& a- ?
- uint32_t FifoThreshold; 3 f8 u( ^; I' ?8 d( L2 j1 g
- uint32_t SampleShifting;
% H- t& M9 z/ o! o( h - uint32_t FlashSize; ! i, k, @+ }8 L% G9 H! _( K* L
- uint32_t ChipSelectHighTime; 3 y1 m7 P9 X0 n* S* m0 g
- uint32_t ClockMode;
: c2 S7 h* X6 b0 k; z - uint32_t FlashID;
( L F5 u) X2 u- o$ x - uint32_t DualFlash;
. [+ o& q3 z: r - }QSPI_InitTypeDef;
复制代码 4 ]; G0 M2 f0 D* P/ `
下面将结构体成员逐一做个说明:$ ?4 P, `1 d5 u$ u- y8 m* | b4 T
0 j8 K) n3 w. Z7 t; D ClockPrescaler, X" X& f* C- q5 \1 s9 M
设置时钟分频,参数范围0到255。特别注意,这里是针对HCLK3作为QSPI时钟来说的。
* i+ N, C, i$ T" L1 e4 }' X2 T
7 T( }0 C+ p9 |7 \- G Y6 |4 U3 R" X FifoThreshold
* `: a$ l2 c" s+ n7 b用于设置FIFO阀值,仅用于间接模式,参数范围1到32,单位字节。/ q6 q/ r& Y' A4 O
3 K( h2 d9 h n SampleShifting% {6 V2 t. l" k2 z2 F4 p' X
QUADSPI在FLASH驱动信号后可以选择再过半个CLK周期后才对FLASH数据采样,这有利于推迟数据采样。支持的参数如下:
, k1 v/ D2 u* p. N, O9 z1 F3 B2 z- h
#define QSPI_SAMPLE_SHIFTING_NONE ((uint32_t)0x00000000U) ) w7 `+ o! r+ n$ x- G
#define QSPI_SAMPLE_SHIFTING_HALFCYCLE ((uint32_t)QUADSPI_CR_SSHIFT)
2 b% p* \* G) u9 U FlashSize4 ?* C3 ^, r; z! \
Flash大小是2^(FlashSize + 1),单位字节。! H; `/ t) ~- b$ m
! ~5 c1 }: S, p7 l7 O间接模式下,最大支持的Flash大小是4GB,内存映射模式,最大支持256MB。, C; K" b" F+ g3 F- @
. w" C# Z C( u4 L6 |/ C% |
- #define SPI_POLARITY_LOW (0x00000000UL). Z( C6 T6 C7 ~) O
- #define SPI_POLARITY_HIGH SPI_CFG2_CPOL
复制代码
1 O) ?" X( p$ k( k& N) B ChipSelectHighTime3 |1 e2 m/ b0 ~: f; D
命令之间的CS片选至少保持的高电平时钟周期ChipSelectHighTime+1。支持的参数如下:* t: l9 ~5 k. s: B# ?
) |- {, Y; p6 L9 D
- #define QSPI_CS_HIGH_TIME_1_CYCLE ((uint32_t)0x00000000U)
% b" n# j5 h7 M# x - #define QSPI_CS_HIGH_TIME_2_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_0)
0 H! o0 p3 U; L7 E2 y7 q - #define QSPI_CS_HIGH_TIME_3_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_1) 6 Q. f8 J( N) @+ |; _
- #define QSPI_CS_HIGH_TIME_4_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_0 | QUADSPI_DCR_CSHT_1) " k) Z) Y) a8 E+ F
- #define QSPI_CS_HIGH_TIME_5_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_2) 8 |" k9 H' | J* g4 M$ q
- #define QSPI_CS_HIGH_TIME_6_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_2 | QUADSPI_DCR_CSHT_0)3 g, ^0 |& N( m: p1 a
- #define QSPI_CS_HIGH_TIME_7_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_2 | QUADSPI_DCR_CSHT_1)
, ]) H# o# C8 j0 |( U - #define QSPI_CS_HIGH_TIME_8_CYCLE ((uint32_t)QUADSPI_DCR_CSHT)
复制代码 - V& G, t0 J! [1 @, [ E$ B
FlashID
; q. g9 i( \' J7 ~6 s; V用于选择要操作的BANK,即用BANK1还是BANK2操作Flash。7 {9 X3 d( Q1 }' c% {
- J; r7 G$ u, @0 G4 R% X- #define QSPI_FLASH_ID_1 ((uint32_t)0x00000000)
+ `* J# T- T/ M: @1 m - #define QSPI_FLASH_ID_2 ((uint32_t)QUADSPI_CR_FSEL)
复制代码 3 R5 N! R7 O$ I0 Q" B+ r5 U6 U
DualFlash
8 {: \( x! S7 u3 }& M1 m) l用于选择是否使用双BANK。
# }# M/ n5 n7 b9 }) |( r# D% ?7 @0 d' T' z" t) s* ~1 I9 L. B
- #define QSPI_DUALFLASH_ENABLE ((uint32_t)QUADSPI_CR_DFM) /*!<Dual-flash mode enabled*/, f/ C7 I8 l8 o; F# [8 _
- #define QSPI_DUALFLASH_DISABLE ((uint32_t)0x00000000) /*!<Dual-flash mode disabled*/
复制代码 . P E Z) ^+ O7 k/ d( K
78.3.3 QSPI总线句柄结构体QSPI_HandleTypeDef
) c! w6 X4 v. N H下面是QSPI总线的初始化结构体:
4 B m- `& w1 M# p3 c2 a
3 o S) c. o: S' f8 T% Z6 l- typedef struct! ~8 `) J/ E( `! R4 I. h
- {
% d3 I g9 A) F9 Z( E& W* f - QUADSPI_TypeDef *Instance; /* QSPI registers base address */
7 v9 `) e% r! [ - QSPI_InitTypeDef Init; /* QSPI communication parameters */
/ |" K* Q$ Z: c! [ - uint8_t *pTxBuffPtr; /* Pointer to QSPI Tx transfer Buffer */7 f- Y* Q! G; {: r
- __IO uint32_t TxXferSize; /* QSPI Tx Transfer size */, Q8 @! {& p# J8 S5 \
- __IO uint32_t TxXferCount; /* QSPI Tx Transfer Counter */9 m# c) m4 x7 Z7 O0 E
- uint8_t *pRxBuffPtr; /* Pointer to QSPI Rx transfer Buffer */
. F" G# s+ O6 R4 r- @! ` - __IO uint32_t RxXferSize; /* QSPI Rx Transfer size */
6 ?6 s' n; {/ g) F - __IO uint32_t RxXferCount; /* QSPI Rx Transfer Counter */# ]7 a+ c* T7 W" J
- MDMA_HandleTypeDef *hmdma; /* QSPI Rx/Tx MDMA Handle parameters */* G, m: }( e& o6 O
- __IO HAL_LockTypeDef Lock; /* Locking object */
2 T: g; z1 {0 n2 g7 F* N - __IO HAL_QSPI_StateTypeDef State; /* QSPI communication state */! R1 U2 ]" Y" f& k! {% N! F6 E9 l
- __IO uint32_t ErrorCode; /* QSPI Error code */$ K! a& j; { Q" R+ M6 Z/ D8 Y
- uint32_t Timeout; /* Timeout for the QSPI memory access */
3 g9 C4 Y) v, W0 s( V1 e2 N+ ?, s - }QSPI_HandleTypeDef;
复制代码 ; w6 y. A7 {" W+ O
下面将结构体成员做个说明:+ `" H0 { q! \8 D& N. y
; V& a3 Z4 ?* P* u6 o1 F QUADSPI_TypeDef *Instance' W$ h& }& g# d- H9 D. J P7 ~: V' a
这个参数是寄存器的例化,方便操作寄存器,比如使能QUADSPI。
5 x: w+ J2 p. ?& X6 U2 U- `3 ~) x, n
SET_BIT(QUADSPI ->CR, QUADSPI_CR_EN)。; w2 F+ K8 t+ d& Q
* n3 h- W. o' C6 O# \ QSPI_InitTypeDef Init6 D/ q- U, b+ X; ?* C; c
这个参数是用户接触最多的,在本章节3.2小节已经进行了详细说明。. `: Q. i. Q F3 l4 p
8 Z/ y7 }. G% J4 G e
MDMA_HandleTypeDef *hmdma
% I0 v* _5 x. v. [用于QSPI句柄关联MDMA句柄,方便操作调用。3 n. A3 R( f& e0 l3 F: S+ ?3 B
6 k/ T7 M+ D8 G& t0 q
其它参数
7 ?0 Q7 h: A4 e8 V其它参数基本都是在函数内部调用,用户基本不用管。4 _, U# t; L7 h) a* s4 W
' B% O" P4 f3 l6 S( X9 w E
78.3.4 QSPI命令结构体QSPI_CommandTypeDef
, _5 ^1 }4 n* _5 x( C5 r% D) C# f下面是QSPI总线的命名结构体:
: N& _. Y1 B& y3 C+ d; A) l
& z. l2 ?) Y } j+ F. ~. ^# v w. r" ]- typedef struct
! u [/ T+ ^2 S* [% `4 i - {
) \! c3 `$ G8 c8 _0 u - uint32_t Instruction;
. d8 U+ F' [& S1 s; d; e - uint32_t Address; " ^$ G1 f0 V z' w( n) v
- uint32_t AlternateBytes;
& T0 R1 S) ^- }$ p& ^4 m/ Z - uint32_t AddressSize; : y6 {" C" M2 E( i0 A
- uint32_t AlternateBytesSize; ( E; W x. n4 I+ x- ~6 _& V0 Z
- uint32_t DummyCycles;
( O2 w! w" \2 r$ b: ?) I5 Y; ?* d* d Y - uint32_t InstructionMode;
# t3 m Z4 d% B/ `' r P0 J4 \ - uint32_t AddressMode;
/ ] [# _! l5 f, R; ^* [. \ - uint32_t AlternateByteMode; $ r3 U# h. b( @
- uint32_t DataMode; ( n& ~6 w9 K, R7 N0 {3 \
- uint32_t NbData; n4 R& B: w8 W9 z* q
- uint32_t DdrMode; # L. B$ l7 p7 q, }: o
- uint32_t DdrHoldHalfCycle; 5 N0 Z- S6 i0 F
- uint32_t SIOOMode;
1 Y6 K$ ^6 {% {0 t* E& t6 h - }QSPI_CommandTypeDef;
复制代码
$ p; w4 Z: c4 W) p; x. L下面将结构体成员逐一做个说明:" F' E+ D$ s$ v) U: ~% V- N
# f" \$ T" D- B+ {& @# r, L) \, J: r Instruction
, F7 K8 S5 @, p8 [1 Q6 r8 [5 e( k设置要发送的指令,参数范围0x00到0xFF。7 k$ [7 w% `2 [3 @( z
. J; y( f" f5 j Address. L& ]( s. u" }; A! u
设置要发送的地址,地址由是1个字节到4个字节来表示,参数范围0x0 到 0xFFFFFFFF。
9 S: h6 D' b# D4 t1 ~; L+ o7 h. q# f U0 |6 k; u
AddressSize+ `+ H, o2 H& s4 P3 \7 ~
地址大小,即表示此地址需要的字节数,支持的参数如下:
`7 q n2 j+ I0 d3 q }
8 @/ m% F( L* J; P' @- #define QSPI_ADDRESS_8_BITS ((uint32_t)0x00000000) /*!<8-bit address*/
. H9 @4 s! [) M" C- D2 U - #define QSPI_ADDRESS_16_BITS ((uint32_t)QUADSPI_CCR_ADSIZE_0) /*!<16-bit address*/' {* D0 m8 q* @1 m9 ^8 h8 g% U
- #define QSPI_ADDRESS_24_BITS ((uint32_t)QUADSPI_CCR_ADSIZE_1) /*!<24-bit address*/
* I8 c0 ?# Z$ O3 L: z4 q - #define QSPI_ADDRESS_32_BITS ((uint32_t)QUADSPI_CCR_ADSIZE) /*!<32-bit address*/
复制代码
, m% d/ U" V- ^ AlternateBytesSize
! P+ ]& d; d5 g5 e1 x+ u交替字节大小,支持的参数如下:' _# }- J9 x3 t9 A4 \
3 J' v& y2 y/ }( a" @* t" C: {- #define QSPI_ALTERNATE_BYTES_8_BITS ((uint32_t)0x00000000) /*!<8-bit alternate bytes*/
; I I" y: g8 K; ]$ S - #define QSPI_ALTERNATE_BYTES_16_BITS ((uint32_t)QUADSPI_CCR_ABSIZE_0) /*!<16-bit alternate bytes*/& T3 @( F9 f6 M# L) C9 j+ v1 Z9 l/ Q
- #define QSPI_ALTERNATE_BYTES_24_BITS ((uint32_t)QUADSPI_CCR_ABSIZE_1) /*!<24-bit alternate bytes*/0 {0 _* p' ?" [- M
- #define QSPI_ALTERNATE_BYTES_32_BITS ((uint32_t)QUADSPI_CCR_ABSIZE) /*!<32-bit alternate bytes*/
复制代码
$ g7 S2 p& N; _/ q DummyCycles
' s D$ T% `) G- m5 `执行空周期个数,参数范围0到31:
; d! f2 R3 ~" [ p& x+ {9 g& f4 V2 _5 U/ }
InstructionMode
' p6 y0 S8 D6 z/ {1 L% R指令阶段需要几线模式:
) U; l- @/ c( I( b9 B' r# Z0 H5 S9 K$ r e' }+ l1 o
- #define QSPI_INSTRUCTION_NONE ((uint32_t)0x00000000) /*!<No instruction*/. y0 i+ u+ e4 t* a" M# g
- #define QSPI_INSTRUCTION_1_LINE ((uint32_t)QUADSPI_CCR_IMODE_0) /*!<Instruction on a single line*/
) V$ ]& U4 J7 P$ o: E5 z4 c - #define QSPI_INSTRUCTION_2_LINES ((uint32_t)QUADSPI_CCR_IMODE_1) /*!<Instruction on two lines*/. n) @7 Z# h1 {! v
- #define QSPI_INSTRUCTION_4_LINES ((uint32_t)QUADSPI_CCR_IMODE) /*!<Instruction on four lines*/
复制代码
2 z- j9 d/ M: s+ S6 @$ d AddressMode8 b- I, l. w K$ w6 N
地址阶段需要几线模式:
9 }5 S6 z6 D: S
( m. N9 H* y. R- #define QSPI_ADDRESS_NONE ((uint32_t)0x00000000) /*!<No address*/
T# x9 a6 \1 L - #define QSPI_ADDRESS_1_LINE ((uint32_t)QUADSPI_CCR_ADMODE_0) /*!<Address on a single line*/
; c: F& L) ^( T+ ^+ {. P - #define QSPI_ADDRESS_2_LINES ((uint32_t)QUADSPI_CCR_ADMODE_1) /*!<Address on two lines*/
7 U3 m/ k. }( O+ v! E1 H: M - #define QSPI_ADDRESS_4_LINES ((uint32_t)QUADSPI_CCR_ADMODE) /*!<Address on four lines*/
复制代码 0 B. m1 O3 @7 O5 v, z5 }* U3 f9 [( d. t
AlternateByteMode" U. [4 S# g- m
交替字节阶段需要几线模式:! x; s! ~+ t- Q. N
_4 N a& ^4 `7 {% B; T9 V
- #define QSPI_ALTERNATE_BYTES_NONE ((uint32_t)0x00000000) /*!<No alternate bytes*/- n# u! G K/ I8 B; A4 t
- #define QSPI_ALTERNATE_BYTES_1_LINE ((uint32_t)QUADSPI_CCR_ABMODE_0) /*!<Alternate bytes on a single line*/
" v+ a7 s! Q/ ~) X" z) n& I - #define QSPI_ALTERNATE_BYTES_2_LINES ((uint32_t)QUADSPI_CCR_ABMODE_1) /*!<Alternate bytes on two lines*/
2 k* e6 s: L2 H# r - #define QSPI_ALTERNATE_BYTES_4_LINES ((uint32_t)QUADSPI_CCR_ABMODE) /*!<Alternate bytes on four lines*/
复制代码 9 `5 L" l* @( _
DataMode
1 h m; f8 O4 ]数据阶段需要几线模式:
7 ^2 R- e! |! H Z8 n/ z
/ |: n. q$ R; m# G- #define QSPI_DATA_NONE ((uint32_t)0X00000000) /*!<No data*/
& b) G9 d7 v. `( r! c' {2 | - #define QSPI_DATA_1_LINE ((uint32_t)QUADSPI_CCR_DMODE_0) /*!<Data on a single line*/! n i1 r+ s- S! b2 Z) \: a
- #define QSPI_DATA_2_LINES ((uint32_t)QUADSPI_CCR_DMODE_1) /*!<Data on two lines*/
; Q. s' A7 b' ]% J; `8 L - #define QSPI_DATA_4_LINES ((uint32_t)QUADSPI_CCR_DMODE) /*!<Data on four lines*/
复制代码
, E8 O% @0 Z8 Y0 G, _ NbData+ c; A& v9 ]* p9 r* E0 \) o9 C$ s
要传输的数据大小,参数范围0 到 0xFFFFFFFF,如果设置为0表示不定长,直到存储器末尾。# E+ C9 n' G2 s5 Z. k4 U' S
* W( z( i3 l3 v$ q
DdrMode4 d- C4 F$ b- B6 R5 s
用于设置是否使能DDR模式。数据阶段,交替字节阶段和数据传输阶段可以使用DDR模式。支持的参数如下:; h; x. C; l, `" Y* ~
) i% [" h ^; o7 G
- #define QSPI_DDR_MODE_DISABLE ((uint32_t)0x00000000) /*!<Double data rate mode disabled*/3 o- H" `' e$ a+ \
- #define QSPI_DDR_MODE_ENABLE ((uint32_t)QUADSPI_CCR_DDRM) /*!<Double data rate mode enabled*/
复制代码 2 B) f) S0 a: Z/ ]: F: P* u
DdrHoldHalfCycle. i+ T9 T7 h7 V( T F. |+ W2 @
DDR模式下,用于设置延迟半个时钟周期再做数据输出。3 [" x; p4 h2 Y4 `) |
$ |% }# L' _; K4 m( l6 }' u* f- G- #define QSPI_DDR_HHC_ANALOG_DELAY ((uint32_t)0x00000000)
5 v- v9 J5 g- F& i) h: T$ v; i4 ?/ k - #define QSPI_DDR_HHC_HALF_CLK_DELAY ((uint32_t)QUADSPI_CCR_DHHC)
复制代码
) X' O4 I. r s5 S: ` SIOOMode
, o6 u! y, X/ `1 ~# M: F9 ~, S设置仅发送一次指令还是每次操作都发送指令,支持的参数如下:
4 o4 @% m2 ^4 X. [0 Z- C2 u- B* i0 ?& c* w/ ?& O' z W
- #define QSPI_SIOO_INST_EVERY_CMD ((uint32_t)0x00000000)
3 U% ~6 @& N% j8 H$ t. O0 u - #define QSPI_SIOO_INST_ONLY_FIRST_CMD ((uint32_t)QUADSPI_CCR_SIOO)
复制代码 " Q$ t! h8 g% M2 G0 r
78.3.5 QSPI自动查询结构体QSPI_AutoPollingTypeDef- \: U4 j' }8 A
下面是QSPI总线自动查询结构体:
3 R& J$ F. D6 I0 M+ V+ L& I
- H: K2 z: @" G6 ^+ F; h H% A- typedef struct
, h$ e% J; Q; B& c. H - {" P! G$ V; m H" j R
- uint32_t Match; 4 e. X- H: z& G+ x8 }, c/ d
- uint32_t Mask; & G6 \, R6 t3 B/ L4 u
- uint32_t Interval;
' z4 O, n. f, u" J4 I' e { M% n - uint32_t StatusBytesSize;
) \, z+ _) ^3 z( q; Q* A" ~ - uint32_t MatchMode; 8 ~3 _8 M a( O8 Y5 D9 W& x
- uint32_t AutomaticStop;
1 o. m. P! R5 k3 {7 `9 i - }QSPI_AutoPollingTypeDef;
复制代码
3 f2 a) j1 L/ e1 x下面将结构体成员逐一做个说明:
* f, Y! B- y2 p; p, \0 m
2 r1 D9 @* U; a0 G1 X7 j& U5 A Match
. g. _2 x5 L) i+ Q3 ^) |2 d' B8 B8 Z参数成员Mask屏蔽了状态寄存器的某些位后,状态寄存器的值与此参数成员值做匹配。参数范围0x0 到 0xFFFFFFFF。
2 O- d: H' c; V) V0 [: f' U& D' }' I& F: Z
Mask9 v( G' g" @, W
用于设置屏蔽位,比如Mask = 0x01,表示仅保留bit0的数值,其它bit忽略。参数范围0x0 到 0xFFFFFFFF。
% _! `# _ @; t1 S# a- H) \
- @# D4 V+ M" @8 Z1 B Interval! M' R* H; P0 n. j& i. Q4 y5 k
指定自动轮询阶段两次读取之间的时钟周期数。参数范围0 到 0xFFFF。
6 u. f8 i m% X( O& N- ^
0 o j }5 ^) w2 | StatusBytesSize
U& {1 c2 w# }% u, O/ Z# J用于设置状态寄存器大小,参数范围1到4个字节。. m1 C/ U* d. K* W
- Q1 \4 `& H' Y
MatchMode
/ k! S3 V) K! d5 x' W. j参数成员Mask屏蔽了状态寄存器的某些位后,状态寄存器完全与参数成员Match一样(与操作的含义)或者任意一个bit的值与参数成员Match中一个bit的值一样(或操作的含义),比如Mask = 0x01,Match=0x00,MatchMode=与操作,表示不断查询状态寄存器bit0,等待其为0。
- z) L. Q1 o3 R. k: R' S- F: }' L' g; ~
MatchMode支持的参数成员如下:( f0 I- i6 }: d" ^# ~7 ^. E; t
) D1 |0 O% M3 a
- #define QSPI_MATCH_MODE_AND ((uint32_t)0x00000000) /*!<AND match mode between unmasked bits*/
/ `' T7 h0 b k - #define QSPI_MATCH_MODE_OR ((uint32_t)QUADSPI_CR_PMM) /*!<OR match mode between unmasked bits*/
复制代码 2 a" z$ k7 {& }) f& {
AutomaticStop
# B* Q) K% ]- R, v当与参数成员Match匹配时,自动停止检测。
' t+ T) ]% M0 R1 r% ^$ p2 Z4 C0 {( r0 Q2 R
78.3.6 QSPI内存映射结构体QSPI_MemoryMappedTypeDef) U' v0 ?' t6 ]! j2 [$ b
下面是QSPI总线的内存映射结构体:8 I1 U% L- }+ M+ H2 H, _+ [
3 k1 E! m w' O9 W6 S- typedef struct5 N: l! b' U. r$ c. \
- {
" H$ I) X8 N! ?( ^( W$ q - uint32_t TimeOutPeriod; ' V0 t; a/ _# m O6 h
- uint32_t TimeOutActivation; 6 O ~& T; E9 E
- }QSPI_MemoryMappedTypeDef;
复制代码
2 e* o5 f- b1 ?下面将结构体成员逐一做个说明:( ~4 Y. V% L* U. d$ T
3 i* w" ^( _9 }( x& ?0 ]8 y' u TimeOutPeriod0 A q3 N4 H+ V
FIFO满时,释放芯片选择之前要等待的时钟周期数。参数范围0到0xFFFF。. ]' r, v7 K. H& a
5 [2 f9 ]- f* V% ~( R& z TimeOutActivation
2 h4 e& i3 v5 D* N+ S指定是否启用超时计数器以释放芯片选择,支持的参数成员如下:- P0 i' P. z( ~( W) O! R4 z
) K/ n! e& c) Q
- #define QSPI_TIMEOUT_COUNTER_DISABLE ((uint32_t)0x00000000)
6 K4 m$ ^; d- C5 h% X& {* s4 B9 y - #define QSPI_TIMEOUT_COUNTER_ENABLE ((uint32_t)QUADSPI_CR_TCEN)
复制代码 ! s$ \4 g1 F+ @) E; \
78.4 QSPI总线源文件stm32h7xx_hal_qspi.c9 N7 e4 o2 }. S8 w& O6 l# g
此文件涉及到的函数较多,这里把几个常用的函数做个说明:% \/ ~- q, C& E8 |' T: f
2 ?5 m/ q* m5 ?
HAL_QSPI_Init
( y* s' I5 A4 s HAL_QSPI_DeInit8 J1 L+ A- k5 x" M2 g
HAL_QSPI_Command
! r9 _1 Q E9 |6 H, r5 ] HAL_QSPI_Command_IT
( ^2 D, k& j q HAL_QSPI_AutoPolling
8 U& {4 P2 e v5 ~% |3 I" F2 R5 W HAL_QSPI_AutoPolling_IT- n! ^: {0 B; B7 [( u. y
HAL_QSPI_Transmit
0 w6 g; E8 _% n) u( Q8 S& j$ s HAL_QSPI_Receive4 [( o$ `* x! l0 ?6 @$ X4 x, c& R
HAL_QSPI_Transmit_DMA
$ N/ S U% ]+ k HAL_QSPI_Receive_DMA B& P% y1 Q" e" S9 f! I3 V# F
HAL_QSPI_MemoryMapped
1 g) T2 b* J; n# d78.4.1 函数HAL_QSPI_Init: P+ O. A7 u' K' r, v+ d! k: u; L
函数原型:# q0 h/ Q9 J6 o3 e0 e
9 u$ B% ^( _; [ `1 B8 @
- HAL_StatusTypeDef HAL_QSPI_Init(QSPI_HandleTypeDef *hqspi)" {3 \7 F; N2 f2 y! t+ P3 s2 o
- { l- z5 R5 S5 A0 _) D
- HAL_StatusTypeDef status;4 }. c6 {- G* }4 @1 \
- uint32_t tickstart = HAL_GetTick();
# o, L( `& C7 }$ U: M' N+ { - * M- w h0 n8 \( |
- /* 检测句柄是否有效 */7 {/ Y0 B0 _( X1 U
- if(hqspi == NULL)6 A+ a$ I% v0 E
- {
/ \' N3 R+ d; s7 D5 `+ A9 N0 H6 y& | - return HAL_ERROR;$ z) q2 I) H0 n& J
- }3 I% f+ {0 _4 x0 I, B4 ?/ N5 G
/ C, t. A! f4 V6 {+ i- /* 检查参数是否有效 */
+ k# U. b2 v, q( H. `/ s6 F K - assert_param(IS_QSPI_ALL_INSTANCE(hqspi->Instance));& ?# d) l: M! b+ L, R) V2 R
- assert_param(IS_QSPI_CLOCK_PRESCALER(hqspi->Init.ClockPrescaler));
; E' U0 M9 m) a3 U' F - assert_param(IS_QSPI_FIFO_THRESHOLD(hqspi->Init.FifoThreshold));
* T9 D& i8 e+ e9 @* n( W' Z7 | - assert_param(IS_QSPI_SSHIFT(hqspi->Init.SampleShifting));
+ J& Y: M. p. j- @! r8 d - assert_param(IS_QSPI_FLASH_SIZE(hqspi->Init.FlashSize));& \: ^2 u, x/ R
- assert_param(IS_QSPI_CS_HIGH_TIME(hqspi->Init.ChipSelectHighTime));
! S Q+ c' H c - assert_param(IS_QSPI_CLOCK_MODE(hqspi->Init.ClockMode));( u- O" U5 z; E4 K% {
- assert_param(IS_QSPI_DUAL_FLASH_MODE(hqspi->Init.DualFlash));
& Z9 z9 |0 s. Y - % }0 d" B0 ^# T7 }. K3 |/ ~
- if (hqspi->Init.DualFlash != QSPI_DUALFLASH_ENABLE )2 z, o" Q# s X# O/ K$ }( C
- {
+ u. a b! N7 q - assert_param(IS_QSPI_FLASH_ID(hqspi->Init.FlashID));
$ y' C& _6 y0 K5 x0 z& g1 L. p! S - }
; ]0 T/ F; }1 [ - , `; o o7 y7 p5 A( c( q* q
- if(hqspi->State == HAL_QSPI_STATE_RESET)
, X; p4 a" v# P+ a0 ] - {
9 A. e6 I! O' O# \2 U - ' }9 i" t6 Y3 } f) b( t5 i' T
- #if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)2 H1 _& m% e A6 j! Y* p" e+ S
- /* 复位状态,设置默认的回调 */
4 s. N$ Q$ k7 s - hqspi->ErrorCallback = HAL_QSPI_ErrorCallback;6 \- D( C1 E$ M: d: W
- hqspi->AbortCpltCallback = HAL_QSPI_AbortCpltCallback;' h* ?9 ?- l" |5 J* S+ l3 \
- hqspi->FifoThresholdCallback = HAL_QSPI_FifoThresholdCallback;
6 {) n9 N: c8 B6 f/ I( @$ ?5 f( K2 p$ X - hqspi->CmdCpltCallback = HAL_QSPI_CmdCpltCallback;
$ K; M6 X- H7 V( E0 L1 x3 m6 W - hqspi->RxCpltCallback = HAL_QSPI_RxCpltCallback;
B8 J6 q% @! }5 S - hqspi->TxCpltCallback = HAL_QSPI_TxCpltCallback;
! [! Y' X4 _9 [9 _ - hqspi->StatusMatchCallback = HAL_QSPI_StatusMatchCallback;2 G# o' x9 v- i! ^9 P2 [4 z5 B
- hqspi->TimeOutCallback = HAL_QSPI_TimeOutCallback;
$ z% O% _& V# r+ m1 S# n - ( ^) [! b: n- y9 @. Y/ k
- if(hqspi->MspInitCallback == NULL)4 ~6 w" c# }# ?3 \* h) A% g8 e
- {* I4 A0 M1 j- G- R
- hqspi->MspInitCallback = HAL_QSPI_MspInit;7 c8 ~# R" Z2 O* O- }* L2 j6 z
- }
: \6 u. N/ S* j8 \9 X& k( F - % o# H0 t0 m* N9 l! W1 @
- /* 初始化底层硬件 */$ i! h4 k" v& l: k, l8 {
- hqspi->MspInitCallback(hqspi);
6 u" V* w. a! @* C - #else$ y7 t% C7 L z0 R& T
- /* 初始化: GPIO, CLOCK */
$ z5 u0 R/ Y# T. D2 \+ N# P - HAL_QSPI_MspInit(hqspi);6 Z. H! w: U9 E9 Y9 m
- #endif
; z) h N* [- X% g# c- V1 g8 v# F- i
/ r3 z0 y2 o+ m# J- /* 配置QSPI内存访问默认的溢出时间 */
8 H; k) |! F; K2 A - HAL_QSPI_SetTimeout(hqspi, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
8 F/ v; z" X( F$ K$ @% L6 Q - }
: {% N8 s' ?0 M2 L$ j8 K
; ?: H3 r, o0 [2 T( O$ K6 n4 t- /* 配置QSPI FIFO阀值 */7 J' X* P/ f* H7 e- H6 [) F
- MODIFY_REG(hqspi->Instance->CR, QUADSPI_CR_FTHRES,
! H; d4 P2 Z3 s# z - ((hqspi->Init.FifoThreshold - 1U) << QUADSPI_CR_FTHRES_Pos));
; l; b4 f6 o0 F( { - $ J1 N2 g+ u4 z# A* S% |5 Z+ \
- /* 等BUSY标志复位 */
: k7 Q9 m- r2 Q - status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, hqspi->Timeout);
7 q. J1 [' @# v6 f
% S& o% D/ k4 s/ \9 y* d& E/ H- V- if(status == HAL_OK)
* R( W( L! y q3 F2 E2 i - {
2 s b- I9 e& S0 l' T. M# X - /* 配置QSPI时钟分频和采样延迟 */. w" K. O. f/ P- B. ?3 a
- MODIFY_REG(hqspi->Instance->CR, (QUADSPI_CR_PRESCALER | QUADSPI_CR_SSHIFT | QUADSPI_CR_FSEL |
# e5 f! p! f7 X - QUADSPI_CR_DFM),2 a. |1 p! q) R$ e# w+ [1 \: o" R. S, `
- ((hqspi->Init.ClockPrescaler << QUADSPI_CR_PRESCALER_Pos) |# J% ^7 p- S, K6 u) W `
- hqspi->Init.SampleShifting | hqspi->Init.FlashID | hqspi->Init.DualFlash));4 {3 W% ~- J1 H: H( \2 T
! q5 q# l2 C4 e3 G- X8 A- /* 配置QSPI Flash大小,CS片选高电平时间和时钟模式 */0 Y+ [/ ]6 z" N( h6 U) G
- MODIFY_REG(hqspi->Instance->DCR, (QUADSPI_DCR_FSIZE | QUADSPI_DCR_CSHT | QUADSPI_DCR_CKMODE),
/ x p1 A8 j3 C6 Y" h - ((hqspi->Init.FlashSize << QUADSPI_DCR_FSIZE_Pos) |
& r) }4 j* y: ?+ X - hqspi->Init.ChipSelectHighTime | hqspi->Init.ClockMode));
/ R" P- h* {) T& A6 |" n7 J - 4 U" r. e. \& D: q9 v2 W* }* T: Q
- /* 时钟QSPI外设 */2 s, O. m/ E" t$ {# {) R- O
- __HAL_QSPI_ENABLE(hqspi);
9 V: T* |, Z2 T) B* z& k" r: F0 d- @
5 F9 ?$ g& G3 }6 \9 `: Z- /* 设置QSPI无错误代码 */) w6 Q5 k5 s+ H/ t. x
- hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;
0 I3 C) h8 \7 N* N7 O
N+ e V8 i, Y$ ]- /* 初始化QSPI状态就绪 */
% a8 g. m, W1 ^. D6 _5 t, r. a - hqspi->State = HAL_QSPI_STATE_READY;+ d: f/ V+ n: s5 A% {8 ]. l! f
- }: M- W, n" N- O5 t
- 1 ^' a0 y, r" c z& c
- /* 返回状态信息 */* v8 a: C, m% M; G; z( b
- return status;" f; [) O! q; @0 L O% G6 ?
- }
复制代码 ! X/ \) |" |9 H
函数描述:; U t9 h5 s: Z5 V
, B5 i4 S( ~$ t5 N! t, a) R0 q
此函数用于初始化QSPI。
! t+ b0 e( k& X" T' s* E
' h+ B* i! V% n1 |函数参数:# L4 Z" L i+ E, G% B. J$ @2 G" ?! m
5 L9 D; N& h/ H! [5 L( Z
第1个参数是QSPI_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数,详见本章3.3小节。
" U9 n( R( t% O; ?1 h+ i6 R- H 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。, D6 b- L- a2 l8 T9 _. J
注意事项:
: {2 n# N) z/ C2 s' |2 R5 g; N% w& t" G
函数HAL_QSPI_MspInit用于初始化QSPI的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。! M3 @. B6 I1 ]' s. [) Z& [" E
如果形参hqspi的结构体成员State没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量QSPI_HandleTypeDef SpiHandle。
, [) ]: w4 p4 |2 C0 _; `对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_QSPI_STATE_RESET = 0x00U。 [9 A( |% ]0 ~$ U! Z B% ]. b
$ b1 S% E! |, M3 n( A' {
解决办法有三
1 ^1 ~- Y( i/ o/ H% S) N
8 l: n! v4 `9 @方法1:用户自己初始化QSPI和涉及到的GPIO等。
( n/ D+ i5 j' t" s) h9 K
# R! o% j+ f/ o方法2:定义QSPI_HandleTypeDef QspiHandle为全局变量。
7 d0 k1 U3 N+ g- x6 }& W, o8 c& Z0 ]% _
方法3:下面的方法* y$ z$ J& m* Y6 m2 o$ M; Q |3 m
+ l% n" r! _7 s7 R8 w4 f. ~- if(HAL_QSPI_DeInit(&QspiHandle) != HAL_OK)
2 C8 R2 O* L2 K0 C* y; U- b( p; e - {
G+ Y j/ v( | - Error_Handler();7 i( A) V+ ?" P. @0 V+ P5 Q" r3 R
- } 5 y8 c6 e @9 g
- if(HAL_QSPI_Init(&QspiHandle) != HAL_OK)/ ^& [) x. }# i
- {
, E* N7 m, P& g6 s; y9 F } - Error_Handler();8 V2 d# ~# D; [& j( T) C" x
- }
复制代码
/ |9 {$ {/ e8 F' }" L使用举例:
s$ I2 K* p" Y/ E: l6 w M. s) o
- /*( n' a2 l- p& d0 [. ~' C- J! S% n
- *********************************************************************************************************3 Q" g* o4 }) B ^& i0 l$ [
- * 函 数 名: bsp_InitQSPI_W25Q2563 g. c) U2 J( @" t6 d/ Q
- * 功能说明: QSPI Flash硬件初始化,配置基本参数
2 p4 \! j, B2 Y+ B( o0 d3 q - * 形 参: 无% f, V- z/ F2 s, N
- * 返 回 值: 无
+ n5 q$ y* i9 u3 y4 j* e - *********************************************************************************************************- H% H9 A3 I+ U2 \8 o S$ L
- */
! j7 o! @* N( s$ o8 Q6 E* U) | - void bsp_InitQSPI_W25Q256(void)
) W. ]. C. F0 p2 t/ E# N3 ?2 p; g* V - {7 j" J' G. J6 o9 r* B" K( Q2 B
- /* 复位QSPI */$ u, H* ]' y3 ?# h& I2 U* c
- QSPIHandle.Instance = QUADSPI; \4 [+ a0 \2 m, e& P2 N" {2 z3 u
- if (HAL_QSPI_DeInit(&QSPIHandle) != HAL_OK)4 ]4 B: k( } k& ~9 T
- {2 j& O: s M4 m% \6 n6 r
- Error_Handler(__FILE__, __LINE__);) O4 p! _4 ]4 p% P3 s6 l- w' |* s9 \
- }
D' G) A( M) B0 |: Y! u) z$ e - 8 o" b/ m' a" B( g
- /* 设置时钟速度,QSPI clock = 200MHz / (ClockPrescaler+1) = 100MHz */' n$ b/ P: Q4 P2 X# `4 u
- QSPIHandle.Init.ClockPrescaler = 1; 2 G# A, t. O4 M1 d. C8 e
- 4 i3 p4 A2 f7 t
- /* 设置FIFO阀值,范围1 - 32 */
3 J% Q8 X. R- d; W - QSPIHandle.Init.FifoThreshold = 32; % E; Z+ z" ]4 G' e+ y0 P' [9 R
- / ]0 z1 b# K( @2 p, a( y
- /*
3 J x7 u9 A I) ~, Q* Y0 a - QUADSPI在FLASH驱动信号后过半个CLK周期才对FLASH驱动的数据采样。: Z6 Q2 _3 g% T. l5 _1 q
- 在外部信号延迟时,这有利于推迟数据采样。& ~3 `4 ?; [) D8 w3 F
- */
0 E: Q) j/ I5 u' s4 {( @( }( u$ a - QSPIHandle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; * `7 z% U$ n" Q& c, w5 S% n" Z
- ' E& F0 Z. P0 x- g, T
- /*Flash大小是2^(FlashSize + 1) = 2^25 = 32MB */
4 f, U- m5 q( C& z' l! O - //QSPI_FLASH_SIZE - 1; 需要扩大一倍,否则内存映射方位最后1个地址时,会异常。
. }1 m, K1 l5 `6 g0 ` - QSPIHandle.Init.FlashSize = QSPI_FLASH_SIZE;
+ Y8 h4 z/ A+ ] ^: r' w' { - 3 y* \# N! D0 c/ M, H0 |: L
- /* 命令之间的CS片选至少保持2个时钟周期的高电平 */
. ?& n5 B' \' R8 m. Y - QSPIHandle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE;4 ?' g% w. R/ j1 G
- # r* c- V+ ~2 E! }8 u
- /*
" R; K& d; ]( v U/ _( H - MODE0: 表示片选信号空闲期间,CLK时钟信号是低电平
% e3 E4 V4 ]) F% Y3 m - MODE3: 表示片选信号空闲期间,CLK时钟信号是高电平* b. b" B7 H" N# l6 E6 i
- */
1 l) {% p* o3 t0 r4 K4 K - QSPIHandle.Init.ClockMode = QSPI_CLOCK_MODE_0;
6 O) R' x* [& y, v$ ] - % y. M" T- S0 Y# ~% P
- /* QSPI有两个BANK,这里使用的BANK1 */
5 D) c% p2 m; `2 I- k3 B - QSPIHandle.Init.FlashID = QSPI_FLASH_ID_1;- F& J6 z$ d- c* }4 h$ G; s
- * C6 w1 a! {8 Q M7 S" E9 K! i, H
- /* V7开发板仅使用了BANK1,这里是禁止双BANK */
" r4 Q) A3 `9 A( @, F$ g3 ~8 j5 e - QSPIHandle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;* `6 v% m0 K/ V) Q+ E+ ^" z5 S+ Y
- 2 _! e) P7 U4 A9 ?( b
- /* 初始化配置QSPI */
# |. x; ~$ Q$ z3 ?4 o: ~ - if (HAL_QSPI_Init(&QSPIHandle) != HAL_OK)
1 [, b! G9 O4 o+ [% K9 B, F - {
& x/ P1 e$ J: _4 [- F' f3 ` - Error_Handler(__FILE__, __LINE__);+ ^3 ?' e1 m. H/ W6 ~
- }
/ c/ k' }/ o4 u a - }
复制代码
. e; }4 Y" I2 M5 e3 h! o3 ]: x78.4.2 函数HAL_QSPI_DeInit
2 }1 o) L# i; U! @: p9 N8 A/ |函数原型:* S$ K% ^: P) d# B+ H; q: K
' D; R+ I% |5 v4 N* o; R- HAL_StatusTypeDef HAL_QSPI_DeInit(QSPI_HandleTypeDef *hqspi)
5 i& u9 q; O* C# s5 \' i* g - {; z$ B P% p5 O. [9 r# u) p
- /* 检测QSPI句柄 */1 h) ~8 [; g* I5 S1 C" l: a" t
- if(hqspi == NULL)
- C3 _/ P) |6 j# u0 L+ p - {
8 t# ?- b/ \# l6 G( g# X! j! N - return HAL_ERROR;. K. Y& @) D, M) p+ E% h
- }
1 C) \5 ^+ d7 R2 [0 b
" W9 ~& `! }9 \4 ~& y- /* 禁止QSPI外设时钟 */$ K# z/ D9 x$ A, V- ]
- __HAL_QSPI_DISABLE(hqspi);
& E- t9 p: ]; h9 j - 0 \9 c$ w3 M0 e* D9 k1 d) ?
- #if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)% m$ [& s3 @- ]- P; K
- if(hqspi->MspDeInitCallback == NULL)
: r9 r/ F% K$ s) E' z - {
; w" {! q" M( Y0 A. { - hqspi->MspDeInitCallback = HAL_QSPI_MspDeInit;# }# h7 @5 N$ w8 a. B" r
- }' \0 y, D: f' v4 a( q1 A) x9 w% M
1 H4 `" S: Y7 e5 D# n3 d0 w/ }- /* 复位硬件底层 */0 |( t; m/ }2 x1 y( @+ U+ }( \
- hqspi->MspDeInitCallback(hqspi);
. Y! C! L4 {4 m/ b6 c3 o4 n - #else; r( _! U9 s! E& _
- /* 复位: GPIO, CLOCK, NVIC... */
% @3 B) b1 e" e1 D9 l6 \ - HAL_QSPI_MspDeInit(hqspi);3 g) `2 L' B- x+ g0 {, \
- #endif
/ `; l. D: x& i9 b& ^
, J9 x2 M% Q5 i. j- /* 设置无错误 Set QSPI error code to none */
" I3 ?9 R$ i2 E2 X } - hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;) ~& `& T4 g/ k4 F6 ]
- 5 g5 d3 U* K; |' b9 Q, b4 C
- /* 设置QSPI状态为复位 */
3 q. E, \% {9 }/ t1 a# ?" D - hqspi->State = HAL_QSPI_STATE_RESET;
, n; Q$ Q ~* m3 ~6 N9 k
/ O6 S2 G5 ?; N) G! X, e- return HAL_OK;, Q. }0 a7 Y) D A" h
- }
复制代码 " P( @6 k; i& }# d
函数描述:
5 I: H: ?3 X9 B* H2 _6 g
( j: G- H g u用于复位QSPI总线初始化。5 Z5 i r P( V9 K
0 E) G7 z# w0 L6 L/ H1 e. \+ q! N( |- q函数参数:+ B) p9 a7 _" D6 I
8 o% B7 q4 E& J+ N8 e
第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。5 ]6 e3 K; X4 ~8 k3 r/ O b
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。, C8 H0 A9 o3 H, g; v9 x
78.4.3 函数HAL_QSPI_Command
) ]4 n3 E& Q' Z5 g函数原型:
7 w6 p) J. z9 U7 ^5 ~5 y; H8 Q* V/ o1 x0 a+ {3 N. `! t- f* |
- HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout)( L; P! r0 P" [' M( \* B
- {; _/ W% B6 y+ F4 o8 K N$ ?
- HAL_StatusTypeDef status;& E( \8 |2 k6 b! Z
- uint32_t tickstart = HAL_GetTick();
' Z5 m1 o0 p, S7 v# t! P$ R - 6 m. I9 O y+ t
- /* 检测参数 */
8 c7 A1 t% H# F: M5 y - assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));
* _ J: z$ H& G% n/ j/ m& a& {& O/ r - if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE)
0 o" o! F9 y4 g0 t% E( i - {
- K% u' p$ s- k; \, r# K - assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));
& z) Q. e0 D! C, ?/ _ - }
! \# m Q$ c. ^% \; y8 { - . ^% f5 F* k( ^7 N: m
- assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));
3 `* K$ t# z: x+ j; m - if (cmd->AddressMode != QSPI_ADDRESS_NONE)
! R8 E7 D6 _4 o - {
; k7 ?2 Y# K, _: s( B2 A% o - assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));; @& m1 I) q3 w- D1 v+ c4 y. A
- }* `' n/ |& v! Z# Z
- ( w/ |7 Q3 w) H- C
- assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));* S: s2 ?/ i9 z9 N" w# r$ D% x
- if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE)( \0 `4 z# L/ V* F) B _( k' \
- {' G {/ A! }& @1 n3 u
- assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));
! l1 ?0 A c7 }) L0 ~' m% j3 t5 { - }
_" o0 d4 r# x' T( v& i. v! a - / I" L; j- b# X
- assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles)); Q2 s5 ]# K2 Y4 h$ I+ Q5 Y; ~8 _
- assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));" S' {7 j8 T9 z m# _
# q7 {! l* ]8 g; w7 ^* P+ ^- assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));
! Y9 b- D+ D8 N- x3 w P9 |; n - assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));" H7 J8 W4 G5 O# Y0 ]. H
- assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));3 M. ^$ p6 M, ^, O7 _
, m. a5 R% @/ [7 _- /* 上锁 */( d3 l/ k' p7 I2 y+ z8 W2 ~
- __HAL_LOCK(hqspi);& t' Q9 V1 i( \/ E' B5 V
7 s( r. x3 a' d ` K b( N- if(hqspi->State == HAL_QSPI_STATE_READY)
) H, R7 K! J7 _* {. N - {" [ w+ O2 E( P5 @# [6 b9 s% T
- hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;
4 J+ I6 m1 x2 Y+ {8 X* A0 i
& |2 n2 w- E/ ?' j- /* 更新QSPI状态 */
, e) t6 X( {) r - hqspi->State = HAL_QSPI_STATE_BUSY;
1 H. m, ?4 v- m8 j
1 e" W) h7 c" n! B- /* 等待BUSY标志复位 */
% Z8 D, x2 `8 R- C- s+ o - status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, Timeout);% e' r5 E$ z4 t8 V' C8 X: Z& z
- ) L$ u% ?$ C% i/ ~) ?
- if (status == HAL_OK)" @) t5 Z! J f! r: b4 N# S
- {4 K- ?8 z S2 p" w* u
- /* 配置QSPI */
5 H' `3 e5 x6 S8 ~ - QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);! i+ f1 u9 l/ N
- 8 ^2 l z6 }4 q8 j$ }. }
- if (cmd->DataMode == QSPI_DATA_NONE)$ Q. k2 u. n1 A+ z
- {
7 W! v, b8 w7 `& ~' j4 ^ - /* 没有数据阶段时,配置完成后立即开始传输,所以请等到TC标志设置并返回到空闲状态 */( N, r4 u5 h; V5 p5 P
- status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_TC, SET, tickstart, Timeout);% D3 L- \# B# G8 E& K, L
9 f6 M9 J0 x; f- if (status == HAL_OK)
3 w( h$ N2 C& ` - {
5 K$ X& K e3 R - __HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TC);: S) X+ v y. B% T: H3 L+ y
( Z2 V7 G" f7 U- /* QSPI就绪 */
; v3 t+ M7 ^4 X6 r2 H$ y" f - hqspi->State = HAL_QSPI_STATE_READY;
4 N/ {) g2 c1 z, G - }
/ X' A8 @9 T5 r! G - }$ d' p2 u$ ]3 q4 f
- else8 v: ?" b- l% l n: @4 F T
- {
1 D+ [ |+ H& X - /* QSPI就绪 */% z2 t8 R) ?0 f( v+ V, m& I) @
- hqspi->State = HAL_QSPI_STATE_READY;/ U$ G5 _' F" D# j
- }
2 \, _9 H5 i& L - }, U; t% e/ l2 ~# D
- }2 n! _5 s6 s$ ]4 G ~5 g: e
- else
5 W, q& T8 t$ |/ G) ?0 M% @1 M' I" m+ ^ - {
+ w7 w }; n+ U% g8 a7 }, y - status = HAL_BUSY;& C0 B$ g; ^! j/ w
- }
$ A8 y# G. E: q( l0 s8 m
7 Q. K6 S9 d& }, S% R4 W- /* 解锁 */
5 c+ [7 T) w5 | - __HAL_UNLOCK(hqspi);
" E5 ~# U7 h4 B. h: y - 5 ?: p+ \& y' M( r T+ s$ c" T5 O2 X
- /* 返回函数状态 */
9 J; o: D* F$ ~2 {1 h; S( n - return status;" t- n& k9 W( @5 A0 I) I, _
- }
复制代码 3 x. M( w& W( i# m l$ N
函数描述:/ S2 x( |8 N& n9 f( U. n
6 l+ _0 j9 p6 E" D
此函数主要用于为QSPI Flash发送操作命令,查询方式。) s, l( {7 C' {/ c/ r
9 v G( ?( I! m& n! g
函数参数:( a- \) k) Q9 Q' c9 D7 i
' k6 n9 A% x- e7 \, E9 ^
第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
( P8 l+ ^. L6 c5 }' V 第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。* G& l4 C# B) c8 Z0 u+ V! t. E
第3个参数是溢出时间,单位HAL库时间基准,一般我们设置的是1ms。
0 {) B1 L' m5 q$ d 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。# o1 `7 a( k/ K" Y
使用举例:" J2 Q* e" @0 r% i/ ~. n
z" t, \4 w' B+ Q1 A
- /*
. T/ }. v4 q' d - *********************************************************************************************************
% N6 B9 X& g8 U* V - * 函 数 名: QSPI_WriteEnable
' H) \: f2 y' T. Y6 [8 R& ^ - * 功能说明: 写使能$ Y; C/ Y# s; J
- * 形 参: hqspi QSPI_HandleTypeDef句柄。& R- ]& e: R$ I7 U. R, m5 A
- * 返 回 值: 无' _- @; o* r" d; k' p
- *********************************************************************************************************
% @. Q- P: x6 }; g8 W( X& } - */
7 w3 @ f B; L3 U - static void QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi)
' n$ E! f. S Z" }0 E - {3 H$ u t: D% k* n2 U+ D7 V: A. m
- QSPI_CommandTypeDef sCommand = {0};1 ?$ b; g* H7 g+ ~% \: n9 }/ Q
- 6 c# k. i5 a; a. d' T
- /* 基本配置 */* e( X) f9 J+ U6 s
- sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */
- a' g8 H* x/ x0 [$ g# j% V - sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */' w6 g; ~: T; X) j, B) _
- sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
* s+ D1 [/ h4 U: N+ q( E c' ~ - sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */9 r5 Q, v3 j; t7 G1 G
- sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */! A: S8 P+ v4 _/ ~, t; r
- sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 */1 F0 l+ t8 E, M3 n# {
; I, _' R- Y* C& A- /* 写使能 */
[- ^+ l C8 E3 s: u' L, o - sCommand.Instruction = WRITE_ENABLE_CMD; /* 写使能指令 */+ v- X. z; |4 |& g
- sCommand.AddressMode = QSPI_ADDRESS_NONE; /* 无需地址 */' O; i# Y# Z$ v' w$ [$ L- B
- sCommand.DataMode = QSPI_DATA_NONE; /* 无需数据 */
) q) }- K- h* Q$ s4 Y, G6 B - sCommand.DummyCycles = 0; /* 空周期 */
" w3 Q3 r+ z9 W. t9 q/ s! K" {- H8 y
3 S4 S! h) l* e) a# j1 @- if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)% j; t* v, T( q" b! E( B6 n
- {8 C- ]- {, p- u, n1 C, z) F
- Error_Handler(__FILE__, __LINE__);
/ j2 V& E& Z5 ~0 e+ E" r' S - }
, }3 T# F& l: _4 w1 `& e3 b* M) b - }
复制代码
8 I2 B# @' R$ R( T& _78.4.4 函数HAL_QSPI_Command_IT
! |& Y; Q4 k+ G函数原型:; Q3 h5 O& [7 f, u& j- D
3 M' Z) _6 j% D# x- HAL_StatusTypeDef HAL_QSPI_Command_IT(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd)
# p7 Z" q, \# j+ W7 v - {
* p1 _1 s. v: t1 `! _4 E - HAL_StatusTypeDef status;
1 c+ h; j6 f3 B j) V - uint32_t tickstart = HAL_GetTick();, k, O) [- u$ v! r7 L
- " A+ U: [2 q: C
- /* 检测参数 */! o1 x1 F+ G! @! ~
- assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));
* d; C/ M6 W' u7 s- t2 u+ P - if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE)0 d ^1 D7 V' l( A. g) h( A
- { S* U0 A* H4 w1 V
- assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));4 }1 P7 ~7 I/ Q9 R5 X: G4 x5 I
- }2 d: o) m( ^$ H4 X
- ' J' s: _; a" m8 d
- assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));- ?! n% o0 n8 S* G& ]
- if (cmd->AddressMode != QSPI_ADDRESS_NONE)+ ~0 ~, E- m0 X' ? [. y
- {1 A7 S k$ l# Y a( Q4 s7 L
- assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));
- }/ F" L, ?# `& l) G - }
0 S# ~; X) v- P* I+ |8 i
\5 o( l' w r5 M2 W3 I- assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));+ d2 b/ w; g5 N/ e
- if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE); e- y" P& j3 {0 u! M5 S& E
- {
: ?8 m& y( \+ `; h' b) k: T - assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));
! f7 R' \: ?- P4 N+ r - }2 t! I6 g5 I6 Y& z4 P; A7 Y
- `4 w7 i: P/ \, J
- assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));
" ]4 H0 {$ H! N* Y# E- Q, P - assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));
4 p7 V$ r: l! l - 0 ?5 O; i; }1 E$ T: e3 ~6 Y) ^
- assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));' }. A1 f( i( X+ s! h
- assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));
) ]7 `- h! J: E w6 V; A' T& h - assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));
$ G( M: ~2 |! u% v9 Q - 3 }8 c2 V: K0 Y( w% N# O
- /* 上锁 */$ {" F) w6 i6 m# d' n2 m: ^
- __HAL_LOCK(hqspi);
0 ], ^; f+ M2 i% b
# Z/ U+ O& R k% h# X- if(hqspi->State == HAL_QSPI_STATE_READY)
6 \' _ [! x8 {, } - {$ z* U9 S, ^$ c# T( Z' L, a
- hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;
! b4 Z' W1 A/ o1 J - 7 O) Z5 w; H7 \# S' i6 G- k
- /* QSPI忙 */
& l; O; A: w7 ~8 |0 }- a, M - hqspi->State = HAL_QSPI_STATE_BUSY;! p1 w/ @+ L3 N, L# b
2 ~ Y! U- e# e1 {# s9 I- /* 等待BUSY标志复位 */( B) _# M9 M4 u+ a) p
- status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, hqspi->Timeout);& z( Y3 @9 _/ t) ~& C* i; _
- 6 v0 G$ C; h) b) e7 L
- if (status == HAL_OK)
1 I$ |/ \7 u" s - {
4 A3 b2 z: x/ e4 c$ I% g: s - if (cmd->DataMode == QSPI_DATA_NONE)' u) F$ o7 b& m; V, D4 a( s
- {, E# h) G/ w5 V% ~) h o
- /* 清除中断 */
* [" U, B4 u7 @9 ?$ p - __HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TE | QSPI_FLAG_TC);
$ [* Y& i* i# p2 g3 ? - }' `2 N A `$ c0 m
- ( y9 m* l; B* W7 @- X- ^6 x! b
- /* 调用所有配置函数 */
5 ?( E! O8 Y1 y+ H/ Z# l - QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);' j7 a2 n; \5 R0 j7 y9 W
- " @& |$ @1 T) E$ @6 S
- if (cmd->DataMode == QSPI_DATA_NONE)
; r+ x4 s/ ]! O2 o - {3 G+ W; V* G! H2 v5 O% |
- /* 无数据阶段,配置完毕后,立即开始传输,所以机会TC和TE中断 */
% ^7 q/ ^0 u. X" @1 D( [ - /* 解锁 /( j' E* t3 ]8 r
- __HAL_UNLOCK(hqspi);
' j9 f' y1 _- n; R$ ^ - - R* x/ Q R# r$ Y) B8 f
- /* 使能TE(Transfer Error)和TC中断 */& r1 C, w& N# _( b8 N
- __HAL_QSPI_ENABLE_IT(hqspi, QSPI_IT_TE | QSPI_IT_TC);
9 H5 Z# T1 V1 E0 H0 s( e9 l - }! p3 p/ i0 O- v$ B+ S+ @2 Z
- else
2 a# V1 P) e& i - {( `3 c: h6 G; u0 K& Y+ g" N! q
- /* 更正QSPI状态 Update QSPI state */
+ N) C: s1 c3 Y - hqspi->State = HAL_QSPI_STATE_READY;
& N, g2 z& M6 l - % d! y: p0 C" \) z' n6 y9 X, D1 ~
- /* 上锁 */
1 t+ Q* U/ N. a0 o0 x1 K - __HAL_UNLOCK(hqspi);
! _6 c% y" Y% _* G Z - }
" }) H) t5 G9 L8 D+ g2 @ - }
2 t6 y' n. M+ K) h- k- k; w - else
! P! p# q+ J* `- B2 O7 X - {
) u* ~: c4 b8 l6 o/ n - /* 解锁 */
: w& W \$ F; E( a/ u5 \ - __HAL_UNLOCK(hqspi);
7 L4 @; x9 N7 U; d3 ]) d$ ~ - }1 Z" v, @* \3 I/ K, }
- }. c) @! y# E4 {
- else
' W% M% b+ b7 Q p* u+ A - {
5 i8 h; q/ N/ u& d - status = HAL_BUSY;- G' h4 a' Z" i3 i4 G# h5 x1 r) T
- 8 q4 z" X$ I# U5 S" r1 M7 S9 l
- /* 解锁 */
% P& q2 e' h% R' |( d( Y - __HAL_UNLOCK(hqspi);
2 ~5 m* D5 m, L* @2 H% A _4 w- j - }8 y- U( E5 B, V1 M
7 Z0 q1 o0 x0 y- /* 返回状态 */
! r6 @3 F. a0 V& u, r - return status;
& B6 ?+ P: @" x, X& N& [ - }
复制代码
4 e7 u; }- g2 B3 T8 T函数描述:
) }8 A7 r1 z: n# B# f* v% u
7 u4 K% E2 g7 u此函数主要用于为QSPI Flash发送操作命令,中断方式。# o; X$ Z$ `2 q Y% n
2 o( |6 q: T* s5 |* s6 v% l6 y函数参数:
3 R# d9 X1 e9 s* g9 ], E' F$ n( r, Z3 M( j) p0 g F
第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
2 L* h. U2 d8 J2 O4 ^ 第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。& z0 L! t* n9 s8 @% e+ P
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。% y9 j' v" o# d% X3 ]* h, F7 m2 z7 r! ~
使用举例:: q: f- ] i) G* l. y
b8 g# T$ x) _& t8 P, u6 N; M- /*% [1 d5 Y7 @& M P
- *********************************************************************************************************; w4 u: ?( y3 ?# Y9 k7 _
- * 函 数 名: QSPI_WriteEnable
/ S( b- W# o7 y% T% f8 } - * 功能说明: 写使能
/ m5 V7 V3 T, J6 O$ [5 T2 o0 | - * 形 参: hqspi QSPI_HandleTypeDef句柄。
3 V5 }& G4 L7 b. o) `5 J - * 返 回 值: 无* o9 x' k4 R+ g( g, I
- *********************************************************************************************************
" b' b+ x1 K7 [ - */3 R5 C( R& H) e6 Q7 T
- static void QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi)
& }$ ~# M; Y9 d) x: L2 ` - {" m: ~- q' C' w5 H
- QSPI_CommandTypeDef sCommand = {0};
& d/ \ x( Y1 x# q, T* c7 U - , w" C" |3 ^( z5 j( B
- /* 基本配置 */7 O& x( d2 y' d1 A% F+ \( O
- sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */
s! C7 [! Y) L& ? - sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */% {# j: i6 A% t
- sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */2 g- |' L: S/ y) a" D. {* ]9 R9 a) y2 ]
- sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */
; ^, D9 Z8 }( E4 I% e - sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */' [) j# N' c9 M, E ?/ [- B3 s& N& b% _
- sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 */0 J5 m3 G& [$ v: ~
! _* ^. P: P: s# m& f# P8 `( C, h- /* 写使能 */
4 s- w7 l d' ~ - sCommand.Instruction = WRITE_ENABLE_CMD; /* 写使能指令 */
& t: J8 x1 }# m8 b - sCommand.AddressMode = QSPI_ADDRESS_NONE; /* 无需地址 */6 u3 P! p0 @) S
- sCommand.DataMode = QSPI_DATA_NONE; /* 无需数据 */
) _& t! d8 u+ N% \: {7 v& e, X9 U - sCommand.DummyCycles = 0; /* 空周期 */8 a0 h* ^- C, E' V% s
- C% U) Y9 z4 d$ |2 l; C- if (HAL_QSPI_Command_IT(&QSPIHandle, &sCommand) != HAL_OK)" @/ ?& U$ a" w. j/ K
- {
8 L, y- H( o7 P5 n' L* X - Error_Handler(__FILE__, __LINE__);
3 ^1 _1 q& {. T+ C - } % d/ [; x3 q- y2 Z% t& E
- }
复制代码
1 }3 Y6 H2 ]8 k1 I, Z8 S s( \78.4.5 函数HAL_QSPI_AutoPolling* s6 N% d( Q1 T' p+ c
函数原型:
1 l+ B: f/ a. ]7 Z4 N- b
( @- M' ?& v7 g! x$ C& B- HAL_StatusTypeDef HAL_QSPI_AutoPolling(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, QSPI_AutoPollingTypeDef *cfg, uint32_t Timeout)& g+ w8 {" M: `6 b) a! X; ^- J
- {2 c* B; U3 U! q z) B4 p
- HAL_StatusTypeDef status;; `0 O" x1 p. N; n" m# G
- uint32_t tickstart = HAL_GetTick();) W" i5 \' u% i6 K) |
1 I! [% t& m3 J; P1 ]& T# s- /* 检查参数 *// b U6 ^) J3 I; i3 b; Q
- assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));! p4 z3 `4 B( O( R+ s" p
- if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE)$ H) _- j8 \. U- H0 @% A
- {& J" i$ ?3 P8 n l; A
- assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));
: o$ G1 h! }& Z3 c. T - }
( [5 j7 r5 {) P5 t - 3 r6 a0 u6 U' o7 o7 s# h# w
- assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));: T) q$ H7 E. ?$ f" |
- if (cmd->AddressMode != QSPI_ADDRESS_NONE)) r. {8 V" _( `2 @
- {7 v% {* f8 J/ s$ b' l
- assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));- U5 ]- V. F% ?. }
- }
( O1 v: P1 T9 `, y7 M G - & C# Y- G( V0 J' S8 f
- assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));: u# {8 p/ r% [$ ]9 x2 i u
- if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE)0 c4 O6 G+ |+ }& B
- {& t* O+ f" I' D; w5 K- l7 J# Y
- assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));
: j' Y: }; i" S% M( q3 Z1 w8 \ - }
1 e1 o/ n7 P* c" |4 m - : K1 k/ o4 W& p+ I8 T1 D" W
- assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));: ]7 [/ \) H, _6 L) v
- assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));
0 H( Y9 J9 r9 g$ a+ L" ]2 e - + j1 n1 o* [* ^6 z' q
- assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));, |6 t( L" r5 x
- assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));! v! ]4 R% Q5 B' R
- assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));
) L6 P& J' X! ~- n" r - 1 |8 u$ _6 K6 n& p* j
- assert_param(IS_QSPI_INTERVAL(cfg->Interval));0 p6 v" c; C+ S" ` v8 p
- assert_param(IS_QSPI_STATUS_BYTES_SIZE(cfg->StatusBytesSize));
, y2 y4 d' ?! {! E+ V5 n- n9 T - assert_param(IS_QSPI_MATCH_MODE(cfg->MatchMode));
% l1 \0 [! \+ H
, {' R1 I, ?5 \; k- /* 上锁 */8 _8 o: H6 A; l% k
- __HAL_LOCK(hqspi);) E* N6 B% b, _! ?* a7 C, w
- 4 @/ C9 T) X5 B% B- u! M
- if(hqspi->State == HAL_QSPI_STATE_READY)
5 ^0 y/ Z4 x5 s3 d8 w* W" t3 n3 a - {. d* I; z( H& X1 U) S/ d; a
- hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;% }# G% ?. t4 B5 {$ X) W) }
5 e& v) r! y7 ~; ^# q- /* 更新状态 */
5 h* m; L- T, \: e t - hqspi->State = HAL_QSPI_STATE_BUSY_AUTO_POLLING;
8 O# E2 ]; p( v; M
: n- B/ z' d$ m0 M- /* 等待BUSY复位标志 */
* P: x( G9 s7 e. F* i, k - status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, Timeout);" n8 u2 F: o8 J* w3 W2 _3 X
- 6 h; z% a3 m U7 Z' y5 |
- if (status == HAL_OK)
5 O" W, d2 H5 U1 W0 n2 l) b; v - {
$ y0 o, z. ]+ @ - /* 配置QSPI匹配位 */
O+ l" C, j& k/ z - WRITE_REG(hqspi->Instance->PSMAR, cfg->Match);6 f+ N& m8 U3 z; [
* Q) l5 v. b* i- /* 配置QSPI屏蔽位 */" r% j l: a2 A& a2 J: a
- WRITE_REG(hqspi->Instance->PSMKR, cfg->Mask);
L0 x" G8 i# ~9 A6 S - 2 ^* b: J2 @& ?8 F' l7 q
- /* 配置查询时间间隔 */% l1 [7 ^6 }0 M4 D, u( b. {
- WRITE_REG(hqspi->Instance->PIR, cfg->Interval);
* ?) ^' G% \" b# y, B2 u9 c. [" E
+ `- ] O( _ C: e- /* 配置匹配模式,使能自动停(否则阻塞方式无限等待) */
/ F$ P2 |% N& ~4 j - MODIFY_REG(hqspi->Instance->CR, (QUADSPI_CR_PMM | QUADSPI_CR_APMS),
, z. r% v+ ^. V; r' ]7 e9 k - (cfg->MatchMode | QSPI_AUTOMATIC_STOP_ENABLE));
- j5 F0 v- t# L+ k2 z B" D - ; z8 K3 z$ q4 ~/ F; ~
- /* 调用配置函数 */- Y+ B% ?, N/ c' A" h
- cmd->NbData = cfg->StatusBytesSize;
0 U" D# c7 k; T: N - QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_AUTO_POLLING);/ p2 n% h* M5 ` X8 J# F
4 Y. i$ s, m1 {& y- /* 等待SM标志 */
$ t# E4 o2 e7 [) V( x. i) l: ]0 ?$ U - status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_SM, SET, tickstart, Timeout);# ~9 w9 F% u2 A
! c1 q0 s( m6 Y0 ?; e& G- if (status == HAL_OK)
! q7 C5 l! b& r; E( h" ~# S; ` - {1 u8 W* o- X. g! @ D% o3 g# U7 j( s
- __HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_SM);
7 U0 K+ Y* d# M6 H5 k3 q( R0 }
7 C( A1 W2 Z+ r" q- /* 更新状态 */$ J, T" m8 K5 ~# [9 u0 m/ `
- hqspi->State = HAL_QSPI_STATE_READY;
: N, \0 @# u7 h - }
4 b/ _! v) h8 E- | - }
?1 V; z, s+ f* Q" b2 N/ w/ V - }
$ n# s2 |+ R" X+ L8 W - else
5 j; `5 V9 W ~1 ?: L6 [4 ~ - {5 Q2 `4 C7 M, {$ i$ j2 O4 S
- status = HAL_BUSY;, Y2 l) a$ C5 L8 C' r% J7 Z
- }
% H, p2 r) N2 K, H1 D2 n
( X1 {; `& c! Q8 m7 E3 g- /* 解锁 */
- X( S5 b8 N9 E6 E - __HAL_UNLOCK(hqspi);8 d ^) k; m% Z" ~/ m- d
1 p7 _5 g3 t0 E: f* }2 y6 V- /* 返回状态 */( H2 o# e, L0 J G1 I+ n
- return status;
6 X& C R( p" a& s. Q& E5 h, V; z8 f - }
复制代码
8 V O0 V s0 c+ g E- Z/ K* \函数描述:- W+ p; V8 O8 Q3 n5 f
# n5 d7 [5 H: } d# p1 f
用于状态标志查询,函数采用的查询方式。' H3 q- y( f1 A/ P6 Y z
% q q& E7 Q$ i
函数参数:
* |5 Q6 W& D0 X- O, I; o5 ?) K
0 e; K' C# S/ Q: U( s% G, R2 X 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。2 q3 b% w% N' |' G5 K+ i! R' k
第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。
" P1 B6 }- R! g: ^ 第3个参数是QSPI_AutoPollingTypeDef类型结构体变量,详见本章3.5小节。( z/ E3 Z, V- |+ z" B
第4个参数是溢出时间,单位HAL库时间基准,单位1ms。4 G7 O% S, Y& j- m, k4 n0 R9 h( y
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。' E( b, D6 u* o0 \/ t9 f
使用举例:# [2 I C, W5 O+ j0 B4 ^! |) _7 o/ ]
* W8 Z: K- \2 s2 V! a! f$ C+ u' ~9 D
- /*4 i+ H" t: p/ P
- *********************************************************************************************************% u& |, f. H! p/ Y
- * 函 数 名: QSPI_AutoPollingMemReady( E: I. `! e) I% H7 x; K+ ?5 O
- * 功能说明: 等待QSPI Flash就绪,主要用于Flash擦除和页编程时使用
0 p t0 P, W% ?& l - * 形 参: hqspi QSPI_HandleTypeDef句柄
& j$ k! {" i' h: d, |! t - * 返 回 值: 无 j' R I' s0 E# ~
- *********************************************************************************************************
2 Q/ c- F$ }9 S. B7 Z& _8 @: l! V6 `% O/ r - *// ~- h2 j5 E' g7 V) Z
- static void QSPI_AutoPollingMemReady(QSPI_HandleTypeDef *hqspi)7 C I/ i, v' X9 `2 s! K: k
- {
6 {6 {, w6 Q. J. j O+ C. P6 M4 L - QSPI_CommandTypeDef sCommand = {0};) [5 m$ }+ a8 J! t) B' M
- QSPI_AutoPollingTypeDef sConfig = {0};
8 k7 s- L( f A; Y& k- v - ' P' }6 ^- V$ N+ L4 ?% _
- 7 o. r$ S8 ~( {0 ~: c& c
- /* 基本配置 */; R, K. ~" ]/ R6 M5 p+ S. ~! w% q
- sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */
$ i4 I$ ?) W Z0 q - sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */
1 V8 l& e: y7 v$ a! U2 y; w6 m: | - sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
- L. m+ v5 K8 f6 Z - sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */' x- G% f2 b# Y0 Z9 ]& ]
- sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */8 K( p: S# K- q* c, `# R
- sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 */7 q3 r$ c0 ]* |- U" z
1 \' O: K# x8 W3 J( q- /* 读取状态*/7 i. T9 c+ Z! x# u* {) K
- sCommand.Instruction = READ_STATUS_REG_CMD; /* 读取状态命令 */$ K6 J# y8 H4 C3 I! f% ]" U
- sCommand.AddressMode = QSPI_ADDRESS_NONE; /* 无需地址 */, {' O1 Q2 d; ^) G
- sCommand.DataMode = QSPI_DATA_1_LINE; /* 1线数据 */
. `8 G6 o# V& n1 U6 H' R - sCommand.DummyCycles = 0; /* 无需空周期 */* [: ?7 _9 ]: h: u/ O. ~
- i% I6 U3 `6 M9 m) i- /* 屏蔽位设置的bit0,匹配位等待bit0为0,即不断查询状态寄存器bit0,等待其为0 */
1 X" k' X/ R+ A9 ^/ @: p - sConfig.Mask = 0x01;' n9 ]1 o) T9 a9 [9 @4 O
- sConfig.Match = 0x00;
5 t1 k5 a$ j2 s D8 M8 T2 z - sConfig.MatchMode = QSPI_MATCH_MODE_AND;- z; W# M; {) s8 f8 i6 z
- sConfig.StatusBytesSize = 1;
7 ]8 n& g8 `3 m; W( r3 K& K: y - sConfig.Interval = 0x10;6 ^ A( U9 ]: [- _% r6 Z
- sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;* e3 R! @. @1 K* X7 S5 U" D* `8 v
- 0 S$ |9 b; Y. E
- if (HAL_QSPI_AutoPolling(&QSPIHandle, &sCommand, &sConfig, 10000) != HAL_OK)
; _8 S; _& K" H/ V2 y9 ~7 {; t) `; L - {
/ `* v- Z+ y% v; g4 I. ]" | - Error_Handler(__FILE__, __LINE__);
* G- N; v& W% }$ w - }
3 q! s6 t _% |: ` - }
复制代码
2 H, w4 r, ?8 m' r' N78.4.6 函数HAL_QSPI_AutoPolling_IT( s, T7 J+ j) ?" `
函数原型:
& n0 k5 x y7 D7 U/ V. S7 i/ Y
* s: R- V# Z( G- HAL_StatusTypeDef HAL_QSPI_AutoPolling_IT(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, QSPI_AutoPollingTypeDef *cfg)* _% l* B( P$ [# B& w! T
- {
$ r% u; M" L z8 B$ h3 Q# @3 y - HAL_StatusTypeDef status;, @# J. g# P6 y I, Z# ~' O5 i
- uint32_t tickstart = HAL_GetTick();# @8 p1 X8 q9 w+ Y, }* e* e' I% Q+ T
- 6 ~1 g7 o2 E: H9 ?* X6 J% x& }
- /* 检查参数 */
" N, {' o% U4 X4 P5 u - assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));2 m$ \ r- U" w# {1 a, @
- if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE)
- V) p* t+ d* x% \, F* x2 J - {
$ s+ D, V) g, Y6 K - assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));
9 ?7 Q- k8 n+ ? - }
- B6 E1 y; V2 K" E* Q - 4 l. P4 c7 o) Z
- assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));
6 B/ G6 r: V' z - if (cmd->AddressMode != QSPI_ADDRESS_NONE)
! N* F% |" m( P$ M9 E - {9 K5 j4 i( u% x
- assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));) A8 ?0 M Q. q R3 _; j ^2 O
- }
) d( a2 }5 O' b- h& E6 w; d8 M - " v! Q7 U# J# D; l K2 p4 g& k: v& t
- assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));& o) R4 S/ A4 k8 u3 n- c b
- if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE)
8 J$ p* q# i! p. f - {
$ A8 z3 F" N4 h3 B - assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));
$ t3 Z$ H$ i, ~# y, e5 `/ D - } N* o9 Q8 S6 R" v9 D7 I. f
6 r5 ~; b9 u1 I2 h8 d- assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));2 h( D' \* c+ b8 _
- assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));
" g; ^$ l; H; E& [$ A& Z' v - * q/ y( |" t6 m* E
- assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));
6 T! G L4 l* I1 \( [4 b - assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));6 ^6 C" C# T9 `2 p* t
- assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));
* K' E/ D" G: B5 S6 i - ' U7 E3 D! N: w% P. ^9 s; q% E
- assert_param(IS_QSPI_INTERVAL(cfg->Interval));+ m; ?7 z/ H7 m/ V
- assert_param(IS_QSPI_STATUS_BYTES_SIZE(cfg->StatusBytesSize)); P# ?- d; `2 O; l8 ]
- assert_param(IS_QSPI_MATCH_MODE(cfg->MatchMode));
6 O8 ~& _. B/ M - assert_param(IS_QSPI_AUTOMATIC_STOP(cfg->AutomaticStop));4 R! N: G* P& D& q
% s5 Y( ~( @; M$ z& `; s$ a& d. s Q- /* 上锁 */
]9 c) j1 J" J! D - __HAL_LOCK(hqspi);: G- x/ e! }% T0 D2 O' \. |/ H
U! h# h7 K, I- if(hqspi->State == HAL_QSPI_STATE_READY)
# |& K5 r! U9 c' l - { h" Z+ y! H ~
- hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;
7 Q; v8 D9 U1 B! N* \. U/ s
3 }% l5 X6 v9 S& q: r" {- /* 更新状态 */
$ Y+ A! s* ?5 U - hqspi->State = HAL_QSPI_STATE_BUSY_AUTO_POLLING;
* a9 d, I* i% L4 g) J/ w
3 X1 P8 d5 i- ?2 @! Y4 p- /* 等BUSY标志复位 */, c5 F- n* N6 Q1 {5 b( {
- status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, hqspi->Timeout);4 j$ a. s+ s# T; I% O* @9 {1 C8 W
! F: m. O/ J* q, g" B- if (status == HAL_OK)3 y% }$ @8 }+ u
- {
7 j* g" P: v. X3 ]0 p - /* 配置匹配值 */0 |8 K8 J% U G: J& ^# F
- WRITE_REG(hqspi->Instance->PSMAR, cfg->Match);
" y8 m( w* g+ `: A8 v) X
+ e6 S/ n: h6 S1 [# p% D! I2 n- /* 配置屏蔽值 */: t1 k$ U( w9 a
- WRITE_REG(hqspi->Instance->PSMKR, cfg->Mask);
; W) i1 J& g, ^! O6 W! \
) a6 {$ L4 H+ p" w- /* 配置查询间隔 */+ o/ }: g3 [, Y9 j. ]5 ^( Z
- WRITE_REG(hqspi->Instance->PIR, cfg->Interval);" H* M) B% O2 g- D- b" `
) D8 O) x3 U( C/ g+ f# ?! G- /* 配置匹配模式和自动停止位 */
4 @, i$ F* q' h0 m0 O) H. n. i - MODIFY_REG(hqspi->Instance->CR, (QUADSPI_CR_PMM | QUADSPI_CR_APMS),9 m% \' l: U2 a3 ^( s" n. W( ]
- (cfg->MatchMode | cfg->AutomaticStop));
+ ~ ]9 H, z8 y6 I& [8 I
! U! ]/ X6 |* ^& q1 I- /* 清标志 */) N" T0 `6 a+ O3 b& t, v
- __HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TE | QSPI_FLAG_SM);
" \; S/ i* m3 p7 Y) |6 C) T1 _4 r# p - + S. v) n/ D# h' ^* w5 g0 E
- /* 调用配置函数 */
, s$ S( S; t+ ?0 z - cmd->NbData = cfg->StatusBytesSize;+ Y1 j& b4 @% Y; h5 W
- QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_AUTO_POLLING);
5 D2 ?8 ?* Z! `. s
& |. ~6 |0 r9 B, J- /* 解锁 */$ J' _/ o- |* K+ |
- __HAL_UNLOCK(hqspi);9 o' O- i# l. y/ z8 H( Q5 O( U# C
* J. o5 Y# P2 ?: e: @, U- /* 使能SM(status match)和TE(Transfer Error)标志 */
+ A8 \0 T- }& x3 v - __HAL_QSPI_ENABLE_IT(hqspi, (QSPI_IT_SM | QSPI_IT_TE));' j" `4 c7 w$ ~4 h9 y
- 9 F; n$ Y% X# M9 X4 q
- }) H l3 {* A$ B1 ~* x# U. n7 C
- else$ X; b! R# X) ?( P
- {/ Y# [% G. f; R: v
- /* 解锁 */
) L& J& B, V4 l& Y3 I - __HAL_UNLOCK(hqspi);
7 U6 o4 L# ~8 I: { - }
# K4 v' V5 I: w! d& U$ i, {9 I, l9 F - }
8 m! S- [, R8 { - else
( ?" c1 ^% A- A& M9 J - {7 [; @) O; z" n, P
- status = HAL_BUSY;% |/ r8 A6 K" l. |" F9 F; w- t6 X
$ _' p% `3 w/ d' k7 {- /* 解锁 */
/ W/ {' C9 W4 e4 D, } - __HAL_UNLOCK(hqspi);4 t" v Z7 E4 P/ I4 ~& I
- }& B& K. B7 ?- I' @) m
7 d F# t3 W M! y W* e p- /* 返回函数状态 */
2 s) L0 ` E% r+ E - return status;7 y$ a) P; ^5 V4 z
- }
复制代码 ! F9 @) |8 a, u g
函数描述:8 O; q( E" v& Q4 J) p3 D6 Y% t( D3 t
2 ?8 C! v& I8 H
用于状态标志查询,函数采用的中断方式。
- K" x& r* r* b `1 O$ I7 L6 k) b% a
0 k6 o& L& p" x. i+ M函数参数:
, R. D* \. F2 Y1 a8 |
0 w+ H' i$ p, x! s 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。2 v3 S& n+ g$ Q0 S3 q
第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。 t1 d. x& I$ `7 I
第3个参数是QSPI_AutoPollingTypeDef类型结构体变量,详见本章3.5小节。6 L" P: p% D8 a1 B! w* C3 f6 M
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。! i) g& b; }1 b: n1 D/ P: Q
使用举例:
; w, l$ ]; N' w" c' B# ?* ~# z [2 V7 G/ w
- /*: a/ C7 g5 _; N
- *********************************************************************************************************& m* ? r2 B- \4 v
- * 函 数 名: QSPI_AutoPollingMemReady
$ _3 o6 p, U) Q: `' O$ z$ J) a - * 功能说明: 等待QSPI Flash就绪,主要用于Flash擦除和页编程时使用
* e; | X# ?2 [ - * 形 参: hqspi QSPI_HandleTypeDef句柄1 `$ ~+ x0 h! p" a- O7 E8 Q/ f, F% P
- * 返 回 值: 无
( Y2 O0 x4 h5 S- O6 J - *********************************************************************************************************( S. ~6 K, @7 U4 H8 ^5 i9 o7 I
- */; D+ ]) g& G% b1 s+ ?! e% i
- static void QSPI_AutoPollingMemReady(QSPI_HandleTypeDef *hqspi)7 C- J9 B& A' ]: W# z
- {
$ k. s" V( k6 G3 |1 r& L - QSPI_CommandTypeDef sCommand = {0};
, R% c- l( x) Z( A7 \ - QSPI_AutoPollingTypeDef sConfig = {0};
$ b" t B. \( L - ) f5 o6 Z1 e( {
8 o. }( W' _2 L7 J9 z- /* 基本配置 */
& C) q9 l" ]1 m/ N4 v - sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */" X4 t4 _0 x$ ?; g- j5 G) F
- sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */2 D) H. F1 D4 x% D6 f! x
- sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
l" z! }: b) w' t. w - sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */
8 a2 \! W+ Y Q - sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */! \, _! l# z/ q# V y
- sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 */+ w) f6 ^8 B; x, b3 J% n
6 o# W4 C. d' S8 G* X. t- /* 读取状态*/
' j: Z* \; Z/ v# Q# r+ v1 x: z - sCommand.Instruction = READ_STATUS_REG_CMD; /* 读取状态命令 */
1 j/ T8 t% q/ @% Y - sCommand.AddressMode = QSPI_ADDRESS_NONE; /* 无需地址 */5 S* G/ x; V K+ N( ^
- sCommand.DataMode = QSPI_DATA_1_LINE; /* 1线数据 */6 x- i l3 F& ~' O' A
- sCommand.DummyCycles = 0; /* 无需空周期 */
; n p) `" j. J0 M6 U/ ?
' ^: d+ e4 _, `) B; T- /* 屏蔽位设置的bit0,匹配位等待bit0为0,即不断查询状态寄存器bit0,等待其为0 */
. T# C" o$ c' H+ ]2 W1 @ - sConfig.Mask = 0x01;1 B8 d0 M& `" D- U
- sConfig.Match = 0x00;
8 Q* J, W% E3 f - sConfig.MatchMode = QSPI_MATCH_MODE_AND;" U2 u: j- Y% E4 I5 p# s, `
- sConfig.StatusBytesSize = 1;
# a" e) ^$ u# e. O - sConfig.Interval = 0x10;2 ?6 k/ t: s2 e6 \1 N
- sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; p+ d. x1 K) \' J
- 7 D/ x& h4 ]# t S7 U m; p) e
- if (HAL_QSPI_AutoPolling_IT(&QSPIHandle, &sCommand, &sConfig) != HAL_OK)
5 W1 i0 {2 s- }. g3 B+ G; N - {
' c+ A- J( L+ p - Error_Handler(__FILE__, __LINE__);; h9 ?4 c6 @1 |9 @5 L: a z; W( H
- }
7 t9 g/ \3 d4 y- r0 ^$ F9 h, V+ @ - }
复制代码
3 ~9 X/ z7 _2 ~9 p78.4.7 函数HAL_QSPI_Transmit
8 z3 l/ G: ]1 _3 {6 n6 ^. z* ?函数原型:8 V M! B7 d2 Y# c0 m. i
9 g+ H# S% W$ J* [" x* z# Y* n- `# y- HAL_StatusTypeDef HAL_QSPI_Transmit(QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout)
g& P+ |- g% ^$ a - {
6 m; d7 d. ~4 E+ d, z+ p - HAL_StatusTypeDef status = HAL_OK;, P2 s1 W2 \/ ~% Q3 [1 _- c
- uint32_t tickstart = HAL_GetTick();; s8 D* r+ A8 Y! l8 T8 | R9 f; v
- __IO uint32_t *data_reg = &hqspi->Instance->DR;4 |: f8 m6 q) u: g
- 4 n1 u; p5 Z+ W2 D
- /* 上锁 */$ ?1 j; F( e$ O! x
- __HAL_LOCK(hqspi);
3 g- T) l* }3 U% N
3 _0 M2 X+ p! n- }- if(hqspi->State == HAL_QSPI_STATE_READY)
; @ i2 M& W7 r- c7 c7 R - {
, O9 @2 K6 F/ i7 I- p - hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;, l. f! P: U& L: ^% C0 b
( C5 |8 c& ^" j1 r) x5 |) a# p2 U- if(pData != NULL )
3 k( I: T+ R2 d; g. M' ? - {
+ w/ p9 T6 H# f2 t | - /* 更新状态 */
" G+ `) U7 Q. {, X; t9 ` - hqspi->State = HAL_QSPI_STATE_BUSY_INDIRECT_TX;
, I# ~: K. {+ T' j! e* w
1 M/ Y& S/ _5 @) K# k- `8 a- /* 配置发送 */: t, ~$ d5 W, a1 b0 |
- hqspi->TxXferCount = READ_REG(hqspi->Instance->DLR) + 1U;
; Z2 P# W' V( h# d4 o# U* R, V! y - hqspi->TxXferSize = READ_REG(hqspi->Instance->DLR) + 1U;5 }% m1 ]. D' e% _ d. E
- hqspi->pTxBuffPtr = pData;
9 H/ t0 }) Y7 N- b Y
9 g& V: p7 ]4 o3 b' `/ }- /* 配置QSPI,间接模式 */
+ u b: J8 L+ n# j - MODIFY_REG(hqspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);
4 j z1 [. ^9 `4 C7 L, k3 K( D - ' c5 p8 F; @5 `) n
- while(hqspi->TxXferCount > 0U)5 y# p7 ?. ], v% U: ~$ H; L# ?
- {: d7 I" n# q( N# N) l- i; B0 H
- /* 等待FT标志 */
- P1 A, c7 b b+ E6 S2 @ - status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_FT, SET, tickstart, Timeout);& ~* A6 Q E9 |6 S: o) K
- 2 Q# ~- A* K2 U* R( u$ v
- if (status != HAL_OK)4 ~1 e, v$ D( g- i
- {
% F! ?6 G, d5 H# i - break;$ O7 C/ Z& R5 Q1 T
- }9 u) x1 O; K6 I0 i" I; ^; h
N0 I. r! v0 V# y4 O. m- *((__IO uint8_t *)data_reg) = *hqspi->pTxBuffPtr;
% d2 F# D( t0 ^. d - hqspi->pTxBuffPtr++;) M2 o3 G& Q8 d) ~" B
- hqspi->TxXferCount--;1 W5 b" | m( Z% q$ a* H3 b
- }
3 c) f+ e' \* z: H( Z - ) r8 u8 z9 F3 Y, U2 h
- if (status == HAL_OK)0 E3 h/ Q$ J) o+ _* ]7 x
- {* ^$ L, I5 ?4 k8 W Y* l3 q
- /* 等待TC标志 */
6 b2 k9 l7 F$ p* j8 R9 s - status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_TC, SET, tickstart, Timeout);! w3 Q; m( t0 J# v
& f3 J" Y( f, o' \- if (status == HAL_OK)
: \$ b; d8 } g4 v4 v5 O* z - {
$ m5 N% e- P& D+ z - /* 清楚传输完成 */
* z: N/ C2 y- q" D: K2 N1 @ - __HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TC);
3 k2 P6 t6 ^6 z R! t' C
! ^; Z# P; i6 Q( f- }' P/ x+ y' e2 H
- }% p0 V5 ]7 o. M" J' F* T
+ d; J) K. N4 z+ q/ v- /* 更新 */. \2 K- d9 L( M4 X
- hqspi->State = HAL_QSPI_STATE_READY;2 \1 ]( w' a3 i# a
- }
7 F I' h! {3 \5 b& j d - else. t3 v: n& K* Z, S3 D
- {
+ H% a$ V) ]5 H p- F, h" r+ v - hqspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;
* w1 x4 z2 A# H9 u) K( Q! W/ R - status = HAL_ERROR;
* H- H! j# T- K - }
4 s# `8 F- q5 [: D0 m' o; ?; M - }
; L, C* ]; j2 Y7 ? - else
4 L8 Y6 f/ ?5 f! q - {" R+ I1 [* d- v: m5 R+ f
- status = HAL_BUSY;3 K$ T2 h* d7 x% ~
- }" x6 t- y& i8 h) T4 o: `3 ?, N
- 2 n) f; x- J o9 s: L4 h
- /* 解锁 */6 q' `, \% j9 ]; W9 |8 i4 L2 n
- __HAL_UNLOCK(hqspi);- J8 ~% O1 g: }: j+ j q
0 _7 c9 t0 e7 c9 M5 J9 K" m- return status;
! Z9 A# T8 M5 I' J - }
复制代码 ' c9 s5 w" m2 G( H
函数描述:
1 u6 ~3 ]/ w' o9 K% a: d: H
9 F" o4 _6 z+ u1 h: x% N此函数用于QSPI接口数据发送,函数采用查询方式。
9 E3 r7 T& `& w+ ?2 ? b5 e* a0 Q
- X; u/ H& e& ^5 A函数参数:8 F9 [* D( B1 r0 }
* w: n8 C$ r# t+ I: X6 b
第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
( Z k; Y# X" Y 第2个参数是要发送的数据地址。8 ~6 N! Y8 @( `9 y v% ]
第3个参数是溢出时间,单位HAL库时间基准,我们一般设置的是1ms。
$ u: N7 i2 t: t1 M- B, w* D- w 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
' G+ U; ~ u* x0 q使用举例:
6 Y# E+ Y: o. C& j3 f5 H ?) K9 E2 _7 F: N$ o; g. D
- /*
- i: B7 h: H$ v2 }8 g - *********************************************************************************************************
* ?* P8 _! I% h0 h - * 函 数 名: QSPI_WriteBuffer& Q" \3 n6 \& J. F- ^
- * 功能说明: 页编程,页大小256字节,任意页都可以写入3 p4 x0 Y- [* U8 z: }. ]
- * 形 参: _pBuf : 数据源缓冲区;
& P; A4 i' ?8 y9 h" K2 S* H: l - * _uiWriteAddr :目标区域首地址,即页首地址,比如0, 256, 512等。
& j W. }4 |3 f3 R; { - * _usWriteSize :数据个数,不能超过页面大小,范围1 - 256。8 _' M3 w# A2 p- K
- * 返 回 值: 1:成功, 0:失败
3 A9 v6 K- T7 Q! J - *********************************************************************************************************
$ O {% Y' J, ~& f2 M! L- A - */5 Y; |2 b! E* @9 `
- uint8_t QSPI_WriteBuffer(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize)
6 ?; }9 C! ~: n% K! u - {" @! i& s9 l9 W$ Y& M Z
- QSPI_CommandTypeDef sCommand={0}; F0 m2 |: M9 |1 Z
! \# O7 ~( N8 v' A% W- /* 写使能 */1 p4 r9 s5 i. Q- x. |5 N* N
- QSPI_WriteEnable(&QSPIHandle); ( A4 X6 W% H$ F. G3 E
' N2 I# ^) i+ V/ Y$ @- /* 基本配置 */
, j& O: T1 g0 w - sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */
: v- ]/ v6 Z5 L* c& X - sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */
% } T7 |/ S2 Z9 _( `2 H, n+ k1 ^% C: H - sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
) n c# X. a6 B D5 }1 e4 Y: o - sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */+ N' K7 m. ~0 s3 M
- sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */4 I2 [+ M _2 i5 r( ?% }8 [! A
- sCommand.SIOOMode = QSPI_SIOO_INST_ONLY_FIRST_CMD; /* 仅发送一次命令 */ ( _8 D3 b1 G' `" U+ J; t
- : V! K4 s J5 ~, E2 B; a4 M
- /* 写序列配置 */
% M: I0 Z: V8 ]3 n J0 ~ - sCommand.Instruction = QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD; /* 32bit地址的4线快速写入命令 */
2 Z0 U( t# P; m! `; G - sCommand.DummyCycles = 0; /* 不需要空周期 */
. ~, d* H1 ^0 L6 O$ e' }; t* X* m& H - sCommand.AddressMode = QSPI_ADDRESS_1_LINE; /* 4线地址方式 */
. k4 D, b& V! K, g3 ]* g. n - sCommand.DataMode = QSPI_DATA_4_LINES; /* 4线数据方式 */
* y- j" W8 @/ c - sCommand.NbData = _usWriteSize; /* 写数据大小 */
5 a* b! f5 b1 i x* @9 N( W, y) } - sCommand.Address = _uiWriteAddr; /* 写入地址 */8 w6 y6 R$ S' h* U7 n& b
- / n. @) k3 Y4 A0 M
- if (HAL_QSPI_Command(&QSPIHandle, &sCommand, 10000) != HAL_OK)
8 X0 M! Y6 w/ K; j4 U( G - {: S/ p# I& ^8 H
- //return 0;5 |6 n o5 y9 w* s# g
- Error_Handler(__FILE__, __LINE__);
+ E' C( T6 N$ \5 n- Y - }4 O( v: Z/ f6 t1 O
- # {* u1 s4 z: e9 n6 ?: n4 C
- /* 启动传输 */
; p7 ^$ R+ _2 `9 U - if (HAL_QSPI_Transmit(&QSPIHandle, _pBuf, 10000) != HAL_OK)8 z5 ~( e- [ }* f' i
- {
* J% L8 C8 N0 a& a% @ |! f. _$ Q - //return 0;
* w5 C% w" O2 |) q9 L1 Z - Error_Handler(__FILE__, __LINE__);
% Q4 K2 Y$ Y2 b$ A7 @& a - - I4 V! C6 x. @* N- h$ c
- }+ g3 W# j6 [8 n. D
- 5 F' H2 K/ e/ }0 A+ M, Z
- QSPI_AutoPollingMemReady(&QSPIHandle); ' q& @3 d1 n! ^6 M I& P
) U' e- [+ g' y- return 1;
* y) |6 ]7 v k' z/ N, k& d$ ` - }
复制代码 6 k- r8 ^9 R: N0 R5 o7 I
78.4.8 函数HAL_QSPI_Receive% [, Z8 `. [ j+ g2 ^; ?/ s
函数原型:
6 S% h2 d6 E) q( R: W
, ]' h7 M4 s7 Y& Q0 L- HAL_StatusTypeDef HAL_QSPI_Receive(QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout) o% U5 x% R# ]& A8 _
- {8 ^; Z) K5 J6 o/ {, O5 h
- HAL_StatusTypeDef status = HAL_OK;. ]. q9 s5 D \( ^6 w' b5 u
- uint32_t tickstart = HAL_GetTick();" w" d; g; d, e8 A0 h$ U0 q# w
- uint32_t addr_reg = READ_REG(hqspi->Instance->AR);8 W3 t K( d2 q( _
- __IO uint32_t *data_reg = &hqspi->Instance->DR;
! }6 j7 m$ S* U) ^5 z0 \
1 q$ }3 S6 m k! b0 M, n, I/ s) H- /* 上锁 */4 F1 x7 H; z8 n5 C
- __HAL_LOCK(hqspi);# G3 Z" z6 t7 u8 m, S
- 6 v) I j4 @, ]1 ~6 J. o
- if(hqspi->State == HAL_QSPI_STATE_READY); K* ~3 V( g) g, w" N& b. R1 n
- {2 N6 p" {7 a5 b1 r5 \ V. f
- hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;
: n8 m- R; s) W3 v/ @6 L
* A5 Q. h6 r) n1 e- if(pData != NULL )
, N% i2 C2 T( K7 i2 V - {( g& O- U8 R, \. b( b+ s0 e" u% G
- /* 更新状态 */' h! x0 u" F- {0 K% _0 {
- hqspi->State = HAL_QSPI_STATE_BUSY_INDIRECT_RX;6 X& G3 v! p( v+ N$ E
- 9 |5 z$ M4 K2 t8 F* I# H( K) ?
- /* 配置接收 */
8 W( K+ d) z) f% f# m* X! { - hqspi->RxXferCount = READ_REG(hqspi->Instance->DLR) + 1U;
+ U- X2 V _6 l& p& i7 L - hqspi->RxXferSize = READ_REG(hqspi->Instance->DLR) + 1U; a& X' h6 M) R; X( }
- hqspi->pRxBuffPtr = pData;; [1 q8 s0 d5 v, e& i
- % s$ Q0 I, b/ F5 Q+ c
- /* 配置QSPI,间接模式 */
7 J$ B" F3 h+ d, R% f( S! e# F1 v - MODIFY_REG(hqspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_READ);
' C% D, A5 D: b8 k/ \
9 L5 K' i4 k$ C0 ^9 S- /* 启动传输 */+ U. L" [- D/ F0 ]# S
- WRITE_REG(hqspi->Instance->AR, addr_reg);: t! w! H7 n* E( T* p1 p# S9 Y
3 ]' f" g* j: N# c$ t! ^3 L( T- while(hqspi->RxXferCount > 0U); J9 K6 `! H1 [- V) `1 ~
- {1 `4 ?. F/ J7 P
- /* 等FT和TC标志 */
4 ^5 l; S! p1 x - status = QSPI_WaitFlagStateUntilTimeout(hqspi, (QSPI_FLAG_FT | QSPI_FLAG_TC), SET, tickstart, Timeout);$ O( s+ s0 q% Q! z
; O. |9 X8 K" ~1 ~" G: f5 @7 d* E! _- if (status != HAL_OK)
6 O7 Y0 V- L7 W - {
$ W1 m5 d4 O: p" ]3 b6 T& R - break;' ~ k3 `) u2 z0 h0 M% {
- }
$ v. X( U1 l0 _: {1 D) Z
# w2 K4 I6 Q$ V2 ^0 m- *hqspi->pRxBuffPtr = *((__IO uint8_t *)data_reg);8 c u& F8 _" V# Y7 {% _, y
- hqspi->pRxBuffPtr++;
$ Q2 }+ d8 H1 P/ E( C( v - hqspi->RxXferCount--;
3 ]! o; }. d0 u/ e - }
3 t9 ^" h! p# l
, h& X+ v8 i: V- h9 \5 m- if (status == HAL_OK)) O, o; W* Z! k
- {
# F6 O$ Q& C1 S* i R - /* 等待TC标志 */
8 z" S9 A: ]: t2 W - status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_TC, SET, tickstart, Timeout);
, l5 M* L J' \2 z
4 G! G5 D' }# X! u- if (status == HAL_OK) ?. R2 M" _8 [+ e
- {
0 X- u: s4 c% ?5 ^! r - /* 清楚传输完成bit */
0 |% r" D9 F% R5 g% J$ A - __HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TC);
3 _; j. D0 a J4 ^ - Z I' W1 r# u; K' ?8 J6 r# P
- }4 l8 a) m, t9 `9 \6 B! r# N
- }3 ~ c2 j8 p* e+ g% V2 c
7 `# [4 j. P0 l, M0 x- /* 更新QSPI状态 */8 S# z% P0 Q6 v0 u, t) ?% U5 \
- hqspi->State = HAL_QSPI_STATE_READY;
$ }+ o/ o& z+ F7 J: Z - }
5 c3 ^; _* b4 A I( w- K* e - else" e. a4 r4 }% D% d" r& {, e2 u
- {6 O/ l" \3 K. W3 {0 f$ t
- hqspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;0 `7 L: \1 l; V7 \! x' g/ s, @5 d
- status = HAL_ERROR;
4 R5 f: E8 ` c8 F# v& d( g - }
5 \( [! r' f$ e" {6 N - }# ]+ F. a6 m( f; |* G
- else- b( w& k& Y, U: y
- {
4 p4 r7 j8 r4 H8 d3 C, N! B - status = HAL_BUSY;
. w `: U4 O$ | - }
: P) S% K, q# a( } - % p3 C( k: |% C, {0 g9 ~$ X
- /* 解锁 */+ Q) c. {5 f! _' D1 h+ i1 }
- __HAL_UNLOCK(hqspi);
/ P T3 `! Z+ z( F
& Q9 W j1 k7 j3 {" n5 P7 P- return status;2 @9 }1 Y L! j, r) a# Z; S& G) a
- }
复制代码 + O W( k: q0 e5 K
函数描述:
4 d+ F) l- w0 n. u1 `# N& s0 j+ U0 _6 [
此函数用于QSPI接口数据接收,函数采用查询方式。/ p6 }% @7 l9 P
! x/ C+ Z0 W. n2 b1 @
函数参数:
& h# J* v1 Z/ F( D$ d
- G1 ~. l3 l) Y; A4 i- h) V 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。9 {8 }. K# `" e7 ^+ n
第2个参数是要接收的数据地址。' X6 ^5 o4 O! T' C% b. t' f" `
第3个参数是溢出时间,单位HAL库时间基准,我们一般设置的是1ms。
. n; V& t0 x, C6 l; r/ e- ?- c9 N 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。* F1 w( N. T: ^6 C5 _$ w
使用举例:, U, A7 z1 u) |4 t+ k0 o
( z2 m( M, s% V$ r- /*
0 u2 b0 p, q) o- d9 \) X) W9 n6 t - *********************************************************************************************************
8 K& E/ p9 m/ R, @ u - * 函 数 名: QSPI_ReadBuffer
o2 E+ h/ J- Y; o' V4 j+ { - * 功能说明: 连续读取若干字节,字节个数不能超出芯片容量。' u" s: P, O0 ^: A/ T& `; H
- * 形 参: _pBuf : 数据源缓冲区。
$ J6 Y7 r- ]) z+ G - * _uiReadAddr :起始地址。( d Z, r) |, h& `5 C. P4 m4 ?
- * _usSize :数据个数, 可以大于PAGE_SIZE, 但是不能超出芯片总容量。
% u% d8 D& i$ c% S - * 返 回 值: 无
( L# f; ~+ _ C, x% g# B9 ? - *********************************************************************************************************
6 `# n( z7 S$ P4 R# | x$ C7 u+ f - */
$ F+ v& o: C) i/ K: ? - void QSPI_ReadBuffer(uint8_t * _pBuf, uint32_t _uiReadAddr, uint32_t _uiSize)
2 T* O' P: h+ t - {0 c! P d2 I- O) U N" ?
# W# Z! j- H) y0 J( a7 s: X5 t- QSPI_CommandTypeDef sCommand = {0};6 `6 ?/ ~0 t/ ? O; G
- + r3 d6 E8 I2 b0 a9 s9 u" r% }
8 _4 y% b5 d# O0 g7 ]& Y: W- /* 基本配置 */
' W. Y/ _- d9 u - sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */- _# V1 c: m; n3 x
- sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */6 q4 M* U, I8 F0 s9 G' N
- sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
* W, ~: Q, O4 o) | - sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */
& d. M h" D- N( { ] - sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */
2 W' B* s, G0 ]8 o2 y" i - sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输要发指令 */
! D7 G% _) O' X
4 R/ N/ q9 r6 [7 K- /* 读取数据 */2 s2 o# w3 I) D7 D R0 v+ f
- sCommand.Instruction = QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD; /* 32bit地址的4线快速读取命令 */+ @% |3 q0 c; [0 I# ]' c& w
- sCommand.DummyCycles = 6; /* 空周期 */ e5 h' O! v, G1 Y) H; }9 ~2 n/ c
- sCommand.AddressMode = QSPI_ADDRESS_4_LINES; /* 4线地址 */9 ^ Y, l" e: A" F% s4 k+ L
- sCommand.DataMode = QSPI_DATA_4_LINES; /* 4线数据 */ " K6 o$ y' Z k
- sCommand.NbData = _uiSize; /* 读取的数据大小 */
, J5 v T/ G8 y7 z7 T - sCommand.Address = _uiReadAddr; /* 读取数据的起始地址 */ 3 Y \3 H. n( J; x: {
' y7 j% b- f4 B1 ^* T' U- if (HAL_QSPI_Command(&QSPIHandle, &sCommand, 10000) != HAL_OK): I" @- L0 U6 e3 R/ T
- {
( M$ {: L1 e+ V( j, _+ s- e - Error_Handler(__FILE__, __LINE__);$ A: I/ f& ~& R+ _$ Z+ K2 [
- }3 B; E7 [3 R+ }" A2 v4 H, ~
- 9 _- M" v9 I |' ?7 O
- /* 读取 */
) Z) y) R/ L, I3 ], Q. _/ e - if (HAL_QSPI_Receive(&QSPIHandle, _pBuf, 10000) != HAL_OK)
3 L1 o9 b! Y, \& O, { - {
- [0 W+ r* U; V' m y - Error_Handler(__FILE__, __LINE__);
) T! b/ h- r) ^% { - }
1 h% j0 w5 L4 X" ?! p6 p7 [ - }
复制代码 - i, _/ T* B$ _; b
78.4.9 函数HAL_QSPI_Transmit_DMA
' Q& d, S" P4 _% @; c函数原型:( }/ ]7 P/ [+ {6 H; A( ^4 m
/ |: k' S% m: i* L- B" P
- HAL_StatusTypeDef HAL_QSPI_Transmit_DMA(QSPI_HandleTypeDef *hqspi, uint8_t *pData)
( Y$ U: W! m9 K5 s - {+ U m, L- @8 i
- HAL_StatusTypeDef status = HAL_OK; B( A" N, `$ n- ]# j: }3 u5 P
- uint32_t data_size = (READ_REG(hqspi->Instance->DLR) + 1U);
; @1 L9 m( c6 c) a - ! |9 v/ _, T# i3 n
- /* 上锁 */- n. L, f$ m3 l. [5 q0 ?
- __HAL_LOCK(hqspi);1 ?1 ]$ m8 N% ?. b5 t- @
9 Q, f6 K3 Q E5 E# \* |$ g- if(hqspi->State == HAL_QSPI_STATE_READY)
" o% U8 R1 F# l - {
( y3 ^; [% i. T5 j0 M - /* 无错误 */
# s" N4 C" V* f0 \3 r, D - hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;
& g9 [ U- m+ L" \0 t& l! @% }2 w* f - ; |' @* N3 S n3 @- w6 `
- if(pData != NULL )5 U( _$ w7 @3 d0 _4 H! E$ l
- {! {/ L. C: `: N5 \
- /* 配置发送字节数 */& P x7 H+ [! p& D
- hqspi->TxXferCount = data_size;
5 |, I' H/ X! D" j* X
" A; O7 A2 R2 s" z R- /* 设置QSPI状态 */
7 N3 |9 i- a$ U+ Q - hqspi->State = HAL_QSPI_STATE_BUSY_INDIRECT_TX;
6 i' l' D; I7 i' M - 9 i7 L A( j. f; F
- /* 清除中断标志 */8 U5 Y, F# R) |0 l( U p3 B
- __HAL_QSPI_CLEAR_FLAG(hqspi, (QSPI_FLAG_TE | QSPI_FLAG_TC));% O% W9 M4 p# M( ~
; q2 b( z/ J# A6 ]4 f' l9 M& |- /* 配置发送 */& w' \! _0 Q- ^3 U1 x( L
- hqspi->TxXferSize = hqspi->TxXferCount;
9 [$ ^ @6 E9 X1 [1 N2 s/ F; @0 A - hqspi->pTxBuffPtr = pData;& e1 j# m, n$ Q; X
5 j# F! V! Y1 ~! w- /* 配置间接写模式 */% w) P6 @! s- Y' g* y5 f
- MODIFY_REG(hqspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);
# E! v* s1 m5 I& L
4 X$ t* q! Q3 Z6 A- /* 设置MDMA发送完回调 */9 X. |% `, |& R5 z- h, |3 Q
- hqspi->hmdma->XferCpltCallback = QSPI_DMATxCplt;( e+ e- D U. g0 e$ w7 u
' U) O; ?0 U" E) x* u. q4 B- /* 设置MDMA错误回调 */% _7 ^+ X4 N q! l5 o
- hqspi->hmdma->XferErrorCallback = QSPI_DMAError;5 |* n# q& ?" k3 a
- % L& T, L5 _2 f% ?) v4 e
- /* 设置MDMA终止传输回调为NULL */% B3 w/ `! o& c8 ]5 @
- hqspi->hmdma->XferAbortCallback = NULL;) L7 H+ ~& O( Z/ J7 o* ~- N
- : p7 o9 s7 e! o- `4 x
- /* MDMA的目的地址是QSPI DR寄存器,设置禁止递增 */
3 Q( K: R/ }( k; g - MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_DINC | MDMA_CTCR_DINCOS) ,MDMA_DEST_INC_DISABLE);) v% z6 d. C5 I- p
K' ~" q7 X) k! b" n4 |- /* 更新MDMA源地址配置 */; c! F$ c" u( F$ ^
- if (hqspi->hmdma->Init.SourceDataSize == MDMA_SRC_DATASIZE_BYTE)
7 S% z' w* z1 `7 P8 v1 w4 Z* }5 Q - {- e) Q" E- @5 G4 Q; F, j
- MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_SINC | MDMA_CTCR_SINCOS) , MDMA_SRC_INC_BYTE);0 W% [+ f Q8 R9 }5 j% N+ b$ }; N
- }
+ V$ j5 j- p" s& v- l, @ - else if (hqspi->hmdma->Init.SourceDataSize == MDMA_SRC_DATASIZE_HALFWORD)
1 w1 k5 c$ o8 V G - {4 X3 ^( b/ B% Y+ ~6 ~
- MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_SINC | MDMA_CTCR_SINCOS) ,
& R+ i9 i G) r6 s- P) G% C - MDMA_SRC_INC_HALFWORD);4 @! J* Q, C8 L9 F9 h
- }
' D: ]/ t1 s, `- E! `/ B - else if (hqspi->hmdma->Init.SourceDataSize == MDMA_SRC_DATASIZE_WORD)
! q- p/ u$ W+ j4 ?/ m$ n$ M- Z - {6 I6 O- @4 q. l+ C9 {4 N5 L& z
- MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_SINC | MDMA_CTCR_SINCOS) , MDMA_SRC_INC_WORD);
4 A: z3 j- @: k. q" _7 r W# c. _/ w - }
9 h: ^# g. T1 l/ ` - else" M) E, m5 P2 K
- {
% ~+ Z3 p. w" m) ?" f- W - /* 配置错误 */5 k& Q' a, u6 E: D
- hqspi->ErrorCode |= HAL_QSPI_ERROR_DMA;
6 e5 d- E$ j* C1 b - status = HAL_ERROR;
- t2 U4 E7 J7 R/ V - }3 W1 r( ~5 y6 N2 o6 q7 S
- ) G' }3 t& @0 n
- /* 使能QSPI MDMA发送 */1 [! f0 V( Z( D( m
- if (HAL_MDMA_Start_IT(hqspi->hmdma, (uint32_t)pData, (uint32_t)&hqspi->Instance->DR,; J& N( r/ ]0 h# W3 l
- hqspi->TxXferSize, 1) == HAL_OK)
8 o- l; [0 Z5 t! c7 y: i d% Q - {/ \& d6 W+ t6 y/ M8 x" |
- /* 解锁 */
% I& |( k) h4 k8 Z& [) w$ g, S - __HAL_UNLOCK(hqspi);
3 z! q& ?/ ]! G" l% o - 8 f& j; d* Y$ Q# I
- /* 使能QSPI传输错误中断 */
: z8 j: S( s0 m9 J0 a - __HAL_QSPI_ENABLE_IT(hqspi, QSPI_IT_TE);# |7 ?6 M# w1 P. u! B/ J$ t Y
- ( a/ U4 t6 W/ H. ^
- /* 使能MDMA传输 */# ^: L. i/ Q, M
- SET_BIT(hqspi->Instance->CR, QUADSPI_CR_DMAEN);1 J5 W* A' N R( ]* g' s& t& j
- }; G$ e* P4 I/ ]! L/ i
- else
: O8 y4 R3 Z4 {8 j9 }9 S - {
! I5 n3 z7 [7 k" O; a3 d: }/ L - status = HAL_ERROR;
7 A5 S) A: O6 J- p1 q3 K; o# |8 _ - hqspi->ErrorCode |= HAL_QSPI_ERROR_DMA;& C* D* Q' n; O; L
- hqspi->State = HAL_QSPI_STATE_READY;% H7 q4 B/ w$ R' L# P
& T2 r7 v) p, D5 l- o- /* 解锁 */
# C; {/ u5 M- ` - __HAL_UNLOCK(hqspi);+ f1 M3 X- F( r( c( F' ^3 q' I
- }
: ^# D, V2 F3 @ - }
' m7 H$ {$ l/ s - else
& E, U1 f) M/ n) O7 W: _+ I - {4 N; s9 R1 b. D) H; B: V$ I) ?
- hqspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;
P9 \0 {* y- I* X) C ~ - status = HAL_ERROR;
@ ]# _* O- w: o, L3 M - / J+ Y) o+ j' b8 V0 \
- /* 解锁 */
- |' v6 @% Y3 }2 b3 Y, S- q - __HAL_UNLOCK(hqspi);
' ?/ R9 D/ i- @8 ?/ {4 D - }9 |6 k; c! @9 n6 T5 W" S) M
- }9 l5 c' q, Q! Z1 G. O0 }
- else
# C* N6 S+ I9 D; k: S - {
$ j% p8 X: U* o - status = HAL_BUSY;
P& h' e7 M" o6 C# `4 a/ C. i
$ b4 y( h* Z* A8 K6 Q- /* 解锁 */
k$ t4 I: {0 @) O7 F - __HAL_UNLOCK(hqspi);
+ z* t) E, l. P$ ?7 l# r0 S8 g9 Y8 m - }
( {. T% [: ~- \& Q8 ]: G0 b; Y - 3 ?6 j$ y0 L3 J! _2 M) R z6 j( `
- return status;
4 t* F0 ]9 M" K- d0 {( `5 C - }
复制代码 ( c5 C3 Q1 I7 Q- t: ]% _% s- m9 @
函数描述: [8 Y0 T; F6 Z1 G; J
( E1 P. T" N: Y) K+ d/ \" [; H
此函数用于QSPI接口数据发送,函数采用DMA方式。1 S' v; t! Y' c1 L+ K
5 i; _6 x! u& p函数参数:0 i6 g! L: ~7 E& L5 _
% l9 A4 A( x: u2 X
第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。. B& I8 V$ A6 |/ n% s2 v
第2个参数是要接收的数据地址。 x) O3 [( ?8 t0 K$ j% U
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
1 j6 z8 C, C* [+ i; b, y使用举例:
# K5 q# z- ?) ?- I4 ]/ f! m/ t: P0 w6 \& R- m& ^
- /*3 A9 B8 ? B I. Z. P( Z+ Y" A; H
- *********************************************************************************************************4 k7 w* E; B5 R3 H5 i6 N# c& Z
- * 函 数 名: QSPI_WriteBuffer
" s0 Q( L) g6 a6 E1 p1 P9 [& N - * 功能说明: 页编程,页大小256字节,任意页都可以写入, r6 c+ k6 w. W' O) t) S6 a3 R' O
- * 形 参: _pBuf : 数据源缓冲区;& s" U: s, G4 O0 n2 |3 n) l
- * _uiWriteAddr :目标区域首地址,即页首地址,比如0, 256, 512等。% u. J3 c4 x7 s; A
- * _usWriteSize :数据个数,不能超过页面大小,范围1 - 256。1 |2 F( c! Y3 c6 {
- * 返 回 值: 1:成功, 0:失败
3 A9 W4 `8 o% d+ @( }3 p- @3 ` - *********************************************************************************************************
3 _' _" g6 `# ~% g0 J. a1 f9 a3 }( h4 i - */
3 L$ z; S% Z2 x9 @0 n3 P; R - uint8_t QSPI_WriteBuffer(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize) U" A- |, E7 u
- {# ]4 i- B" U" A* U( @) ^1 R8 [4 F
- QSPI_CommandTypeDef sCommand={0};
8 ]! y2 u6 y& l, l6 k' ~ - 1 i( `6 a: U0 r) g$ ^, j
- /* 写使能 */
- l+ O( i$ A6 | - QSPI_WriteEnable(&QSPIHandle); 0 K3 D( }$ c) q4 j% Y! c
- 0 k* a* x( o& v8 m ?) Z+ P
- /* 基本配置 */
0 Z9 Q( r# Q- x! i* e2 ?3 V - sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */
) o0 z Q+ {9 n. }. }1 z- L% ~ - sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */; Z( U) I! |5 G2 v, D
- sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
' i3 E/ |( |& J- r6 k6 B/ T - sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */2 I6 |9 Y8 U% O2 s
- sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */( Y$ t$ x9 t6 B+ D! o9 ]
- sCommand.SIOOMode = QSPI_SIOO_INST_ONLY_FIRST_CMD; /* 仅发送一次命令 */
- n! g5 ^1 I, O9 c0 h; H - " j5 N+ ^ u9 Y3 ~
- /* 写序列配置 */6 \2 V7 O! i4 Z
- sCommand.Instruction = QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD; /* 32bit地址的4线快速写入命令 */* O6 w: d' P$ E
- sCommand.DummyCycles = 0; /* 不需要空周期 */
. E* g% }8 x: E - sCommand.AddressMode = QSPI_ADDRESS_1_LINE; /* 4线地址方式 */
/ I" x8 i1 v- k8 a - sCommand.DataMode = QSPI_DATA_4_LINES; /* 4线数据方式 */
! T; y6 j" c) r - sCommand.NbData = _usWriteSize; /* 写数据大小 */
) ^' d& c# }8 Y - sCommand.Address = _uiWriteAddr; /* 写入地址 */- U) Q J7 i0 j# @ u9 M/ [( ]
- - B( T. G9 H2 z! ^
- if (HAL_QSPI_Command(&QSPIHandle, &sCommand, 10000) != HAL_OK)
1 D" g3 @9 }& r) S1 S" e - {: L) y$ F, C8 f
- //return 0; b0 S: c4 t- C2 @2 P
- Error_Handler(__FILE__, __LINE__);
7 f% Q' Y- W7 S& F: Q5 N9 G - }
. }; o: Z9 Z1 A. ?# A4 @
e" e1 W m) G& _- /* 启动传输 */
% j* z6 n, Z1 @ - if (HAL_QSPI_Transmit_DMA(&QSPIHandle, _pBuf) != HAL_OK). M* `7 ~3 [1 o8 s9 R5 n% I8 X
- {% _7 M5 S. d: e4 f8 l
- //return 0;
) w) I v Z2 v8 A) ~, ?# y- N - Error_Handler(__FILE__, __LINE__);6 r, ^, B$ w4 l9 d* H
- }
" `& E- n' b% M0 i
6 i; H9 q" e$ C# t- T" G8 ]0 a# s$ j- QSPI_AutoPollingMemReady(&QSPIHandle);
7 }& ~ c7 j0 r - 5 z& L3 F; b) T: m
- return 1;9 D+ P- u4 R7 E& ~# h6 h" F6 z: }# O1 w
- }
复制代码
8 j- ?0 k3 j, \78.4.10 函数HAL_QSPI_Receive_DMA
6 F; n8 D' e( [) r o函数原型:' S1 K: Y3 y0 U% q, x( T
2 T+ b6 j0 A; e9 C" A- HAL_StatusTypeDef HAL_QSPI_Receive_DMA(QSPI_HandleTypeDef *hqspi, uint8_t *pData)1 G+ R( {+ [0 Q
- {# ~4 _' N& J3 x' R
- HAL_StatusTypeDef status = HAL_OK;
; X* F( u. k/ C+ ]2 q' b - uint32_t addr_reg = READ_REG(hqspi->Instance->AR);7 j# p4 }, ^ S8 o
- uint32_t data_size = (READ_REG(hqspi->Instance->DLR) + 1U);- i" O5 V1 G1 O. ^; z3 X
- & H% f6 \8 y( {% a5 E
- /* 上锁 */3 o. D5 A" x# ]8 }
- __HAL_LOCK(hqspi);/ L( O: P* m# t' v
( S. B. O! I+ a6 e8 L% B) i6 W- if(hqspi->State == HAL_QSPI_STATE_READY): W( b" V' S$ Z# y. B. O2 I8 y# A
- {
0 Q* [# T3 n4 O. o( {7 J2 ~ - /* 无错误 */
( }7 |$ l% A+ L - hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;0 o+ w) B( F* q8 t' z$ ]
, e5 c# Q6 g; \2 i2 H3 h: b- if(pData != NULL )
( r- X3 v- a* H I2 J! Q% C - {
2 y& T" c9 X- } - /* 配置接收 */9 Y5 i- A4 i. w4 ?0 b5 N
- hqspi->RxXferCount = data_size;3 z$ I3 q, o8 C5 p6 s7 y
- /* 更新状态 */
: l+ y4 o6 D9 s F: i$ I& ~2 ~0 E - hqspi->State = HAL_QSPI_STATE_BUSY_INDIRECT_RX;0 F8 r, a1 G5 `* ?& I
, q& E6 d3 k& w- S$ \3 v- /* 清除中断标志 */
. L2 |9 X! W7 H% ^& y7 H9 d - __HAL_QSPI_CLEAR_FLAG(hqspi, (QSPI_FLAG_TE | QSPI_FLAG_TC));
* D$ ]2 n: C6 n% @6 ~
3 p9 w9 O' d7 Z4 F# Q- /* 配置接收 Configure */7 p- x9 q3 k L( W2 M, M
- hqspi->RxXferSize = hqspi->RxXferCount;
# `' t- Z) h$ A6 w$ ] - hqspi->pRxBuffPtr = pData;
! m4 L* |! j' d* i$ e' {9 @
; L Z1 K5 c' z [- /* 设置接收完成中断 */1 {+ S, P3 Y0 ^1 B) H G k
- hqspi->hmdma->XferCpltCallback = QSPI_DMARxCplt;: P5 c- x1 v% P
' D F. F/ o6 k3 P* b- /* 设置MDMA错误回调 */' B0 S$ j; a3 I, b' J v
- hqspi->hmdma->XferErrorCallback = QSPI_DMAError;; w+ Q. ~3 s9 H& _
- 4 }- `5 S) c! E; y! U
- /* 置空MDMA终止回调 */) I. Z! k. ]# @: [& m7 _$ M
- hqspi->hmdma->XferAbortCallback = NULL;- x3 j) X8 @/ {# Q6 y
9 E* l0 W3 J9 J$ c$ A- /* 接收模式下,MDMA的源地址是QSPI DR,要禁止地址自增 */0 s' X" r& c* T1 g
- MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_SINC | MDMA_CTCR_SINCOS) , MDMA_SRC_INC_DISABLE);
# b# {5 Y7 P- G5 r- U0 J7 ?' H$ ? - % K! w3 O4 |2 Z1 [. O1 }
- /* 更新目的地址配置 */2 f' ^9 r3 U6 G; \" v
- if (hqspi->hmdma->Init.DestDataSize == MDMA_DEST_DATASIZE_BYTE)6 S! a" w+ Z P% T% o9 Z) b; V' T) r
- {& @: y' e3 \0 e5 l- i
- MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_DINC | MDMA_CTCR_DINCOS) , MDMA_DEST_INC_BYTE);
. K2 ]) ^# R" g; x ^ - }
( `: e1 w+ E. Y) k) W4 M. J8 X1 D7 E - else if (hqspi->hmdma->Init.DestDataSize == MDMA_DEST_DATASIZE_HALFWORD)/ h; t+ f$ R* h8 M4 n0 f0 T
- {
( S$ N! _$ z0 F6 J- @& r$ }. ^+ s - MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_DINC | MDMA_CTCR_DINCOS) ,
2 F" m; R* q1 y - MDMA_DEST_INC_HALFWORD);. s ^% f- O: `7 k
- }
+ ~0 t3 R3 ~3 [) |8 D3 ]4 @ - else if (hqspi->hmdma->Init.DestDataSize == MDMA_DEST_DATASIZE_WORD)
/ S3 T7 M% L5 S& p5 V. d' N8 P - {
$ F! s- z _/ _' h, V! X - MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_DINC | MDMA_CTCR_DINCOS) , MDMA_DEST_INC_WORD);
1 E* _0 {3 u2 u% L# E - }
* z) O, J* W8 W1 A8 Y - else( D k& i( P9 Q7 W# i1 Z5 Z8 M
- {
$ Z% f( X% M. s9 F0 j% P - /* 配置错误 */6 S$ ]: j* I- [/ E. Z& F
- hqspi->ErrorCode |= HAL_QSPI_ERROR_DMA;
1 ?+ H4 ]1 ?4 P- m, a - status = HAL_ERROR;+ D" o2 {* ? B3 Z* U
- }! k0 x9 q: R# R0 B1 e' e
- /* 配置CCR寄存器,间接读 */" a: b/ h5 Q, Q% ^! D6 _
- MODIFY_REG(hqspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_READ);
2 Q9 l j7 n+ a8 ^ - ; Y' ?1 Y8 z4 L- w* y8 T
- /* 启动传输 */
, S( [2 H3 g# t& H4 f4 Z - WRITE_REG(hqspi->Instance->AR, addr_reg);
! c! x( D; O, v% I# Z1 m) C - ' k" }7 D2 `9 g4 ?- I3 w; {
- /* 使能MDMA */
6 h- v; w. Q9 ^2 ] - if (HAL_MDMA_Start_IT(hqspi->hmdma, (uint32_t)&hqspi->Instance->DR, (uint32_t)pData,
/ \! H/ n4 P( Z$ O2 W - hqspi->RxXferSize, 1) == HAL_OK)# Y! y% t3 ^& q0 U* |* y- E" T2 [ c% J
- {! f5 E) L9 j2 s: ^, }# v$ B% {6 X
- /* 解锁 */9 v) T6 h9 S% a- ~7 N! D7 e
- __HAL_UNLOCK(hqspi);
$ [) M8 m3 B- K" w! h, _1 k, N - " c- G; ~8 G! n+ E6 V" \) u
- /* 使能传输错误中断 */
( X/ v# {3 K8 ?3 q1 | - __HAL_QSPI_ENABLE_IT(hqspi, QSPI_IT_TE);7 D4 {3 C2 n+ u0 N8 Z
) L" d8 |4 W! w9 u! F4 v8 q- /* 使能MDMA传输 */2 x% d+ }+ E x0 P
- SET_BIT(hqspi->Instance->CR, QUADSPI_CR_DMAEN);
2 @ j: y) l2 o: G) y' s - }
- M# m0 ]2 y9 ?$ S! C - else. ~6 M* X. s2 |/ ^* t3 M9 O |$ {5 S
- {
$ e; {) p+ `0 `$ o- p, F) H$ E" ] - status = HAL_ERROR;. F/ m" F+ v+ q
- hqspi->ErrorCode |= HAL_QSPI_ERROR_DMA;+ j) a/ H; r( M! G
- hqspi->State = HAL_QSPI_STATE_READY;
7 s0 A; ]; n- v+ K. m
8 N! o& A" [& f. A; S- /* 解锁 */. B7 _/ ^* L) h) |
- __HAL_UNLOCK(hqspi);
* y9 d0 ~) |3 r8 R8 U; z5 C - }
) w R _* J9 a- ` l - }
# `8 s- f+ T! j0 \& U- K3 m+ X2 G - else
) x/ H/ F2 N* x/ a; T+ B - {
% e1 d; O9 X% C5 C! [# a - hqspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;* Z% V K! T/ |3 V% h+ J
- status = HAL_ERROR;* L5 w- O0 f1 |; T0 i4 n1 m1 a
- * y& c0 P' c8 R
- /* 解锁 */
5 k1 L4 U4 R2 } - __HAL_UNLOCK(hqspi);
& @8 u+ s0 ~2 U2 d9 Z7 y8 N/ J& R - }/ T% U* Y, \$ n* R5 I
- }
+ V p! I$ [5 v# x - else
* O& n( ]2 l7 I0 g4 t' [/ v - {* @6 w) ~& _" Z, ^ S5 n
- status = HAL_BUSY;
7 c& N$ T' ?* }* ]- u - ' d3 E2 d( K6 S E
- /* 解锁 */
0 [) }5 a( m4 ^2 a. f% \& N3 ^ - __HAL_UNLOCK(hqspi);
( m( ^4 h7 I% V. V" I - }- t3 n. A+ |/ J/ u
$ i. Q _# Y/ E8 D8 W& O- return status;
$ ^) }; J9 o+ b6 p H - }
复制代码 / ?: y2 x8 y2 @9 h# b
函数描述:
) m, M$ h% U: _; G# X& X) N) l; t+ V7 U$ X8 K
此函数用于QSPI接口数据接收,函数采用DMA方式。. N. Q" J8 t- M/ O( }
) E/ c v+ n2 @, i, F4 Y: r函数参数:5 u$ A% |7 J1 C- ?4 Y! @ M
8 M5 {4 E% D$ Y7 }! X
第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。- T1 p( t. Q% \
第2个参数是要接收的数据地址。) A3 R1 ^, e% _2 I, n/ W$ g
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
2 r& q: {+ z! D1 z) X0 M5 d使用举例:6 i9 j5 S- `9 T
( w2 y `) v5 a6 D" M0 q
- /*
% m% h/ l0 U, A1 E: J5 q - *********************************************************************************************************
7 P Z0 U$ M1 K: H4 T( h( V - * 函 数 名: QSPI_ReadBuffer
# ^# i+ c% m% q5 U - * 功能说明: 连续读取若干字节,字节个数不能超出芯片容量。
( G7 W+ H P; y: J8 C' } - * 形 参: _pBuf : 数据源缓冲区。
! G) T' l) {+ x2 ?8 l3 n' f - * _uiReadAddr :起始地址。9 H. e. e5 i5 i
- * _usSize :数据个数, 可以大于PAGE_SIZE, 但是不能超出芯片总容量。
( k' l' V+ @/ R7 s - * 返 回 值: 无# c9 ^3 t/ c& a
- *********************************************************************************************************
# A3 L' J2 ~" N - */% |/ g/ N, a8 x& c" s& D6 H; r
- void QSPI_ReadBuffer(uint8_t * _pBuf, uint32_t _uiReadAddr, uint32_t _uiSize)
. b' c h, ~6 }; A4 s' F+ d a - {/ e; N+ a6 ]* K; f" i1 e1 ^
& L$ u- r4 ^+ W- QSPI_CommandTypeDef sCommand = {0};
1 m5 A: e, E R' O& g% t4 R1 v- p- r - % Z0 }, X# ~2 J% I3 r: b
- $ }! N. s% L! s! V! P2 E! a
- /* 基本配置 */& _8 `) R7 x7 \/ s' S
- sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */6 c9 G& s# v+ V$ s
- sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */
$ G" k! ~* r0 V& ]( B. E7 d - sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */1 L' F+ v( O$ E- a, x
- sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */
: {4 a$ j4 c3 h$ z8 L - sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */- u6 \6 c% {2 J
- sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输要发指令 */
3 w! I, g4 j8 c3 L# S1 o' q: Y! e - ( k- y; _+ v6 x/ l
- /* 读取数据 */& A; h% l9 |1 }! ?6 c2 T
- sCommand.Instruction = QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD; /* 32bit地址的4线快速读取命令 */ n/ j1 h" N5 h+ s/ @
- sCommand.DummyCycles = 6; /* 空周期 */: @9 s' |" q+ S; D7 v, Y
- sCommand.AddressMode = QSPI_ADDRESS_4_LINES; /* 4线地址 */7 k5 @5 i$ n4 _% c: z5 T. p
- sCommand.DataMode = QSPI_DATA_4_LINES; /* 4线数据 */ 9 i W& `% Z, `# B6 o) v8 R. L' m
- sCommand.NbData = _uiSize; /* 读取的数据大小 */ $ x N+ Q N5 u
- sCommand.Address = _uiReadAddr; /* 读取数据的起始地址 */ * {, F, x. R4 U$ F, ^+ ?0 H$ `4 F
. r- u2 @. @" w1 Y5 V- if (HAL_QSPI_Command(&QSPIHandle, &sCommand, 10000) != HAL_OK): f" V. b6 B- z0 W! f0 Z6 \
- {* b, [2 f. l* X
- Error_Handler(__FILE__, __LINE__);
# h! J4 u& M( L' q - }" }3 \+ W$ d! ~# C8 F4 H# F
- % n. v% ^0 e- n9 I/ g. `3 k
- /* 读取 */6 C% I$ \3 A' \, \$ m' w
- if (HAL_QSPI_Receive_DMA(&QSPIHandle, _pBuf) != HAL_OK)
: K3 y/ J/ C6 i. J6 J* ? - {7 Q: k) ]% a5 x+ ^7 _5 ~- z
- Error_Handler(__FILE__, __LINE__);
, m: A$ s* d& P1 t2 K6 ? - } ! x7 ~7 Z( e. ?* t V6 l9 ]$ s. d. V
- }
复制代码 ) W0 D- }( e0 Q
78.4.11 函数HAL_QSPI_MemoryMapped- t+ F+ u0 i4 O4 ~" E- E. X
函数原型:* @( Z! { X& b( a4 m
1 s) N# ~% y7 b5 z Q3 ~
- HAL_StatusTypeDef HAL_QSPI_MemoryMapped(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, QSPI_MemoryMappedTypeDef *cfg)( \( b5 K% D. M- Q7 N3 p& d' f" A
- {
0 w- c O& @# N2 M - HAL_StatusTypeDef status;9 q, S5 z/ k0 b' Y; f6 I
- uint32_t tickstart = HAL_GetTick();/ ~% b" n2 r" t( W# l
! q) E( b' M( j7 F- /* 检查参数 */3 _& P) z! y0 \0 }: S4 E- L
- assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));8 j- V8 ]4 H3 w/ {( q" v
- if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE)7 g; E! D* f7 B- c+ p# U
- {9 ~% k: O* n4 Q9 R3 T; Z: ]
- assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));
5 }, i& ^! b6 k. a$ p - }8 M6 P3 y9 u0 Z% j' B" o
- b; C) J! o2 X. p0 e5 G8 A
- assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));
1 S. K! w J: g# [0 B" @ - if (cmd->AddressMode != QSPI_ADDRESS_NONE)
/ U1 `) c) t. v: k - {
- o* M: Y# n2 I7 k: i3 m- D - assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));* h) w8 b+ `/ R7 o+ f2 v1 P. T
- }; n3 `8 P8 d, g! g- W+ U' r
- " Z- I% \# z$ h$ G# b. h) p
- assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));
; r$ [+ L# j6 _1 {3 u# U7 l - if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE)
& ?8 Q/ D2 S0 l - {
: Q; s6 }9 ^* V; S+ T# B2 k4 y - assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));4 I- p! u; k; Z! Y5 ]
- }4 u* T/ K: A3 N. E, E( r5 m
8 a4 B# p& @! b9 X- assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));
) Z/ T6 V P% g* d' H" Y - assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));
Z% ^' p; |9 h0 ~7 {' F0 w - 3 h5 @$ _0 E0 b* p N! |" ?8 e
- assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));
( T% \7 x: ]5 C4 X9 B+ Q - assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));
- j3 W% s+ S) E* f& F& B" q0 p# P - assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));2 B! ]$ T0 I% L2 P: l* D) F7 S1 z, D
. v) Z9 s: y. O5 g6 R9 f- assert_param(IS_QSPI_TIMEOUT_ACTIVATION(cfg->TimeOutActivation));
- a* @& j' S% N2 D% J9 d4 E" s& _ - / n8 `5 e1 t$ z) g4 h% Z( R3 J
- /* 上锁 */% |$ @9 `) t5 ~; ]
- __HAL_LOCK(hqspi);: e+ Y9 Q/ n. l2 U. E
- % D- v' c0 P" y* P9 n
- if(hqspi->State == HAL_QSPI_STATE_READY)
7 N& X/ q) T7 i8 ?# _+ J - {' l: W5 W- A6 {( P
- hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;
! Q7 ^, |( ?6 f. q2 Q* Y
( A4 I) ?: F6 \* |- /* 更新状态 */
" K2 ?5 |4 h2 P - hqspi->State = HAL_QSPI_STATE_BUSY_MEM_MAPPED;
$ C* ~ \* x7 Y0 ~5 O$ T! E# R0 _
% t6 {9 y" | W. T9 O$ O- /* 等待BUSY标志复位 */, x5 n/ E6 P' K3 T
- status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, hqspi->Timeout);: B" U7 X/ k j2 Q
^. B1 H# D! J8 M, E- if (status == HAL_OK), T9 ]; Z4 a% | S5 v
- {
* F; l: ~1 u( a! C9 ?4 O - /* 配置QSPI CR寄存器溢出时间计数 */
9 E! v6 b( J/ q - MODIFY_REG(hqspi->Instance->CR, QUADSPI_CR_TCEN, cfg->TimeOutActivation);
, B% p, R ^$ o4 ^. m( l" o2 e - 2 J7 _4 A' t }) ]- y
- if (cfg->TimeOutActivation == QSPI_TIMEOUT_COUNTER_ENABLE) j! I9 E' I; c; j# D, a1 [
- {7 ]" t3 F) X$ C2 Q$ w
- assert_param(IS_QSPI_TIMEOUT_PERIOD(cfg->TimeOutPeriod));
0 P( p7 p& k% {# L. C
$ X/ b7 p; ^, e" k, q$ x- /* 配置溢出时间 */
( E0 w/ ?% `2 g8 \7 I- F9 k - WRITE_REG(hqspi->Instance->LPTR, cfg->TimeOutPeriod);) n+ C9 [8 o, F8 a ^9 d
- 2 g8 h0 |+ k! x2 |1 O
- /* 清楚中断标志 */
5 ~2 {' f1 K- b8 {9 C; }9 j - __HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TO);
: b2 _' p+ f k A - G& i0 Y% X. }. z8 e9 ]
- /* 使能QSPI溢出中断 */& J0 d% z7 j" e* W Z/ u
- __HAL_QSPI_ENABLE_IT(hqspi, QSPI_IT_TO);
# G5 E: z( c* P) e - }$ d* Y8 f8 V* r+ L$ \, p
8 B9 ~# U* Z7 M1 U; Q1 W; T7 k- J- /* 调用配置函数 */) D" X- B/ c5 A' y; q! w& X8 U
- QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED);# i3 e# v8 O: h$ k7 H& M$ c& v# F
- }
# ?' H9 F4 z* I: {3 F. x2 R+ g - }2 M& K% E0 o8 W% T# J
- else5 f6 [8 f: M w' u9 p2 O6 {9 @3 L4 L
- {9 `* H3 I8 a, |
- status = HAL_BUSY;
" o. c- R' K" w2 `$ h7 X( t - }( k& ~0 d( `# C5 m
% A( X, |3 Y' O4 M5 X( a- /* 解锁 */: \) [3 W5 ^( b6 P# q
- __HAL_UNLOCK(hqspi);
3 A6 B# [) r! o9 L0 C" c
" \& A- u" T8 v9 a- /* 返回状态 */6 x% s( H3 V) p+ `4 N: z/ G
- return status;
0 R7 B4 A% F I - }
复制代码 - p7 q: \. t7 E I& ~' ~0 O0 k
函数描述:
6 m7 F9 W) J' E5 e4 n) G
: D. [8 T) R7 |+ c$ T6 o5 N用于QSPI内存映射设置。 {: k0 R4 r1 I4 R- F2 ^( u
, d4 P* X, U/ B! Z$ r$ H' a7 G函数参数:
; \; z0 G) P; s* l* X! A) N
8 H# \3 F8 S" d 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。: f) g' T: i( m1 D5 I. M' i
第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。
0 U s7 @0 d8 t/ D/ a4 ^ 第3个参数是QSPI_MemoryMappedTypeDef类型结构体变量,详见本章3.6小节。: J# j+ U8 Y% o, N+ c4 ]$ H6 ^
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
6 P# d4 v3 T8 e3 A7 y' g0 H使用举例:
8 W8 ~9 b; W6 @- Z: W h
1 S3 ^& { s$ s' y, {! {& m- /*
+ O5 O& G& `7 z: M - *********************************************************************************************************( Z' Y/ v' h* t
- * 函 数 名: QSPI_MemoryMapped
0 K! G, Y$ h2 s w7 h+ P3 F+ l - * 功能说明: QSPI内存映射,地址 0x90000000+ d) |$ p3 d/ K8 U' ~. ]
- * 形 参: 无( g3 k: _# c' P4 R/ r
- * 返 回 值: 无+ u; r" d9 W& Z4 n+ a' g
- ********************************************************************************************************** P! f8 q' n% s- U4 _2 f5 j9 ]1 `& [6 {
- */+ \6 ?! e' l% A- I
- void QSPI_MemoryMapped(void), x7 l0 @7 h; n$ P2 q
- {* {4 D' U/ L' z& E
- QSPI_CommandTypeDef s_command = {0};
" z& M1 u4 }1 j4 } - QSPI_MemoryMappedTypeDef s_mem_mapped_cfg = {0};
5 \. U7 X! z# w+ X* J
, W. k# i! l' v3 U- /* 基本配置 */# n) M+ Q& \- p( v0 |: L
- s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */ " @1 j9 O9 |. |0 c, J
- s_command.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */- z5 g1 t( [4 E2 y$ i# I
- s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */ S2 H. \; H9 @) U d/ x* p; W1 ^
- s_command.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */2 X+ O( ~/ O1 S* {+ { D, h& F3 n
- s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */
! j- K' v: B% E, k4 f4 l - s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 */
2 M6 M3 H J( w+ r9 ?6 n - , P5 r7 E& G6 C3 j
- /* 全部采用4线 */
$ h8 S- E q& E8 [( C. V8 M; y - s_command.Instruction = QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD; /* 快速读取命令 */% W* R1 L3 H/ z# q* p* x
- s_command.AddressMode = QSPI_ADDRESS_4_LINES; /* 4个地址线 */
6 L0 {0 k- b" i- c# @* b! ~ - s_command.DataMode = QSPI_DATA_4_LINES; /* 4个数据线 */
6 |1 T1 c' g! r( A5 A: ? - s_command.DummyCycles = 6; /* 空周期 */
5 s3 [$ a: ]) p3 b4 A% C. g
# V$ }: c$ n3 ^" D3 K# H- /* 关闭溢出计数 */# m: I- j9 }" g( W* s1 o6 g! H5 g- \
- s_mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;+ e+ B8 y8 L8 z" R: q. d
- s_mem_mapped_cfg.TimeOutPeriod = 0;
4 |2 O4 p* x4 q
, h+ X& ?9 K1 ?6 p& e+ [- if (HAL_QSPI_MemoryMapped(&QSPIHandle, &s_command, &s_mem_mapped_cfg) != HAL_OK)7 p- ^/ E7 S' b; d6 r7 B& P4 b
- {
4 e2 F* n7 e; z0 b - Error_Handler(__FILE__, __LINE__);
& a T4 }& C% Q, c+ U9 E% P) c. _ - }- k& ?7 c) w6 W& P
- }
复制代码
1 Z5 `! H" j) M, ?. q. z78.5 总结2 J# S2 c2 q3 ]
本章节就为大家讲解这么多,要熟练掌握QSPI总线的查询,中断和DMA方式的API用法。- b2 w6 B; z) M6 z9 D a- c% y" N
5 |! |, E3 E `% ~' o: H' V$ \) x, z$ F
|