news 2026/5/1 10:53:32

Linux内核驱动--IIC子系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内核驱动--IIC子系统

一、IIC子系统

Linux I2C 子系统是内核提供的分层式软件框架,用于统一管理 I2C(Inter-Integrated Circuit)总线相关的硬件与软件,无需关注底层总线时序,就能快速实现 I2C 外设(如传感器、存储芯片)的驱动开发与应用交互。
其核心是 “分层解耦”,从上到下分为应用层、内核核心层、总线驱动层、硬件层,每层通过标准化接口通信,屏蔽了 I2C 控制器硬件差异和总线时序细节,简化了驱动开发与外设适配流程。

二、Linux IIC 子系统核心架构

  • 应用层:通过操作/dev目录下的设备节点(如/dev/lm75),调用标准文件 IO 函数(open、read、write、close)与底层驱动交互,无需关注 I2C 总线细节。
  • 内核核心层(I2C Core):内核提供的核心框架,负责管理 I2C 适配器、设备驱动的匹配与绑定,提供统一的 API 供设备驱动调用(如 I2C 消息传输函数)。
  • 总线驱动层(I2C Adapter Driver):对应硬件的 I2C 控制器驱动(如 I2C0、I2C1 控制器),实现底层总线通信时序(如 SCL、SDA 信号控制),向上为核心层提供传输接口。
  • 设备驱动层(I2C Client Driver):具体外设的驱动(如 LM75、AT24C08 驱动),注册设备相关的文件操作接口,通过核心层调用总线驱动完成数据收发。
  • 硬件层:包括 I2C 控制器(如 MCU 内置 I2C 外设)和 I2C 从设备(如 LM75 温度传感器、AT24C08 存储芯片)。

三、LM75温度传感器驱动开发

1.驱动核心结构设计

I2C 设备驱动的核心是填充 i2c_driver 结构体,该结构体包含驱动匹配规则、探针函数(probe)、移除函数(remove)等关键信息,是驱动与内核核心层交互的桥梁。

// LM75驱动核心结构体 static struct i2c_driver lm75_driver = { .probe = probe, // 设备与驱动匹配成功后执行 .remove = remove, // 设备被移除或驱动卸载时执行 .driver = { .name = DEV_NAME, // 驱动名称,用于sysfs显示 .of_match_table = of_lm75_table // 设备树匹配表 }, .id_table = lm75_table // 传统匹配表(非设备树场景) };

2. 驱动匹配机制

设备树匹配:通过of_device_id结构体指定兼容属性(如"ti,lm75"),与设备树中定义的 I2C 设备节点的compatible属性匹配。

