你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

什么是SPI通信?怎样进行SPI通信?

[复制链接]
STMCU小助手 发布时间:2022-11-30 14:00
介绍
2 u( O/ l  N% O5 o3 ]& ^SPI 简介
9 d; w, W0 B' w3 ^! E
SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率较高的场合。并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议。: I! c: U' v+ F0 C# I' V
SPI接口是全双工三线同步串行外围接口,采用主从模式架构;支持多slave模式应用,一般仅支持单Master.时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后;SPI接口有两根单向数据线,为全双工通信,目前数据速率可达几Mbps的水平,速率较高。
% i9 H8 d/ Y, U3 W
% d$ M% C% x0 USPI特点
7 l9 v  U1 _6 r1.
可以同时发出和接收串行数据;2.可以当作主机或从机工作;/ ~0 [: V7 g# K1 U
3.提供频率可编程时钟;
* S  o# q. M& d* ]6 B4 R% {2 \4 b4.发送结束中断标志;
! {/ t7 W# q; l4 d6 i5.写冲突保护;
7 ]$ e8 m9 J( H' [8 ~: V6.总线竞争保护;& B/ x1 W' Y0 W: y" G# Q
7.传输速度快
' K4 d5 C$ r2 H' K, S, r1 `. t/ v  e1 X3 `( C+ t0 _3 v1 ^- H* t
SPI 物理层/ a  r" u+ S/ O
SPI总线是一种4线总线,因其硬件功能很强,所以与SPI有关的软件就相当简单,使中央处理器有更多的时间处理其他事务。) F% u5 `/ b. Z) Q6 U
1.MOSI:主器件输出,从器件数据输入;这条线上数据的方向为主机到从机。* c  j3 n: X( q( ]& D$ m
2.MISO主器件数据输入,从器件数据输出,即在这条线上数据的方向为从机到主机。
/ T+ G' j" |, d8 N0 X4 i1 N3.SCLK:时钟信号,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。0 y. A# w8 x6 u% ?; N. b
4./SS:从器件使能信号,由主器件控制(片选)。而 SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从
; x+ u% Y2 d9 {  V设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以SPI 通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。' d3 ?4 e7 L2 ]+ `0 k& P

- C; A; P/ O/ u* ~5 }SPI 协议层
4 O# W/ d4 S# s) N2 L( @$ _SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。NSS 信号线由高变低,是 SPI 通讯的起始信号。NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。MOSI及 MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定。MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,1 r- q- ^8 l' F+ x5 A8 G+ C
在 SCK 的下降沿时被采样。即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI 及 MISO为下一次表示数据做准备。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。/ v0 Y* E6 b+ ?% ]

$ a# A3 V% @; r; mSTM32 的 SPI 特性及架构8 p& J; k, |, Z( l# b2 s0 a3 R1 c7 j
STM32F1 的 SPI 功能很强大,SPI 时钟最高可以到 18Mhz,支持 DMA,可以配置为 SPI协议或者 I2S 协议。
' S; u# Y! {. f8 K3 u, J2 _" {STM32 的主模式配置步骤如下:4 H5 g$ A" x- y& T0 C  r
1.配置相关引脚的复用功能,使能 SPI2 时钟。
' v/ ^8 o$ h, a: m2 K我们要用 SPI2,第一步就要使能 SPI2 的时钟,SPI2 的时钟通过 APB1ENR 的第 14 位来设置。其次要设置 SPI2 的相关引脚为复用输出,这样才会连接到 SPI2 上否则这些 IO 口还是默认的状态,也就是标准输入输出口。这里我们使用的是 PB13、14、15 这 3 个(SCK.、MISO、MOSI,CS 使用软件管理方式),所以设置这三个为复用功能 IO。2 e" M# q- Z% ], k& o
使能 SPI2 时钟的方法为:. g$ s' z$ C) J: j2 p
3 ^+ W7 S$ |+ j
  1. __HAL_RCC_SPI2_CLK_ENABLE(); //使能 SPI2 时钟
