20221212-SPI协议详解

https://zhuanlan.zhihu.com/p/290620901

UART 没有时钟信号,无法控制何时发送数据,也无法保证双方按照完全相同的速度接收数据,因此,双方以不同的速度进行数据接收和发送,就会出现问题

如果要解决这个问题,uart 为每个字节添加额外的起始位和停止位,以帮助接收器在数据到达时进行同步

双方还必须事先就传输速度达成共识,设置相同的波特率 例如每秒9600位

传输速率如果有微小差异不是问题,因为接收器会在每个字节的开头重新同步

如果您注意到上图中的11001010不等于0x53,这是一个细节。串口协议通常会首先发送最低有效位,因此最小位在最左边LSB。低四位字节实际上是0011 = 0x3,高四位字节是0101 = 0x5。

异步串行工作得很好,但是在每个字节发送的时候都需要额外的起始位和停止位 以及在发送和接收数据所需的复杂硬件方面都有很多开销

不难发现,如果接收端和发送端设置的速度都不一致,那么接收到的数据将是垃圾 乱码

SPI 通讯协议

spi 是一个同步的数据总线,也就是说它是用单独的数据线和一个单独的时钟信号来保证发送端和接收端的完美同步

时钟是一个振荡信号,它告诉接收端在确切的时机对数据线上的信号进行采样

产生时钟的一侧称为主机,另一侧为从机,总是只有一个主机 一般来说可以是微控制器 MCU 但是可以有多个从机

数据的采集时机可能是时钟信号的上升沿 或下降沿

具体要看对 spi 的配置

整体的传输大概可以分为以下几个过程

主机先将NSS 信号拉低,这样保证开始接收数据

当接收端检测到时钟的边沿信号时,它将立即读取数据线上的信号,这样就得到了一位数据 1bit

由于时钟是随数据一起发送的,因此指定数据的传输速度并不重要,尽管设备将具有可以运行的最高速度

主机发送到从机时:主机产生相应的时钟信号,然后数据一位一位地将从MOSI信号线上进行发送到从机;
主机接收从机数据:如果从机需要将数据发送回主机,则主机将继续生成预定数量的时钟信号,并且从机会将数据通过MISO信号线发送;

注意 spi 是全双工 具有单独的发送和接收线路,因此可以在同一时间发送和接收数据,另外 spi 的接收硬件可以是一个简单的移位寄存器,这笔异步串行通信所需的完整uart 要简单得多,并且更加便宜

spi 特性

spi 总线包括4条 逻辑线,定义如下

MISO 主机输入,从机输出,数据来自从机

MOSI 主机输出,从机输入

SCLK 串行时钟信号,由主机产生发送给从机

SS 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号

其他制造商可能会遵循其他命名规则,但是最终他们指的相同的含义。以下是一些常用术语;

MISO也可以是SIMO,DOUT,DO,SDO或SO(在主机端);
MOSI也可以是SOMI,DIN,DI,SDI或SI(在主机端);
NSS也可以是CE,CS或SSEL;
SCLK也可以是SCK;
本文将按照以下命名进行讲解[MISO, MOSI, SCK,NSS]

时钟频率

SPI 总线上的主机必须在通信开始时候配置并生成相应的时钟信号,在每个SPI 时钟周期内,都会发生全双工数据传输

主机在mosi 线上发送一位数据,从机读取它,而从机在miso 线上发送一位数据,主机读取它

就算只进行单向的数据传输,也要保持这样的顺序,这就意味着无论接收任何数据,必须实际发送一些东西,在这种情况下,我们称其为虚拟数据

从理论上讲,只要实际可行,时钟速率就可以是您想要的任何速率,当然这个速率受限于每个系统能提供多大的系统时钟频率,以及最大的SPI传输速率。

时钟极性 CKP Clock Polarity

除了配置串行时钟频率 外,SPI 主设备还需要配置时钟极性

根据硬件制造商的命名规则不同,时钟极性通常写为CKP或CPOL。时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据;

CKP 可以配置为1或0 ,这意味着您可以根据需要将时钟的默认状态 IDLE 设置为高或低,极性反转可以通过简单的逻辑逆变器实现,您必须参考设备的数据手册才能正确设置CKP 和CKE

CKP = 0:时钟空闲IDLE为低电平0;
CKP = 1:时钟空闲IDLE为高电平1;

时钟相位 CKE Clock Phase Edge

