news 2026/5/28 19:45:38

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/28 18:54:46

【开题答辩全过程】以 基于微信小程序的当当手办店铺为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华
网站建设 2026/5/20 16:53:26

蛋白质设计(十二)— —(搬运)Protein Design Workflows introduction

关于RFdiffusion还有太多没搞懂的地方&#xff0c;不方便出教程误导大家。近期看到了RFdiffusion团队在油管平台有很多视频&#xff0c;近期准备整理成系列图文转发在本人平台&#xff0c;以饲读者。 初步定为两三天一期&#xff0c;如果视频较长&#xff0c;会整理为不同部分…

作者头像 李华
网站建设 2026/5/25 18:46:26

XMSLEEP:白噪音神器,哄娃睡觉不再难

XMSLEEP&#xff1a;白噪音神器&#xff0c;哄娃睡觉不再难 有宝宝的家长或许都有过类似经历&#xff1a;为了让哭闹的宝宝安静下来&#xff0c;我们不得不长时间举着嗡嗡作响的吹风机&#xff0c;或是让水龙头持续哗哗地流水。这些临时制造的“白噪音”虽然偶尔能短暂起效&am…

作者头像 李华
网站建设 2026/5/28 16:49:01

Windows Android子系统探索指南:从入门到精通

Windows Android子系统探索指南&#xff1a;从入门到精通 【免费下载链接】WSABuilds Run Windows Subsystem For Android on your Windows 10 and Windows 11 PC using prebuilt binaries with Google Play Store (MindTheGapps) and/or Magisk or KernelSU (root solutions) …

作者头像 李华
网站建设 2026/5/14 0:19:08

Qwen2.5-VL-7B零基础教程:5分钟搭建RTX 4090专属视觉助手

Qwen2.5-VL-7B零基础教程&#xff1a;5分钟搭建RTX 4090专属视觉助手 你不需要懂模型结构&#xff0c;不用配环境变量&#xff0c;不装CUDA驱动——只要有一张RTX 4090显卡&#xff0c;5分钟内就能跑起一个真正能“看图说话”的本地视觉助手。它不是网页版Demo&#xff0c;不是…

作者头像 李华