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