SPI通信接口(c/c++)

本文详细介绍了SPI(串行外设接口)协议,包括其工作原理、特点,以及如何通过Linuxspidev接口进行参数设置,如传输模式、位对齐、每字位数和最大速度。还对比了半双工和全双工通信方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、SPI介绍

1. SPI特点

二、SPI参数设置

1. SPI传输模式

2. 位对齐方式

3. 每字位数

4. 最大传输速度

三、通信


一、SPI介绍

SPI:Serial Peripheral Interface的缩写,即为串行外设接口,是一种在主控板和交采底板之间传输数据的常用通信协议。SPI 协议采用全双工的通信方式,同时支持多路从设备。

1. SPI 协议使用四根线进行通信:

  • 时钟线(SCLK):用于同步通讯数据,由主控板产生,决定了通讯的速率。
  • 主设备输出从设备输入的 MOSI 线
  • 主设备输入从设备输出的 MISO 线
  • 片选线(SS):当有多个SPI从设备与SPI主机相连时,设备的其它信号线SCK、MOSI及MISO同时并联到相同的SPI总线上,即无论有多少个从设备,都共同使用这3条总线;而每个从设备都有独立的这一条CS_N信号线,本信号线独占主机 的一个引脚,即有多少个从设备,就有多少条片选信号线。

在 SPI 协议中,主设备始终是传输数据的发起者,从设备则是被动响应的对象。SPI 协议具有简单、高速的特点,适用于小规模的数据传输。

spidev.h:是一个位于Linux内核空间的接口程序。其作用是充当用户程序与内核空间SPI核心底层驱动之间的桥梁。通过spidev接口,开发者可以直接在用户空间进行程序开发以调用底层SPI驱动,这大大降低了SPI驱动开发的门槛。

在编写应用程序时需要使用ioctl函数设置spi相关配置,其函数原型如下:

 #include <sys/ioctl.h>

 int ioctl(int fd, unsigned long request, ...);

1. SPI特点

优点:

  • 全双工串行通信;使用ioctl实现
  • 高速数据传输速率。
  • 极其灵活的数据传输,不限于8位,它可以是任意大小的字;
  • 从站不需要唯一地址。从机使用主机时钟,不需要精密时钟振荡器/晶振。不需要收发器

缺点:

  • 没有硬件从机应答信号(主机可能在不知情的情况下无处发送);
  • 通常仅支持一个主设备;
  • 需要更多的引脚;
  • 没有定义硬件级别的错误检查协议;
  • 与RS-232和CAN总线相比,只能支持非常短的距离;
     

二、SPI参数设置

1. SPI传输模式

SPI 4种模式:( 主机与从机需要工作在相同的模式下才可以正常通信)

  • 模式0:CPOL= 0,CPHA=0。
  • 模式1:CPOL= 0,CPHA=1。
  • 模式2:CPOL= 1,CPHA=0。
  • 模式3:CPOL= 1,CPHA=1。

CPOL:表示设备未被选中的空闲状态时,串行时钟SCK的电平状态,CPOL = 0,空闲状态时SCK为低电平,CPOL = 1,空闲状态时SCK为高电平。

CPHA:表示数据采样是在SCK时钟的奇数边沿还是偶数边沿,CPHA = 0,数据采样是在SCK时钟的奇数边沿,CPHA = 1,数据采样是在SCK时钟的偶数边沿。

编写应用程序:

使用SPI_IOC_RD_MODE和SPI_IOC_WR_MODE请求,可以获取或设置SPI传输模式。这些请求需要一个指向字节(byte)的指针,该字节将存储或接收SPI模式。

SPI_IOC_RD_MODE:获取当前的SPI模式

SPI_IOC_WR_MODE:设置新的SPI模式

#include </linux/spi/spidev.h>
#include <linux/ioctl.h>
#indelux <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

const char* dev = "dev/devide.2.0";

void spi_set_mode(int fd,int mode)
{
    ioctl(fd,SPI_IOC_WR_MODE,&mode);
}

void main()
{
    int fd = open(dev,O_RDWR);
    spi_set_mode(fd,1);
    close(fd);
}

2. 位对齐方式

数据传输的位对齐方式是一个重要的参数。位对齐方式决定了在SPI字(通常为8位或16位)传输过程中,是最高有效位(MSB)优先还是最低有效位(LSB)优先。

  • MSB-first(最高有效位优先):在这种模式下,每个SPI字的最高有效位(MSB)将首先被发送或读取。这是SPI通信中最常见的配置。
  • LSB-first(最低有效位优先):与MSB-first相反,在LSB-first模式下,每个SPI字的最低有效位(LSB)会首先被发送或读取。

