I2C与SPI设备驱动实战:嵌入式Linux下的外设通信协议开发完整指南
I2C与SPI设备驱动实战嵌入式Linux下的外设通信协议开发完整指南一、总线选择的工程决策I2C与SPI的本质差异I2C和SPI是嵌入式Linux中最常见的两种外设通信总线但很多选型决策停留在看引脚数的直觉层面。实际上两条总线在物理层、协议层和软件栈上都有本质差异选型需要从四个核心维度综合评估引脚数量、通信速率、拓扑复杂度和内核驱动的成熟度。flowchart TD A[外设通信需求分析] -- B{MCU引脚资源紧张?} B --|是仅2根可用| C{速率要求} B --|否引脚充裕| D{速率要求与实时性} C --|低于400kbps| E[I2C: 2线SDASCL] C --|超过1Mbps| F[SPI: 至少4线MISOMOSISCLKCS] D --|低于10Mbps| G{拓扑复杂度分析} D --|高于10Mbps| F G --|多从设备共享总线| E G --|单从设备独占或星型连接| H[SPI也可选用] E -- I[设备树配置与驱动编写] F -- I H -- I I -- J[集成测试逻辑分析仪验证] style E fill:#3498db,color:#fff style F fill:#e67e22,color:#fff style J fill:#27ae60,color:#fff在实际工程经验中规则非常清晰温度、湿度、加速度、陀螺仪等传感器几乎都用I2C因为它们的数据量小每帧几个字节、不需要高速率400kHz以内足够、总线上可以挂多个设备最多127个受7位地址限制。而LCD屏幕、SPI Flash、高速ADC/DAC等外设几乎必用SPI因为它们的吞吐量要求1~50MHz远超I2C的能力范围而且通常是控制器的独占设备。I2C的另一个优势是硬件实现简单——两根线加两个上拉电阻即可。但这恰恰也是I2C最常见故障的根源上拉电阻的阻值选择影响总线速率和抗干扰能力。SPI不需要上拉电阻但它的多线结构最少4线带中断可能到6线在PCB布局时需要更谨慎的布线规划。二、I2C设备驱动的标准框架与关键细节I2C驱动在Linux内核中有非常成熟的标准框架i2c_driver结构体是核心入口probe函数负责设备初始化和资源申请remove负责清理id_table和of_match_table负责设备和驱动的匹配。/* i2c_temp_sensor.c — I2C温度传感器完整驱动 */ #include linux/i2c.h #include linux/module.h #include linux/of.h #include linux/sysfs.h #include linux/delay.h #define SENSOR_REG_TEMP 0x00 #define SENSOR_REG_CONF 0x01 #define SENSOR_REG_HYST 0x02 #define SENSOR_I2C_ADDR 0x48 #define SENSOR_CONV_DELAY 10 /* ADC转换等待时间ms */ struct sensor_data { struct i2c_client *client; struct mutex lock; int last_temp; /* 单位: m°C */ }; static int sensor_write_reg( struct i2c_client *client, u8 reg, u8 value ) { u8 buf[2] {reg, value}; int ret; ret i2c_master_send(client, buf, 2); if (ret ! 2) { dev_err(client-dev, I2C write reg 0x%02x failed: %d\n, reg, ret); return ret 0 ? ret : -EIO; } return 0; } static int sensor_read_temp( struct i2c_client *client, int *temp ) { u8 reg SENSOR_REG_TEMP; u8 buf[2] {0}; int ret; /* 第一步发出寄存器地址 */ ret i2c_master_send(client, reg, 1); if (ret 0) { dev_err(client-dev, I2C write reg addr failed: %d\n, ret); return ret; } /* 等待ADC完成转换 */ msleep(SENSOR_CONV_DELAY); /* 第二步读取温度值2字节大端序 */ ret i2c_master_recv(client, buf, 2); if (ret 0) { dev_err(client-dev, I2C read temp failed: %d\n, ret); return ret; } /* 将2字节原始值拼为16位有符号整数 */ *temp (buf[0] 8) | buf[1]; /* 补码转有符号如果最高位为1 */ if (*temp 0x8000) *temp - 65536; /* 典型传感器12位精度LSB0.0625°C */ /* 转换为毫摄氏度m°C */ *temp (*temp * 625) / 10; return 0; } static int sensor_probe(struct i2c_client *client) { struct sensor_data *data; struct device *dev client-dev; int ret, temp; /* 使用设备资源管理分配无需手动释放 */ data devm_kzalloc( dev, sizeof(*data), GFP_KERNEL ); if (!data) return -ENOMEM; >/* spi_flash.c — SPI Flash驱动含DMA */ #include linux/spi/spi.h #include linux/dma-mapping.h #define FLASH_CMD_READ 0x03 #define FLASH_CMD_WREN 0x06 #define FLASH_PAGE_SIZE 256 #define FLASH_SPI_SPEED 20000000 /* 20MHz */ struct flash_data { struct spi_device *spi; struct mutex lock; u8 *tx_buf; u8 *rx_buf; dma_addr_t tx_dma; dma_addr_t rx_dma; }; static void flash_setup_spi(struct spi_device *spi) { /* 配置SPI工作模式 * MODE0: CPOL0, CPHA0 * 即空闲时钟低电平上升沿采样 */ spi-mode SPI_MODE_0; spi-bits_per_word 8; spi-max_speed_hz FLASH_SPI_SPEED; if (spi_setup(spi)) dev_err(spi-dev, SPI setup failed\n); } static int flash_read_page( struct spi_device *spi, u32 addr, u8 *buf, size_t len ) { struct spi_transfer xfer[2] {0}; struct spi_message msg; u8 cmd[4]; int ret; /* 构造命令头读命令(1B)地址(3B) */ cmd[0] FLASH_CMD_READ; cmd[1] (addr 16) 0xFF; cmd[2] (addr 8) 0xFF; cmd[3] addr 0xFF; /* 第一个传输段发送命令地址 */ xfer[0].tx_buf cmd; xfer[0].len 4; /* 第二个传输段接收数据可使用DMA加速 */ xfer[1].rx_buf buf; xfer[1].len len; /* 构造并同步执行SPI消息 */ spi_message_init(msg); spi_message_add_tail(xfer[0], msg); spi_message_add_tail(xfer[1], msg); ret spi_sync(spi, msg); if (ret) dev_err(spi-dev, SPI read page failed: %d\n, ret); return ret; } static int flash_probe(struct spi_device *spi) { struct flash_data *data; struct device *dev spi-dev; flash_setup_spi(spi); data devm_kzalloc( dev, sizeof(*data), GFP_KERNEL ); if (!data) return -ENOMEM; >/* 设备树配置I2C和SPI总线的完整声明 */ /* I2C总线 */ i2c1 { pinctrl-names default; pinctrl-0 pinctrl_i2c1; clock-frequency 100000; /* 100kHz标准模式 */ status okay; temp_sensor: temp-sensor48 { compatible vendor,temp-sensor; reg 0x48; /* 7位I2C地址 */ interrupt-parent gpio1; interrupts 5 IRQ_TYPE_EDGE_FALLING; }; }; /* SPI总线 */ ecspi2 { pinctrl-names default; pinctrl-0 pinctrl_ecspi2; cs-gpios gpio5 13 GPIO_ACTIVE_LOW; status okay; flash: flash0 { compatible vendor,spi-flash; reg 0; /* 片选索引0 */ spi-max-frequency 20000000; spi-cpha; /* CPHA1, MODE1 */ }; }; /* GPIO引脚复用 */ iomuxc { pinctrl_i2c1: i2c1grp { fsl,pins MX8MQ_IOMUXC_I2C1_SCL_I2C1_SCL 0x4000007f MX8MQ_IOMUXC_I2C1_SDA_I2C1_SDA 0x4000007f ; }; pinctrl_ecspi2: ecspi2grp { fsl,pins MX8MQ_IOMUXC_ECSPI2_SCLK_ECSPI2_SCLK 0x82 MX8MQ_IOMUXC_ECSPI2_MOSI_ECSPI2_MOSI 0x82 MX8MQ_IOMUXC_ECSPI2_MISO_ECSPI2_MISO 0x82 MX8MQ_IOMUXC_ECSPI2_SS0_GPIO5_IO13 0x82 ; }; };调试三板斧在实际工程中非常重要I2C设备检测i2cdetect -y 1扫描总线上所有地址确认设备在位。如果设备显示UU表示被内核驱动占用正常显示数字地址表示设备在线但无驱动接管异常显示--表示无回应硬件连接或上拉电阻问题。SPI通道测试spidev_test -D /dev/spidev0.0测试SPI通道的基本读写确认信号连通。逻辑分析仪排查当软件层面查不出问题时逻辑分析仪是终极武器。连接到SDA/SCLI2C或MOSI/MISO/SCLK/CSSPI直接观察总线波形。I2C常见问题包括上拉电阻过大导致信号边沿变缓、时钟拉伸过长。SPI常见问题包括时钟极性和相位配置错误、片选信号在传输完成前提前拉高。五、总结I2C适合引脚少2线、多从设备最多127个、速率不超过400kbps的低速传感器场景SPI适合高速1~50MHz、全双工、外设独占的显示和存储场景I2C驱动标准框架i2c_driver结构体probe/remove/id_table、devm_kzalloc资源管理、probe末尾做验证性读取确认硬件就绪SPI驱动核心概念spi_transfer数组spi_message实现命令/数据连续传输高速场景必须用dmam_alloc_coherent分配DMA缓冲区设备树是硬件到驱动的配置桥梁compatible属性匹配驱动of_match_tablereg属性指定I2C地址或SPI片选索引调试三阶段软件层面i2cdetect/spidev_test/dmesg→电压层面万用表测上拉电压→信号层面逻辑分析仪看波形按这个顺序逐层排查效率最高

