SPI
本章介绍 SPI 控制器的相关内容,面向使用 SPI 模块做驱动开发或者用户态程序开发或维护人员。具体包括设备能力,驱动和用户态程序接口,配置流程以及核 心参考代码。
概述
SPI(Serial Peripheral Interface,串行外设接口)是一种同步串行通信协议,主要用于短距离设备间的高速数据交换。常见应用包括微控制器与传感器、存储器、显示屏等外设之间的通信。
SPI 采用主从架构,通过四根信号线实现全双工通信:
SCK:时钟信号(由主设备提供)MOSI:主设备输出,从设备输入MISO:主设备输入,从设备输出CS_N:片选信号(低电平有效)
SPI 不具备寻址机制,通信依赖硬件片选,具备以下特点:
- 硬件结构简单
- 无需握手机制
- 传输速率高(可达数十 MHz 乃至百 MHz 级)
- 广泛应用于 EEPROM、LCD、ADC、触控芯片等嵌入式场景
关键特性
- SPI 通道:系统共支持 6 路 SPI(
SPI0–SPI5)SPI0–SPI4位于非安全空间,可由普通应用访问,支持通用 SPI 功能SPI5位于安全空间,仅供安全模块调用,通常用于控制板载关键芯片,如 PMIC、电源芯片、RTC 等
- 支持主模式:所有 SPI 控制器均支持主模式通信
- 通信引脚:使用
SCK、CS_N、MISO、MOSI四根基 本信号线 - 频率范围:
SCK时钟频率范围为 0–50 MHz,常见值为 25 MHz 和 50 MHz - 数据传输方式:支持中断模式传输(不支持
DMA) - FIFO 缓冲:每通道具有 256-word(32-bit 宽)
FIFO缓冲区
内核文件
SPI 涉及到的内核文件包含以下几个:
| 文件路径 | 用途说明 |
|---|---|
linux-mthreads/drivers/spi/spi-dw-core.c | DesignWare SPI 模块的核心处理文件 |
linux-mthreads/drivers/spi/spi-dw-m1000.c | SPI0–SPI4 模块初始化文件 |
linux-mthreads/drivers/spi/spi-dw-sm-ss.c | SPI5 模块初始化文件(仅供测试使用,非必要不建议在内核中使用 SPI5) |
linux-mthreads/drivers/spi/spi.c | SPI 子系统的通用程序接口 |
linux-mthreads/drivers/spi/spidev.c | SPI 驱动的用户接口,生成字符设备节点供用户态访问 |
linux-mthreads/tools/spi/spidev_test.c | SPI 用户态测试示例程序 |
以用户态程序为例:
- 用户的
spidev_test.c通过/dev目录下的spidev字符设备节点与内核的spidev.c进行通信。 - 内核中的
spidev.c调用spi.c提供的通用SPI接口进行数据读写操作。 spi.c根据初始化时注册的spi-dw-core.c中的函数,完成硬件的初始化和 调度。spi-dw-core.c最终调用对应的硬件相关实现文件spi-dw-m1000.c,执行具体的硬件层面数据读写操作.
API 接口
内核态核心接口
在编写 SPI device 驱动的时候,先保证打开对应的 config 配置文件和设备树相关说明,之后正常调用 Linux SPI 的标准接口即可。
SPI 相关数据结构
| 结构体/宏 | 描述 |
|---|---|
struct spi_device | 代表一个 SPI 设备,包含片选号、最大速率、模式等配置信息。 |
struct spi_driver | SPI 设备驱动结构体,需实现 probe、remove 等方法。 |
struct spi_transfer | 描述一次 SPI 传输的数据(发送/接收缓冲区、长度、速率等)。 |
struct spi_message | 包含多个 spi_transfer 的传输队列,用于组合复杂传输时序。 |
SPI 相关函数
| 函数名 | 描述 | 参数说明 | 备注 |
|---|---|---|---|
spi_setup() | 配置 SPI 设备参数(如模式、速率等)。 | struct spi_device *spi:SPI 设备指针。 | 在 probe 函数中调用。 |
spi_sync() | 同步传输 SPI 数据(阻塞直到完成)。 | struct spi_device *spi,struct spi_message *message:设备和消息指针。 | 需预先构建 spi_message。 |
spi_async() | 异步传输 SPI 数据(立即返回,通过回调通知完成)。 | struct spi_device *spi,struct spi_message *message。 | 需设置 message->complete 回调函数。 |
spi_write() | 向设备写入数据(仅发送)。 | struct spi_device *spi,const void *buf,size_t len。 | 内部封装了 spi_sync。 |
spi_read() | 从设备读取数据(仅接收)。 | struct spi_device *spi,void *buf,size_t len。 | 同样基于 spi_sync。 |
spi_write_then_read() | 先写后读(常用于命令+数据的交互)。 | struct spi_device *spi,const void *txbuf,u32 n_tx,void *rxbuf,u32 n_rx。 | 高效处理常见双阶段操作。 |
spi_message_init() | 初始化 spi_message 结构体。 | struct spi_message *m:消息指针。 | 必须在添加 spi_transfer 前调用。 |
spi_message_add_tail() | 将 spi_transfer 添加到 spi_message 队列尾部。 | struct spi_transfer *t,struct spi_message *m:传输和消息指针。 | 支持多段传输组合。 |
spi_sync_transfer() | 同步执行一组 SPI 传输(自动构建消息,无需手动初始化)。 | struct spi_device *spi,struct spi_transfer *xfers,int num_xfers:设备、传输结构数组、传输数量。 | 封装了消息构建过程,适用于单次或多段连续传输。 |
用户态核心接口
在打开默认的 spidev
节点之后(注:如想自行配置节点文件,可以参考 章节自行定义内核态接口),用户态便可通过标准系统调用(open、read、write、ioctl)和预定义的
ioctl 命令完成配置和数据传输。以下是需要关注的核心接口和结构体:
核心结构体
用户态主要依赖 spi_ioc_transfer 结构体定义 SPI 数据传输参数,定义在 linux/spi/spidev.h 中:
struct spi_ioc_transfer {
__u64 tx_buf; // 发送数据缓冲区地址(用户态指针)
__u64 rx_buf; // 接收数据缓冲区地址(用户态指针)
__u32 len; // 传输数据长度(字节数)
__u32 speed_hz; // 本次传输的时钟频率(可选覆盖设备默认值)
__u16 delay_usecs; // 传输后的延迟(微秒)
__u8 bits_per_word; // 本次传输的每字位数(可选覆盖设备默认值)
__u8 cs_change; // 传输后是否切换片选(1=切换,0=保持)
__u8 tx_nbits; // 发送数据的线宽(如 SPI_NBITS_DUAL)
__u8 rx_nbits; // 接收数据的线宽
__u8 pad[4]; // 填充字段(保留)
};
-
tx_buf / rx_buf:用户态缓冲区地址,需通过
ioctl传递指针(用户态地址会被自动转换)。 -
len:单次传输的字节数,内核默认限制最大字节为 4096 字节。
-
speed_hz:可覆盖设备默认速度,实现动态速率调整。
-
cs_change:控制片选信号,适用于多设备共享总线时的片选管理。
关键 ioctl 命令
通过 ioctl(fd, request, arg) 控制 SPI 设备,核心命令分为配置类和传输类:
// 配置类
SPI_IOC_RD_MODE // 读取 SPI 模式(CPOL/CPHA)
SPI_IOC_WR_MODE // 设置 SPI 模式
SPI_IOC_RD_LSB_FIRST // 读取字节传输顺序(0 = MSB first)
SPI_IOC_WR_LSB_FIRST // 设置字节传输顺序
SPI_IOC_RD_BITS_PER_WORD // 读取每字位数
SPI_IOC_WR_BITS_PER_WORD // 设置每字位数
SPI_IOC_RD_MAX_SPEED_HZ // 读取最大时钟频率
SPI_IOC_WR_MAX_SPEED_HZ // 设置最大时钟频率
// 传输类
SPI_IOC_MESSAGE(N) // 执行一次或多次全双工传输(N 为传输次数)