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