SPI通讯

本文详细介绍了SPI物理层的连接方式和功能,包括SCK、MOSI、MISO和NSS的作用。进一步阐述了SPI协议层的基本通讯过程、数据有效性、CPOL/CPHA设置以及与I2C的比较,指出SPI在高速应用中的优势和I2C在低速场景的适用性。



前言

本章可与I2C章节对比阅读,体会两种通讯总线差异。本章主要对SPI协议层进行总结。

一、介绍SPI通讯

基本概念

SPI通讯是一种串行数据传输接口,通常用于在数字系统中通过高速(Mbps)的方式进行短距离通信。它被广泛应用于嵌入式系统、传感器网络、外围设备等领域。
SPI通讯设备之间的常用连接方式见下图。

工作原理

SPI通讯使用3条总线及片选线,3条总线分别为SCKMOSIMISO,片选线为NSS ,它们的作用介绍如下:
SCK (Serial Clock):时钟信号线,用于通讯数据同步。
MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。
MISO(Master Input,Slave Output):主设备输入/从设备输出引脚。
NSS(Slave Select):从设备选择信号线,即有几个从设备就有几个NSS引脚。
在这里插入图片描述

二、总线结构

基本通讯过程

这是一个主机的通讯时序。NSS、SCK、MOSI信号都由主机控制产生,而MISO的信号由从机产生。

先发送高位再发送低位,发送完一个字节之后无需应答即可开始下个字节,NSS片选从设备作为起始/终止信号

在这里插入图片描述

数据有效性

  1. 在时钟线SCK上升沿/下降沿(CPOL=0/1)时,MOSI及MISO数据传输一位 数据传输时
  2. MSB先行或LSB先行并没有作硬性规定,但要保证两个SPI通讯设备之间使用同样的协定
  3. SPI每次数据传输可以8位或16位为单位,每次传输的单位数不受限制

CPOL/CPHA及通讯模式

SPI一共有四种通讯模式,如图

CPOLCPHA空闲时SCK时钟采样时刻
00低电平奇数边沿
01低电平偶数边沿
10高电平奇数边沿
11高电平偶数边沿

代码分析

  1. 打开SPI 总线的设备文件
  2. 设置SPI 工作模式
  3. 设置SPI通信过程中一个字节所占的位数(8)
  4. 设置SPI通信的波特率(500K)
static  uint32_t mode = SPI_MODE_2;    //用于保存 SPI 工作模式
static  uint8_t bits = 8;        // 接收、发送数据位数
static  uint32_t speed = 500000; // 发送速度

/*
* 初始化SPI
*/
void spi_init(void)
{
  int ret = 0;
  /*-------------------第一部分-------------------------*/
  /*打开 SPI 设备*/
  fd = open("/dev/spidev2.0", O_RDWR);
  if (fd < 0)
  {
    pabort("can't open /dev/spidev2.0 ");
  }

  /*-------------------第二部分-------------------------*/
  /*
      * spi mode 设置SPI 工作模式
      */
  ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
  if (ret == -1)
    pabort("can't set spi mode");

  ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
  if (ret == -1)
    pabort("can't get spi mode");

  /*-------------------第三部分-------------------------*/
  /*
      * bits per word  设置一个字节的位数
      */
  ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
  if (ret == -1)
    pabort("can't set bits per word");

  ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
  if (ret == -1)
    pabort("can't get bits per word");

  /*-------------------第四部分-------------------------*/
  /*
      * max speed hz  设置SPI 最高工作频率
      */
  ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
  if (ret == -1)
    pabort("can't set max speed hz");

  ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
  if (ret == -1)
    pabort("can't get max speed hz");

  printf("spi mode: 0x%x\n", mode);
  printf("bits per word: %d\n", bits);
  printf("max speed: %d Hz (%d KHz)\n", speed, speed / 1000);
}

SPI发送:将要发送的数据填入结构体spi_ioc_transfer,然后调用ioctl函数执行发送

void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{
  int ret;
  /*------------------第一部分--------------------*/
  struct spi_ioc_transfer tr = {
      .tx_buf = (unsigned long)tx,
      .rx_buf = (unsigned long)rx,
      .len = len,
      .delay_usecs = delay,
      .speed_hz = speed,
      .bits_per_word = bits,
      .tx_nbits = 1,
      .rx_nbits = 1
  };

  /*------------------第二部分--------------------*/
  ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
  if (ret < 1)
  {
    pabort("can't send spi message");
  }
}
/*-------------------第一部分---------------------*/
/*SPI 接收 、发送 缓冲区*/
unsigned char tx_buffer[100] = "hello the world !";
unsigned char rx_buffer[100];

/*-------------------第二部分---------------------*/
int main(int argc, char *argv[])
{
     /*初始化SPI */
     spi_init();

     /*执行发送*/
     transfer(fd, tx_buffer, rx_buffer, sizeof(tx_buffer));

     /*打印 tx_buffer 和 rx_buffer*/
     printf("tx_buffer: \n %s\n ", tx_buffer);
     printf("rx_buffer: \n %s\n ", rx_buffer);

     close(fd);

     return 0;
}

