spidev设备节点缺失主因是设备树未正确配置或内核未启用CONFIG_SPI_SPIDEV;需检查DTS中SPI控制器status="okay"及子节点定义,并确认内核模块已加载。

spidev 设备节点没出现,/dev/spidev* 找不到怎么办
核心原因通常是设备树(DTS)里没正确声明 SPI 总线和子设备,或者内核没编译进 spidev 驱动。先确认内核配置:CONFIG_SPI_SPIDEV=y 或 =m(模块需 modprobe spidev)。再检查 DTS 中对应 SPI 控制器是否启用,比如 &spi0 { status = "okay"; },并在其下添加子节点:
&spi0 {
status = "okay";
my_sensor: spi-sensor@0 {
compatible = "vendor,my-sensor";
reg = ; // CS0
spi-max-frequency = ;
#address-cells = ;
#size-cells = ;
};
};
注意:即使不写 compatible,只要节点存在且父总线启用,内核通常会自动绑定 spidev;但某些内核版本要求 compatible = "rohm,dh2228fv" 这类已知兼容字符串才能触发绑定。
open() /dev/spidevB.C 失败,返回 ENXIO 或 EACCES
ENXIO 表示设备节点根本不存在或主从编号不匹配(比如你写了 /dev/spidev0.1,但实际只有 spidev0.0);EACCES 是权限问题——嵌入式系统常默认禁止非 root 访问 spidev。解决方式:
- 用
ls -l /dev/spidev*确认设备号,再查dmesg | grep spidev看内核是否成功注册 - 改权限:
chmod 666 /dev/spidev0.0(临时),或在 udev 规则中固化(如/etc/udev/rules.d/99-spidev.rules添加KERNEL=="spidev*", MODE="0666") - 检查用户是否在
spi组里(若系统启用了组权限控制)
调用 ioctl(fd, SPI_IOC_MESSAGE(1), &tr) 读写失败,数据全为 0
最常见原因是没正确设置 struct spi_ioc_transfer 的字段,尤其 tx_buf/rx_buf 指向的内存必须是物理连续的(用户空间一般没问题),但更隐蔽的是 bits_per_word、speed_hz、delay_usecs 没初始化——它们会继承上次调用的垃圾值。关键点:
Linux 安全审计工具,检查 SSH 配置、开放/监听端口、防火墙规则(ufw/iptables/nftables)、失败登录尝试、sudoers 权限...
-
tx_buf和rx_buf必须指向有效用户空间地址,不能为 NULL(哪怕只读也要传 dummy buffer) - 务必显式赋值:
tr.bits_per_word = 8;、tr.speed_hz = 1000000;、tr.delay_usecs = 0; - 若传感器需要 CS 保持低电平发多字节(如 ADIS16470),得设
tr.cs_change = 0并一次性传整个序列,不能分多次SPI_IOC_MESSAGE - 某些 SoC(如 Allwinner)对
len有硬件限制(如必须偶数),超长需拆包
C++ 封装时别直接裸用 ioctl,优先考虑 spidev_utils 或轻量 wrapper
裸写 ioctl 容易漏掉错误检查、资源清理和参数校验。一个最小可用的 RAII 封装要点:
class SpiDev {
int fd_;
public:
SpiDev(const char* dev) : fd_(open(dev, O_RDWR)) {
if (fd_ = 0) close(fd_); }
void xfer(std::vector<uint8_t>& tx, std::vector<uint8_t>& rx) {
struct spi_ioc_transfer tr = {};
tr.tx_buf = reinterpret_cast<__u64>(tx.data());
tr.rx_buf = reinterpret_cast<__u64>(rx.data());
tr.len = tx.size();
tr.speed_hz = 1000000;
tr.bits_per_word = 8;
if (ioctl(fd_, SPI_IOC_MESSAGE(1), &tr) != tr.len)
throw std::runtime_error("SPI_IOC_MESSAGE failed");
}
};
</__u64></__u64></uint8_t></uint8_t>
注意:C++ 标准库容器内存是连续的(std::vector),但不要传 std::string 的 c_str() 做 rx_buf(只读不可写)。另外,spidev 不支持异步 I/O,别试图用 epoll 监听它。
真正麻烦的从来不是打开设备或发几个字节,而是传感器上电时序、CS 延迟、读写命令格式这些硬件层约定——spidev 只管搬运字节,不管协议语义。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










