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