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