
本文详解 spidev.xfer2() 的工作机制:它执行全双工同步传输,发送字节数必须等于期望接收字节数;要读取4字节响应,需发送共7字节(含命令、填充和占位),结果从返回列表的对应偏移处提取。
本文详解 `spidev.xfer2()` 的工作机制:它执行全双工同步传输,发送字节数必须等于期望接收字节数;要读取4字节响应,需发送共7字节(含命令、填充和占位),结果从返回列表的对应偏移处提取。
在使用 Linux spidev 驱动通过 Python 控制 SPI 外设时,xfer2() 是最常用也最容易被误解的核心方法。其本质是全双工、同步、等长收发——即每一次调用中,主机向 MOSI 发送 N 字节的同时,MISO 线上也恰好采样并返回 N 字节(无论从设备是否“有意”驱动这些字节)。因此,不存在独立指定“接收长度”的参数,也没有隐式时钟延时或自动忽略机制。
回到典型场景:某 SPI 设备要求先发送 2 字节指令 + 1 字节填充(padding),再连续输出 4 字节有效响应(前 3 字节 MISO 可忽略)。对应时序如下:
byte 0 1 2 3 4 5 6 MOSI: txdata0 txdata1 padding dummy dummy dummy dummy MISO: ignored ignored ignored data0 data1 data2 data3
关键点在于:为让硬件在第 3–6 个 SCLK 周期稳定输出 data0–data3,主控必须持续提供时钟——这通过向 MOSI 发送“占位字节”(dummy bytes)实现。由于总共需要 7 个时钟周期才能完成该交互,xfer2() 必须传入一个长度为 7 的发送缓冲区。
✅ 正确写法:
立即学习“Python免费学习笔记(深入)”;
# 构造 7 字节发送缓冲区:[cmd0, cmd1, pad, dummy, dummy, dummy, dummy] txbuf = [txdata0, txdata1, padding_byte] + [0] * 4 rxbuf = spi.xfer2(txbuf) # 返回长度恒为 7 的列表 # 提取有效响应:MISO 第 4~7 字节(索引 3~6) response = rxbuf[3:7] # 即 [data0, data1, data2, data3]
⚠️ 常见误区澄清:
- ❌ spi.xfer2([a,b], 7) —— xfer2() 不接受长度参数,此调用会报错;
- ❌ spi.xfer2([a,b,0], 4) —— 同样非法,且即使语法正确也无法保证时序;
- ❌ 试图用 xfer()(非阻塞变体)替代——xfer2() 才保证原子性与确定时序,是推荐首选。
? 实际应用建议:
- 始终确保 len(txbuf) == len(rxbuf),这是 SPI 协议物理层约束;
- 利用 [0] * N 快速构造 dummy 字节序列,提高可读性;
- 对时序敏感设备,可在 xfer2() 前后添加必要延时(如 time.sleep()),但优先通过设备手册确认是否需 CS 重置或等待周期;
- 调试时可用逻辑分析仪捕获 MOSI/MISO 波形,验证发送内容与采样位置是否严格对齐。
总结:spidev.xfer2() 的设计哲学是“所发即所得,所收即所发之时序镜像”。掌握这一等长全双工本质,即可精准构造任意复杂 SPI 读写序列,无需依赖模糊猜测或错误封装。










