Linux SPI 驱动开发学习笔记:从框架到实战

发布时间:2026/10/15 5:17:21
Linux SPI 驱动开发学习笔记:从框架到实战
1. 引言SPISerial Peripheral Interface串行外设接口是一种高速、全双工、同步的通信总线广泛应用于嵌入式 Linux 系统中连接传感器、Flash、ADC、显示屏等外设。本文记录我在学习 Linux SPI 驱动开发过程中的核心知识点、框架结构和实战经验希望能帮助同样在学习 SPI 驱动的读者少走弯路。2. SPI 协议基础在深入 Linux 驱动之前先回顾 SPI 协议的基本概念。SPI 总线由四根信号线组成SCLK时钟线由主机产生用于同步数据传输。MOSI主机输出从机输入主机向从机发送数据。MISO主机输入从机输出从机向主机发送数据。CS片选线低电平有效用于选中目标从机设备。SPI 通信有四种工作模式由时钟极性CPOL和时钟相位CPHA组合决定模式CPOLCPHA数据采样时机Mode 000时钟上升沿采样Mode 101时钟下降沿采样Mode 210时钟下降沿采样Mode 311时钟上升沿采样驱动开发中必须根据从机数据手册正确配置模式否则通信将完全失败。3. Linux SPI 子系统架构Linux 内核将 SPI 驱动划分为三个层次理解这个分层结构是学习的关键控制器驱动SPI Master Driver负责操作具体的 SPI 控制器硬件如寄存器读写、DMA 传输等。核心层SPI Core提供统一的框架管理总线、设备和驱动的注册与匹配。设备驱动SPI Device Driver面向具体外设实现读写逻辑和业务功能。这种分层设计使得控制器驱动与设备驱动解耦同一外设驱动可以运行在不同平台的 SPI 控制器上。4. 关键数据结构Linux SPI 子系统围绕几个核心数据结构展开理解它们是编写驱动的前提。4.1 spi_master 结构体struct spi_master代表一个 SPI 控制器主要字段包括struct spi_master { struct device dev; u16 bus_num; /* 总线编号 */ u32 mode_bits; /* 支持的 SPI 模式 */ u32 bits_per_word_mask; /* 支持的位宽 */ u32 min_speed_hz; u32 max_speed_hz; int (*transfer_one)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer); /* ... */ };4.2 spi_device 结构体struct spi_device代表挂在 SPI 总线上的从设备通常在设备树中描述struct spi_device { struct device dev; struct spi_master *master; /* 所属控制器 */ u32 max_speed_hz; /* 最大时钟频率 */ u8 chip_select; /* 片选编号 */ u8 mode; /* SPI 模式 */ u8 bits_per_word; /* 每次传输的位数 */ /* ... */ };4.3 spi_transfer 与 spi_message一次 SPI 通信由spi_message承载其中包含一个或多个spi_transfer每个 transfer 描述一段独立的收发数据struct spi_transfer { const void *tx_buf; /* 发送缓冲区 */ void *rx_buf; /* 接收缓冲区 */ unsigned len; /* 传输长度 */ u32 speed_hz; /* 本次传输速率 */ u8 bits_per_word; u16 delay_usecs; /* ... */ }; struct spi_message { struct list_head transfers; struct spi_device *spi; int actual_length; /* ... */ };5. 设备树配置在现代 Linux 内核中SPI 从设备通常通过设备树Device Tree描述。以下是一个典型的 SPI 从设备节点示例spi0 { status okay; cs-gpios gpio1 10 GPIO_ACTIVE_LOW; mcp3008: mcp30080 { compatible microchip,mcp3008; reg 0; /* 片选索引 */ spi-max-frequency 1000000; /* 1MHz */ spi-mode 0; /* Mode 0 */ }; };设备树中的compatible属性用于与驱动中的of_match_table匹配reg指定片选编号spi-max-frequency和spi-mode分别配置通信速率和工作模式。6. 编写 SPI 设备驱动下面以一个虚拟的温度传感器驱动为例演示 SPI 设备驱动的完整编写流程。6.1 驱动框架#include linux/module.h #include linux/spi/spi.h #include linux/of.h static int temp_probe(struct spi_device *spi) { /* 初始化设备 */ spi-mode SPI_MODE_0; spi-max_speed_hz 1000000; spi_setup(spi); return 0; } static void temp_remove(struct spi_device *spi) { /* 清理资源 */ } static const struct of_device_id temp_of_match[] { { .compatible vendor,temp-sensor }, { } }; MODULE_DEVICE_TABLE(of, temp_of_match); static struct spi_driver temp_driver { .driver { .name temp_sensor, .of_match_table temp_of_match, }, .probe temp_probe, .remove temp_remove, }; module_spi_driver(temp_driver); MODULE_LICENSE(GPL);6.2 数据传输核心的数据读写通过spi_sync_transfer或手动构造spi_message完成static int temp_read_reg(struct spi_device *spi, u8 reg, u8 *val) { u8 tx_buf[2] { reg, 0x00 }; u8 rx_buf[2] { 0 }; struct spi_transfer tr { .tx_buf tx_buf, .rx_buf rx_buf, .len 2, }; struct spi_message msg; spi_message_init(msg); spi_message_add_tail(tr, msg); int ret spi_sync(spi, msg); if (ret) return ret; *val rx_buf[1]; return 0; }7. 调试技巧与常见问题SPI 驱动调试过程中我总结了一些实用技巧和常见坑点使用逻辑分析仪抓取 SCLK、MOSI、MISO、CS 信号确认时序是否符合预期这是最直观的调试手段。检查设备树通过/sys/bus/spi/devices/查看设备是否成功注册确认 compatible 匹配是否正确。模式不匹配CPOL/CPHA 配置错误会导致数据错位务必对照数据手册确认。片选信号异常如果使用 GPIO 模拟片选注意确认 GPIO 的 active 电平配置。时钟频率过高超出从机支持的最大频率会导致通信不稳定适当降低spi-max-frequency。8. 总结Linux SPI 驱动开发的核心在于理解三层架构、掌握关键数据结构、正确配置设备树并熟悉数据传输流程。建议初学者先从简单的字符设备驱动入手配合逻辑分析仪逐步验证再过渡到复杂的 DMA 传输和中断处理场景。希望这份学习笔记对你有帮助欢迎在评论区交流讨论。