02. SPI 数据结构体成员
一、层次:
二、结构体
**spi_master**
(SPI控制器): 它是SoC(系统级芯片)中SPI控制器的软件表示,负责管理整个SPI通信过程。**spi_bus_type**
(SPI总线类型): 表示硬件中的SPI总线,相当于连接所有SPI设备的物理线路或通信通道。**spi_device**
(SPI从设备): 指连接在SPI总线上的从设备(如传感器、存储芯片等),需要由主设备(spi_master
)控制通信。**spi_driver**
(SPI设备驱动): 是具体设备的软件驱动程序,用于控制某个SPI从设备的硬件功能。
2.1、spi_controller
控制器
SPI主控制器接口说明
- 结构体组成
struct spi_master {
struct class_device cdev; // 驱动的设备类接口
u16 bus_num; // 控制器编号(由硬件决定)
u16 num_chipselect; // 支持的片选信号数量(最多能连接的从设备数)
// 核心功能函数
int (*setup)(struct spi_device *spi); // 配置设备通信参数(如时钟、模式)
int (*transfer)(struct spi_device *spi, struct spi_message *mesg); // 发送数据包
void (*cleanup)(const struct spi_device *spi); // 清理配置资源
};
2
3
4
5
6
7
8
9
10
- 主要功能
一个SPI主控制器可同时管理多个从设备(spi_device)
所有设备共享时钟线(SCK)和数据线(MOSI/MISO),但通过独立的片选信号区分不同设备
每个从设备可独立设置通信时钟速度
通过消息队列传输数据:
- 将数据包(spi_message)加入传输队列
- 自动处理片选信号:默认保持选通状态直到所有消息发送完毕
- 支持灵活的设备切换(通过设置spi_transfer.cs_change标志)
- 驱动开发流程 (1) 分配资源:
struct spi_master *master = spi_alloc_master
// 必须在可睡眠的上下文调用,失败返回NULL
2
(2) 注册控制器:
int ret = spi_register_master(master);
// 成功返回0,失败返回错误码
// 必须在探测函数(如platform_driver的probe)中调用
2
3
(3) 卸载:
spi_unregister_master(master); // 必须手动调用释放资源
- 关键说明
传输队列处理:
- 自动管理设备切换和数据传输顺序
- 每个消息发送完毕会触发完成回调函数
片选管理:
- 默认保持选通状态直到队列处理完毕
- 可通过设置
cs_change
标志主动切换设备
设备仲裁:
- 多设备时的调度顺序由驱动自行实现(FIFO/优先级等)
2.2、spi_bus_type
设备
需要关注match匹配规则:
2.2、spi_device
设备
以下是更直白的解释:
SPI设备结构说明(struct spi_device)
这个结构体描述了SPI设备的基本信息,定义在内核头文件中:
struct spi_device {
struct device dev; //设备的驱动模型
struct spi_master *master; //SPI 设备控制器
u32 max_speed_hz; //芯片使用的最大时钟速率,可被设备驱动修改,spi_transfer.speed_hz 可以在每次传输中覆盖它
u8 chip_select; //芯片选择信号,主设备通过它来选择从设备
u8 mode; //SPI 模式定义数据是如何发送和接收的,可被设备驱动修改。
#define SPI_CPHA 0x01 //时钟相位
#define SPI_CPOL 0x02 //时钟极性
#define SPI_MODE_0 (0|0)
#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 //芯片选择信号高电平有效
#define SPI_LSB_FIRST 0x08 //LSB 先传输
u8 bits_per_word; //数据传输可以包括一个或多个字节,如 8 或 12 比特。可以被设备驱动修改,默认值为 0 指示传协议字节数为 8bit,spi_transfer.bits_per_word 可以在每次传输中覆盖它
int irq; //负数或传入 request_irq()中的数值来接收设备中断
void *controller_state; //控制器运行时状态
void *controller_data; //控制器的开发板相关定义,这些信息对控制器驱动很有用
const char *modalias;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
各字段含义:
master 设备连接的SPI控制器(总线)。比如,设备是挂在哪个SPI总线上的。
max_speed_hz 设备支持的最高时钟频率。比如设置为1000000表示1MHz。可以在驱动中动态调整。
chip_select 用来选通设备的信号线编号。默认低电平激活(拉低时选中设备)。 如果设置
SPI_CS_HIGH
标志,则改为高电平激活。irq 设备的中断号。驱动需要通过
request_irq()
注册这个中断号来处理硬件中断。mode 定义数据与时钟的同步方式。包含两个关键参数:
- CPOL:时钟初始电平(0=低电平,1=高电平)
- CPHA:数据采样边沿(0=下降沿,1=上升沿)
bits_per_word 每次传输的数据位数。比如8位表示每个数据包是1字节。
SPI通信模式详解(4种模式)
通过mode
字段的组合值定义,共4种模式:
// 定义方式
SPI_MODE0 = 0 // CPOL=0, CPHA=0
SPI_MODE1 = 1 // CPOL=0, CPHA=1
SPI_MODE2 = 2 // CPOL=1, CPHA=0
SPI_MODE3 = 3 // CPOL=1, CPHA=1
2
3
4
5
参数说明:
CPOL(时钟极性) 决定时钟信号初始电平:
- 0:初始低电平 → 第一个有效边沿是上升沿
- 1:初始高电平 → 第一个有效边沿是下降沿
CPHA(时钟相位) 决定数据采样边沿:
- 0:在第一个边沿(上升/下降)采样数据
- 1:在第二个边沿采样数据
模式示例:
- MODE0(0,0) 低电平起始 → 数据在第一个下降沿(上升沿后的下降)被读取 (传统MicroWire协议模式)
- MODE1(0,1) 低电平起始 → 数据在第一个上升沿被读取
- MODE2(1,0) 高电平起始 → 数据在第一个下降沿被读取
- MODE3(1,1) 高电平起始 → 数据在第二个上升沿被读取
:::
简单总结:
mode字段通过组合CPOL和CPHA选择通信方式
chip_select决定如何选通设备
max_speed_hz设置通信速度
其他参数定义数据格式和中断机制 :::
2.3、spi_driver 驱动
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;
};
2
3
4
5
6
7
就像需要i2c_device_id处理I2C设备一样,对于SPI设备,必须使用spi_device_id,以便提供device_id数组来匹配设备。它在include/linux/mod_ devicetable.h中定义:
struct spi_device_id {
char name[SPI_NAME_SIZE];
kernel_ulong_t driver_data; /* Data private to the driver */
};
2
3
4
需要将数组嵌入struct spi_device_id,以便把驱动程序需要管理的设备ID通知SPI核心,并在该驱动程序结构上调用MODULE_DEVICE_TABLE宏。当然,该宏的第一个参数是设备所在总线的名称。在这个例子中,它是SPI:
案例:net/can/spi/mcp251x.c