SPI 驱动
一、Linux SPI 子系统
应用(spidev)
↓
SPI 核心(spi.c)
↓
spi_controller(控制器驱动,如 spi-imx.c、spi-stm32.c)
↓
spi_device → 设备驱动(如 mysensor.c)
↓
SPI 硬件
二、关键结构
struct spi_controller {
struct device dev;
int bus_num;
int num_chipselect;
const struct spi_controller_ops *ops;
struct list_head queue;
struct spi_message *cur_msg;
// ...
};
struct spi_device {
struct device dev;
struct spi_controller *controller;
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
u32 mode;
#define SPI_CPHA 0x01
#define SPI_CPOL 0x02
#define SPI_CS_HIGH 0x04
#define SPI_LSB_FIRST 0x08
#define SPI_3WIRE 0x10
#define SPI_LOOP 0x20
#define SPI_NO_CS 0x40
#define SPI_READY 0x80
#define SPI_TX_DUAL 0x100
#define SPI_TX_QUAD 0x200
#define SPI_RX_DUAL 0x400
#define SPI_RX_QUAD 0x800
// ...
};
struct spi_driver {
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
struct device_driver driver;
const struct spi_device_id *id_table;
};
struct spi_transfer {
const void *tx_buf;
void *rx_buf;
unsigned len;
u32 speed_hz;
u8 bits_per_word;
u16 delay_usecs;
u8 cs_change;
// ...
};
struct spi_message {
struct list_head transfers;
struct spi_device *spi;
void (*complete)(void *context);
void *context;
// ...
};
三、设备驱动示例
1. 设备树
&spi1 {
status = "okay";
pinctrl-0 = <&pinctrl_spi1>;
clock-frequency = <10000000>;
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <20000000>;
spi-cpol; spi-cpha;
// ...
};
mysensor: mysensor@1 {
compatible = "myvendor,myspi";
reg = <1>;
spi-max-frequency = <1000000>;
spi-cpha;
interrupt-parent = <&gpio2>;
interrupts = <10 IRQ_TYPE_LEVEL_HIGH>;
};
};
2. 驱动代码
#include <linux/spi/spi.h>
struct my_priv {
struct spi_device *spi;
struct mutex lock;
};
static int my_probe(struct spi_device *spi)
{
struct my_priv *priv;
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) return -ENOMEM;
priv->spi = spi;
mutex_init(&priv->lock);
spi_set_drvdata(spi, priv);
// 配置已在设备树中设置(mode、speed)
// 也可以再设:
spi->mode |= SPI_CPHA;
spi->max_speed_hz = 1000000;
spi_setup(spi);
return 0;
}
static int my_remove(struct spi_device *spi)
{
return 0;
}
static const struct spi_device_id my_id[] = {
{ "myspi", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, my_id);
static const struct of_device_id my_of[] = {
{ .compatible = "myvendor,myspi" },
{ }
};
static struct spi_driver my_driver = {
.driver = {
.name = "myspi",
.of_match_table = my_of,
},
.probe = my_probe,
.remove = my_remove,
.id_table = my_id,
};
module_spi_driver(my_driver);
四、SPI 通信
1. 同步(spi_sync)
// 单字节读写
u8 tx[2] = { reg, 0 };
u8 rx[2];
struct spi_transfer t = {
.tx_buf = tx,
.rx_buf = rx,
.len = 2,
.speed_hz = 1000000,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(spi, &m);
2. 异步(spi_async)
ret = spi_async(spi, &m);
// m.complete 回调中处理
3. 工具函数
// 单字节读写
int spi_write_then_read(struct spi_device *spi,
const void *txbuf, unsigned n_tx,
void *rxbuf, unsigned n_rx);
// 单向写
int spi_write(struct spi_device *spi, const void *buf, int len);
// 单向读(写 0xFF 触发)
int spi_read(struct spi_device *spi, void *buf, int len);
4. 多 transfer 消息
struct spi_message m;
struct spi_transfer t1 = { .len = 1, .tx_buf = ® };
struct spi_transfer t2 = { .len = 4, .rx_buf = buf };
spi_message_init(&m);
spi_message_add_tail(&t1, &m);
spi_message_add_tail(&t2, &m);
spi_sync(spi, &m);
五、控制器驱动
static int my_transfer_one_message(struct spi_controller *ctlr,
struct spi_message *msg)
{
// 处理每个 transfer
struct spi_transfer *t;
list_for_each_entry(t, &msg->transfers, transfer_list) {
// 配置 DMA / FIFO
// 启动
// 等待完成
}
spi_finalize_current_message(ctlr);
return 0;
}
static const struct spi_controller_ops my_ops = {
.transfer_one_message = my_transfer_one_message,
// ...
};
static int my_probe(struct platform_device *pdev)
{
struct spi_controller *ctlr = spi_alloc_master(&pdev->dev, sizeof(*priv));
ctlr->bus_num = -1; // 自动分配
ctlr->num_chipselect = 4;
ctlr->ops = &my_ops;
devm_spi_register_controller(&pdev->dev, ctlr);
return 0;
}
六、用户态驱动(spidev)
int fd = open("/dev/spidev1.0", O_RDWR);
u8 mode = SPI_CPHA;
ioctl(fd, SPI_IOC_WR_MODE, &mode);
u32 speed = 1000000;
ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
u8 tx[] = { 0x10, 0 };
u8 rx[2];
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = 2,
.speed_hz = 1000000,
};
ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
七、调试
# 工具
spidev_test -D /dev/spidev1.0 -v -p "\xAA\x55"
# 内核
dmesg | grep spi
cat /sys/bus/spi/devices/spi1.0/
# 时序问题
- 用逻辑分析仪看 MISO/MOSI/SCK/CS
- 降低频率排查
- 检查 cs_change、bits_per_word