详解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进行通信。