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