除配置串行时钟速率和极性外,SPI 主设备还应配置时钟相位,根据硬件制造商的不同,时钟相位通常写为CKE 或CPHA

顾明思义,时钟相位/边沿,也就是采集数据时是在时钟信号的具体相位或者边沿

CKE = 0 在时钟信号SCK 的第一个跳变沿采样

CKE = 1 在时钟信号SCK 的第二个跳变沿采样

时钟配置总结

综上几种情况,下图总结了所有时钟配置组合,并突出显示了实际采样数据的时刻

其中黑色线为采样数据的时刻

蓝色线为SCK 时钟信号

具体如下图所示

模式编号

SPI 的时钟极性和相位的配置通常称为SPI 模式,所有可能的模式都遵循以下约定,具体如下表所示

除此之外,我们还应该仔细检查微控制器数据手册中包含的模式表,以确保一切正常

多从机模式

前面说到的SPI 总线必须有一个主机,可以有多个从机,那么具体连接到SPI 总线的方法有以下两种

第一种方法 多NSS

通常,每个从机都需要一条单独的SS 线

如果要和特定的从机进行通讯,可以将相应的NSS 信号线拉低,并保持其他NSS 信号线的状态为高电平,如果同时将两个NSS 信号线拉低,则可能会出现乱码,因为从机可能都试图在同一条MISO 线上传输数据,最终导致接收数据乱码

具体连接方式如下图所示

第二种方法 菊花链

在数字通信世界中,在设备信号 (总线信号或中断信号) 以串行的方式从一个设备依次传到下一个设备,不断循环直到数据到达目标设备的方式被称为菊花链

  1. 菊花链的最大缺点是因为信号串行传输,所以一旦数据链路中的某设备发生故障的时候,它下面优先级较低的设备就不可能得到服务了

另一方面,距离主机越远的从机,获得服务的优先级越低,所以需要安排好从机的优先级,并且设置总线检测器,如果某个从机超时,则对该从机进行短路,防止单个从机损坏造成整个链路崩溃的情况

具体的连接如下图所示

其中红线加粗为数据的流向

所以最终的数据流向图可以表示为

SCK 为时钟信号,8clks 表示8个边沿信号

其中D 为数据,X 为无效数据

所以不难发现,菊花链模式充分使用了SPI 其移位寄存器的功能,整个链充当通信移位寄存器,每个从机在下一个时钟周期将输入数据复制到输出

优缺点

SPI 通讯的优势

使SPI 作为串口通信接口脱颖而出的原因很多

全双工串行通信;
高速数据传输速率。
简单的软件配置;
极其灵活的数据传输,不限于8位,它可以是任意大小的字;
非常简单的硬件结构。从站不需要唯一地址(与I2C不同)。从机使用主机时钟,不需要精密时钟振荡器/晶振(与UART不同)。不需要收发器(与CAN不同)。

SPI 的缺点

没有硬件从机应答信号 主机可能在不知情的情况下无处发送

通常仅支持一个主设备

需要更多的引脚 与I2C 不同

没有定义硬件级别的错误检查协议

与RS232 和CAN 总线相比,只能支持非常短的距离

编程实现
下面是通过STM32的cubemx自动生成的HAL库代码,比较简单,截取了其中一部分,具体如下;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER; //主机模式
hspi1.Init.Direction = SPI_DIRECTION_2LINES; //全双工
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; //数据位为8位
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; //CPOL=0
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; //CPHA为数据线的第一个变化沿
hspi1.Init.NSS = SPI_NSS_SOFT; //软件控制NSS
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2分频,32M/2=16MHz
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; //最高位先发送
hspi1.Init.TIMode = SPI_TIMODE_DISABLE; //TIMODE模式关闭
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//CRC关闭
hspi1.Init.CRCPolynomial = 10; //默认值,无效
if (HAL_SPI_Init(&hspi1) != HAL_OK) //初始化
{
_Error_Handler(__FILE__, __LINE__);
}
}

//发送数据
HAL_StatusTypeDef
HAL_SPI_Transmit(SPI_HandleTypeDef *hspi,
uint8_t *pData,
uint16_t Size,
uint32_t Timeout);
//接收数据
HAL_StatusTypeDef
HAL_SPI_Receive(SPI_HandleTypeDef *hspi,
uint8_t *pData,
uint16_t Size,
uint32_t Timeout);

% SPI、UART、RS232、RS485、IIC 5

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2024 TeX_baitu
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~