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