static const struct of_device_id of_lm75_table[] = { {.compatible = "ti,lm75"}, // 与设备树节点compatible属性一致 {} // 数组结束标记 };

传统 ID 匹配:通过 i2c_device_id 结构体指定设备名称,适用于无设备树的旧系统

static const struct i2c_device_id lm75_table[] = { {.name = "ti,lm75"}, {} };

3. 探针函数(probe):驱动与设备的 “绑定入口”

当内核核心层检测到 I2C 总线上的设备与驱动匹配时,会调用probe函数,完成设备初始化、设备节点注册等核心工作。

static int probe(struct i2c_client *client, const struct i2c_device_id *device) { int ret = misc_register(&misc_dev); // 注册misc设备节点(/dev/lm75) if(ret < 0) goto err_misc_register; lm75_client = client; // 保存I2C客户端指针(包含从设备地址、适配器等信息) printk("lm75 probe\n"); return 0; err_misc_register: printk("lm75 probe misc_register failed\n"); return ret; }
  • misc_register:注册为杂项设备(miscdevice),无需手动分配主设备号,内核会自动分配动态次设备号,设备节点名为 DEV_NAME(即 lm75)。
  • i2c_client:代表 I2C 总线上的从设备,包含从设备地址(client->addr)、对应的 I2C 适配器(client->adapter)等关键信息。

4.文件操作接口

// 文件操作结构体 static struct file_operations fops = { .owner = THIS_MODULE, // 声明驱动所属模块 .open = open, // 应用层调用open时触发 .read = read, // 应用层调用read时触发 .write = write, // 本文暂不实现写功能 .release = close // 应用层调用close时触发 }; static int open(struct inode *inode, struct file *file) { printk("lm75 open\n"); return 0; } static int close(struct inode *inode, struct file *file) { printk("lm75 close\n"); return 0; } static ssize_t read(struct file *file, char __user *buf, size_t size, loff_t *loff) { int ret = 0; unsigned char temp[2] = {0}; // 存储LM75读取的2字节温度数据 // 构造I2C读取消息 struct i2c_msg msg = { .addr = lm75_client->addr, // 从设备地址(LM75默认0x48) .flags = I2C_M_RD, // 读取标志(I2C_M_RD表示读操作) .len = 2, // 读取数据长度(LM75温度数据为2字节) .buf = temp // 数据存储缓冲区 }; // 调用I2C适配器的传输函数,发送1条I2C消息 lm75_client->adapter->algo->master_xfer(lm75_client->adapter, &msg, 1); // 将内核空间的数据拷贝到用户空间(应用层缓冲区) ret = copy_to_user(buf, temp, sizeof(temp)); return sizeof(temp); // 返回读取的字节数(固定2字节) }

5.. 驱动加载与卸载

static int __init lm75_driver_init(void) { int ret = i2c_add_driver(&lm75_driver); // 注册I2C驱动到内核核心层 if(ret < 0) goto err_i2c_add; printk("lm75_driver_init ...\n"); return 0; err_i2c_add: printk("lm75_driver_init failed...\n"); return ret; } static void __exit lm75_driver_exit(void) { i2c_del_driver(&lm75_driver); // 从内核注销I2C驱动 printk("lm75_driver_exit ...\n"); } module_init(lm75_driver_init); // 驱动加载入口 module_exit(lm75_driver_exit); // 驱动卸载入口 MODULE_LICENSE("GPL"); // 声明驱动遵循GPL协议(必须添加,否则内核报警)

四、应用层测试程序开发

int main(int argc, const char *argv[]) { // 打开/dev/lm75设备节点(O_RDWR表示可读可写) int fd = open("/dev/lm75", O_RDWR); if(fd < 0) { perror("open iic failed"); return 1; } unsigned char temp[2] = {0}; // 存储读取的2字节原始数据 while(1) // 循环读取温度,每秒1次 { // 读取2字节数据(与驱动read函数返回值一致) int ret = read(fd, temp, sizeof(temp)); // 温度解析:(temp[0]<<8 | temp[1]) 拼接两字节,右移7位取11位有效数据,乘以0.5 float t = 0.5 * ((temp[0] << 8 | temp[1]) >> 7); printf("Temperature: %.1f℃\n", t); // 输出温度(保留1位小数) sleep(1); // 延时1秒 } close(fd); // 关闭设备(实际不会执行,因循环无退出条件) return 0; }

五、总结

  1. Linux I2C 子系统是内核提供的分层式软件框架,核心作用是解耦 I2C 硬件与软件开发,屏蔽底层总线时序、控制器差异等细节,让开发者聚焦外设功能实现。
  2. 该子系统分为应用层(文件 IO 操作设备节点)、内核核心层(管理驱动匹配 / 数据传输)、总线驱动层(实现硬件时序)、硬件层(控制器 + 外设)四层,通过标准化接口完成数据交互。
  3. 基于该框架开发 I2C 外设驱动(如 LM75)时,核心是实现驱动匹配、probe 初始化、文件操作接口,应用层可通过标准文件 IO 与驱动交互,大幅降低开发复杂度。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 8:17:25

‍AI客服软件:应对高复杂售前咨询 图书行业客服能力的进化路径

图书行业一直被认为是“低客单、重咨询”的典型代表。看似标准化的商品背后&#xff0c;实际承载的是高度复杂的决策过程&#xff1a;版本差异、出版社不同、教材地区适配、内容体系差异、是否配套音视频资源……任何一个环节解释不清&#xff0c;都会直接影响用户是否下单。在…

作者头像 李华
网站建设 2026/5/1 6:47:34

Kafka 与大数据存储系统的完美融合方案

Kafka 与大数据存储系统的完美融合方案 关键词:Kafka、大数据存储、实时数据流、数据融合、分布式系统 摘要:在数字化时代,企业每天产生海量实时数据(如用户行为、IoT传感器、交易记录),这些数据需要“既快又稳”地被处理和存储。Kafka作为全球最流行的实时数据流平台,擅…

作者头像 李华
网站建设 2026/5/1 8:49:00

cv_resnet50_face-reconstruction VSCode Python环境配置详解

cv_resnet50_face-reconstruction VSCode Python环境配置详解 想在自己的电脑上跑通那个能从一张照片生成3D人脸的神奇模型吗&#xff1f;很多朋友在第一步——配置开发环境上就卡住了。网上的教程要么太零散&#xff0c;要么默认你已经是个Python老手&#xff0c;对新手一点都…

作者头像 李华
网站建设 2026/5/1 9:13:12

零基础教程:用亚洲美女-造相Z-Turbo一键生成惊艳人像

零基础教程&#xff1a;用亚洲美女-造相Z-Turbo一键生成惊艳人像 最近想用AI生成一些符合亚洲审美的精致人像&#xff0c;但发现很多模型要么对中文理解不好&#xff0c;要么生成的亚洲面孔总带着点“异域风情”&#xff0c;要么就是部署起来太复杂&#xff0c;对电脑配置要求…

作者头像 李华
网站建设 2026/5/1 7:57:06

小白必看!Cosmos-Reason1-7B推理工具保姆级使用教程

小白必看&#xff01;Cosmos-Reason1-7B推理工具保姆级使用教程想用AI解决数学题、逻辑推理或编程问题&#xff0c;但担心隐私泄露或使用限制&#xff1f;这个纯本地运行的推理工具可能是你的最佳选择你是否遇到过这样的情况&#xff1a;遇到复杂的数学题需要一步步推理&#x…

作者头像 李华