前言
本文章是根据韦东山老师的教学视频整理的学习笔记https://video.100ask.net/page/1712503
SPI 通信协议采用同步全双工传输机制,拓扑架构支持一主多从连接模式,这种模式在实际应用场景中颇为高效。其有效传输距离大致为 10m ,传输速率处于 1 - 70Mbps 区间,能够满足多种场景的速率要求。SPI 采用大端传输方式,电气类型为 TTL 电平标准,在常见数字电路中具有良好的兼容性。
一、SPI硬件框架

SPI主要由四条信号线组成,分别是 SCK、MOSI、MISO 和 SS。
1、串行时钟线(SCK)
- 功能:SCK 是由主设备产生的时钟信号,用于同步主设备和从设备之间的数据传输。主设备通过控制 SCK 的频率来决定数据传输的速率,每一个时钟脉冲对应一位数据的传输。
- 工作原理:在时钟信号的上升沿或下降沿,主设备和从设备会进行数据的发送和接收操作。具体是上升沿还是下降沿进行数据操作,取决于 SPI 的工作模式(由时钟极性 CPOL 和时钟相位 CPHA 决定)。
2、主输出从输入线(MOSI)
- 功能:MOSI用于主设备向从设备发送数据。。当主设备需要向从设备传输数据时,它会将数据通过 MOSI 线逐位发送出去,从设备在相应的时钟信号控制下接收这些数据。
- 工作原理:在 SCK 的有效沿(上升沿或下降沿),主设备将待发送的数据位放到 MOSI 线上,从设备在同一时刻读取该线上的数据位。这样,随着 SCK 时钟信号的不断变化,主设备就可以将数据依次发送给从设备。
3、主输入从输出线(MISO)
- 功能:MISO 用于从设备向主设备发送数据。当从设备需要将数据反馈给主设备时,它会将数据通过 MISO 线逐位发送出去,主设备在相应的时钟信号控制下接收这些数据。
- 工作原理:与 MOSI 类似,在 SCK 的有效沿,从设备将待发送的数据位放到 MISO 线上,主设备在同一时刻读取该线上的数据位。通过这种方式,从设备可以将数据传输给主设备。
4、从设备选择线(SS)
- 功能:SS 线用于主设备选择要与之通信的从设备。SPI 支持一主多从的拓扑结构,主设备通过拉低特定从设备的 SS 线来选中该从设备,使其进入工作状态,准备进行数据传输。
- 工作原理:在 SPI 通信中,每个从设备都有一个独立的 SS 线,当主设备需要与某个从设备通信时,将该从设备的 SS 线拉低(通常为低电平有效),其他从设备的 SS 线保持高电平。被选中的从设备会响应主设备的时钟信号和数据传输请求,而未被选中的从设备则处于空闲状态,不参与数据传输。
二、SPI协议
SPI四种模式
时钟极性(CPOL)定义了时钟空闲状态电平
- CPOL=0,SCK 信号在空闲状态(即没有数据传输时)保持低电平。在这种情况下,SCK 信号的有效沿为上升沿(从低电平到高电平的跳变),数据通常在上升沿被采样。
- CPOL=1,SCK 信号在空闲状态保持高电平。此时,SCK 信号的有效沿为下降沿(从高电平到低电平的跳变),数据通常在下降沿被采样。
时钟相位(CPHA)定义了数据的采集时间
- CPHA=0,在SCK的第一个跳变沿(上升沿或下降沿)进行数据采样。
- CPHA=1,在SCK的第二个跳变沿(上升沿或下降沿)进行数据采样。
表1 SPI四种工作模式
|-------|------|------|----------------------------------------------------------------------------|
| 模式 | CPOL | CPHA | 描述 |
| mode0 | 0 | 0 | |
| mode1 | 0 | 1 | |
| mode2 | 1 | 0 | |
| mode3 | 1 | 1 | |
三、SPI总线设备驱动模型
1、平台总线驱动模型
先来回顾一下平台总线驱动模型。
平台总线驱动模型将驱动程序分成两部分:
一部分注册一个platform_driver结构体;
另一部分注册一个platform_device结构体
- 可以使用C文件注册platform_device。
- 也可以使用设备树创建一个节点,内核解析设备树时注册platform_device。

