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