三、SPI与I2C对比

  1. I2C速率:标准: 100 KHz、快速: 400 KHz、快速+: 1 MHz、高速: 3.4 MHz
    SPI速率:主要取决于 CPU 的 SPI 控制器时钟(clock)。常用设备比如STM32F103 最高支持 18 MHzi.MX6ULL 最高支持 52 MHzW25Q128 最高支持 80 MHz
    因此 I2C适用于低速外围设备,多用于传输命令SPI适用于高速应用,如SD卡、液晶屏多用于传输数据
  2. IIC为半双工,SPI为全双工
  3. IIC有应答机制信号,SPI无
  4. IIC的时钟和极性是固定的(空闲时高电平,SCL为高电平时读取数据),SPI是可以变化设置的
  5. 寻址方式不同,IIC是发送一个字节去寻址,SPI通过片选位选择

### SPI通信协议的基本原理 SPI(Serial Peripheral Interface)是一种同步串行通信协议,广泛用于嵌入式系统中,以实现微控制器与外围设备之间的高速数据交换。SPI协议的基本工作原理基于主从架构,其中主设备(通常是微控制器)控制通信的时序和方向,而从设备(如传感器、存储器、显示器等)响应主设备的请求[^2]。 SPI通信涉及四条信号线: - **MOSI(Master Out Slave In)**:主设备向从设备发送数据的线路。 - **MISO(Master In Slave Out)**:从设备向主设备发送数据的线路。 - **SCLK(Serial Clock)**:由主设备生成的时钟信号,用于同步数据传输。 - **SS/CS(Slave Select/Chip Select)**:用于选择特定的从设备进行通信的线路。 数据在时钟信号的上升沿或下降沿被采样,具体取决于配置的SPI模式。SPI支持全双工通信,这意味着数据可以在两个方向上同时传输,从而提高了通信效率[^1]。 ### SPI通信的四种工作模式 SPI协议定义了四种不同的工作模式,这些模式由两个参数决定:CPOL(Clock Polarity)和CPHA(Clock Phase)[^3]。 - **CPOL** 决定了时钟信号在空闲状态时的电平(0或1)。 - **CPHA** 决定了数据是在时钟的上升沿还是下降沿被采样。 根据CPOL和CPHA的不同组合,SPI可以配置为以下四种模式: - **模式0**:CPOL=0,CPHA=0 —— 时钟空闲时为低电平,数据在上升沿被采样。 - **模式1**:CPOL=0,CPHA=1 —— 时钟空闲时为低电平,数据在下降沿被采样。 - **模式2**:CPOL=1,CPHA=0 —— 时钟空闲时为高电平,数据在下降沿被采样。 - **模式3**:CPOL=1,CPHA=1 —— 时钟空闲时为高电平,数据在上升沿被采样。 选择正确的SPI模式对于确保主设备和从设备之间的正确通信至关重要。不同的外设可能需要不同的SPI模式,因此在设计系统时需要仔细考虑这一点。 ### SPI通信的应用实例 SPI协议因其高速率和简单性而被广泛应用于各种嵌入式系统中。常见的应用包括: - **传感器接口**:SPI常用于连接温度、湿度、加速度等传感器,以获取实时数据。 - **存储器访问**:SPI可用于访问外部Flash存储器或EEPROM,实现数据的读写操作。 - **显示屏驱动**:许多LCD或OLED显示屏使用SPI接口与微控制器通信,以显示图形或文本信息。 - **音频设备控制**:SPI可用于控制音频编解码器(Codec),实现音频信号的输入和输出。 例如,在使用SPI连接一个温度传感器时,微控制器作为主设备,通过SPI接口发送命令以启动温度测量,并接收传感器返回的温度数据。整个过程可以在几微秒内完成,确保了实时性和高效性[^2]。 ### SPI通信的优点与缺点 SPI通信协议的主要优点包括: - **高速率**:SPI支持较高的数据传输速率,通常可达几Mbps甚至更高,适合需要快速数据传输的应用。 - **简单灵活**:SPI协议的硬件实现相对简单,且支持多种数据格式和通信模式。 - **实时性强**:由于SPI是同步通信协议,数据传输的时序非常精确,适合实时控制系统。 然而,SPI也存在一些局限性: - **引脚数量较多**:SPI需要至少四根信号线,这在引脚资源有限的系统中可能会成为问题。 - **线缆长度限制**:由于SPI是短距离通信协议,线缆长度通常受到限制,不适合长距离的数据传输。 尽管如此,通过合理的设计和配置,SPI通信协议仍然能够满足大多数嵌入式应用的需求,并提供可靠的数据传输[^1]。 ### 示例代码:SPI通信的基本实现 以下是一个简单的SPI通信示例,展示了如何使用Python的`spidev`库与一个SPI设备进行通信。假设我们正在与一个温度传感器进行通信,该传感器通过SPI接口返回温度数据。 ```python import spidev # 打开SPI设备 spi = spidev.SpiDev() spi.open(0, 0) # 总线0,设备0 # 设置SPI模式和时钟频率 spi.mode = 0 spi.max_speed_hz = 1000000 # 1 MHz # 发送命令并读取数据 def read_temperature(): # 向传感器发送读取温度的命令 command = [0x01] # 假设0x01是读取温度的命令 response = spi.xfer2(command) # 发送命令并接收响应 return response[0] # 返回温度值 # 主程序 if __name__ == "__main__": temp = read_temperature() print(f"当前温度: {temp}°C") ``` 此代码演示了如何初始化SPI设备、设置通信参数,并通过SPI接口与外部设备进行数据交换。实际应用中,具体的命令和数据格式将取决于所使用的外设规格。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值