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