大家好,我是王鸽,之前文章有说过I2C 外设的调试技巧,这篇文章是SPI调试技巧,应遵循 “硬件→设备树→内核→用户态→波形→稳定性” 的分层闭环流程,结合内核动态调试、用户态工具与逻辑分析仪,可高效定位 90% 以上的通信与驱动问题。 | | |
|---|
| 对照原理图,核对 SCLK/MOSI/MISO/CS | |
| | |
| cat /sys/kernel/debug/pinctrl/*/pinmux | |
| grep -r "cs-gpios" /sys/firmware/devicetree/base | |
&spi0 { status = "okay"; pinctrl-name = "default"; pinctrl-0 = <&spi0_pins>; ti,pindir-d0-out-d1-in; wk2124A { compatible = "wk2124A"; // 匹配字符串 reg = <0>; //哪一个片选第0个片选 spi-cpha = <1>; // 配置 spi 的模式 #spi-tx-bus-width = <1>; // 这是是 spi-tx 的总线宽度 #spi-rx-bus-width = <1>; spi-max-frequency = <10000000>; // spi 最大速率配置 }; };
首先判断加载SPI Device 驱动有没有进入probe函数中,后面就要考虑一下属性配置这块。
这些SPI Device必选的属性如下:
compatible:根据它找到SPI Device驱动reg:用来表示它使用哪个片选引脚spi-max-frequency:必选,该SPI设备支持的最大SPI时钟
可选属性如下:
spi-cpol:这是一个空属性(没有值),表示CPOL为1,即平时SPI时钟为低电平spi-cpha:这是一个空属性(没有值),表示CPHA为1,即在时钟的第2个边沿采样数据spi-cs-high:这是一个空属性(没有值),表示片选引脚高电平有效spi-3wire:这是一个空属性(没有值),表示使用SPI 三线模式spi-lsb-first:这是一个空属性(没有值),表示使用SPI传输数据时先传输最低为(LSB)spi-tx-bus-width:表示有几条MOSI引脚,没有这个属性时默认只有1条MOSI引脚spi-rx-bus-width:表示有几条MISO引脚,没有这个属性时默认只有1条MISO引脚spi-rx-delay-us:单位是毫秒,表示每次读传输后要延时多久spi-tx-delay-us:单位是毫秒,表示每次写传输后要延时多久
大传输(>4KB)时启用 DMA:设备树中添加dma-names = "tx", "rx";,驱动中设置spi->dma_tx/spi->dma_rx。 /sys/bus/spi/devices/ # 应显示spiX.Y(如spi0.0)/sys/bus/spi/devices/spi0.0
中断调试
用cat /proc/interrupts | grep spi检查中断次数,若中断未触发,排查中断控制器配置是不是正确的?
内核调试
内核日志与驱动加载
dmesg | grep -i spi # 检查是否有“probe success”或错误 lsmod | grep spi # 确认控制器与设备驱动已加载
常见错误:chipselect already used(CS 冲突)、timeout(时钟 / 引脚错误)。static int my_spi_probe(struct spi_device *spi) { dev_dbg(&spi->dev, "probe: mode=%d, speed=%d, bits=%d\n", spi->mode, spi->max_speed_hz, spi->bits_per_word); // 传输前后打印tx/rx缓冲区 struct spi_transfer t = { .tx_buf = tx_buf, .rx_buf = rx_buf, .len = 4, }; struct spi_message msg; spi_message_init(&msg); spi_message_add_tail(&t, &msg); dev_dbg(&spi->dev, "tx: %02x%02x%02x%02x\n", tx_buf[0], tx_buf[1], tx_buf[2], tx_buf[3]); ret = spi_sync(spi, &msg); dev_dbg(&spi->dev, "rx: %02x%02x%02x%02x, ret=%d\n", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3], ret); return ret;}
将从设备临时改为spidev(设备树中compatible = "spidev";),生成/dev/spidevX.Y,用工具做基础通信测试: | | |
|---|
| ./spidev_test -D /dev/spidev0.0 -s 1000000 -p "\x9F\x00\x00\x00" -v | 验证基本通信(如 Flash 的 0x9F 指令) |
| ./spidev_test -D /dev/spidev0.0 -s 500000 -l -v | |
| ./spidev_test -D /dev/spidev0.0 -s 1000000 -O -H -v | |
| ./spidev_test -D /dev/spidev0.0 -s 1000000 -b 16 -p "\x1234" -v | |
工具补充:spi-config -d /dev/spidev0.0 -q(查询当前配置)、printf "\x01\x02" | spi-pipe -d /dev/spidev0.0 | hexdump -C(自定义数据传输)。使用逻辑分析仪(如 Saleae、PulseView)抓取 SCLK/MOSI/MISO/CS,重点核对:- SPI 模式:CPOL(空闲时钟电平)、CPHA(采样沿)是否与从设备手册一致。
- 片选信号:传输期间 CS 是否持续拉低,无提前释放或抖动。
- 数据位序:确认 MSB 优先(默认)或 LSB 优先(需在设备树设置
spi-lsb-first)。 - 时钟速率:实际频率是否接近配置值(控制器可能自动降频)。
- 字节间隙:部分设备要求字节间有延时,用
spidev_test -d 10(10μs)测试。
| | |
|---|
| | |
| | 1. 切换 CPOL/CPHA(0-3 模式逐一测试); |
| | |
| | 设备树中分配不同 CS,或使用 GPIO 模拟 CS |
| | 确认compatible与驱动中of_match_table一致 |
调试 SPI 的核心是先隔离硬件与协议问题,再定位驱动逻辑。优先通过spidev验证硬件与基本通信,再用动态调试与波形分析解决驱动细节。记住:逻辑分析仪是解决 SPI 问题的终极工具,90% 的协议不匹配问题可通过波形一眼定位。