复制代码
, ~& F) O1 H% K" a# c
复用 PB13,14,15 为 SPI2 引脚通过 HAL_GPIO_Init 函数实现,代码如下:! x1 A: ]6 r# Y% }7 H! R+ t
5 ~, P- n& b+ r9 D' ?; n. D3 r( c
  1. GPIO_Initure.Pin=GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;( `7 x" i1 G+ v. O, U
  2. GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
    ' C) v6 o& @( Y1 F6 M; a9 J
  3. GPIO_Initure.Pull=GPIO_PULLUP; //上拉! \% x9 h, F# C" a4 B  _: t5 f* u! s
  4. GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //快速
    + Z" k: L1 ]2 O6 {6 l
  5. HAL_GPIO_Init(GPIOB,&GPIO_Initure);
复制代码

$ y0 y7 S* c5 T2.设置 SPI2 工作模式! J/ D) V5 @" d' ^, Q6 ]9 r/ n
这一步全部是通过 SPI2_CR1 来设置,我们设置 SPI2 为主机模式,设置数据格式为 8 位,然后通过 CPOL 和 CPHA 位来设置 SCK 时钟极性及采样方式。并设置 SPI2 的时钟频率(最大18Mhz),以及数据的格式(MSB 在前还是 LSB 在前)。在 HAL 库中初始化 SPI 的函数为:
: @& L# B5 U; X- r5 P9 r* Q) t0 _2 h. @$ x  S+ c$ I
  1. HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
复制代码

( W( Z5 E6 j: rSPI 初始化实例代码如下:
* K5 w! B7 O& {0 s! O8 v
& ]8 O+ h& Y) d, \
  1. SPI1_Handler.Instance= SPI2; // SPI2% I4 E% r" F- J- j! b8 d- n
  2. SPI1_Handler.Init.Mode=SPI_MODE_MASTER; //设置 SPI 工作模式,设置为主模式
    ' U2 {; d# I  e! N  m& n
  3. SPI1_Handler.Init.Direction=SPI_DIRECTION_2LINES;
    ' d% ^  V' F/ C0 J9 j
  4. //设置 SPI 单向或者双向的数据模式:SPI 设置为双线模式
    - a- C, |( s; l
  5. SPI1_Handler.Init.DataSize=SPI_DATASIZE_8BIT;   @) a8 F5 @6 L( i
  6. //设置 SPI 的数据大小:SPI 发送接收 8 位帧结构) E) n1 I8 g4 X" O# P0 O) ?4 w( ~
  7. SPI1_Handler.Init.CLKPolarity=SPI_POLARITY_HIGH; 1 q$ Z$ s" d3 _  B4 w$ L" A: p  q
  8. //串行同步时钟的空闲状态为高电平
    % M7 d9 d* R3 K. C3 n
  9. SPI1_Handler.Init.CLKPhase=SPI_PHASE_2EDGE; % ]1 K" g0 {* C- F+ a4 {) f
  10. //串行同步时钟的第二个跳变沿(上升或下降)数据被采样0 N3 _. H7 V% j& N5 q8 u
  11. SPI1_Handler.Init.NSS=SPI_NSS_SOFT; //NSS 信号由硬件(NSS 管脚)还是软件, J% h+ S5 G! j8 P: h
  12. //(使用 SSI 位)管理:内部 NSS 信号有 SSI 位控制
    : x/ }; d, Q3 Y: W4 V  [% n3 K
  13. SPI1_Handler.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;
    . R/ m5 b3 c8 w3 s
  14. //定义波特率预分频的值:波特率预分频值为 2560 u$ b( i; l3 V* L( d/ U
  15. SPI1_Handler.Init.FirstBit=SPI_FIRSTBIT_MSB;
    / W% G, d; B7 z. p  B% b; r
  16. //指定数据传输从 MSB 位还是 LSB 位开始:数据传输从 MSB 位开始
    ) O: T6 V( p! T9 k( M
  17. SPI1_Handler.Init.TIMode=SPI_TIMODE_DISABLE; //关闭 TI 模式
    5 c% [& t/ q. A- e
  18. SPI1_Handler.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;
    $ D7 r+ t" k+ t& y& O
  19. //关闭硬件 CRC 校验
    5 e2 o6 A$ x8 w0 i* t3 C- z, `; x5 Q
  20. SPI1_Handler.Init.CRCPolynomial=7; //CRC 值计算的多项式: z1 K9 }; T% b9 ?7 U7 t! Q8 b
  21. HAL_SPI_Init(&SPI2_Handler);//初始化
复制代码
8 k: _6 ?1 H& h. v4 W# R
同样,HAL 库也提供了 SPI 初始化 MSP 回调函数 HAL_SPI_MspInit,定义如下:
: B5 D) p+ s  K8 G7 p- e/ g/ Y: M% s. M* X6 D
  1. void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi);
复制代码

