news 2026/4/16 22:40:13

IIC子系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
IIC子系统

实现基于IIC总线驱动的IIC设备驱动
例在之前的bsp里面裸机驱动,lm75->设备驱动,IIC->总线驱动 IIC的收发->(master_xfer())
调master_xfer所需资源:从机地址,adapter

分离,分层

设备跟驱动分离,总线驱动和设备驱动分层

一、设备跟驱动分离

这一层核心是两层总线驱动的解耦与映射,完成从硬件外设到内核抽象对象的转换。左侧 Platform 总线负责承载 I2C 控制器(主机侧),通过设备树(device dts)描述 I2C0/1 控制器的寄存器基地址、中断及时钟等硬件资源,内核据此生成 platform_device;与之匹配的 platform_driver(I2C 控制器驱动)在 probe 阶段初始化硬件、实现底层时序逻辑,并注册出 i2c_adapter(适配器),将控制器硬件抽象为统一的总线接口。右侧 I2C 总线则负责承载从设备,通过设备树(client dts)描述 LM75 等从设备的从机地址,内核生成对应 i2c_client;匹配的 i2c_driver(设备驱动)通过 adapter 绑定的通信算法,最终调用 master_xfer 完成与从设备的实际数据交互,实现了 “控制器驱动” 与 “从设备驱动” 的彻底分离。

操作起来就相当于client->adapter->algo->master_xfer

adapter:IIC的驱动程序,也可以认为一个IIC接口就是一个adapter,在上图中iic0和master_xfer()结合就可以当成一个adapter,一般认为板子上有几个iic接口就有多少个adapter

二、总线驱动和设备驱动分离

这一层是从用户操作到硬件动作的完整执行通路,体现了严格的分层设计。最上层是用户层 APP,通过打开 /dev/i2c-0、/dev/lm75 等设备节点发起业务请求;中间内核层进一步拆解为 “设备驱动层” 与 “core 核心层”:LM75、AT24C08 等设备驱动实现具体业务逻辑(如 lm75_read),并通过 I2C 核心层(core)做路由转发,将请求精准分发到对应的 adapter 总线驱动;最下层是 adapter 总线驱动与硬件:adapter 拿到请求后,调用其绑定的 master_xfer 函数,直接操作 I2C0/1 物理控制器寄存器,生成 SCL/SDA 时序信号,最终完成与硬件从设备的真实通信,实现了用户操作、内核逻辑与硬件控制的层层递进。

总线驱动相当于parent,设备驱动相当于child

只要把设备驱动写到client中

即应用程序可以通过调设备驱动再调adapter,也可以直接调adapter

一、直接调用adapter

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <linux/i2c-dev.h> #include <sys/ioctrl.h> int main() { int fd,ret; fd = open("/dev/i2c-0", O_RDWR); if(fd < 0) { perror("open /dev/key failed"); return -1; } ioctrl(fd,I2C_SLAVE,0x48); while(1) { unsigned char buf[2] = {0}; read(fd,buf,sizeof(buf)); printf("temp = %d %d\n",buf[0],buf[1]); } close(fd); return 0; }

二、通过调用设备驱动调用adapter

在写设备树的时候,与之前不同,拿lm75举例,要么和i2c-0匹配,要么和i2c-1匹配,而adapter总线驱动人家已经帮你实现了,设备树里面有i2c的信息,因此便把lm75的接口加到i2c的后面,加到哪个后面就用哪个i2c

