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