7 ]% f& i. z0 L" x! |' ^3.使能 SPI24 n6 i. u9 w  U( [: _7 X6 h
$ q! |3 C& M% `8 w% s$ x
  1. __HAL_SPI_ENABLE(&SPI2_Handler); //使能 SPI2
复制代码

0 T6 w' i# H. U4 {  W4.SPI 传输数据2 z8 v2 I, M/ r$ r' E3 @. o
  g8 G8 |1 q+ ~6 Q" B* V) ], E
通信接口当然需要有发送数据和接受数据的函数,HAL 库提供的发送数据函数原型为:, b' i6 x, @2 e" Y6 a

2 I$ Q8 e& K. u; T7 v$ t
  1. HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData,
    , ?8 C+ [8 Z$ U' ?; N
  2. uint16_t Size, uint32_t Timeout);
复制代码

  b1 e7 o. V5 h7 ^! t. z这个函数很好理解,往 SPIx 数据寄存器写入数据 Data,从而实现发送。, M8 u+ X! g) n2 e* x- y
HAL 库提供的接受数据函数原型为:6 \% c% w7 Q- g- l; D1 B: X

! W; P8 ?7 y) ]1 Q
  1. HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData,
    ; R- W. ^  |8 {% m* n7 c4 q
  2. uint16_t Size, uint32_t Timeout);
复制代码
9 ?" _; S1 D! W9 s: d
这个函数也不难理解,从 SPIx 数据寄存器读出接受到的数据。& E' `; E. A' [0 k# x1 [" O
前面我们讲解了 SPI 通信的原理,因为 SPI 是全双工,发送一个字节的同时接受一个字节,发送和接收同时完成,所以 HAL 也提供了一个发送接收统一函数:" {" q" [! S5 ^% q/ k  q
% q2 m3 k; c) J
  1. HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData,) m, W5 N- l. p# N
  2. uint8_t *pRxData, uint16_t Size, uint32_t Timeout);