SPI_IOC_RD_LSB_FIRST和SPI_IOC_WR_LSB_FIRST,可用于获取或设置位对齐方式。

SPI_IOC_RD_LSB_FIRST:读取位对齐方式

SPI_IOC_WR_LSB_FIRST:设置位对齐方式

数值为0,表示MSB-first,也是默认的设定值。当数值为非0时,表示很少使用的LSB-first。

#include </linux/spi/spidev.h>
#include <linux/ioctl.h>
#indelux <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct spiBitOrder
{
    MSB_FIRST,
    LSB_FIRST,
}spiBitOrder_t

const char* dev = "dev/devide.2.0";

void spi_set_bit_order(int fd,int bit_order)
{
    ioctl(fd,SPI_IOC_WR_LSB_FIRST,&bit_order);
}

void main()
{
    int fd = open(dev,O_RDWR);
    spi_set_bit_order(fd,MSB_FIRST);
    close(fd);
}    

3. 每字位数

即每个SPI传输字中的位数。通常情况下,默认值为8位。

可SPI_IOC_RD_BITS_PER_WORD和SPI_IOC_WR_BITS_PER_WORD,可以读取或设置每个SPI传输字中的位数。

SPI_IOC_RD_BITS_PER_WORD:读取每个SPI传输字中的位数

SPI_IOC_WR_BITS_PER_WORD:设置每个SPI传输字中的位数

#include </linux/spi/spidev.h>
#include <linux/ioctl.h>
#indelux <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct spiBitOrder
{
    MSB_FIRST,
    LSB_FIRST,
}spiBitOrder_t

const char* dev = "dev/devide.2.0";

void spi_set_bits_per_word(int fd,int bits_per_word)
{
    ioctl(fd,SPI_IOC_WR_BITS_PER_WORD,&bits_per_word);
}

void main()
{
    int fd = open(dev,O_RDWR);
    spi_set_bits_per_word(fd,8);
    close(fd);
}

4. 最大传输速度

需要注意的是,虽然你可以设置一个理想的传输速度,但硬件和驱动程序可能不一定支持你设置的确切速度。

使用SPI_IOC_RD_MAX_SPEED_HZ和SPI_IOC_WR_MAX_SPEED_HZ,可以获取或设置SPI设备的最大传输速度。(以Hz为单位)。

SPI_IOC_RD_MAX_SPEED_HZ:获取SPI设备的最大传输速度

SPI_IOC_WR_MAX_SPEED_HZ:设置SPI设备的最大传输速度

#include </linux/spi/spidev.h>
#include <linux/ioctl.h>
#indelux <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct spiBitOrder
{
    MSB_FIRST,
    LSB_FIRST,
}spiBitOrder_t

const char* dev = "dev/devide.2.0";

void spi_set_max_speed(int fd,int max_speed)
{
    ioctl(fd,SPI_IOC_WR_BITS_PER_WORD,&max_speed);
}

void main()
{
    int fd = open(dev,O_RDWR);
    spi_set_max_speed(fd,200000);
    close(fd);
}

三、通信

两种方式:

(1)使用read、write:这种只能读或写,是半双工的通信方式。

// 执行半双工读写
read(fd, rxbuf, len); 
write(fd, txbuf, len);
//
close(fd); //关闭spidev的通讯通道。

(2)使用ioctl:可以实现全双工的读写。

SPI_IOC_MESSAGE(N) :表示一次进行双向/多次读写操作

#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

// 执行全双工读写的函数,可被调用以进行SPI通讯。
bool transfer(const uint8_t *tx, uint8_t *rx, size_t len) 
{
    struct spi_ioc_transfer tr; //定义在 spidev.h中,描述了一个独立的SPI传输消息
    memset(&tr, 0, sizeof(tr));

    tr.tx_buf = (unsigned long)tx;//指向待发送数据的缓冲区
    tr.rx_buf = (unsigned long)rx;//指向用于接收数据的缓冲区。
    tr.len = len;//传输的字节数。
    tr.delay_usecs = 0;//传输完成后的延迟时间,单位为微秒
    tr.speed_hz = 0;//传输速率。
    tr.bits_per_word = 0;//每个数据字的位数。
    if((ioctl(fd, SPI_IOC_MESSAGE(1), &tr)==-1))
    {
        std::cout<<"transfer error"<<std::endl; //如果ioctl()调用返回-1,则输出一个错误消息。    
        return false;
    }
    return true;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值