72.1 初学者重要提示0 b9 J5 F% ^6 N; z) m
STM32H7的SPI支持4到32bit数据传输,而STM32F1和F4系列仅支持8bit或者16bit。; w, v7 M" U' z0 s: n1 n# V% D
STM32H7的主频400MHz时,SPI1, 2, 3最高通信时钟是100MHz,而SPI4, 5, 6是50MHz。
" u0 }/ N+ _/ g* J$ E) C* u# o. T1 j STM32H7的MISO和MOSI引脚功能可以互换,使用比较灵活。1 W/ @/ d2 ~/ W( k( F- M0 L
SPI总线的片选引脚SS在单一的主从器件配置下是可选的,一般情况下可以不使用。/ C' }* i; v4 ~- ~4 N% R
: a: _6 y1 V$ D+ e& ?( }
72.2 SPI总线基础知识/ t ^0 E0 @0 k D
72.2.1 SPI总线的硬件框图6 i) t: r, |2 ?5 S* w% R
认识一个外设,最好的方式就是看它的框图,方便我们快速的了解SPI的基本功能,然后再看手册了解细节。
' F8 l$ V B# O0 }1 @# \
/ @/ c) K" [* {1 [$ A
% i0 k( W. x3 q+ z; E; v: i4 b$ }* f. ?3 R5 b
通过这个框图,我们可以得到如下信息:( X) I+ s& y4 T8 w/ p. E4 s
: o. }$ @ w6 n& U2 h4 [: d# W& Z
spi_wkup输出
5 }0 B @3 m; O4 ^低功耗唤醒信号。+ Z+ q' L3 F. U6 N
" O8 p! R8 e! H spi_it输出
0 D, `2 E/ n- v6 V0 q7 H8 Wspi的中断请求信号。9 m7 a0 ]: `3 \, v1 B
9 W6 {$ p; G; w% ^" ?2 F2 U
spi_tx_dma# n$ ]1 G4 Z# J2 O7 _( o
spi_rx_dma
1 l% M% ?' r) xspi的DMA发送和接收请求信号。
$ V1 b& J7 Z. b7 `6 l- C- n$ ^
t8 N* E- `) S& a6 d" X) _ spi_pclk
& Q) @6 ?0 A# f9 g7 C& V% v为寄存器提供时钟。
4 F/ }6 d0 L5 @$ j9 \# H. s( o/ X. m' W
spi_ker_ck0 W* q1 J2 Q# i4 d$ V: O# Y
为spi内核时钟。, V- Q* m: e3 X
- d# T" v' ]7 h7 I SCK(CK),Serial Clock
# c! z2 w! H3 ~* `此引脚在主机模式下用于时钟输出,从机模式下用于时钟输入。
# Z' s3 i6 O( s' n/ h( E
" _7 ~. p9 g7 F' F) E3 x MISO(SDI),Master In / Slave Out data- e1 I6 u5 R; Z! U( Z a; l+ j
此引脚在从机模式下用于发送数据,主机模式下接收数据。
( A4 N4 ?/ L- M5 d/ r9 t
7 j- p# U- P1 U/ C9 k7 E0 I% B MOSI(SDO), Master Out / Slave In data
* L/ m1 _& ^# S; Y' w6 o$ X此引脚在从机模式下用于数据接收,主机模式下发送数据。1 |& s4 O8 Q$ H0 j: z
- ]! E1 Z3 A2 ~* q( d SS(WS), Slave select pin
2 e T' V- r" q+ m3 D7 a/ a% P根据SPI和SS设置,此引脚可用于:
! A) U+ m* S$ r Q6 H4 p; v, C* W$ {8 B
a. 选择三个从器件进行通信。" }% d& t0 c% Y Q( k6 S9 x
" {& M6 m$ L; f/ ob. 同步数据帧。8 V0 n( r1 D% g+ G8 _5 s
- R4 J J3 \* V/ I" ^
c. 检测多个主器件之间是否存在冲突。6 `' u" S% U$ e; J1 S
- w+ z1 K5 z, I4 C! J* E+ u
通过这个框图还要认识到一点,SPI有三个时钟域,分别是寄存器所在的ABP总线时钟域,内核时钟发生器时钟域以及内核时钟发生器分频后的串行时钟域。
+ N5 D$ P9 A- m1 B3 n' a# B* Y2 v% S @. a
72.2.2 SPI接口的区别和时钟源(SPI1到SPI6)' Y6 q7 x, |* A4 [
这个知识点在初学的时候容易忽视,所以我们这里整理下。
, }& {1 i7 V; p: W I( S* g: c% H$ Y& W6 e: e6 p$ n& u2 _# m
SPI1到SPI6的区别
: ~/ f4 M7 N! }* S! `1 a SPI1,SPI2和SPI3支持4到32bit数据传输,SPI4,SPI5和SPI6是4到16bit数据传输。& E: |% R! S9 u2 |! Y
SPI1,SPI2和SPI3的FIFO大小是16*8bit,而SPI4,SPI5和SPI6的FIFO大小是8*8bit。
6 T0 [: j3 K$ Y. n# v' t9 x4 a. L! B4 g) M
* L6 g: f: D" g. u
/ n5 n7 `# B( a- [# M: P& h" \; x SPI1到SPI6的所在的总线(对应SPI框图的SPI_CLK时钟域)' ^1 g$ Z5 {/ u) _- F/ N
SPI1,SPI4和SPI5在APB2总线,SPI2,SPI3在APB1总线,SPI6在APB4总线。注意,SPI的最高时钟不是由这些总线决定的。9 X: K E$ a* n) f$ ^, }
; M/ N- ^& X1 Q1 c. T { SPI1到SPI6的支持的最高时钟(对应SPI框图的SPI_KER_CK)
- s4 L6 J- S1 E1 ], Q2 QSTM32H7主频在400MHz下,SPI1,SPI2和SPI3的最高时钟是200MHz,而SPI4,5,6是100MHz, 以SPI1为了,可以选择的时钟源如下:
! t& M* s2 V8 ]7 o4 ?5 m" `- z
2 S2 d" n$ z5 k# t) ^: f. @6 E7 |
) g5 O3 I6 a* ?. g7 d+ y% J6 V8 e这里特别注意一点,SPI工作时最少选择二分频,也就是说SPI1,2,3实际通信时钟是100MHz,而SPI4,5,6是50MHz。
5 g6 K' s6 d& @& y6 h6 B# g) t
72.2.3 SPI总线全双工,单工和半双工通信8 `: n# T2 A' i& z2 u
片选信号SS在单一的主从器件配置下是可选的,一般情况下可以不使用。但需要同步数据流,或者用于TI模式时需要此信号。
4 y; l# M- x) F8 `3 \' n1 A+ u" e2 Z
全双工通信
2 m7 N g- ^8 J l! s全双工就是主从器件之间同时互传数据,SPI总线的全双工模式接线方式如下: ?4 c3 c7 H) o
( I/ {2 H j+ g e5 S8 `, W3 W5 a$ n2 f! L% s* p
5 t: B) s9 `1 e6 |: G* D关于这个接线图要认识到以下几点:
: _) G2 h6 G7 m6 R5 l
: b5 m/ f/ n, n& R 注意接线方式,对于主器件来说MISO引脚就是输入端,从器件的MISO是输出端,即Master In / Slave Out data。MOSI也是同样道理。$ s' w: [7 Q. `$ T2 | W8 s; n5 q
每个时钟信号SCK的作用了,主器件的MISO引脚接收1个bit数据,MOSI引脚输出1个bit数据。
$ J2 Y2 O8 y Y/ r; c; {$ U 这种单一的主从接线模式下,SS引脚可以不使用。) R6 P; d- r0 S! z4 [: I1 ?( _
半双工通信
+ ^- t0 j) N, d; i) d) T半双工就是同一个时刻只能为一个方向传输数据,SPI总线的半工模式接线方式如下:+ j& }5 x! G& r( k2 ?
2 K$ l" f- A `9 D! n/ V: M0 A
z, G3 V* H9 Y" V4 r `+ D: o
% x. l* A; _8 \3 r关于这个接线图要认识到以下几点:
4 b1 ^3 \' t! v f, @
" Q5 a; ^: a" z/ K 更改通信方式时,要先禁止SPI。
, X9 w; E* r; z2 o0 n- o 主器件的MISO和从器件的MISO不使用,可以继续用作标准GPIO。3 I0 z) j6 d' C! [
1KΩ的接线电阻很有必要,因为当主器件和从器件的通信方向不是同步变化时,容易出现其中一个输出低电平,另一个输出高电平,造成短路。
5 q& g! i* T1 t i5 E F 这种单一的主从接线模式下,SS引脚可以不使用。; S9 c7 R- g" _& k: n/ l
单工模式
) W' `* P+ P" i" s+ w单工就是只有一种通信方向,即发送或者接收,SPI总线的全双工模式接线方式如下:
5 i# w) Y" J! g# }0 j3 U" x
4 J3 M% U ^* [
7 Y; p* L3 s7 ^
- Y$ ^! i6 j* W5 X* t0 }$ [* h关于这个接线图要认识到以下几点:* s- M$ h5 I, W1 v1 j* p
& O ^* q( ?4 w( |7 N8 h 未用到的MOSI或者MISO可以用作标准GPIO。
) k8 \! i5 @) }. n- {! ~ 这种单一的主从接线模式下,SS引脚可以不使用。 \$ _! ~. P& O
72.2.4 SPI总线星型拓扑& x; v8 C3 [3 `8 W) f4 g
SPI总线星型拓扑用到的地方比较多,V7开发板就是用的星型拓扑外接多种SPI器件:; n* M* j' M! h$ ]* J+ F9 ^6 d
' h/ \2 m3 i% A1 Q. p7 Z
1 O' Z. [' K3 z7 u1 z& J# P! z
4 Y8 Q# ]' \4 h( b
关于这个接线图,有以下几点需要大家了解:8 d, [% `( g4 j8 U0 z/ K# @0 s
7 ?+ J- x+ O Z' d4 c- U 主器件的SS引脚不使用,使用通用GPIO控制。为每个器件配一个SS引脚,方便单独片选控制。1 `& L U( Z7 J. |; e
从器件的MISO引脚要配置为复用开漏输出(很多外部芯片在未片选时,数据引脚是呈现高阻态)。' k5 W* i! L9 `' ?$ c9 K
72.2.5 SPI总线通信格式+ K }9 T) D4 f5 z
SPI总线主要有四种通信格式,由CPOL时钟极性和CPHA时钟相位控制:
, [( P* t' @" W3 _ J& b) x9 l0 z; N7 [# I, C, a0 a) y
" t- r- H; F. i( \4 O1 Q" i4 p3 ~. K) I( V$ h/ \- `6 n; y; Y/ u3 n
四种通信格式如下:0 x, g5 O# f) l
5 }$ O: N6 v5 ~& T6 p4 N% X
当CPOL = 1, CPHA = 1时
4 C& d" U' y i1 X( E2 ^SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。
. X0 |# ~8 D+ p; E
! Z5 _1 Y/ V- d8 c# E 当CPOL = 0, CPHA = 1时
3 ^' T8 }9 N# P" H5 \; XSCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。8 K: o; ^& e/ @/ Y+ X5 Y8 }
) }! p# L4 g1 p' @# z 当CPOL = 1, CPHA = 0时4 d3 u3 m: X8 r% e
SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。- g) n F$ u: a# {7 f: ^3 @
# ~3 y* J o2 [+ v$ n
当CPOL = 1, CPHA = 0时
, [9 ^* x0 R0 j- H, ~/ R& gSCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。
0 L- M: h1 i' R* A6 g. Y4 Y% o4 V) U1 t% a! ]* D% G
72.3 SPI总线的HAL库用法
% o% A! k' I# A& M72.3.1 SPI总线结构体SPI_TypeDef* B" d# Y7 b1 i3 i2 [6 X1 {/ s
SPI总线相关的寄存器是通过HAL库中的结构体SPI_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义: n4 I- [" s/ ]
- z2 v9 |2 A, ~; x/ y" b& e- typedef struct
* V1 a. X; s( W) n+ L. K - {, ^6 L& B' M- l( S
- __IO uint32_t CR1; /*!< SPI/I2S Control register 1, Address offset: 0x00 */1 ~3 U' f- k; y9 |$ o, s
- __IO uint32_t CR2; /*!< SPI Control register 2, Address offset: 0x04 */
6 N h8 O% v% f2 S' r - __IO uint32_t CFG1; /*!< SPI Configuration register 1, Address offset: 0x08 */9 B/ }" d; H. x4 q
- __IO uint32_t CFG2; /*!< SPI Configuration register 2, Address offset: 0x0C */
& K' h/ [+ g, v# U- l - __IO uint32_t IER; /*!< SPI/I2S Interrupt Enable register, Address offset: 0x10 */
5 _7 X e% m" ?, v9 k - __IO uint32_t SR; /*!< SPI/I2S Status register, Address offset: 0x14 */
) E' a3 @& I7 h4 \% R - __IO uint32_t IFCR; /*!< SPI/I2S Interrupt/Status flags clear register, Address offset: 0x18 */3 O4 G" ?. S% e: X8 O
- uint32_t RESERVED0; /*!< Reserved, 0x1C */8 M6 Q8 p; `/ d$ \- z5 M
- __IO uint32_t TXDR; /*!< SPI/I2S Transmit data register, Address offset: 0x20 */
/ ]% y7 {' y6 K( P# |9 \$ h - uint32_t RESERVED1[3]; /*!< Reserved, 0x24-0x2C */
" ~/ ~' E* J% v# z - __IO uint32_t RXDR; /*!< SPI/I2S Receive data register, Address offset: 0x30 */
1 M- u) ]. Z2 U+ t - uint32_t RESERVED2[3]; /*!< Reserved, 0x34-0x3C */
/ M @+ T& E( h, ^" f5 N' t$ { - __IO uint32_t CRCPOLY; /*!< SPI CRC Polynomial register, Address offset: 0x40 */0 l% G( |# h' K$ H9 d
- __IO uint32_t TXCRC; /*!< SPI Transmitter CRC register, Address offset: 0x44 */
) q/ x( p5 _& N - __IO uint32_t RXCRC; /*!< SPI Receiver CRC register, Address offset: 0x48 */
3 O! i6 `2 s4 w( _ z - __IO uint32_t UDRDR; /*!< SPI Underrun data register, Address offset: 0x4C */7 A' L" v: a+ O5 i9 L
- __IO uint32_t I2SCFGR; /*!< I2S Configuration register, Address offset: 0x50 */
) z. B7 W3 W: H8 e+ F3 Q4 k - % B- R5 q$ M: x! ~
- } SPI_TypeDef;
复制代码
% h5 A8 T! V) B, l$ I这个结构体的成员名称和排列次序和CPU的寄存器是一 一对应的。( T. ~' v: M# a
& L$ i) V# s4 P% l! `- Q6 j7 P; b__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:
* v! H! h+ p) ]8 B9 M- ^0 [0 D2 z9 N/ t+ m$ S) s1 }
- #define __O volatile /*!< Defines 'write only' permissions */
R j+ D7 D2 R/ M5 u: X - #define __IO volatile /*!< Defines 'read / write' permissions */
复制代码
5 X7 j; o) |$ M1 k下面我们看下SPI的定义,在stm32h743xx.h文件。
' D; ?& p: }& V! [2 }# L6 R
; ~, e5 }2 }0 l1 |- #define PERIPH_BASE (0x40000000UL)
P- x' \: k" ~6 o - #define D2_APB1PERIPH_BASE PERIPH_BASE
# n0 e- B- U; U/ r: O - #define D2_APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL)7 a% ^ U3 r) {# l0 J% T/ |; H
- #define D3_APB1PERIPH_BASE (PERIPH_BASE + 0x18000000UL)4 C" O8 b5 t9 F
- 8 S1 n1 d& o: g# e1 U \$ h7 k9 _
- #define SPI2_BASE (D2_APB1PERIPH_BASE + 0x3800UL)
8 l6 p0 u& n) z# }; A - #define SPI3_BASE (D2_APB1PERIPH_BASE + 0x3C00UL)
4 ~ n$ @0 P8 g6 J% z E - #define SPI1_BASE (D2_APB2PERIPH_BASE + 0x3000UL)
; Y" m, B! d/ s1 ~ - #define SPI4_BASE (D2_APB2PERIPH_BASE + 0x3400UL)/ y0 D0 M; P! t
- #define SPI5_BASE (D2_APB2PERIPH_BASE + 0x5000UL)( L+ F% {! g1 x! _, z+ L
- #define SPI6_BASE (D3_APB1PERIPH_BASE + 0x1400UL), v: ?/ a S( p2 D9 Y9 T9 [6 h: f
5 N1 D3 @; D1 c* ]* L# w% W+ b- #define SPI1 ((SPI_TypeDef *) SPI1_BASE)
5 g' w6 r' {% w& i/ D/ G - #define SPI2 ((SPI_TypeDef *) SPI2_BASE)
/ f9 _) K f8 r - #define SPI3 ((SPI_TypeDef *) SPI3_BASE)/ N2 D+ I( ?2 y! _. h
- #define SPI4 ((SPI_TypeDef *) SPI4_BASE)3 f# }" h) R# T( M
- #define SPI5 ((SPI_TypeDef *) SPI5_BASE)
4 _! p' P3 C$ n; L - #define SPI6 ((SPI_TypeDef *) SPI6_BASE) <----- 展开这个宏,(FLASH_TypeDef *)0x58001400
复制代码
9 K' G# W$ n1 B8 K6 S8 a4 f我们访问SPI的CR1寄存器可以采用这种形式:SPI->CR1 = 0。
% F3 O4 Z! @5 l5 }! t# @) e* W3 W" ` g' K. E) d/ I- B
72.3.2 SPI总线初始化结构体SPI_InitTypeDef8 w5 g, T7 p; q6 D% S, m
下面是SPI总线的初始化结构体,用到的地方比较多:4 j# ~' E1 {+ L. i! V
; f T \7 Z, F0 g3 `, o1 v: n4 S
- typedef struct
6 W3 j9 `2 T" ^0 Y: B6 w7 F - {5 |& \3 c( [+ W' k
- uint32_t Mode;
4 Y' u/ v. x& P9 w - uint32_t Direction;
* y/ c- v& _' L4 y" S - uint32_t DataSize; / f7 [: @7 u% [: N
- uint32_t CLKPolarity; . p, L: @- ]& p8 I( K
- uint32_t CLKPhase; / t4 A2 v" ?1 j* Z. Q
- uint32_t NSS;
* a% a- l+ M. D- d) ^ - uint32_t BaudRatePrescaler; & E& j) {& y/ U9 ]$ q1 r0 }
- uint32_t FirstBit;
+ B: j* e5 A* t5 Z$ V - uint32_t TIMode; ( s Z6 i% N. Z, V
- uint32_t CRCCalculation;
* y. A6 q s) d) ]& |3 [ - uint32_t CRCPolynomial; / f2 O" o! {4 h& h
- uint32_t CRCLength;
7 k9 |7 {2 a3 B5 E' Y L8 R. _ - uint32_t NSSPMode;
% {0 f: I, I& F) A7 b - uint32_t NSSPolarity;
2 @4 i+ m) m8 q4 J: S5 u - uint32_t TxCRCInitializationPattern; ; ~) L- R# J, [7 i# x# e
- uint32_t RxCRCInitializationPattern;
4 j# A* e/ K+ V2 T7 Z4 W6 e - uint32_t MasterSSIdleness;
1 {+ r- y6 v7 c0 W* G - uint32_t MasterInterDataIdleness;
2 k; n& b9 E( T7 C+ L$ ] - uint32_t MasterReceiverAutoSusp;
6 e' ^4 P9 y9 Z$ b Z - uint32_t MasterKeepIOState;
2 z& v9 l! a; J) s5 i4 {2 a/ i - uint32_t IOSwap; ( X |3 H* Z, V# I. }) U0 {
- } SPI_InitTypeDef;
复制代码
+ x/ c4 ^: G, Z: m' B& P下面将结构体成员逐一做个说明:
% m! G! h( M1 P/ C+ ^# p. [) M
) m5 n5 v7 u. A6 n# F Mode
* b3 S3 [! F1 T& m* J用于设置工作在主机模式还是从机模式。2 W8 N+ D, \4 D% o% t1 B
' R4 U- F& I& g# O6 ?: a. p- #define SPI_MODE_SLAVE (0x00000000UL): b: g* x/ ?% r5 I" g
- #define SPI_MODE_MASTER SPI_CFG2_MASTER
复制代码
& t- \! T& I5 B- H* W6 o8 @+ g; R Direction
% m K" ~# q) t0 c3 q用于设置SPI工作在全双工,单工,还是半双工模式。; A7 u. X8 C$ Q& X; K; R+ s& |4 ?1 Z
- #define SPI_DIRECTION_2LINES (0x00000000UL) /* 全双工 */
2 z- D0 j4 X1 V, J2 e& }, S" x$ ] - #define SPI_DIRECTION_2LINES_TXONLY SPI_CFG2_COMM_0 /* 单工,仅发送 */2 Z) h2 r) ?/ n' i/ h8 a
- #define SPI_DIRECTION_2LINES_RXONLY SPI_CFG2_COMM_1 /* 单工,仅接收 */
0 X. ^5 `7 F- F6 }( o% R+ x - #define SPI_DIRECTION_1LINE SPI_CFG2_COMM /* 半双工 */
复制代码
7 S& v7 G1 S. {& ` DataSize- P6 q# v! }9 X+ W |
用于设置SPI总线数据收发的位宽,支持4-32bit。2 V7 a* T3 x: J) X$ m' {
! I* b0 I7 ]% i6 F( ]) w" r/ W, J
- #define SPI_DATASIZE_4BIT (0x00000003UL)' N% g- A5 \0 T5 ?* \* X+ R
- #define SPI_DATASIZE_5BIT (0x00000004UL)
8 L" Z1 A% ?* G% u. | - #define SPI_DATASIZE_6BIT (0x00000005UL)6 g8 l9 S; I0 l4 z
- #define SPI_DATASIZE_7BIT (0x00000006UL)$ i6 P9 R2 h& w1 r" l7 u
- #define SPI_DATASIZE_8BIT (0x00000007UL)& D8 j* l J0 v8 P0 T
- #define SPI_DATASIZE_9BIT (0x00000008UL)
* H' Y# r6 Q9 j6 T4 d - #define SPI_DATASIZE_10BIT (0x00000009UL), c2 @% z( Z" l S' G; Y' R/ A: t+ ^* \
- #define SPI_DATASIZE_11BIT (0x0000000AUL)2 h9 I% v5 N0 K% X
- #define SPI_DATASIZE_12BIT (0x0000000BUL)
3 g, z" s4 Y3 l1 }; N6 g% q) p - #define SPI_DATASIZE_13BIT (0x0000000CUL)
" S9 F2 y% G& Q9 m; X0 G, @ - #define SPI_DATASIZE_14BIT (0x0000000DUL)
5 ~( _' ~# ^8 U$ u) u - #define SPI_DATASIZE_15BIT (0x0000000EUL)
|; @- ]' P: |6 }6 H - #define SPI_DATASIZE_16BIT (0x0000000FUL)
3 R0 R+ E% ]7 x8 y3 V: m' l - #define SPI_DATASIZE_17BIT (0x00000010UL)
# c% Z2 @" B2 c0 m' m/ c4 K) ? - #define SPI_DATASIZE_18BIT (0x00000011UL)- [' T- |! ]2 k. u3 H- c
- #define SPI_DATASIZE_19BIT (0x00000012UL)8 Y- G! I1 o3 W/ c H# a% c
- #define SPI_DATASIZE_20BIT (0x00000013UL)# Q2 T+ s' C. {$ e7 D
- #define SPI_DATASIZE_21BIT (0x00000014UL)0 R$ N; z8 i" m) p2 K# s
- #define SPI_DATASIZE_22BIT (0x00000015UL): u: P# L' X3 L. [7 s
- #define SPI_DATASIZE_23BIT (0x00000016UL)
% ^, B+ v! h, P1 X4 z* d& s# |2 i - #define SPI_DATASIZE_24BIT (0x00000017UL)8 P/ A X1 ?5 i4 Z p
- #define SPI_DATASIZE_25BIT (0x00000018UL)
; M# r! ~3 S3 t! [2 g, e: d; B4 ?" \% \ - #define SPI_DATASIZE_26BIT (0x00000019UL), d1 y- i+ g' N" Z2 I: g
- #define SPI_DATASIZE_27BIT (0x0000001AUL)
- S8 l1 q/ n7 J9 H* ?+ W - #define SPI_DATASIZE_28BIT (0x0000001BUL)# t& r" M8 L. ~& _* {
- #define SPI_DATASIZE_29BIT (0x0000001CUL)6 a9 X7 J2 C" o$ l
- #define SPI_DATASIZE_30BIT (0x0000001DUL)8 c, e( M0 q# P, z1 R
- #define SPI_DATASIZE_31BIT (0x0000001EUL)! U, t$ l1 v2 f/ I) A/ S
- #define SPI_DATASIZE_32BIT (0x0000001FUL)
复制代码
8 B$ J( w& L' k. Q CLKPolarity/ G9 G4 z2 X5 o; T9 r/ E+ W5 l" j
用于设置空闲状态时,CLK是高电平还是低电平。' r( w, i3 w' z
& v* c. T% I8 s
- #define SPI_POLARITY_LOW (0x00000000UL) u8 ]; `+ _. c9 c
- #define SPI_POLARITY_HIGH SPI_CFG2_CPOL
复制代码 0 u& o- [" ~( N6 p. J
NSS
1 S9 }: k7 ~" b2 i8 @' c. f用于设置NSS信号由硬件NSS引脚管理或者软件SSI位管理。
+ f7 N7 \$ D# B9 e4 m, y1 \# R
6 z4 @. i1 h1 W$ {( d% h& E- #define SPI_NSS_SOFT SPI_CFG2_SSM( B$ V3 L, P$ r
- #define SPI_NSS_HARD_INPUT (0x00000000UL)1 o$ Y% J6 m. m! [" g1 q/ K7 T6 ]$ R
- #define SPI_NSS_HARD_OUTPUT SPI_CFG2_SSOE
复制代码
1 j; f, v4 u1 d J1 e BaudRatePrescaler- J* K7 z$ Z: Q; q) K, @
用于设置SPI时钟分频,仅SPI工作在主控模式下起作用,对SPI从机模式不起作用。
: b; I* N4 o6 \) Y7 x/ Y# H
+ _. N% e# ~- q3 r$ I8 E' q6 q- #define SPI_BAUDRATEPRESCALER_2 (0x00000000UL)
7 k/ o, J3 t; w D& Q9 ` - #define SPI_BAUDRATEPRESCALER_4 (0x10000000UL)
1 l- c0 L9 o! `- w - #define SPI_BAUDRATEPRESCALER_8 (0x20000000UL)3 U, t4 n; K4 j6 H; e
- #define SPI_BAUDRATEPRESCALER_16 (0x30000000UL)% {* y( h, L, U$ ~
- #define SPI_BAUDRATEPRESCALER_32 (0x40000000UL)2 I2 N. U" H4 Y: A! g4 W4 `& }
- #define SPI_BAUDRATEPRESCALER_64 (0x50000000UL)
+ r7 A$ t0 \, _" H9 M" B - #define SPI_BAUDRATEPRESCALER_128 (0x60000000UL)
: S+ s% c/ l l* |$ t - #define SPI_BAUDRATEPRESCALER_256 (0x70000000UL)
复制代码
& e {- I, g$ ] FirstBit! c+ S4 }0 I6 m+ |4 O0 V; I
用于设置数据传输从最高bit开始还是从最低bit开始。
M; t: V; T* r* S/ b( {9 C6 b% `9 M# t e
- #define SPI_FIRSTBIT_MSB (0x00000000UL)
3 F9 L$ K. P0 J - #define SPI_FIRSTBIT_LSB SPI_CFG2_LSBFRST
3 c; C" @& _* V
复制代码 % |0 M4 {! s& {- I: F" ~
TIMode' M! ?( m0 H' m( H
用于设置是否使能SPI总线的TI模式。
0 P, i! y& y- {3 [3 E+ r* I2 v3 `: I; q1 S3 f
- #define SPI_TIMODE_DISABLE (0x00000000UL)
9 P% u/ ?$ J; G& I: W) I2 w - #define SPI_TIMODE_ENABLE SPI_CFG2_SP_0
复制代码
* Y. O, S4 s; i# B CRCCalculation& c; W3 U2 a* ~0 V: U- \
用于设置是否使能CRC计算。
* a$ v- W* i4 O q2 E7 I7 @" S4 G, q7 M) F
- #define SPI_CRCCALCULATION_DISABLE (0x00000000UL)
: v- b2 K1 a' o1 h - #define SPI_CRCCALCULATION_ENABLE SPI_CFG1_CRCEN
复制代码 . S" i- q V" z& z0 f* a* X5 b( p
CRCPolynomial
1 w; B! x& o" k5 t* Q用于设置CRC计算使用的多项式,必须是奇数,范围0到65535。, u2 B& C- H+ r& V! p
: w, Y* N( A- |* w0 W: r p$ } CRCLength
, a' b7 n1 t1 Y$ w2 Y用于设置CRC计算时的CRC长度。大小要与同属此结构体的DataSize一致。或是DataSize的整数倍。
) ^3 }8 w0 d- Z% k
6 i- r: U( }' e/ f- #define SPI_CRC_LENGTH_DATASIZE (0x00000000UL)) M2 C8 H( L! P- j% E+ i
- #define SPI_CRC_LENGTH_4BIT (0x00030000UL)5 b$ }/ O4 d; m
- #define SPI_CRC_LENGTH_5BIT (0x00040000UL)# @$ R; W/ H+ r( y5 ^5 C1 a G& v
- #define SPI_CRC_LENGTH_6BIT (0x00050000UL)' T- B4 o! n8 X a+ C2 J/ X
- #define SPI_CRC_LENGTH_7BIT (0x00060000UL)* ^1 Q! u' D- C# ?
- #define SPI_CRC_LENGTH_8BIT (0x00070000UL)( T9 M$ f+ y. D$ D
- #define SPI_CRC_LENGTH_9BIT (0x00080000UL)
6 [* I4 g* x$ u. q - #define SPI_CRC_LENGTH_10BIT (0x00090000UL)0 r+ {: d, q6 n6 a( w$ M4 H% a
- #define SPI_CRC_LENGTH_11BIT (0x000A0000UL)
: a3 w; [1 n. ^2 \: D - #define SPI_CRC_LENGTH_12BIT (0x000B0000UL)
' [9 A! G( ^# v0 F8 F C - #define SPI_CRC_LENGTH_13BIT (0x000C0000UL)
! m5 t O' Q! E' z4 |4 S: v - #define SPI_CRC_LENGTH_14BIT (0x000D0000UL)
3 K1 b* o9 e4 c1 U4 O, J2 M8 q - #define SPI_CRC_LENGTH_15BIT (0x000E0000UL)4 W5 K2 y( d( ]. G1 @( w7 I5 v" \" f8 u
- #define SPI_CRC_LENGTH_16BIT (0x000F0000UL)+ }) R& g% B0 L
- #define SPI_CRC_LENGTH_17BIT (0x00100000UL)& K6 r% i# U2 Y# I8 n; u
- #define SPI_CRC_LENGTH_18BIT (0x00110000UL)
. _) w" I- H& Q: W3 L - #define SPI_CRC_LENGTH_19BIT (0x00120000UL)
& w( M9 Y2 H9 z2 U9 Y2 C( n - #define SPI_CRC_LENGTH_20BIT (0x00130000UL)
9 N. |$ W) k2 y$ R4 L5 w8 C/ _6 P - #define SPI_CRC_LENGTH_21BIT (0x00140000UL)
! ~/ v/ s: P; g' j% M7 k - #define SPI_CRC_LENGTH_22BIT (0x00150000UL)
/ x( |+ U! ]; Y9 z+ k* u- c - #define SPI_CRC_LENGTH_23BIT (0x00160000UL)
% ~, z. Z9 g( S. P/ f - #define SPI_CRC_LENGTH_24BIT (0x00170000UL)
9 Q: R, e7 y& E9 _* M - #define SPI_CRC_LENGTH_25BIT (0x00180000UL)1 u3 G# B+ {& X. q
- #define SPI_CRC_LENGTH_26BIT (0x00190000UL)
# p. R; y3 S* Y' |9 ^0 T - #define SPI_CRC_LENGTH_27BIT (0x001A0000UL)
. z' h( m/ |, T& s# r, w! B - #define SPI_CRC_LENGTH_28BIT (0x001B0000UL)
* g5 Q% D7 j4 e) A" _ - #define SPI_CRC_LENGTH_29BIT (0x001C0000UL)
- p( ^0 ? L! |9 p# E/ b - #define SPI_CRC_LENGTH_30BIT (0x001D0000UL)* C' }* \0 w8 P) A {, j
- #define SPI_CRC_LENGTH_31BIT (0x001E0000UL)' v3 Q2 t! h9 Q% a# d
- #define SPI_CRC_LENGTH_32BIT (0x001F0000UL)
复制代码
; v+ y; B! Z9 [2 s3 z+ E* p" g; g" A- {" _0 g
& u2 G/ a* |" L$ J8 o, W& l NSSPMode0 l' n. h2 H, e: _) U* Y/ Z
用于设置是否使能NSSP信号,可以通过SPIx_CR2寄存器的SSOM位使能。注意,只有配置为摩托罗拉SPI主控模式时设置此成员才有用。6 u; L, z. Y( L4 ^5 {, h' w8 u( y
2 S4 R' k1 A* {
- #define SPI_NSS_PULSE_DISABLE (0x00000000UL)
8 B/ I8 l t) d/ r B6 S - #define SPI_NSS_PULSE_ENABLE SPI_CFG2_SSOM
复制代码
0 c- D6 }" w0 d! I( v NSSPolarity: P1 n+ k0 s7 k) q
用于设置NSS引脚上的高电平或者低电平作为激活电平。
1 s+ `" H/ h) b, y4 \8 X: Y" o+ g0 o3 @5 W
- #define SPI_NSS_POLARITY_LOW (0x00000000UL)2 g& ]' \% `' h; U3 D
- #define SPI_NSS_POLARITY_HIGH SPI_CFG2_SSIOP
复制代码
; R0 J) O0 C3 m1 _3 e4 r2 g) X FifoThreshold
+ y8 m9 u4 p* Y9 A$ ~用于设置SPI的FIFO阀值。
' U( `6 {# v. d. p7 a2 r/ v) _ A" ]* R" R
- #define SPI_FIFO_THRESHOLD_01DATA (0x00000000UL)3 y- k) S: l8 y8 O7 L3 {0 ~) [
- #define SPI_FIFO_THRESHOLD_02DATA (0x00000020UL)
9 G1 s2 {$ R/ D8 W# d2 B5 l' z - #define SPI_FIFO_THRESHOLD_03DATA (0x00000040UL)$ ]% e8 n8 f( I# c" p8 L; G: c! k4 `
- #define SPI_FIFO_THRESHOLD_04DATA (0x00000060UL)- S) k- ^. ~& W
- #define SPI_FIFO_THRESHOLD_05DATA (0x00000080UL)' Y) m! e" ~' y) Z, i7 C
- #define SPI_FIFO_THRESHOLD_06DATA (0x000000A0UL)6 O B1 u' V/ R0 c5 R2 Y( L4 K
- #define SPI_FIFO_THRESHOLD_07DATA (0x000000C0UL)
) I% o# J8 D) n7 u2 b - #define SPI_FIFO_THRESHOLD_08DATA (0x000000E0UL)
9 z9 \: e& B- r: G- i8 I - #define SPI_FIFO_THRESHOLD_09DATA (0x00000100UL)5 \2 ?8 M8 B! S% T B: M
- #define SPI_FIFO_THRESHOLD_10DATA (0x00000120UL)6 r& P$ n# V* j, o7 [4 c! O
- #define SPI_FIFO_THRESHOLD_11DATA (0x00000140UL)
* V5 @7 A3 ^- G - #define SPI_FIFO_THRESHOLD_12DATA (0x00000160UL)
( U! }" R* ]1 F1 j( V( ^2 a8 a0 @ - #define SPI_FIFO_THRESHOLD_13DATA (0x00000180UL)
5 _" q& Q0 _' o' P/ H - #define SPI_FIFO_THRESHOLD_14DATA (0x000001A0UL)6 U6 {3 R' X! [
- #define SPI_FIFO_THRESHOLD_15DATA (0x000001C0UL)4 S' Z1 X9 P" U
- #define SPI_FIFO_THRESHOLD_16DATA (0x000001E0UL)
& f3 n8 ]( f- x# d' B3 U
复制代码 6 H$ @4 k) b) y0 t5 K* A7 S! F
5 t( i7 x- `9 K8 s0 Y" Q TxCRCInitializationPattern
; S/ H, ~1 B- L c; a @5 \发送CRC初始化模式。: X. w: [% E' S. G, z4 g
5 i: M7 O/ ?. K3 C. z- #define SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN (0x00000000UL)
7 s- f" [$ i% X* p6 V - #define SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN (0x00000001UL)! k9 r; S: i: Q5 j$ X5 `
复制代码 8 E! `8 V1 t0 |, s$ j% R+ c
RxCRCInitializationPattern
: N7 d- M$ Z* W% c接收CRC初始化模式
9 f: b& G. U6 L+ U5 ~( D" t
5 H- K; y f6 D3 b- #define SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN (0x00000000UL); a9 u2 @6 z, k1 f7 o
- #define SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN (0x00000001UL)
复制代码
5 A$ [1 ]8 o+ e$ ` MasterSSIdleness; S' Y1 T# s) L2 Q* U
在主模式下插入到SS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数。
& \' ~, { ^( A, r" D! J8 X, }/ `4 _- P8 k6 I9 y) }: _* v. f; T
- #define SPI_MASTER_SS_IDLENESS_00CYCLE (0x00000000UL)3 E: `& Z8 B& U' C q& N
- #define SPI_MASTER_SS_IDLENESS_01CYCLE (0x00000001UL)
- R3 g$ _4 m7 V - #define SPI_MASTER_SS_IDLENESS_02CYCLE (0x00000002UL)
" M. m+ `( Z* j9 Y - #define SPI_MASTER_SS_IDLENESS_03CYCLE (0x00000003UL)
; p4 ~' {' [6 C' d0 `+ ` - #define SPI_MASTER_SS_IDLENESS_04CYCLE (0x00000004UL)
" |" Z, n7 q! }1 y4 K- v; O - #define SPI_MASTER_SS_IDLENESS_05CYCLE (0x00000005UL)
% T# x& Y% v3 K/ W - #define SPI_MASTER_SS_IDLENESS_06CYCLE (0x00000006UL)
" p# f4 k+ U8 e; E1 ? - #define SPI_MASTER_SS_IDLENESS_07CYCLE (0x00000007UL)
, U) P1 f h* S, ~ - #define SPI_MASTER_SS_IDLENESS_08CYCLE (0x00000008UL)3 y4 o( Z/ @# b1 F$ e
- #define SPI_MASTER_SS_IDLENESS_09CYCLE (0x00000009UL); M5 \- R2 l4 N% r3 @
- #define SPI_MASTER_SS_IDLENESS_10CYCLE (0x0000000AUL)
3 `" R' n7 `+ b2 X9 e7 A( n$ D - #define SPI_MASTER_SS_IDLENESS_11CYCLE (0x0000000BUL)
0 G/ X# h. ?+ W9 r8 V1 U; z" ] - #define SPI_MASTER_SS_IDLENESS_12CYCLE (0x0000000CUL)7 F; ~) i' W" p+ w1 V
- #define SPI_MASTER_SS_IDLENESS_13CYCLE (0x0000000DUL)
) Y5 r+ Q! k a7 `6 o% [7 } - #define SPI_MASTER_SS_IDLENESS_14CYCLE (0x0000000EUL)' r {+ x5 T& R8 ^
- #define SPI_MASTER_SS_IDLENESS_15CYCLE (0x0000000FUL)
. q% L% x) r) f! J) s) _- I
复制代码 0 ^ O: G/ C. x& ?( L
, C0 @" j5 z; v+ T
MasterInterDataIdleness0 x- r, E8 I+ k& U$ e
主模式下在两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数。
& _ z! k; @' ]- H6 }( w t" u! R
- #define SPI_MASTER_RX_AUTOSUSP_DISABLE (0x00000000UL)
: q7 g6 q/ b% P5 B - #define SPI_MASTER_RX_AUTOSUSP_ENABLE SPI_CR1_MASRX
复制代码
3 P8 ?6 r" M- J! D* J \ ~) t$ `) t: e MasterReceiverAutoSusp1 R9 P: { V+ L) F
用于控制主器件接收器模式下的连续 SPI 传输以及自动管理,以避免出现上溢情况。
' Z+ Z4 ~; K7 l4 k2 j; u( G5 S& _7 V, P
- #define SPI_MASTER_RX_AUTOSUSP_DISABLE (0x00000000UL)
% u9 F$ l+ t% h$ l1 l7 U - #define SPI_MASTER_RX_AUTOSUSP_ENABLE SPI_CR1_MASRX: X$ D2 S! z- W4 M- D$ n
复制代码
- @. A( c* c$ ^0 B+ ~/ x. l MasterKeepIOState
' B6 g: @# ?+ Q) O. R禁止SPI后,SPI相关引脚保持当前状态,以防止出现毛刺。在从模式下,该位不应该使用。2 y9 n( s' E8 o1 y: V
, e T) b! a/ x1 e$ e
- #define SPI_MASTER_KEEP_IO_STATE_DISABLE (0x00000000UL)0 D; L, y" H! e) F
- #define SPI_MASTER_KEEP_IO_STATE_ENABLE SPI_CFG2_AFCNTR
复制代码
' a+ c& e% l7 c# g7 |4 M) j IOSwap* Y0 i! W, {3 |$ E4 @: [
用于交换MISO和MOSI引脚。6 ^; Z$ G1 F% M7 S3 m
4 s; _. ~, Q2 R; ^0 ]- #define SPI_IO_SWAP_DISABLE (0x00000000UL)
2 l+ b* v0 Q. V - #define SPI_IO_SWAP_ENABLE SPI_CFG2_IOSWP
复制代码 ! M3 {$ O" ~! {2 M8 G& c H, w, F% K
72.3.3 SPI总线句柄结构体SPI_HandleTypeDef( Y9 r1 I( G7 B/ J
下面是SPI总线的初始化结构体,用到的地方比较多:
* K9 T5 B s: q; o
3 e$ \2 v/ o' {% M, Z1 m- typedef struct __SPI_HandleTypeDef
) r A5 \ c! y - {
( s! @# j4 E y; O( w" n: C$ \ - SPI_TypeDef *Instance; $ j1 H; n% b' g
- SPI_InitTypeDef Init;
6 p+ N6 t& E+ q( q, T7 H7 n& W - uint8_t *pTxBuffPtr;
/ Q* w/ E" M& q$ N' C# O( v* `' L - uint16_t TxXferSize;
2 o! @- B9 N/ b# e' E" S8 F9 k$ p - __IO uint16_t TxXferCount; % S7 j: U5 S6 @2 ~
- uint8_t *pRxBuffPtr;
& T ] ]4 L G - uint16_t RxXferSize; : Z+ V4 }% q; [+ J
- __IO uint16_t RxXferCount; $ M) P" X. ]0 P& c
- uint32_t CRCSize;
) I. N& I% E ] i: H - void (*RxISR)(struct __SPI_HandleTypeDef *hspi);
6 q8 |. F5 u/ L: ?: H1 i' V - void (*TxISR)(struct __SPI_HandleTypeDef *hspi);
& s$ s! n( ^/ \ - DMA_HandleTypeDef *hdmatx;
; L: v# C1 j% M( j* ?6 n! t2 e - DMA_HandleTypeDef *hdmarx; $ [& l0 \+ G Q3 {: c D8 S/ A5 y
- HAL_LockTypeDef Lock; * R9 m) X8 c% { [
- __IO HAL_SPI_StateTypeDef State; ' q( Y( k8 H8 S: S: k2 r2 s
- __IO uint32_t ErrorCode; 5 z `2 m3 D/ A! M
- #if defined(USE_SPI_RELOAD_TRANSFER)/ H6 [1 S* t- |, d; r1 {
- SPI_ReloadTypeDef Reload;
# v" l% l$ `8 \1 _) n+ r - #endif 9 p3 y2 E1 P7 ^9 h* @
- / m' q# z; Y* E! S& w2 V
- #if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)
& v+ h8 T# S# o! a, l - void (* TxCpltCallback)(struct __SPI_HandleTypeDef *hspi); , @, n# l. t. e2 }+ g, h) u# I' d
- void (* RxCpltCallback)(struct __SPI_HandleTypeDef *hspi); 5 \2 G! z7 f3 Z: Z+ e
- void (* TxRxCpltCallback)(struct __SPI_HandleTypeDef *hspi);
" Q% ~2 J: ~7 O' E( ~! t( ? - void (* TxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi); * I; _& ]; C0 |! x8 u+ R1 V: I
- void (* RxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);
, g, s! q+ f$ `. Q3 Q9 o - void (* TxRxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);
1 P! S3 o* s; X! d- y - void (* ErrorCallback)(struct __SPI_HandleTypeDef *hspi);
5 @ t* ~1 J- ^7 \7 j8 i - void (* AbortCpltCallback)(struct __SPI_HandleTypeDef *hspi); ) ]4 O4 b4 g3 t$ Z
- void (* MspInitCallback)(struct __SPI_HandleTypeDef *hspi);
; e" A3 s0 w/ _* D5 K - void (* MspDeInitCallback)(struct __SPI_HandleTypeDef *hspi); $ D3 u4 q0 _! r" A/ v+ } a9 z+ D
- #endif . Z5 G0 b* }0 E' J2 N) z8 p# ]
- } SPI_HandleTypeDef;+ Q0 a! ^7 A7 n" T8 h% c) {' f
复制代码
`! d8 ~, B9 v1 N
# Q2 o% Z, m9 N7 X注意事项:
3 f* _% P+ w7 ~. e/ ^) M' d7 }8 h3 _/ d& [% R$ {+ V& y5 Y+ C U1 r
条件编译USE_HAL_SPI_REGISTER_CALLBACKS用来设置使用自定义回调还是使用默认回调,此定义一般放在stm32h7xx_hal_conf.h文件里面设置:& K. w; J- K7 M7 o
) L) T: k( Q. t; U; H" z8 c #define USE_HAL_SPI_REGISTER_CALLBACKS 11 k G& V6 ?/ z; g2 ?* M* \& l
5 c. ~( D6 U$ u% z) Y通过函数HAL_SPI_RegisterCallback注册回调,取消注册使用函数HAL_SPI_UnRegisterCallback。
; l, P1 K- X% }
: I+ T9 a3 V1 d! T: ]- v这里重点介绍下面几个参数,其它参数主要是HAL库内部使用和自定义回调函数。
( v) J8 b9 W* ]* K% I) O, M
: b+ ]& `& \- X; v3 i SPI_TypeDef *Instance9 R, [: L, v5 `
这个参数是寄存器的例化,方便操作寄存器,比如使能SPI1。6 a$ j: x: }) o
4 W/ V! j: a6 WSET_BIT(SPI1 ->CR1, SPI_CR1_SPE)。
3 A$ e/ C& X$ \: l( w+ ?: E5 ^' o# z/ }/ Q1 i/ |
SPI_InitTypeDef Init) a5 g$ r- D; R
这个参数是用户接触最多的,在本章节3.2小节已经进行了详细说明。& C# A0 \ r- g8 t- n
1 }" K! s' f2 M8 T! h
DMA_HandleTypeDef *hdmatx & f S* e4 A0 k: y
DMA_HandleTypeDef *hdmarx
$ u3 M& m; b, q1 h用于SPI句柄关联DMA句柄,方便操作调用。
/ F( @( A: ]. {2 N( `$ y! G- Q9 x0 D
72.4 SPI总线源文件stm32h7xx_hal_spi.c9 |0 S! z& R9 k: K8 w( {9 L
此文件涉及到的函数较多,这里把几个常用的函数做个说明:# O, G% j4 z* b" ?" X$ |3 W
1 \( F) Z C6 V( p/ e HAL_SPI_Init
: u; L* M3 |( W- u% W- m HAL_SPI_DeInit7 k% i$ l2 I5 W" B" s' k, o& ^
HAL_SPI_TransmitReceive4 ]# s& p+ O; [7 q- h6 n. l
HAL_SPI_TransmitReceive_IT/ f6 k: ` W6 Y4 r/ N/ C! c0 Q; K5 s0 W& A
HAL_SPI_TransmitReceive_DMA
t3 ]% ]1 o& C72.4.1 函数HAL_SPI_Init- m' R+ L* b R n: `7 g
函数原型:
4 r9 x0 W7 \8 ^8 S) w* u" z! j6 a
- HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi)
% _. a- l$ H. V3 w3 e0 m2 j6 K! O - {
( g3 T3 V3 V3 }8 ^1 j+ a0 s - uint32_t crc_length = 0UL;
: V' o. n4 u% \0 A1 l - uint32_t packet_length;
2 c4 L/ r6 h" C3 F; _7 D' t7 h - 6 E' u6 G G$ Q/ `
- /* 省略未写 */
+ ]9 q1 F3 X/ k, [& [" J
6 P* |+ N. Y- V! s- w$ e- /* 如果数据位宽大于16bit,必须是SPI1,SPI2或者SPI3,而SPI4,SPI5和SPI6不支持大于16bit */
. S/ d* H4 @' S" Q- W. h7 B# E - if ((!IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (hspi->Init.DataSize > SPI_DATASIZE_16BIT))4 |2 ?& |/ r9 ]. w' v( [
- {
0 j& K* ~6 e, M& r# x - return HAL_ERROR;
$ Y2 _6 o( l' N - }2 y" w) N# H$ x: [& v) M
- ) {. S) N- v4 N* S
- /* SPI1,SPI2和SPI3的FIFO大小是16*8bit,而SPI4,SPI5和SPI6的FIFO大小是8*8bit
8 i8 l( b# h; M& j4 ` - 这里是查看设置的缓冲大小是否超出了FIFO支持的大小。
! G) z* T- o- Q1 _ - */
4 Y4 m7 c6 `4 A - packet_length = SPI_GetPacketSize(hspi);4 E9 h" q$ K6 ^
- if (((!IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (packet_length > SPI_LOWEND_FIFO_SIZE)) ||
* [4 r F: m$ P - ((IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (packet_length > SPI_HIGHEND_FIFO_SIZE)))
( ]% d: [2 c% C: K - {, V1 d+ Z1 L0 t
- return HAL_ERROR;0 q. B. @) ^3 i6 l
- }% S- R+ o7 P* |8 Z
: G0 ]' b! e! f5 y- #if (USE_SPI_CRC != 0UL)
2 J3 z& N/ J6 Z4 m a) A - /* 省略未写 */
6 s7 T W2 D/ A9 D - #endif 6 [$ B: D8 K; }7 {
- , Z6 _, B+ E* h" S) Z& P
- if (hspi->State == HAL_SPI_STATE_RESET)
: G! I! ^+ o9 R+ p - {% e/ a* L; y3 V+ ~4 _ b5 T
- /* 解锁 */0 y# Q! [2 I& \! G) ^# v
- hspi->Lock = HAL_UNLOCKED;1 x D; X. l2 s* P) g, F
- 8 B4 l% C. u2 i3 B0 h. X
- /* 使用自定义回调 */
J5 s! }, o B: ]* H5 e9 R4 [4 W - #if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)% P" @, h4 `5 z
- /* 设置默认回调函数 */
L' G' m. _) t - hspi->TxCpltCallback = HAL_SPI_TxCpltCallback; /* Legacy weak TxCpltCallback */# j. O) C9 C* H) e3 H& L0 ~0 F
- hspi->RxCpltCallback = HAL_SPI_RxCpltCallback; /* Legacy weak RxCpltCallback */
# T+ `/ v/ T$ U3 B; D - hspi->TxRxCpltCallback = HAL_SPI_TxRxCpltCallback; /* Legacy weak TxRxCpltCallback */
- d3 V: H7 K2 @( { - hspi->TxHalfCpltCallback = HAL_SPI_TxHalfCpltCallback; /* Legacy weak TxHalfCpltCallback *// X+ J) U, F% k. y* O1 p
- hspi->RxHalfCpltCallback = HAL_SPI_RxHalfCpltCallback; /* Legacy weak RxHalfCpltCallback */! n/ E. ~3 A( s/ G& M( M
- hspi->TxRxHalfCpltCallback = HAL_SPI_TxRxHalfCpltCallback; /* Legacy weak TxRxHalfCpltCallback */
7 F$ Z w+ F. Z - hspi->ErrorCallback = HAL_SPI_ErrorCallback; /* Legacy weak ErrorCallback */
1 n% m m3 }% c8 I4 u7 q - hspi->AbortCpltCallback = HAL_SPI_AbortCpltCallback; /* Legacy weak AbortCpltCallback */8 ?: E, P1 k! ]3 O* V- z# @
' B/ v& o7 t8 a! y$ g0 M) t5 v# G# d- if (hspi->MspInitCallback == NULL)
1 B* h7 X5 X# v8 ~8 c - {
: C! L/ m" j3 ?; h - hspi->MspInitCallback = HAL_SPI_MspInit;
- K; ?8 B" j2 g- f0 i2 u - }4 q) {% k- L f6 Q$ D
- 5 D r4 t0 E( z7 e ^
- /* 初始化地址硬件: GPIO, CLOCK, NVIC... */5 Y2 O5 s; O2 T5 C
- hspi->MspInitCallback(hspi);5 _% u+ F0 r# T* g6 d4 }* I- I
- #else
* {- y% n$ V. ^9 L7 C* L - /* 初始化底层硬件: GPIO, CLOCK, NVIC... */
+ t& u [, u! C. Y$ Z7 p* g& G1 P - HAL_SPI_MspInit(hspi);- q; y2 O* g, j$ c8 I/ r
- #endif! v% C. E y! w6 J3 p2 W5 K
- }
9 Y0 I! E2 {0 U" S1 @
# M& V1 X+ q1 p- hspi->State = HAL_SPI_STATE_BUSY;8 u* H/ K5 s& P
- 5 D/ X1 N* x! Y; H* v& I, h/ O
- /* 禁止SPI外设 */
3 J+ G! i( h8 W& i8 a - __HAL_SPI_DISABLE(hspi);
7 x6 W7 M6 ]. g) d: H! r - 4 W7 g( t& Z" f# z2 A& C. I8 Q
- /*----------------------- SPIx CR1 & CR2 配置---------------------*/, f0 d7 l n% O4 M% T! W3 V
- if ((hspi->Init.NSS == SPI_NSS_SOFT) && (hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.NSSPolarity ==
$ \6 ~ J3 N, s( L! @3 X. x" t - SPI_NSS_POLARITY_LOW))
" h' b. e7 {, k) v% [& g - {% h( r' T8 c7 H6 Y
- SET_BIT(hspi->Instance->CR1, SPI_CR1_SSI);# q; @# a2 }$ z' e9 E3 d* ?- r
- }; |& K5 r' A4 O0 n' k4 M, o
- ' C, k8 C: U1 U' z; ]$ a# T
- /* SPIx CFG1配置 */* f0 X4 e' M. M, C, H- T" m% X
- WRITE_REG(hspi->Instance->CFG1, (hspi->Init.BaudRatePrescaler | hspi->Init.CRCCalculation | crc_length |
( f+ `5 U( K8 V! V - hspi->Init.FifoThreshold | hspi->Init.DataSize));& V' c, c; g' j4 t) O+ g; e
- - d9 A* D% C' L# J( Y* x/ N# ?
- /* SPIx CFG2配置 */ R' C a' @2 K7 q) k
- WRITE_REG(hspi->Instance->CFG2, (hspi->Init.NSSPMode | hspi->Init.TIMode | hspi->Init.NSSPolarity |
; q" m9 F$ ], c" ~& l - hspi->Init.NSS | hspi->Init.CLKPolarity | hspi->Init.CLKPhase |
8 p/ J- e7 [* Z! S# @" ]% G - hspi->Init.FirstBit | hspi->Init.Mode | hspi->Init.MasterInterDataIdleness |
1 G. o) p9 M+ L$ c - hspi->Init.Direction | hspi->Init.MasterSSIdleness | hspi->Init.IOSwap));
. Y/ U0 O) w- ?# e
, Y2 H5 x- r! I- #if (USE_SPI_CRC != 0UL)8 K p. }' v {! t$ _! _% y
- /*---------------------------- SPIx CRC配置 ------------------*/
6 V H I v( u. {& M. h" k; l - /* 配置SPI CRC */
1 F* L& t& E* Z7 l3 W- u1 m- f& u8 D - if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
9 i7 G: a) D5 F3 q( y: Z3 F - {
) ]! s M2 ?/ x/ D; L% J - /* 初始化TX CRC初始值 */, j% U- f* \7 B2 d8 M( x, d
- if (hspi->Init.TxCRCInitializationPattern == SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN)9 J% `6 R; T' B- k! f# _* Q5 Y
- {/ R6 i) B$ G& C P$ b3 ~
- SET_BIT(hspi->Instance->CR1, SPI_CR1_TCRCINI);; F! \! S0 W1 P# r! a, w% k4 B5 [
- }
1 ?% V4 U. W0 w' [% U- D$ f+ S# [4 y' g - else
3 }0 @4 J* G2 x0 r; O3 L/ } - {6 ~7 b0 {8 f e$ v. Y
- CLEAR_BIT(hspi->Instance->CR1, SPI_CR1_TCRCINI);
* n0 g' B& E/ y8 h - }* T) y. t+ E5 R+ g- N
- ) S! [$ G* c7 E# H5 I2 O
- /* 初始化RXCRC初始值 */* z; q6 N7 A+ x$ J8 J, |
- if (hspi->Init.RxCRCInitializationPattern == SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN)9 b: [& A+ q# ]9 u3 i/ x
- {4 B+ S/ Y3 N9 F+ m5 O
- SET_BIT(hspi->Instance->CR1, SPI_CR1_RCRCINI);
6 b0 W1 A$ l, m, q - }
2 [& M$ e, U4 z; L6 t3 P - else
/ j- w$ ]6 ^, x1 Z - {
% F5 |: @5 }( y - CLEAR_BIT(hspi->Instance->CR1, SPI_CR1_RCRCINI);
% h& I6 o' X- `) ? N - }/ c. g- p& d4 Z
7 U+ H7 h* Q8 _. Z. ]! D- /* 使能 33/17 bit CRC计算 */
9 |; }8 O9 |% a8 K% m9 ^$ P - if (((!IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (crc_length == SPI_CRC_LENGTH_16BIT)) ||9 e. H B! [" V
- ((IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (crc_length == SPI_CRC_LENGTH_32BIT)))
6 t# a' I! G6 R. [ - {
8 K- e7 C. f: g$ T( X2 O1 S% E - SET_BIT(hspi->Instance->CR1, SPI_CR1_CRC33_17);" G8 f6 p2 p; @" F
- }
: b2 b, H, \* h - else1 M+ k" L n3 E0 V% }& f% C l
- {
0 [ B: U& b0 O5 q! r - CLEAR_BIT(hspi->Instance->CR1, SPI_CR1_CRC33_17);
9 l: z9 ] m, _4 X% B' R( C0 P - }! F& p, ^" J. Z5 Z1 ~+ F" {
( k0 J4 C3 }( H9 E( E- /* 写CRC多项式到SPI寄存器 */
5 O7 o* g( p3 d! ^ - WRITE_REG(hspi->Instance->CRCPOLY, hspi->Init.CRCPolynomial);
9 y, z z+ F$ B) S3 G - }
& Q* q0 E @. R" Q - #endif
. P6 V! r+ o( y; x7 p
& p% l" E: o' P/ }- /* SPI从模式,下溢配置 */; X0 V. u0 |" @2 l1 V0 v6 M
- if (hspi->Init.Mode == SPI_MODE_SLAVE)
. e" M8 H4 C/ A9 b+ X - {: Q7 h7 ^% S# X8 f& c! N8 ]: V
- /* 设置默认下溢配置 */
# z* V2 C/ l0 O! V - #if (USE_SPI_CRC != 0UL)0 G& b" M7 ~/ U9 E% `
- if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_DISABLE)% ^1 v( A4 R" a& K
- #endif" ]8 C6 }1 \* p
- {
+ z# G7 r% Y2 B) Z' e2 G - MODIFY_REG(hspi->Instance->CFG1, SPI_CFG1_UDRDET, SPI_CFG1_UDRDET_0);
% t s+ N0 m! ~/ b - }
- L* p+ Z+ I+ |) m - MODIFY_REG(hspi->Instance->CFG1, SPI_CFG1_UDRCFG, SPI_CFG1_UDRCFG_1);
Y" A W3 u7 A4 u# ]4 K0 K! ^ - }
( T. n P o/ U1 d, h7 t# `
# X$ p) y2 w9 y* F. p- #if defined(SPI_I2SCFGR_I2SMOD)$ j& o. l# Y$ b0 J+ s+ j
- CLEAR_BIT(hspi->Instance->I2SCFGR, SPI_I2SCFGR_I2SMOD);
9 j9 Q( o# q. Q1 ~; J - #endif 8 E+ d0 t5 I$ m. E) F7 [
- 3 J+ d( j9 ?% E# }
- /* 确保AFCNTR bit由SPI主机模式管理 */0 u7 T; b6 b6 U& J( Z, A) I, {. U9 O
- if ((hspi->Init.Mode & SPI_MODE_MASTER) == SPI_MODE_MASTER); v; H; `1 X, X# ^0 u7 X
- {: g; `$ c' O$ ~9 r
- /* Alternate function GPIOs control */
% W2 x0 _, L. {7 L* m - MODIFY_REG(hspi->Instance->CFG2, SPI_CFG2_AFCNTR, (hspi->Init.MasterKeepIOState));
# K* s2 C x& ?+ F1 X9 y - }- x) r% M7 W: ~, S3 e. m
# E+ k. y- m" {$ c% N1 |- hspi->ErrorCode = HAL_SPI_ERROR_NONE;
8 F3 P2 ], o n7 L8 T$ c - hspi->State = HAL_SPI_STATE_READY;
; J; `$ \( `& M' |
2 d( o, L( E T- return HAL_OK;% |+ a. q m0 D' d$ J9 ] V9 @
- }
# x" d9 K- b; e' P
复制代码 6 w. j. \. k2 O7 ]: V: L
, e: d! r2 S' U1 ^' a9 ^函数描述:
' M2 c% J v6 x& `- O* m% r3 k& \9 [2 x1 F0 u& X9 D3 a
此函数用于初始化SPI。: J8 J- E9 G, D5 Z- y; }. i# z
: \+ H2 C# W$ L8 }( I( P& t函数参数:
1 m& C V" J+ P* P; y7 a# J* Z" f' S3 g8 ?# U
第1个参数是SPI_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数。
: O& J. ]- {6 {4 x" U 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。( Z4 h1 [% K5 i4 ?
注意事项:
, H' k3 m& o4 e6 a2 u8 [6 ?4 J2 B/ B$ u
函数HAL_SPI_MspInit用于初始化SPI的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。
& z$ N$ S( y" p如果形参hspi的结构体成员State没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量SPI_HandleTypeDef SpiHandle。
( d) r: C, H" H: n) @4 p* l对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_SPI_STATE_RESET = 0x00U。
# P* I# }2 \% p h
! Y; P# l2 Z6 z/ B9 @解决办法有三5 }4 b/ E$ Q6 _8 a& S- l% x8 g
5 o7 M/ A4 [1 D方法1:用户自己初始化SPI和涉及到的GPIO等。
4 E7 u2 \# K$ d. g' ~# o6 d% @) ?$ C; p: f
方法2:定义SPI_HandleTypeDef SpiHandle为全局变量。/ q$ Q; y$ f; {; ^$ v: N
2 T- }0 u K" l* t; ~. F) l& r( Y
方法3:下面的方法
9 q4 I( Q4 f' h0 v$ w7 P( n) q& s* ^9 Q0 H, _
- if(HAL_SPI_DeInit(&SpiHandle) != HAL_OK)
0 S% S" o0 w+ T! L: p2 `! d2 D - {
! W; h3 \1 u5 s- y+ v - Error_Handler();
* Q, M) T. m1 Y4 x - } 0 z$ [8 c. f) C' i% a: d3 H
- if(HAL_SPI_Init(&SpiHandle) != HAL_OK)
& _: v3 [3 ~; r" M) G& `% I5 ? - {# m8 d. E" ~& y$ x6 g: U# |- S
- Error_Handler();0 Y# Q% W2 {. N! T: v
- }
复制代码
7 t* Y0 a7 a# A& H# D9 {使用举例:
" r9 ^7 B; O w9 H
7 ]5 |4 G( J$ |# { Q& g- SPI_HandleTypeDef hspi = {0};
* }. l+ ]) B0 h2 A - : s' D+ J. A6 V- i
- /* 设置SPI参数 */1 ?" o" A9 Q" E# \
- hspi.Instance = SPIx; /* 例化SPI */) T6 r5 F; K' g* E- h, L
- hspi.Init.BaudRatePrescaler = _BaudRatePrescaler; /* 设置波特率 */# \6 d5 P# E# U; e. s( r) h9 c
- hspi.Init.Direction = SPI_DIRECTION_2LINES; /* 全双工 */
- Q# ]5 t% G" Z, X8 }7 z% F9 w$ @ - hspi.Init.CLKPhase = _CLKPhase; /* 配置时钟相位 */
, m: M% c. |$ g; W - hspi.Init.CLKPolarity = _CLKPolarity; /* 配置时钟极性 */! u5 I7 y' |0 |
- hspi.Init.DataSize = SPI_DATASIZE_8BIT; /* 设置数据宽度 */
; |% ]7 u+ O, @& A! f. e% B5 v$ C# m - hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; /* 数据传输先传高位 */
( l! Z- ?$ C( q f X1 F+ Q" \7 O. R - hspi.Init.TIMode = SPI_TIMODE_DISABLE; /* 禁止TI模式 */
F% ?! _5 U$ r+ c# @ - hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */! @( e( X, _ e' J1 Z) h" _& h
- hspi.Init.CRCPolynomial = 7; /* 禁止CRC后,此位无效 */" | ^& T8 |+ t; y+ u
- hspi.Init.CRCLength = SPI_CRC_LENGTH_8BIT; /* 禁止CRC后,此位无效 */
7 Z" _; b' b, s8 d C+ n2 l5 \% ] - hspi.Init.NSS = SPI_NSS_SOFT; /* 使用软件方式管理片选引脚 */
6 N0 J2 z% K2 L2 d7 K - hspi.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; /* 设置FIFO大小是一个数据项 */
; j+ @) T' J* Y3 A$ \) _/ D& U" Y - hspi.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; /* 禁止脉冲输出 */
% o. F; j8 K# _- O& g* Q: x2 A - hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */
+ L9 f& c% b+ Z8 _6 ^ r - hspi.Init.Mode = SPI_MODE_MASTER; /* SPI工作在主控模式 */9 P8 f% s* [- v; r3 b: N0 u
- . x* V* T7 M: c2 t3 F3 _9 E
- if (HAL_SPI_Init(&hspi) != HAL_OK)
$ Q d& x9 N* I% a0 e5 m* w - {
3 ]7 S1 {- u n: V r3 u3 L - Error_Handler(__FILE__, __LINE__);
* |7 h. {, ^( w) q5 O - }
复制代码 1 m( t6 g* | c8 r
1 V7 W3 M5 Q2 Y& p0 n6 a j
72.4.2 函数HAL_SPI_DeInit
2 s4 V# x+ L4 R7 Z# q0 E函数原型:
! J F8 x! P! O' t8 S' k( M8 g+ D0 B7 q, G7 f# O
- HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *hspi)" M) o' c- K' i8 e. D: }7 G: l
- {7 {9 p2 X9 z& b3 h; |) ]- A! m
- /* 检测SPI句柄是否有效 */3 T7 G6 L9 k/ N3 m8 D* Q, d3 O0 W. U
- if (hspi == NULL)+ g, U' v) T. V0 k9 e' u
- {
) C) V& h9 o& R3 m: `$ U, t( x& G5 H - return HAL_ERROR;3 D( O1 O8 \6 r; y; l
- }1 \! p$ ~6 D8 w5 L! g6 v- o
- ) v7 f2 L @+ l7 ?
- /* 检查SPI例化参数 */
2 o1 x! _* N$ t - assert_param(IS_SPI_ALL_INSTANCE(hspi->Instance));
/ x/ J' R, F( p% r- Z' g7 E* ? - ! C# r: q. D+ S% X
- hspi->State = HAL_SPI_STATE_BUSY;; Y. r o5 b8 x& e# G' I
- * ]2 W6 {! Z! M! I4 A5 C
- /* 禁止SPI外设时钟 */: _7 _* l) H1 W6 k3 u% ~
- __HAL_SPI_DISABLE(hspi);
& D' C3 V% u0 K, q0 I; z - ; D- ^3 O; W9 ?5 g0 v. u7 a) L
- #if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)9 v6 j0 P) ^! m# M' l& w
- if (hspi->MspDeInitCallback == NULL)
: i. P+ j) K- p7 F: r& t! Y2 k x - {
, y) K* V$ i3 I - hspi->MspDeInitCallback = HAL_SPI_MspDeInit; 3 S3 k) h+ } Y6 m% I
- }
' l6 Z V# i2 r2 s, \+ k0 J' i* h - 5 O* j% t { l" K3 n
- /* 复位底层硬件: GPIO, CLOCK, NVIC... */
/ V' b9 e" }2 m& ]: D - hspi->MspDeInitCallback(hspi);, F: m! `; q, B _4 V
- #else, I/ r4 x" Q& M M4 y5 G* ^
- /* 复位底层硬件: GPIO, CLOCK, NVIC... */7 w9 h @; J) y( \) U9 a2 ]
- HAL_SPI_MspDeInit(hspi);
0 X; r& y4 n% M; \ - #endif
6 N& Q5 t; t& v5 Z, M - ( m" P$ c& ~: J# J$ d+ q
- /* 设置无错误,复位状态标记 */
$ T T3 H6 N3 m& J/ E9 N6 r - hspi->ErrorCode = HAL_SPI_ERROR_NONE;
* e5 y' a& A9 Z1 {* l: V - hspi->State = HAL_SPI_STATE_RESET;) d3 n. c$ ?- O# Z
# [4 u+ ?/ O6 H- /* 解锁SPI */8 d' S+ _/ h ~( x
- __HAL_UNLOCK(hspi);( K& I) E* M$ m C' c
" _, o1 T' u! e; \! L- return HAL_OK;
( \% g8 ?* r/ O, M" d - }
复制代码 7 q( X6 i1 h! Y6 T$ q
函数描述:* D. l/ }/ o& x" a9 l0 F( k% ^
/ o. _! ]7 h: ~- J2 C4 ?, U用于复位SPI总线初始化。
* b9 |* w& _: j: h8 ]0 S. ]: ~) V i3 w6 o# K! g3 v8 }
函数参数:
: v! O1 |3 K/ J0 M6 Y0 S' ? I/ d. q# z0 Q4 E
第1个参数是SPI_HandleTypeDef类型结构体指针变量。
) @( \3 r9 V! ~) J' U2 w 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中, k; p1 z0 B+ g: W' a; K
72.4.3 函数HAL_SPI_TransmitReceive" Z9 d$ D4 Q8 @+ F! d- |
函数原型:2 E' R0 z5 [3 E( K% R8 b
3 r4 |( i: N" L1 q2 U; K
- HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout): s% u+ I4 t) F0 g, p) x" G% C
- {! `" z' ^) c/ X* _
1 [7 p0 @& J$ E `, r- /* 省略未写 */
8 p( h n- k7 L$ `7 o7 s1 N - 5 s7 f& n* ?) M) h' \0 g- g G
- /* 大于16bit的数据收发 */
! L! G" @' e. ]# a. Z( e$ q - if (hspi->Init.DataSize > SPI_DATASIZE_16BIT)( I( X. }, l0 Q7 \' d) d2 t$ F- N
- {
- D' O2 z1 Z T; i% N0 e2 I - /* 省略未写 */
# P7 M! U8 ]- J+ z7 D& s9 [6 g - }; x& B0 } Q( K2 j
- /* 大于8bit,小于16bi的数据收发 */9 L3 _! K7 `7 {" @. P3 U/ F! C, G
- else if (hspi->Init.DataSize > SPI_DATASIZE_8BIT)
/ a4 b, d6 `' G/ B5 Q6 j8 q1 \ V" e6 @ - {
' d% ~5 I$ o6 O) K8 X - /* 省略未写 */2 `) n( J6 ~6 Q& I) ~
- }
0 U6 Y- u& b3 B7 u - /* 小于等于8bit的数据收发 */
: {1 w8 y l' Y# o& e8 j6 m+ L - else& H$ G1 q/ Q B& o- n
- {& ~0 |2 L+ @( S
- /* 省略未写 */
3 m0 t! v4 t5 U: Q) ? - }
9 ]# A5 v% p1 Y/ w2 Y/ e
. h, w. @+ R1 ?0 \0 b- }; ^- q a6 |. ~1 r9 l
复制代码 % v/ I; ~6 |( |
4 x$ f( j5 ]' y
函数描述:6 I% S! a2 Q% O
/ n% w4 r2 e# d Y$ p+ w1 {此函数主要用于SPI数据收发,全双工查询方式。) ]6 P z+ L: u" v0 K1 _0 o
E" L4 C- o' Q" S: ~) s
函数参数:
. h" i+ D0 b+ ^7 M- L6 {9 @7 [+ `
第1个参数是SPI_HandleTypeDef类型结构体指针变量。; {4 v( T* |0 D
第2个参数是发送数据缓冲地址。
5 f/ \+ q$ R. f, c6 O- I A 第3个参数是接收数据缓冲地址。# N: {- p6 ~# {
第4个参数是传输的数据大小,单位字节个数。
, h3 }6 n4 M, X' u! |2 Q 第5个参数是传输过程的溢出时间,单位ms。5 x8 R6 V/ t+ v# L. d1 x
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。
2 ?5 z4 b/ f* j# F1 D! i% @使用举例:
+ z# K! m( M; ?" i& [0 t' y: ^: v& I. I: F: H( e
- SPI_HandleTypeDef hspi = {0};& w, z8 A8 T: ^0 B0 t
1 T9 N! {# @0 W3 L: m2 _- if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)
8 h3 K* Q% h W" P. W - {
$ p; P. v! \! I9 Z - Error_Handler(__FILE__, __LINE__);- n/ r7 N7 D* g8 z% g9 w; D7 a
- }
复制代码 . m% h) y% K6 k! M3 u: _
72.4.4 函数HAL_SPI_TransmitReceive_IT( ~3 p) C: q6 z% ^( Z7 o' ~" ^
函数原型:
/ M& Q* [2 n$ @: {, Y( M1 k
& ]9 S7 F& d" X1 S/ p- HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)* [6 A. C7 h5 U( h0 Z8 T
- { b" P" f) v& n' x) s, ?
- /* 省略未写 */
4 Y$ |4 X5 _" o* G - ( [- R+ z7 C$ r7 \5 m; P, c
- /* 设置传输参数 */* B! ]( C, E0 P! f7 I
- hspi->ErrorCode = HAL_SPI_ERROR_NONE;
q1 O; t5 T" l5 g! q5 ~' {& M - hspi->pTxBuffPtr = (uint8_t *)pTxData;2 Q$ ~ O( H1 j0 [& }
- hspi->TxXferSize = Size;2 k. f& _5 p: v3 j0 H1 z
- hspi->TxXferCount = Size;: Q: v5 N* e( J! d
- hspi->pRxBuffPtr = (uint8_t *)pRxData;( K/ T; j. [* f j$ D
- hspi->RxXferSize = Size;
0 }$ L* U, }) g& e) R8 t# l - hspi->RxXferCount = Size;
4 z8 T0 t: c5 H( s u/ c8 N - : n1 L3 y- B( f1 A: E- c
- /* 设置中断处理 */
! v' D: q2 B; M( m. i0 F - if (hspi->Init.DataSize > SPI_DATASIZE_16BIT)
( P4 \0 X& z* c; A; B D" h' Q - {+ ^( L% ?' ~; X$ [1 K
- hspi->TxISR = SPI_TxISR_32BIT;
' m- v: L" {' ^7 |5 |1 q - hspi->RxISR = SPI_RxISR_32BIT;
# v; D' V. V4 K- O/ B3 G; w - }
8 }6 c: W* O4 s1 ?* W - else if (hspi->Init.DataSize > SPI_DATASIZE_8BIT)3 u9 G; `6 D3 v( F5 c
- {
$ Y# {- D, [' i( n; E9 {1 Y - hspi->RxISR = SPI_RxISR_16BIT;
, ?9 t* o! G+ [ - hspi->TxISR = SPI_TxISR_16BIT;" y" b. v. t" H/ j& K* ~
- }
9 \/ P9 e; _( W/ T - else1 J3 e' U \2 q
- {
' X4 Z0 }/ @2 C5 w. ^% C9 S& P/ r* J - hspi->RxISR = SPI_RxISR_8BIT;
+ S& Z% h+ i2 G- C( N6 d8 \ - hspi->TxISR = SPI_TxISR_8BIT;
* q5 ~& z/ A7 q, X3 I) B - }
% R2 X" n% R w) r$ v( D/ u
" o7 ?2 p+ o$ o- F3 i7 ?; w- /* 设置当前传输数据大小 */- }8 ~1 m# y0 f! t3 i, U
- MODIFY_REG(hspi->Instance->CR2, SPI_CR2_TSIZE, Size);' G% P7 U. w& U: l4 y+ _5 `
5 o- w* j5 P! m- /* 使能SPI外设 */; p! n2 P" ~1 ?1 }$ ^6 _! T% b# h
- __HAL_SPI_ENABLE(hspi);
2 s# t F+ M1 ^4 \, N3 ^ - % p9 J4 p+ s* D; @# q g& H# }
- /* 使能各种中断标志 */
+ D" U9 }8 U, `9 v# f! @& G# T - __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_EOT | SPI_IT_RXP | SPI_IT_TXP | SPI_IT_DXP | SPI_IT_UDR | SPI_IT_OVR | $ u8 X- K$ Q% Z' {; F
- SPI_IT_FRE | SPI_IT_MODF | SPI_IT_TSERF));
, v( A7 y2 C `+ f - 8 o1 u: B+ H5 o
- if (hspi->Init.Mode == SPI_MODE_MASTER)1 o) F4 q R* n) y& o
- {& x/ k5 |7 F" P. N3 }9 Y7 D: |
- /* 启动传输 */
. o' X) [: h$ B5 ^7 D; J - SET_BIT(hspi->Instance->CR1, SPI_CR1_CSTART);
Y$ @' Z5 {$ Z9 J+ ` - }
# {) F1 y4 V u+ D! T) a+ Q - : T1 Y& c2 w+ A! j: ?
- /* 解锁 */6 X; t& @. k7 t! f% V+ P
- __HAL_UNLOCK(hspi);
& W, ]6 `* v e - return errorcode;( J( }, A3 u8 d3 B& c( X
- }
复制代码 + V! `; n8 x( \2 d/ B, u5 J
# @0 S l) _: M8 l: x& Z& I, w
1 d* K. ^$ r7 q
函数描述:
z& b b5 ~+ D0 W& p4 \ H6 Y/ I4 k
此函数主要用于SPI数据收发,全双工中断方式。, p' n, b* ^- t }; h" P% f
0 q1 ?, x/ U% ~# g( ?6 W+ P) G2 T
函数参数:
% r0 R& k* p$ v% F: `; o J
9 w) W. f$ M" i |# s" \7 _ 第1个参数是SPI_HandleTypeDef类型结构体指针变量。. D9 ^( `3 e/ l- F H$ [
第2个参数是发送数据缓冲地址。
. a# f8 D& t6 t ` 第3个参数是接收数据缓冲地址。
. A, x" H3 N9 c$ u: l' Q 第4个参数是传输的数据大小,单位字节个数。0 s$ t( g+ f) e& ?+ M. T
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。4 O+ J5 R! [( e' k
使用举例:/ {1 u* d( N5 H* Q _8 h f% R
, Z3 Q2 L! w8 W" ?
- SPI_HandleTypeDef hspi = {0};
$ K$ C1 N7 `0 v! z, i7 U/ d
! k& Z- S! U/ m# b- if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK) 7 f4 V+ x1 B9 H1 n. }- P3 C
- { G- ^& X3 m8 ?; O% K1 `( m
- Error_Handler(__FILE__, __LINE__);
( L0 A, a3 G7 B5 ~' c0 X6 t) r - }
: X' _7 M7 B) i4 ` - 5 g0 ]0 t5 T- J. \3 m" g' k
- ! F) ^% C# {4 o: a3 X, z" Q- ~. ?
- 72.4.5 函数HAL_SPI_TransmitReceive_DMA' V8 d4 X4 T) X3 A* x- P, y
- 函数原型:- ] m2 H& h3 K" U G
- : a+ X6 t6 P7 O1 V6 z
- HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,# o$ E' s( K$ w. o* T- F0 F4 T
- uint16_t Size)
" a; {- D$ P" B - {
( c! I: `/ k% v+ n B - /* 省略未写 */
0 T0 C6 H& w" A6 K3 G) C! l5 O, ]
, f4 k- s. h& m+ L4 @- /* 注意DMA的位宽和对齐设置 */
# {5 F+ r7 Z3 r; I! z1 J( S1 m" g( r - if (((hspi->Init.DataSize > SPI_DATASIZE_16BIT) && (hspi->hdmarx->Init.MemDataAlignment !=
6 U \ }# b0 {/ t/ E9 b, y5 @ - DMA_MDATAALIGN_WORD)) || ; u6 m- h" Z. \
- ((hspi->Init.DataSize > SPI_DATASIZE_8BIT) && ((hspi->hdmarx->Init.MemDataAlignment != DMA_MDATAALIGN_HALFWORD) && (hspi->hdmarx->Init.MemDataAlignment != DMA_MDATAALIGN_WORD))))
' s; p$ I* J3 D2 ^7 R2 e - {* j. e5 W$ n$ G
- }, R& ^# _" C4 Q
- # B* T+ T" @0 W* I8 w
- /* 调整DMA对齐和数据大小 */& `- M; c. s" A% a9 \9 y
- if (hspi->Init.DataSize <= SPI_DATASIZE_8BIT)
2 V* n1 W2 w, O* m. g - {7 `2 ]' [ P: s% u/ X
- /* 省略未写 */
4 V" {2 a4 t7 D, A3 o - }
3 q( B! D" p7 m( L4 H8 g9 F" o7 M - else if (hspi->Init.DataSize <= SPI_DATASIZE_16BIT)/ h0 c( s" S% K
- {/ p7 W0 d, G/ j9 @8 G+ ~
- /* 省略未写 */2 N' _8 L! @" I
- }
/ P% O2 X4 C% g& ]% D - else b' U. W5 m1 x6 j! i
- {
# @) a; R# d d m r- @ C - /* 省略未写 */
" e& ]2 V: y- A9 p - }
. }9 ]! ]6 c: v! W6 k8 T - * p% o$ j; R9 S) X: \ N3 p! K
- /* DMA接收配置 */
! {3 p; Y, @- V- {% r$ C- [, { - if (HAL_OK != HAL_DMA_Start_IT(hspi->hdmarx, (uint32_t)&hspi->Instance->RXDR, (uint32_t)hspi->pRxBuffPtr,% \# `' p0 g0 ~6 n2 T4 p
- hspi->RxXferCount))' L, }9 H. k7 P6 Y6 a' n/ e
- {- M/ f. ]& ?* T* G
- ; Q/ }8 l* R5 \9 e
- }
2 Q, [4 n, ^: f4 C. S. z - / ?5 Z& a9 b. c/ X0 D; W; h
- /* DMA发送配置 */
4 D. W0 [9 ]- U z4 v" Q - if (HAL_OK != HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->TXDR,
( j: }( K7 m6 a6 F# s: M - hspi->TxXferCount))6 J# t' I# i6 d8 s5 q4 O
- {
' {( U- J; J$ [- x - }
9 l6 }4 }1 e( x1 [7 J% f( e e: b. P
9 O# j$ Q _. b+ n2 |) n/ f- /* 省略未写 */3 Y( h" b8 b9 w
- }' o9 u$ q0 s/ v8 @
复制代码 ! l- T, P4 ~# w8 d8 E8 x4 {
( {5 M# q. J1 ^" d# N. D! k! m
函数描述:
- n: W& A3 [5 Q V9 D+ t2 u
. K6 l. F: `4 { N- x# L3 S$ _此函数主要用于SPI数据收发,全双工DMA方式。0 d* R, h# i4 o& v) {: T* ]/ A4 n
$ c, ?8 |+ b: E/ r }函数参数:
- F5 c+ S3 T# m6 P3 a8 s9 M1 j. M* X" g5 R7 z4 X+ O& j8 O
第1个参数是SPI_HandleTypeDef类型结构体指针变量。
' R' i6 t( f# K7 I( F 第2个参数是发送数据缓冲地址。9 ?9 i7 S+ L0 N$ O
第3个参数是接收数据缓冲地址。; R# `, S! W7 a# y: B7 u
第4个参数是传输的数据大小,单位字节个数。: A) W/ T& T, }% U! N7 K
返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。
- D% T. h8 ]3 N: K! i9 z使用举例: x% L% o0 E, t# E& m C% H, \
! {5 {1 G# \, N0 H7 f# @9 j& C- SPI_HandleTypeDef hspi = {0};
) Q* H, d' p9 e% ]" H. m - ( h* q1 x1 v6 M: n
- if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)
# h9 \' Q+ X# @, \ - {
4 o) P* K7 a4 Z+ r, e p$ V5 |1 Z - Error_Handler(__FILE__, __LINE__);
( N( C& h" _/ n" j - }
复制代码 ( W. m9 T6 p/ \
C4 Y2 l d8 _" _' `
4 ~+ B& R: Y0 w+ @
72.5 总结7 `9 w- @: i" U8 b
本章节就为大家讲解这么多,要熟练掌握SPI总线的查询,中断和DMA方式的实现,因为基于SPI接口的外设芯片很多,熟练后,可以方便的驱动各种SPI接口芯片,以便选择合适的驱动方式。& a1 t% o. q+ S# p4 B5 K
9 v" |/ @+ @. \" B+ L4 R2 Y1 S0 r0 f) E& g9 f. z
|