相关新闻

rag与agent

rag与agent

一些文章 Agent相关面试题整理 30天智能体工程实战 深入理解 ReAct 模式:基于Spring AI从0到1实现一个ReAct Agent 推荐阅读 hello agent 第四章 智能体经典范式构建 | Hello Agent AI Agent上下文工程(context engineering)知识学习总结 九卷 它的github 不错 …

2026/7/10 22:13:31阅读更多 →
基于改进YOLOV11-SOEP的工厂钢材表面缺陷识别系统研究(含源码+数据集+论文)

基于改进YOLOV11-SOEP的工厂钢材表面缺陷识别系统研究(含源码+数据集+论文)

摘要 钢铁作为现代工业的基础材料,其表面缺陷检测对产品质量控制至关重要。传统人工检测方法存在效率低、主观性强、易疲劳等问题,难以满足现代钢铁工业大规模生产需求。本研究提出一种基于YOLOV11-SOEP的钢铁缺陷检测方法,通过引入SOEP&…

2026/7/10 22:13:31阅读更多 →
Linux 命令之 users 详解(查看当前登录用户)

Linux 命令之 users 详解(查看当前登录用户)

1. 命令简介 users 命令用于显示当前登录到系统的所有用户名。该命令会读取系统登录记录文件,并输出当前活跃登录会话对应的用户名列表。如果一个用户有多个登录会话(例如通过多个终端或 SSH 连接登录),则该用户名会在输出中重复…

