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