SPI 子系统
SPI 协议特点
SPI(Serial Peripheral Interface)四线全双工:SCLK、MOSI、MISO、CS(片选)。相比 I2C:速度更快(可达数十 MHz)、全双工、无地址机制(靠 CS 区分从设备)。
Master Slave
SCLK ────────► SCLK
MOSI ────────► MOSI
MISO ◄──────── MISO
CS ────────► CSSPI 设备驱动框架
c
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
struct my_spi_priv {
struct spi_device *spi;
u8 tx_buf[64];
u8 rx_buf[64];
};
/* 单次全双工传输 */
static int my_spi_transfer(struct my_spi_priv *priv,
u8 *tx, u8 *rx, size_t len)
{
struct spi_transfer t = {
.tx_buf = tx,
.rx_buf = rx,
.len = len,
.speed_hz = 1000000, /* 1 MHz,覆盖默认速度 */
.bits_per_word = 8,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(priv->spi, &m);
}
/* 简洁接口:只写 */
static int my_spi_write_reg(struct spi_device *spi, u8 reg, u8 val)
{
u8 buf[2] = { reg, val };
return spi_write(spi, buf, sizeof(buf));
}
/* 简洁接口:先写后读(带 CS 保持) */
static int my_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val)
{
return spi_write_then_read(spi, ®, 1, val, 1);
}
static int my_spi_probe(struct spi_device *spi)
{
struct my_spi_priv *priv;
int ret;
/* 配置 SPI 参数 */
spi->mode = SPI_MODE_0; /* CPOL=0, CPHA=0 */
spi->bits_per_word = 8;
spi->max_speed_hz = 10000000; /* 10 MHz */
ret = spi_setup(spi);
if (ret)
return ret;
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->spi = spi;
spi_set_drvdata(spi, priv);
dev_info(&spi->dev, "my SPI device probed, mode=%d, speed=%d Hz\n",
spi->mode, spi->max_speed_hz);
return 0;
}
static void my_spi_remove(struct spi_device *spi)
{
/* devm 资源自动释放 */
}
static const struct of_device_id my_spi_of_match[] = {
{ .compatible = "myvendor,my-spi-sensor" },
{ }
};
MODULE_DEVICE_TABLE(of, my_spi_of_match);
static const struct spi_device_id my_spi_id[] = {
{ "my-spi-sensor", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, my_spi_id);
static struct spi_driver my_spi_driver = {
.driver = {
.name = "my-spi-sensor",
.of_match_table = my_spi_of_match,
},
.probe = my_spi_probe,
.remove = my_spi_remove,
.id_table = my_spi_id,
};
module_spi_driver(my_spi_driver);
MODULE_LICENSE("GPL");设备树节点
c
&spi0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
sensor@0 {
compatible = "myvendor,my-spi-sensor";
reg = <0>; /* CS 编号 */
spi-max-frequency = <10000000>;
spi-cpol; /* CPOL=1 */
spi-cpha; /* CPHA=1 → SPI_MODE_3 */
};
flash@1 {
compatible = "jedec,spi-nor";
reg = <1>;
spi-max-frequency = <50000000>;
};
};SPI 模式
| 模式 | CPOL | CPHA | 时钟空闲 | 采样边沿 |
|---|---|---|---|---|
| SPI_MODE_0 | 0 | 0 | 低 | 上升沿 |
| SPI_MODE_1 | 0 | 1 | 低 | 下降沿 |
| SPI_MODE_2 | 1 | 0 | 高 | 下降沿 |
| SPI_MODE_3 | 1 | 1 | 高 | 上升沿 |
多段传输(保持 CS 有效)
c
struct spi_transfer t[2];
struct spi_message m;
memset(t, 0, sizeof(t));
spi_message_init(&m);
/* 第一段:发送命令 */
t[0].tx_buf = cmd_buf;
t[0].len = cmd_len;
t[0].cs_change = 0; /* 不释放 CS */
spi_message_add_tail(&t[0], &m);
/* 第二段:接收数据 */
t[1].rx_buf = rx_buf;
t[1].len = rx_len;
spi_message_add_tail(&t[1], &m);
ret = spi_sync(spi, &m);异步传输
c
static void my_spi_complete(void *context)
{
struct my_spi_priv *priv = context;
/* DMA 传输完成回调 */
complete(&priv->done);
}
/* 异步发起传输 */
spi_message_init(&m);
m.complete = my_spi_complete;
m.context = priv;
spi_message_add_tail(&t, &m);
spi_async(spi, &m);
/* 等待完成 */
wait_for_completion(&priv->done);