SPI 驱动开发
本文档介绍 RK3588 SPI 控制器在 Linux 6.1 内核中的配置、设备树绑定,以及用 spidev(内核提供的通用 SPI 用户态接口,启用后会在 /dev 下生成 spidev 设备文件)做测试的方法。
适用范围: RK3588 Linux 6.1 Debian 12 / Ubuntu 22.04 系统,C-3588LQ V1.0 + SHIMETAPI-M5-R2A V1.0 底板。
1. 概念基础
1.1 SPI 是什么
SPI(Serial Peripheral Interface)是一种高速同步串行通信接口,用四根线(SCLK 时钟、MOSI 主出从入、MISO 主入从出、CS 片选)实现全双工的主从通信。RK3588 内置 5 路 SPI 控制器,主模式最高 50 MHz。
| 用途 | 示例 |
|---|---|
| SPI NOR Flash | W25Q128(16MB) |
| SPI LCD | TFT 小屏控制 |
| 高速 ADC | 数据采集 |
| 无线模块 | LoRa、ZigBee |
1.2 SPI 模式
SPI 有四种模式,由 CPOL(时钟空闲电平)和 CPHA(采样边沿)两个参数组合而成,使用时要和从设备数据手册的要求一致:
| 模式 | CPOL | CPHA | 特性 |
|---|---|---|---|
| Mode 0 | 0 | 0 | 空闲低,第一沿采样 |
| Mode 1 | 0 | 1 | 空闲低,第二沿采样 |
| Mode 2 | 1 | 0 | 空闲高,第一沿采样 |
| Mode 3 | 1 | 1 | 空闲高,第二沿采样 |
1.3 控制器基地址
| 控制器 | 基地址 |
|---|---|
| SPI0 | 0xfe580000 |
| SPI1 | 0xfe610000 |
| SPI2 | 0xfe620000 |
| SPI3 | 0xfe630000 |
| SPI4 | 0xfe640000 |
2. 准备工作
2.1 硬件清单
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | C-3588LQ 核心板 + SHIMETAPI-M5-R2A 底板 | 1 | |
| 调试串口线 | USB 转 TTL(3.3V) | 1 | 波特率 1500000 |
| SPI Flash | W25Q128(16MB) | 1 | 或其他 SPI NOR Flash |
| 杜邦线 | 母对母 | 若干 |
2.2 引脚连接示例
以 SPI1 为例:
| 主控板引脚 | 外设引脚 | 功能说明 |
|---|---|---|
| SPI1_CLK(Sheet 20/21) | Flash CLK | SPI 时钟 |
| SPI1_MOSI(Sheet 20/21) | Flash DI | 主出从入 |
| SPI1_MISO(Sheet 20/21) | Flash DO | 主入从出 |
| SPI1_CS0(Sheet 20/21) | Flash CS | 片选(低有效) |
| 3.3V | Flash VCC | 电源 |
| GND | Flash GND | 共地 |
3. 软件准备
3.1 内核配置
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_ROCKCHIP=y /* RK3xxx/RK3588 SPI 控制器 */
CONFIG_SPI_SPIDEV=y /* 用户态 /dev/spidev* 接口 */
CONFIG_OF=y
CONFIG_OF_SPI=y驱动源码
drivers/spi/spi-rockchip.c。
3.2 设备树
SoC 级定义:
spi1: spi@fe610000 {
compatible = "rockchip,rk3588-spi", "rockchip,rk3066-spi";
reg = <0x0 0xfe610000 0x0 0x1000>;
clocks = <&cru CLK_SPI1>, <&cru PCLK_SPI1>;
clock-names = "spiclk", "apb_pclk";
interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&spi1m1_cs0 &spi1m1_pins>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};板级 enable 并挂载 flash:
&spi1 {
status = "okay";
max-freq = <50000000>; // 50MHz
spi_flash: flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <50000000>;
m25p,fast-read;
};
};4. 设备树中添加 spidev 节点(PC 端流程)
想在用户态直接读写 SPI(比如用命令行测试),需要在设备树里加一个 spidev 节点。注意这个节点只在调试时使用:
[电脑端] 编辑板级 .dts:
&spi1 {
status = "okay";
max-freq = <10000000>;
// spidev 仅用于测试,实际产品应由具体驱动接管 CS
spi_test: spi-dev@0 {
compatible = "spidev";
reg = <0>;
spi-max-frequency = <10000000>;
};
};电脑端 重编译并烧录后,设备端会出现 /dev/spidev1.0,可以用 ls /dev/spidev* 确认。
5. 设备端 SPI 操作
5.1 查看 SPI 控制器
ls /sys/class/spi_master/
cat /sys/class/spi_master/spi*/name本机 SPI 总线列表取决于板级 DTB,
/sys/class/spi_master/会列出已 enable 的 spi 控制器。如为空,请先在 PC 端 enable 对应&spiN。
5.2 查看 spidev 节点
ls /dev/spidev*可能为空:出厂镜像通常未加载 spidev。如需调试,按 §4 添加节点后重烧。
5.3 安装 spi-tools(PC / 开发板可选)
spi-tools 提供 spi-pipe 等命令行工具,方便不写代码就完成收发测试:
apt install -y spi-tools5.4 spidev 读写测试
# 读 SPI Flash ID(发送 0x9F 命令)
spi-pipe -d /dev/spidev1.0 -b 8 -r 3 -s 1000000
# 预期返回 (W25Q128): ef 40 18 00 00 ...
# 写 + 读(loopback 测试 MOSI↔MISO 短接时)
# 短接 SPI1_MOSI 和 SPI1_MISO 后执行:
spi-pipe -v -d /dev/spidev1.0 -b 8 -e 0xAA -r 15.5 flashrom 备份 / 烧写
apt install -y flashrom
# 备份
flashrom -p linux_spi:dev=/dev/spidev1.0 -r backup.bin
# 写入(**危险操作**,先确保是 SPI NOR 而非 SPI NAND)
flashrom -p linux_spi:dev=/dev/spidev1.0 -w firmware.bin5.6 通过 sysfs 查看 SPI 设备
sysfs(内核把设备信息映射到 /sys 下的文件接口)里可以看到当前挂在总线上的设备:
# 列出当前已注册到驱动的 SPI 子设备
ls /sys/bus/spi/devices/
# 查看控制器时钟
cat /sys/kernel/debug/clk/clk_spi/clk_rate6. 代码解读
6.1 内核驱动注册
// drivers/spi/spi-rockchip.c(简化)
static struct platform_driver rk3x_spi_driver = {
.probe = rk3x_spi_probe,
.remove = rk3x_spi_remove,
.driver = {
.name = "rk3x-spi",
.of_match_table = rk3x_spi_match,
},
};
static const struct of_device_id rk3x_spi_match[] = {
{ .compatible = "rockchip,rk3588-spi" },
{ /* sentinel */ }
};6.2 用户态调用链
用户空间: open(/dev/spidev1.0)
→ spidev_open() /* spidev 子系统 */
→ spi_setup() /* SPI core */
→ rk3x_spi_setup() /* Rockchip 控制器 */
→ 配置 mode / speed / word size
用户空间: read()/write()
→ spidev_read()/spidev_write()
→ spi_sync_transfer() /* SPI core */
→ rk3x_spi_transfer_one()
→ 操作 SPI 寄存器,发起 DMA / 中断传输7. 故障排除
7.1 SPI 设备无响应
现象:读取 SPI Flash ID 返回 0xFF。
先检查控制器时钟和 CS 引脚状态:
# 控制器时钟
cat /sys/kernel/debug/clk/clk_spi/clk_rate
# CS 引脚状态
cat /sys/kernel/debug/gpio | grep spi常见原因:
- MOSI / MISO 接反
- CS 没有拉低(驱动未把设备树 status 设为
"okay") - SPI 模式(CPOL/CPHA)不匹配
7.2 SPI 数据错误
现象:读出的值与预期不符。
逐项排查:
- 用逻辑分析仪抓 SCK、MOSI 波形
- 调整设备树的
spi-cpol/spi-cpha/spi-cs-high属性 - 检查字节序(MSB first / LSB first)
7.3 时钟频率不对
现象:时钟与 max-freq 设置不一致。
原因:时钟由 APB 分频得到;不同分辨率下可能受硬件分频约束。
解决:调整 &cru CLK_SPI 的相关 parent 时钟,或尝试更小分频值。