复制代码
$ W3 r- i( p* w; z3 X
该函数发送一个字节的同时负责接收一个字节。) I; f* p0 L+ [! H3 C' H

! x5 J% `( v/ z$ r4 Z设计实现% z; X. p; y% a2 C$ p, N- @5 Y
1.SPI1的初始化8 `; M. W8 [. U6 n
以下是SPI模块的初始化代码,配置成主机模式。9 T6 p6 ^( ~" a8 b0 D6 K$ P: g

, |! r: I/ b  q
  1. void SPI2_Init(void)
    # N' t0 g$ X5 j
  2. {
    # }& M1 t9 r. n, _/ v& {
  3.     SPI2_Handler.Instance=SPI2;                         //SPI2
    . j2 l2 Z1 n  z0 x
  4.     SPI2_Handler.Init.Mode=SPI_MODE_MASTER;             //设置SPI工作模式,设置为主模式
    ! [! _; a' S) V5 Z* u, G" x
  5.     SPI2_Handler.Init.Direction=SPI_DIRECTION_2LINES;   //设置SPI单向或者双向的数据模式:SPI设置为双线模式
    : W9 C- i6 h; U, Q# y8 Z! C4 W# R
  6.     SPI2_Handler.Init.DataSize=SPI_DATASIZE_8BIT;       //设置SPI的数据大小:SPI发送接收8位帧结构
    8 H: J: I. S: |( k+ x! x( o/ r
  7.     SPI2_Handler.Init.CLKPolarity=SPI_POLARITY_HIGH;    //串行同步时钟的空闲状态为高电平
    ( m0 s  ~! G! g, J4 `
  8.     SPI2_Handler.Init.CLKPhase=SPI_PHASE_2EDGE;         //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    , m5 _6 B& R5 u- e* d& ]
  9.     SPI2_Handler.Init.NSS=SPI_NSS_SOFT;                 //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制: z) y/ f& _) X5 J( l) ~/ K
  10.     SPI2_Handler.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;//定义波特率预分频的值:波特率预分频值为256
    1 ^6 e% d' \) d5 a
  11.     SPI2_Handler.Init.FirstBit=SPI_FIRSTBIT_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    " D4 i& E8 }! U/ N7 ^$ w& \
  12.     SPI2_Handler.Init.TIMode=SPI_TIMODE_DISABLE;        //关闭TI模式
    ! O; @3 ^% d  Y
  13.     SPI2_Handler.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;//关闭硬件CRC校验
    & M/ z: S0 K; R) c7 E
  14.     SPI2_Handler.Init.CRCPolynomial=7;                  //CRC值计算的多项式  C( s% J  T* n4 {& n, }. ^
  15.     HAL_SPI_Init(&SPI2_Handler);//初始化& n- l# ]/ L( R: c  P# k

  16. 0 d& k" i  [* t2 p
  17.     __HAL_SPI_ENABLE(&SPI2_Handler);                    //使能SPI2
    " M! z) y2 N' `5 T6 T7 r

  18. ; |( B8 Z1 H1 I  \+ e" C4 X; _
  19.     SPI2_ReadWriteByte(0Xff);                           //启动传输
    5 ]; l# Z. @! n9 g& u9 X
  20. }
复制代码

0 f/ F: V  ^# B+ R: ]; ?底层驱动,时钟使能,引脚配置6 |0 {2 X" C# E6 m
9 k6 ~6 C2 K5 H& g2 `
  1. void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
    . o* r9 H# _- [
  2. {2 O+ U* E5 i" U! C, S2 r! {" j
  3.     GPIO_InitTypeDef GPIO_Initure;
    # P7 i. k5 y9 L3 D6 R) D, w% `

  4. 5 U: {9 W" E1 q9 y* b' k" _
  5.     __HAL_RCC_GPIOB_CLK_ENABLE();       //使能GPIOB时钟$ B2 R7 h% N; z' X
  6.     __HAL_RCC_SPI2_CLK_ENABLE();        //使能SPI2时钟4 f9 W! f! m3 J* Z& Q

  7. 3 u; Q3 {* S  l6 s" o6 U% J
  8.     //PB13,14,15
    - p7 [6 k: b4 `* D5 v
  9.     GPIO_Initure.Pin=GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
    4 r2 \8 T% {  F( f
  10.     GPIO_Initure.Mode=GPIO_MODE_AF_PP;              //复用推挽输出# y% j* P$ [! s4 Q$ G% E9 f
  11.     GPIO_Initure.Pull=GPIO_PULLUP;                  //上拉* S( N7 m! \. D7 A0 D) a. F, M/ Z) ?
  12.     GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;        //快速            ' g5 N, w; G% e) _3 Z* s/ j
  13.     HAL_GPIO_Init(GPIOB,&GPIO_Initure);. C* C6 I, s4 B( h9 [2 p/ x
  14. }
复制代码

/ v3 e& ~, l+ u# V& ^+ ^3.SPI速度设置函数
8 p3 v4 \! _& Z
  1. void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)2 \; ~8 L( Q  L$ ]# N% o
  2. {
    & }& S* R- \. q* ]) C4 z
  3.     assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
      q$ C) }, ?& x1 g- `
  4.     __HAL_SPI_DISABLE(&SPI2_Handler);            //关闭SPI
    3 V- C' Z& W& s( `: j' j9 L. R
  5.     SPI2_Handler.Instance->CR1&=0XFFC7;          //位3-5清零,用来设置波特率' I" |( O4 B* v8 B. K/ V2 D3 Y) [
  6.     SPI2_Handler.Instance->CR1|=SPI_BaudRatePrescaler;//设置SPI速度8 c0 b+ T' [& H9 G) n) w
  7.     __HAL_SPI_ENABLE(&SPI2_Handler);             //使能SPI
    ) p3 m: ]0 u! X, t
  8. - a7 t8 j/ r3 g$ h2 a, t
  9. }
复制代码

8 B$ }) R, D+ W$ U" @4.读写一个字节
: X: `( d& R# n. v9 G: ?/ j
  1. //TxData:要写入的字节
    8 U/ ~7 t2 `/ b
  2. //返回值:读取到的字节
    ! w5 R. b  @/ Z/ q% v$ G: m. R4 R4 Q
  3. u8 SPI2_ReadWriteByte(u8 TxData)
    9 d$ k8 j% ]/ X/ p6 V, ]% x
  4. {, m! T/ O, h9 m/ o! N
  5.     u8 Rxdata;) r5 D1 b7 D) y. j
  6.     HAL_SPI_TransmitReceive(&SPI2_Handler,&TxData,&Rxdata,1, 1000);       1 c' o6 b+ G6 T7 V1 f  Z' d' c
  7.    return Rxdata;                  //返回收到的数据    2 c6 `. u/ ^4 m) r$ G0 I
  8. }
复制代码
; C, L/ P9 W: X
转载自: 跋扈洋
0 |% e5 y7 E. U5 [2 x4 a; D7 d6 O  z6 C. R7 D1 ]
4 U- w" P5 Y. z: L* V7 [
收藏 评论0 发布时间:2022-11-30 14:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版