2026/7/10 22:13:31阅读更多 →
HarmonyOS实战《疆域纪行》第04篇|探索页检索链路:关键词、地区筛选和空状态联动

HarmonyOS实战《疆域纪行》第04篇|探索页检索链路:关键词、地区筛选和空状态联动

这一篇讲一个很实用的链路:不接后端,如何在本地内容库上完成搜索、类型切换、地区筛选、主题筛选、排序和无结果提示。探索页是内容 App 的“找东西”能力,写顺了,用户才不会迷路。项目落点:搜索状态:searc…

2026/7/10 23:28:41阅读更多 →
英雄联盟战绩查询神器:Seraphine 实战指南

英雄联盟战绩查询神器:Seraphine 实战指南

英雄联盟战绩查询神器:Seraphine 实战指南 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine 还在为查看队友战绩而频繁切换网页吗?还在为分析对手实力而手忙脚乱吗?Seraphine…

2026/7/10 23:28:41阅读更多 →
空洞骑士模组管理的终极解决方案:Scarab如何彻底改变你的游戏体验

空洞骑士模组管理的终极解决方案:Scarab如何彻底改变你的游戏体验

空洞骑士模组管理的终极解决方案:Scarab如何彻底改变你的游戏体验 【免费下载链接】Scarab An installer for Hollow Knight mods written with Avalonia. 项目地址: https://gitcode.com/gh_mirrors/sc/Scarab 你是否曾经因为模组安装的复杂性而放弃为《空洞…

