详解Linux SPI通信实例 (linux spi实例)

SPI(Serial Peripheral Interface)作为一种常用的外设通信协议,被广泛应用于嵌入式系统中。在Linux中,也提供了完整的SPI驱动支持,本文将结合实例详解Linux下的SPI通信流程和实现方法。

一、硬件连接

在实现SPI通信前,需要先完成硬件的连接。在SPI通信中,分为主设备和从设备之分。主设备在数据传输时控制时序,从设备则被动接收和发送数据。在主设备和从设备之间,需要通过四根连接线进行串行数据传输,具体如下:

* MOSI(Master Output Slave Input):主设备输出,从设备输入

* MISO(Master Input Slave Output):主设备输入,从设备输出

* SCLK(Serial Clock):由主设备产生的时钟信号,控制数据传输速度

* SS(Slave Select):从设备选择信号,用于选择通信的从设备

在连接硬件时,需要将主设备的MOSI连接到从设备的MOSI,主设备的MISO连接到从设备的MISO,主设备的SCLK连接到从设备的SCLK,主设备的SS连接到从设备的SS。

二、SPI驱动框架

Linux中的SPI驱动分为两个部分,一部分是核心部分,另一部分是SPI设备的驱动部分。

核心部分:

在核心部分中,主要包括SPI驱动核心模块(spi-core),负责管理SPI总线和SPI从设备的注册和注销以及SPI数据传输。

SPI从设备驱动部分:

SPI从设备驱动部分主要是根据硬件连接和核心的SPI驱动模块进行交互,负责向核心模块注册从设备并实现对从设备的控制和数据传输功能。

三、SPI总线注册

在使用SPI接口进行通信时,首先需要在Linux中注册SPI总线。SPI设备驱动通常存放于linux/drivers/spi目录下,各个驱动之间彼此独立。有关每一个SPI设备的驱动程序需要负责向Linux kernel注册一个spi_driver结构体变量,以便在需要时调用这些函数。

struct spi_driver {

int (*probe)(struct spi_device *);//硬件连接时,初始化设备

int (*remove)(struct spi_device *);//撤销设备时的回调函数

struct device_driver driver;

const struct spi_device_id *id_table;//注册设备ID

};

– probe函数:在硬件设备匹配到驱动时被调用,负责初始化设备。

– remove函数:在设备被撤销时调用的回调函数。

– id_table:存放设备ID的数组结构,通过id_table可以判断设备是否支持当前的驱动程序。

四、SPI设备注册

在SPI总线完成注册后,需要在Linux中注册每一个SPI从设备。SPI设备驱动通常存放于linux/drivers/spi目录下,各个驱动之间彼此独立。而每一个SPI从设备驱动程序也需要负责向Linux kernel注册一个spi_driver结构体变量。

设备注册分两个步骤:

1. 生产spi_device

在Linux中为每一个从设备分配一个spi_device结构体,用于存储spi_driver和硬件相关的信息。在使用spi_register_device函数之前,需要先创建spi_device结构体并初始化相关的成员参数。

static struct spi_device *spi_validate_device(struct spi_master *master,

struct spi_device *spi)

{

//kzalloc 分配一个 spi_device 个大小的空间到进行初始化

spi = kzalloc(sizeof(struct spi_device), GFP_KERNEL);

//初始化spi_device的成员变量

spi->master = master;

spi->dev.parent = &master->dev;

spi->dev.driver = &spi->driver;

spi->max_speed_hz = 500000;

spi->mode = SPI_MODE_0;

spi->bits_per_word = 8;

return spi;

}

2. 注册spi_device

调用spi_register_device函数,将从设备注册到核心SPI总线。spi_register_device函数包含两个参数:spi_device和spi_driver。

int spi_register_device(struct spi_device *spi)

{

struct spi_master *master = spi->master;

struct spi_drv_data *drv_data = master->drv_data;

spi_setup(spi);

spin_lock(&drv_data->device_lock);

list_add(&spi->device_entry, &drv_data->device_list);

spin_unlock(&drv_data->device_lock);

spi_dev_put(spi);

return 0;

}

五、数据传输

在设备注册完成后,即可进行SPI的读写操作。SPI的数据传输操作分为两种:全双工(full duplex)和半双工(half duplex)。

1. 全双工数据传输

主从设备同步进行全双工的SPI通信。只通过一个clock线控制数据的传输。主设备在源码中对全双工SPI传输方式的设备进行传输时,首先通过spi_transfer函数对一次数据传输的相关参数进行设置,然后通过spi_sync函数进行数据的传输和同步等待操作。

struct spi_transfer {

char *tx_buf;//发送缓存区

char *rx_buf;//接收缓存区

unsigned len;//传输数据长度

unsigned speed_hz;//传输速度

unsigned bits_per_word;//每个字的位数

u16 delay_usecs;//传输间隔

unsigned cs_change;//传输开始之前,先将C进行操作

void *tx_dma;//发送缓存区DMA

void *rx_dma;//接收缓存区DMA

void (*rx_sg)();//scatter-gather 变量的内核提供者

void (*tx_sg)();//scatter-gather 变量的内核提供者

};

int spi_sync(struct spi_device *spi, struct spi_message *msg)

{

mutex_lock(&spi->master->bus_lock);

status = _spi_sync(spi, msg, spi_transfer);

mutex_unlock(&spi->master->bus_lock);

}

2. 半双工数据传输

半双工传输是主从设备顺序半双工传输,一次仅仅进行发送和接收其中的一个操作。主设备在源码中对半双工SPI传输方式的设备进行传输时,分别将SPITRANSF_WRITE和SPITRANSFER_READ两个参数传递给spi_message,分别进行发送和接收的操作。

struct spi_message {

struct list_head transfer_list;

unsigned is_dma_mapped:1; // DMA mapping related

bool is_poll:1;

};

int spi_sync(struct spi_device *spi, struct spi_message *msg)

{

// …

status = _spi_sync(spi, msg, spi_transfer);

// …

}

六、

本文结合实例详解了Linux下的SPI通信流程和实现方法,包括硬件连接、SPI驱动框架、SPI总线注册以及数据传输等多个方面。希望本文能为Linux应用开发者提供借鉴和参考,方便大家在嵌入式系统中使用SPI进行通信。


数据运维技术 » 详解Linux SPI通信实例 (linux spi实例)