匹配机制概述
在 Linux 内核中,platform_bus_type
负责管理平台设备(platform_device
)和平台驱动(platform_driver
)的匹配过程。
platform_bus_type
定义了一个 match
函数,当有新的平台设备或平台驱动注册到系统时,内核会调用这个 match
函数来判断设备和驱动是否匹配。如果匹配成功,内核会调用驱动的 probe
函数来初始化设备。
platform_match源码如下所示:
cpp
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
platform_match函数会按照以下顺序来尝试匹配设备和驱动:
- 检查设备的driver_override字段;
- 尝试使用设备树(of)风格匹配;
- 尝试使用ACPI风格匹配;
- 尝试依据驱动的id_table进行匹配
- 若前面的匹配都失败了,就使用设备和驱动的名称进行匹配。
2、SPI数据结构
前面的硬件框架可以看出,SPI子系统涉及到2类硬件:SPI控制器、SPI设备。
在SPI总线设备驱动模型中,spi_master、spi_device和spi_driver是三个核心的数据结构和概念,它们共同协作实现对SPI设备的管理和驱动。
spi_master结构体
spi_master代表SPI主控制器,它是系统中负责管理SPI总线并与SPI从设备进行通信的硬件实体。在Linux内核中,它抽象魏一个spi_master结构体,用来描述和控制 SPI 主机控制器的各种属性与操作,里面最重要的成员是transfer函数指针。
cpp
struct spi_master {
struct device dev;
struct list_head list;
/* other than negative (== assign one dynamically), bus_num is fully
* board-specific. usually that simplifies to being SOC-specific.
* example: one SOC has three SPI controllers, numbered 0..2,
* and one board's schematics might show it using SPI-2. software
* would normally use bus_num=2 for that controller.
*/
s16 bus_num;
/* chipselects will be integral to many controllers; some others
* might use board-specific GPIOs.
*/
u16 num_chipselect;
/* some SPI controllers pose alignment requirements on DMAable
* buffers; let protocol drivers know about these requirements.
*/
u16 dma_alignment;
/* spi_device.mode flags understood by this controller driver */
u16 mode_bits;
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
/* limits on transfer speed */
u32 min_speed_hz;
u32 max_speed_hz;
/* other constraints relevant to this driver */
u16 flags;
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
#define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
/*
* on some hardware transfer / message size may be constrained
* the limit may depend on device transfer settings
*/
size_t (*max_transfer_size)(struct spi_device *spi);
size_t (*max_message_size)(struct spi_device *spi);
/* I/O mutex */
struct mutex io_mutex;
/* lock and mutex for SPI bus locking */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
/* flag indicating that the SPI bus is locked for exclusive use */
bool bus_lock_flag;
/* Setup mode and clock, etc (spi driver may call many times).
*
* IMPORTANT: this may be called when transfers to another
* device are active. DO NOT UPDATE SHARED REGISTERS in ways
* which could break those transfers.
*/
int (*setup)(struct spi_device *spi);
/* bidirectional bulk transfers
*
* + The transfer() method may not sleep; its main role is
* just to add the message to the queue.
* + For now there's no remove-from-queue operation, or
* any other request management
* + To a given spi_device, message queueing is pure fifo
*
* + The master's main job is to process its message queue,
* selecting a chip then transferring data
* + If there are multiple spi_device children, the i/o queue
* arbitration algorithm is unspecified (round robin, fifo,
* priority, reservations, preemption, etc)
*
* + Chipselect stays active during the entire message
* (unless modified by spi_transfer.cs_change != 0).
* + The message transfers use clock and SPI mode parameters
* previously established by setup() for this device
*/
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
/* called on release() to free memory provided by spi_master */
void (*cleanup)(struct spi_device *spi);
/*
* Used to enable core support for DMA handling, if can_dma()
* exists and returns true then the transfer will be mapped
* prior to transfer_one() being called. The driver should
* not modify or store xfer and dma_tx and dma_rx must be set
* while the device is prepared.
*/
bool (*can_dma)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer);
/*
* These hooks are for drivers that want to use the generic
* master transfer queueing mechanism. If these are used, the
* transfer() function above must NOT be specified by the driver.
* Over time we expect SPI drivers to be phased over to this API.
*/
bool queued;
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work pump_messages;
spinlock_t queue_lock;
struct list_head queue;
struct spi_message *cur_msg;
bool idling;
bool busy;
bool running;
bool rt;
bool auto_runtime_pm;
bool cur_msg_prepared;
bool cur_msg_mapped;
struct completion xfer_completion;
size_t max_dma_len;
int (*prepare_transfer_hardware)(struct spi_master *master);
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master);
int (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
int (*spi_flash_read)(struct spi_device *spi,
struct spi_flash_read_message *msg);
bool (*flash_read_supported)(struct spi_device *spi);
/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
*/
void (*set_cs)(struct spi_device *spi, bool enable);
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
void (*handle_err)(struct spi_master *master,
struct spi_message *message);
/* gpio chip select */
int *cs_gpios;
/* statistics */
struct spi_statistics statistics;
/* DMA channels for use with core dmaengine helpers */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
/* dummy data for full duplex devices */
void *dummy_rx;
void *dummy_tx;
int (*fw_translate_cs)(struct spi_master *master, unsigned cs);
};
spi_device结构体
spi_device代表一个具体的 SPI 从设备,它连接到 SPI 总线上,是 SPI 通信的目标设备。在 Linux 内核中,它抽象为一个spi_device结构体,用于描述 SPI 从设备的各种属性。
cpp
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
u16 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
spi_driver结构体
spi_driver代表 SPI 设备的驱动程序,它实现了对 SPI 从设备的具体操作和功能。在 Linux 内核中,它抽象为一个spi_driver结构体,包含了驱动程序的各种回调函数。
cpp
struct spi_driver {
const struct spi_device_id *id_table;
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
struct device_driver driver;
};
三者关系
spi_master负责管理 SPI 总线,为spi_device提供通信的基础;spi_device代表具体的 SPI 从设备,通过spi_master注册到系统中;spi_driver则负责驱动spi_device,通过与spi_device匹配后,实现对设备的具体操作和数据传输。它们共同构成了 Linux 内核中 SPI 总线设备驱动模型的核心部分。
3、SPI驱动框架

在 Linux 系统中,SPI 控制器通常是通过平台总线驱动模型进行创建和管理的,而 SPI 设备则是基于 SPI 总线驱动模型来创建和操作的。