2026/7/10 23:28:41阅读更多 →
【从5W到240W快充拓扑演进:看懂消费电源与新能源车载电源的底层同源逻辑【2026工程师干货】】

【从5W到240W快充拓扑演进:看懂消费电源与新能源车载电源的底层同源逻辑【2026工程师干货】】

从5W到240W快充拓扑演进:看懂消费电源与新能源车载电源的底层同源逻辑【2026工程师干货】 📌 内容简述(读者速览) 2026年电源行业高频热搜词:高功率密度、软开关、GaN/SiC宽禁带、拓扑迭代、算电协同、800V高压平台。 …

2026/7/10 23:28:41阅读更多 →
3PL OWTB 平台 —— 老板们的系统期望

3PL OWTB 平台 —— 老板们的系统期望

站在 3PL 企业老板视角,看 OWTB 到底要解决什么一、 老板们不关心"功能",关心"四本账"3PL 老板每天睁眼想的不是 PDA 怎么扫、波次怎么拆,而是这四件事:账本老板心里话OWTB 能给什么收入账​"这个月蒙牛…

2026/7/10 23:28:41阅读更多 →
专业排版还原实战:如何解决Grok到word的格式问题?解析数据结构化迁移及“AI 导出鸭”工具的价值

专业排版还原实战:如何解决Grok到word的格式问题?解析数据结构化迁移及“AI 导出鸭”工具的价值

一 AI 导出鸭技术详解:如何解决Grok到word的格式问题 二 如何解决Grok到word的格式问题?硬核方案与AI 导出鸭实践 三 从乱码到规范:借助AI 导出鸭攻克如何解决Grok到word的格式问题正文:深入解析Grok到Word的格式转换困局与智能解…

2026/7/10 23:23:41阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/10 12:10:00阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/10 12:29:21阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/10 4:59:05阅读更多 →
浏览器缓存行为深度解析:Chrome/Firefox/Safari 对 304 响应的 5 种触发场景对比

浏览器缓存行为深度解析:Chrome/Firefox/Safari 对 304 响应的 5 种触发场景对比

浏览器缓存行为深度解析:Chrome/Firefox/Safari 对 304 响应的 5 种触发场景对比当你在浏览器地址栏敲入一个网址时,背后可能隐藏着一场关于"要不要重新下载资源"的精密博弈。这场博弈的裁判是HTTP缓存机制,而304状态码则是这场博弈…

2026/7/10 0:00:01阅读更多 →
RoboWits:面向创造性问题求解的双臂机器人认知推理基准

RoboWits:面向创造性问题求解的双臂机器人认知推理基准

1. 项目概述:这不是又一个机器人抓取数据集,而是一次对“思考力”的压力测试 RoboWits——这个名字里藏着两个关键信号:“Robo”直指物理世界中的具身智能体,“Wits”则毫不掩饰地指向人类最核心的认知能力:机敏、判断…

2026/7/10 0:00:01阅读更多 →
5分钟完全指南:如何使用TegraRcmGUI图形化工具解锁Switch无限可能

5分钟完全指南:如何使用TegraRcmGUI图形化工具解锁Switch无限可能

5分钟完全指南:如何使用TegraRcmGUI图形化工具解锁Switch无限可能 【免费下载链接】TegraRcmGUI C GUI for TegraRcmSmash (Fuse Gele exploit for Nintendo Switch) 项目地址: https://gitcode.com/gh_mirrors/te/TegraRcmGUI TegraRcmGUI是一款专为Windows…

2026/7/10 0:00:01阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/10 13:39:09阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/10 22:20:33阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/10 17:29:22阅读更多 →