Skip to content

SPI 子系统

SPI 协议特点

SPI(Serial Peripheral Interface)四线全双工:SCLK、MOSI、MISO、CS(片选)。相比 I2C:速度更快(可达数十 MHz)、全双工、无地址机制(靠 CS 区分从设备)。

Master          Slave
  SCLK ────────► SCLK
  MOSI ────────► MOSI
  MISO ◄──────── MISO
  CS   ────────► CS

SPI 设备驱动框架

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, &reg, 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 模式

模式CPOLCPHA时钟空闲采样边沿
SPI_MODE_000上升沿
SPI_MODE_101下降沿
SPI_MODE_210下降沿
SPI_MODE_311上升沿

多段传输(保持 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);

褚成志的笔记