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