&i2c1 { clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1>; status = "okay"; ap3216c@1e { compatible = "alientek,ap3216c"; reg = <0x1e>; }; lm75@48{ compatible = "ti,lm75"; reg = <0x48>; }; };
#include <linux/init.h> #include <linux/printk.h> #include <linux/kdev_t.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/export.h> #include <asm/uaccess.h> #include <asm/string.h> #include <asm/io.h> #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/i2c.h> #define DEV_NAME "lm75" static struct i2c_client * pclient; static int open(struct inode * node, struct file * file) { printk("lm75 open...\n"); return 0; } static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset) { int ret = 0; unsigned char data[2] = {0}; struct i2c_msg msg = { .addr = pclient->addr, .flags = I2C_M_RD, .len = 2, .buf = data }; ret = pclient->adapter->algo->master_xfer(pclient->adapter, &msg, 1); if(ret < 0) return ret; ret = copy_to_user(buf, data, sizeof(data)); printk("lm75 read...\n"); return ret; } static int close(struct inode * node, struct file * file) { printk("lm75 close...\n"); return 0; } static struct file_operations fops = { .owner = THIS_MODULE, .open = open, .read = read, .release = close }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEV_NAME, .fops = &fops }; static int probe(struct i2c_client * client, const struct i2c_device_id * id) { int ret = misc_register(&misc); if(IS_ERR_VALUE(ret)) goto err_misc; pclient = client; printk("lm75 probe misc_deregister slave addr = 0x%02x...\n", pclient->addr); return 0; err_misc: printk("lm75 probe err ret = %d\n", ret); misc_deregister(&misc); return ret; } static int remove(struct i2c_client * client) { misc_deregister(&misc); printk("remove lm75 misc_deregister ##############\n"); return 0; } static const struct of_device_id match_table[] = { [0] = {.compatible = "ti,lm75"} }; static const struct i2c_device_id lm75_id_table[] = { [0] = {.name = "ti,lm75"} }; static struct i2c_driver lm75_driver = { .probe = probe, .remove = remove, .driver = { .name = DEV_NAME, .of_match_table = match_table }, .id_table = lm75_id_table }; static int __init lm75_init(void) { int ret = i2c_add_driver(&lm75_driver); if(ret < 0) goto err_reg; printk("lm75 i2c_add_driver ...\n"); return 0; err_reg: printk("lm75 i2c_add_driver err ...\n"); i2c_del_driver(&lm75_driver); return ret; } static void __exit lm75_exit(void) { i2c_del_driver(&lm75_driver); printk("lm75 i2c_del_driver ...\n"); } module_init(lm75_init); module_exit(lm75_exit); MODULE_LICENSE("GPL");

注意:与之前platform总线的驱动不同

1、i2c有自己的driver结构体struct i2c_driver

并且里面要给.id_table赋值

2、与设备树匹配时,不光要匹配compatible,还要匹配name,这两个要同时存在,不然设备树匹配不上

3、注册函数也不同

这里补充一下三种不同的注册

1、普通驱动:
// 1. 注册字符设备 register_chrdev(250, "mydev", &fops); // 2. 创建类 cls = class_create(THIS_MODULE, "mycls"); // 3. 创建设备节点 /dev/mydev device_create(cls, NULL, MKDEV(250,0), NULL, "mydev");
2、i2c:
// 1. 注册I2C驱动 i2c_add_driver(&lm75_driver);
3、spi:
// 1. 注册SPI驱动 spi_register_driver(&adxl345_driver);

i2c和spi字符设备注册在probe里面完成,无需手动写register_chrdev

4、i2c也有自己独特的probe和remove

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 22:39:10

2025集美大学机械考研复试全套资料|海洋装备与机械工程学院专用|含136道机械制造简答+真+网课+导师咨询指南

温馨提示&#xff1a;文末有联系方式2025集美大学机械考研复试权威资料包 专为报考集美大学海洋装备与机械工程学院机械专业考生精心整理&#xff0c;紧扣最新复试大纲与历年考核趋势&#xff0c;内容全面、更新及时、实战性强。超详实机械制造简答库&#xff08;136&#xff0…

作者头像 李华
网站建设 2026/4/16 22:33:12

从芯片到功能:四路抢答器核心电路设计全解析

1. 四路抢答器设计入门指南 第一次接触四路抢答器设计时&#xff0c;我被那一堆芯片和连线搞得头晕眼花。直到亲手焊完第一块板子&#xff0c;才真正理解这个看似复杂的系统其实就像一场精心编排的舞蹈——每个芯片都在自己的位置上发挥着独特作用。四路抢答器本质上就是一个能…

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

用CubeMX+Keil五分钟搞定STM32F4 ADC配置:含多通道扫描模式实战

用STM32CubeMXKeil实现高效ADC配置&#xff1a;从单通道到多通道扫描实战 对于刚接触STM32开发的工程师来说&#xff0c;ADC&#xff08;模数转换器&#xff09;配置往往是第一个需要跨越的技术门槛。传统的手动寄存器配置方式虽然灵活&#xff0c;但对于项目周期紧张或刚入门的…

作者头像 李华
网站建设 2026/4/16 22:25:16

别再傻傻分不清了!钕铁硼磁铁Hcb和Hcj到底啥区别?选型避坑指南

钕铁硼磁铁Hcb与Hcj的工程解码&#xff1a;从参数到选型的实战指南 在电机设计实验室里&#xff0c;张工正对着两款外观几乎相同的钕铁硼磁铁样品发愁——供应商提供的N52和52H牌号磁铁&#xff0c;价格相差近30%&#xff0c;技术手册上密密麻麻的Hcb、Hcj参数让他难以抉择。这…

作者头像 李华