当前位置: 首页 > news >正文

Linux驱动开发:I2C子系统

目录

1、I2C简介

1.1 两根线

1.2 信号

1.3 写时序

1.4 读时序

1.5 I2C速率

1.6 I2C驱动框架简介

2、I2C设备驱动

2.1 I2C相关API

2.1.1 i2c_driver

2.1.2 注册:i2c_add_driver

2.1.3 注销:i2c_del_driver

2.1.4 module_i2c_driver:一键注册,不需要以上注册注销的过程

2.1.5 i2c_client

2.1.6 i2c_msg

2.1.7 i2c_transfer 

3、驱动程序

3.1 写消息封装

3.2 读消息封装

3.3 驱动程序

3.3.1 修改设备树

3.3.2 驱动程序编写

3.4 应用程序


1、I2C简介

1.1 两根线

I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL

1.2 信号

空闲状态:I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。

起始信号:当SCL为高电平期间,SDA由高到低的跳变

停止信号:当SCL为高电平期间,SDA由低到高的跳变

应答信号:在第九个时钟周期的时候,sda上低电平就代表应答

非应答信号:在第九个时候周期的时候,sda维持高电平

1.3 写时序

起始信号+七位从机地址+一位写(写是0 读是1)+ ack + 寄存器地址+数据+ ack +停止位

1.4 读时序

起始信号+七位从机地址+一位写(写是0 读是1)+ ack + 寄存器地址+ ack +

起始信号+七位从机地址+一位读(写实0 读是1)+ ack + 数据+NO ack + 停止位

1.5 I2C速率

100K 低速 400K 全速 3.4M 高速

1.6 I2C驱动框架简介

在Linux 内核中 I2C 的体系结构分为3 个部分:
1、I2C 核心:I2C 核心提供了I2C 总线驱动和设备驱动的注册、注销方法等。
2、I2C 总线驱动:I2C 总线驱动是对I2C 硬件体系结构中适配器端的实现,适配器可由 CPU 控制,甚至可以直接集成在CPU 内部。一般SOC 的 I2C 总线驱动都是由半导体厂商编写的,不需要用户去编写。因此我们不用关心 I2C 总线驱动具体是如何实现的,我们只要专注于 I2C 设备驱动即可。
3、I2C 设备驱动:I2C 设备驱动是对I2C 硬件体系结构中设备端的实现,设备一般挂接在受CPU 控制的I2C 适配器上,通过I2C 适配器与CPU 交换数据。

2、I2C设备驱动

2.1 I2C相关API

2.1.1 i2c_driver

struct i2c_driver { int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);int (*remove)(struct i2c_client *client);struct device_driver driver;
};struct device_driver {const char		*name;const struct of_device_id	*of_match_table;
};

2.1.2 注册:i2c_add_driver

#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)

2.1.3 注销:i2c_del_driver

void i2c_del_driver(struct i2c_driver *driver)

2.1.4 module_i2c_driver:一键注册,不需要以上注册注销的过程

定义在 linux/i2c.h 中

#define module_i2c_driver(__i2c_driver) module_driver(__i2c_driver, i2c_add_driver, i2c_del_driver)#define module_driver(__driver, __register, __unregister, ...) 
static int __init __driver##_init(void) 
{ return __register(&(__driver) , ##__VA_ARGS__); 
} 
module_init(__driver##_init); 
static void __exit __driver##_exit(void) 
{ __unregister(&(__driver) , ##__VA_ARGS__); 
} 
module_exit(__driver##_exit);
module_i2c_driver(myi2c);
->
#define module_i2c_driver(myi2c) module_driver(myi2c, i2c_add_driver, i2c_del_driver)#define module_driver(myi2c, i2c_add_driver, i2c_del_driver) 
static int __init myi2c_init(void) 
{ return i2c_add_driver(&myi2c); 
} static void __exit __driver##_exit(void) 
{ i2c_del_driver(&myi2c); 
} 
module_init(myi2c_init); 
module_exit(myi2c_exit);

2.1.5 i2c_client

struct i2c_client {  unsigned short flags; // 0写  1读unsigned short addr;  //从机地址		char name[I2C_NAME_SIZE]; //驱动的名字struct i2c_adapter *adapter;//控制器驱动的对象struct device dev;		//这是设备的对象
};

2.1.6 i2c_msg

有多少起始信号就有多少消息,消息的长度用字节表示

struct i2c_msg {__u16 addr;    //从机地址__u16 flags;   //读写标志位  0写 1度__u16 len;	   //消息长度__u8 *buf;	   //消息首地址
};

2.1.7 i2c_transfer 

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
/*
功能:消息的发送函数
参数:@adap:控制器的结构体对象@msgs:消息结构体的首地址@num:消息结构体的个数
返回值:成功返回num,否则就是失败
*/

3、驱动程序

3.1 写消息封装

起始信号+七位从机地址+一位写(写是0 读是1)+ ack + 寄存器地址+数据+ ack +停止位

char w_buf[] = {地址,数据};
struct i2c_msg w_msg = {.addr  = client->addr,.flags = 0,.len   = 2,.buf   = w_buf,
};

3.2 读消息封装

起始信号+七位从机地址+一位写(写是0 读是1)+ ack + 寄存器地址+ ack +

起始信号+七位从机地址+一位读(写实0 读是1)+ ack + 数据+NO ack + 停止位

char r_buf[] = {地址};
char val;
struct i2c_msg r_msg[] = {[0] = {.addr = client->addr,.flags = 0,.len = 1,.buf = r_buf,},[1] = {.addr = client->addr,.flags = 1,.len = 1,.buf = &val;},
};

3.3 驱动程序

3.3.1 修改设备树

&i2c1{pinctrl-names = "default", "sleep";pinctrl-0 = <&i2c1_pins_b>;pinctrl-1 = <&i2c1_sleep_pins_b>;i2c-scl-rising-time-ns = <100>;                                                                              i2c-scl-falling-time-ns = <7>;status = "okay";/delete-property/dmas;/delete-property/dma-names;si7006@40{compatible = "aaa,si7006";reg = <0x40>;};
};

3.3.2 驱动程序编写

由于 client 在各个函数中都需要用到,所以有两种方法,第一种是直接定义一个全局变量,在probe函数中获取到这个 client ,第二种实际上和第一种几乎一致,就是用一个 private_data 的指针去接住这个client。

#ifndef __SI7006_H__
#define __SI7006_H__#define GET_HUM  _IOR('l',0,int)
#define GET_TMP  _IOR('l',1,int)#define HUM_ADDR 0xe5
#define TMP_ADDR 0xe3
#endif
#define I2CNAME "si7006"
struct i2c_client* gclient;
int major = 0;
struct class* cls;
struct device* dev;int i2c_read(unsigned char reg)
{// 1.封装消息int ret;unsigned char r_buf[] = { reg };unsigned short val;struct i2c_msg r_msg[] = {[0] = {.addr = gclient->addr,.flags = 0,.len = 1,.buf = r_buf,},[1] = {.addr = gclient->addr,.flags = 1,.len = 2,.buf = (char *)&val,},};// 2.发送消息ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));if (ret != ARRAY_SIZE(r_msg)) {printk("i2c read hum or temp error\n");return -EAGAIN;}return val >> 8 | val << 8;
}
int si7006_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
long si7006_ioctl(struct file* file,unsigned int cmd, unsigned long arg)
{int ret, data;switch (cmd) {  case GET_HUM:data = i2c_read(HUM_ADDR);ret = copy_to_user((void*)arg, &data, 4);break;case GET_TMP:data = i2c_read(TMP_ADDR);ret = copy_to_user((void*)arg, &data, 4);break;}return 0;
}
int si7006_close(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
struct file_operations fops = {.open = si7006_open,.unlocked_ioctl = si7006_ioctl,.release = si7006_close,
};
int si7006_probe(struct i2c_client* client, const struct i2c_device_id* id)
{gclient = client;// 1.注册字符设备驱动major = register_chrdev(0, I2CNAME, &fops);// 2.自动创建设备节点cls = class_create(THIS_MODULE, I2CNAME);dev = device_create(cls, &client->dev, MKDEV(major, 0), NULL, I2CNAME);return 0;
}
int si7006_remove(struct i2c_client* client)
{device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, I2CNAME);return 0;
}struct of_device_id oftable[] = {{.compatible = "aaa,si7006",},{}
};struct i2c_driver si7006 = {.probe = si7006_probe,.remove = si7006_remove,.driver = {.name = "bbb",.of_match_table = oftable,}
};module_i2c_driver(si7006);

3.4 应用程序

#include "si7006.h"int main(int argc, const char* argv[])
{int fd;int data,hum,tmp;if ((fd = open("/dev/si7006", O_RDWR)) == -1)PRINT_ERR("open error");while (1) {ioctl(fd, GET_HUM, &hum);ioctl(fd, GET_TMP, &tmp);usleep(2000);}close(fd);return 0;
}

相关文章:

Linux驱动开发:I2C子系统

目录 1、I2C简介 1.1 两根线 1.2 信号 1.3 写时序 1.4 读时序 1.5 I2C速率 1.6 I2C驱动框架简介 2、I2C设备驱动 2.1 I2C相关API 2.1.1 i2c_driver 2.1.2 注册&#xff1a;i2c_add_driver 2.1.3 注销&#xff1a;i2c_del_driver 2.1.4 module_i2c_driver&#xff…...

[C++] 动态内存与智能指针

众所周知&#xff0c;C五大内存区&#xff1a;全局数据区(静态区)、代码区、栈区、堆区、常量区。 全局数据区(静态区)&#xff1a;存放全局变量&#xff0c;静态数据和常量&#xff1b; 代码区&#xff1a;存放所有类成员函数和非成员函数代码&#xff0c;函数体的二进制代码。…...

多态的原理

有了虚函数&#xff0c;会在类的对象增加一个指针&#xff0c;该指针就是虚函数表指针_vfptr;虚表本质就是函数指针数组,虚表里面存放着该对象的虚函数的地址&#xff1b; 派生类继承有虚函数基类的对象模型 子类继承父类的虚表指针时&#xff0c;是对父类的虚表指针进行了拷…...

RK3588平台开发系列讲解(内存篇)Linux 伙伴系统数据结构

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、 页二、区三、内存节点沉淀、分享、成长,让自己和他人都能有所收获!😄 📢Linux 系统中,用来管理物理内存页面的伙伴系统,以及负责分配比页更小的内存对象的 SLAB 分配器了。 本篇将介绍伙伴系统相关数据结…...

Windows(MFC/C++)上进程间通讯的几种简单又实用的方法

前段时间&#xff0c;做了一个项目&#xff0c;涉及数据传输。项目实现方式有很多种&#xff0c;但不同的实现方式&#xff0c;对数据的传输方法不同&#xff0c;且各有优缺点。 下文就不同情况来如何选择数据传输(通讯)方式。 先说说需求&#xff0c;模块A获取测试数据&#…...

嘉兴桐乡会计考证培训-备考中级职称有必要报班吗?

备考中级会计职称有必要报班吗&#xff1f;其实&#xff0c;备考报班不能说是必需的&#xff0c;但听课学习确实是节省时间的一种方式&#xff0c;根据同学们的反馈&#xff0c;自学所花费的时间远远多于跟着老师学。上元教育就整理了一些学员报班之前走过的弯路 报班之前 在2…...

java元注解和自定义注解的区别

Java的元注解和自定义注解是两个不同的概念。 元注解是Java内置的一组用于修饰其他注解的注解&#xff0c;包括Retention、Target、Inherited和Documented。它们可以控制被修饰的注解的保留策略、目标范围、是否继承等属性&#xff0c;并且可以在编写自定义注解时使用。 Retent…...

技术到底是什么

背景 我发了朋友圈&#xff1a;做了个奇怪的梦&#xff0c;梦见被离职了&#xff0c;理由竟然是&#xff1a;你技术太菜了 我补充评论&#xff1a;我还没想明白怎么回事&#xff0c;就醒了。有点遗憾的是&#xff1a;想再努力反驳两句&#xff0c;结果没机会了… 很多人评论…...

什么CRM客户管理系统最好?

产业互联网背景下&#xff0c;企业数字化转型日渐深化。毋庸置疑&#xff0c;客户是企业的命脉&#xff0c;企业发展的关键便是以客户为中心&#xff0c;为客户创造价值&#xff0c;并不断实现企业的可持续性增长&#xff0c;而这也是每个企业永不落幕的主题。 一套优秀的CRM客…...

吴军《计算之魂》读后感

前言 断断续续&#xff0c;终于完成了这本书的第一次通读&#xff0c;记录下自己的一些想法。 先说一个小故事。前段时间家里买了一个小鱼缸&#xff0c;问我有没有办法让水自动循环&#xff0c;但不想用电。没有好的想法&#xff0c;去小某书上搜了下&#xff0c;好多案例教…...

CSS进阶

01-复合选择器 定义&#xff1a;由两个或多个基础选择器&#xff0c;通过不同的方式组合而成。 作用&#xff1a;更准确、更高效的选择目标元素&#xff08;标签&#xff09;。 后代选择器 后代选择器&#xff1a;选中某元素的后代元素。 选择器写法&#xff1a;父选择器 …...

金兰组织 | 2023金兰解决方案集经营管理篇正式发布

为助力企业创新管理、提质增效&#xff0c;人大金仓携手金兰组织成员单位&#xff0c;于近期发布多项经营管理领域的联合解决方案&#xff0c;共享创新应用成果。 /人大金仓高级副总裁宋瑞/ 人大金仓高级副总裁宋瑞在致辞中表示&#xff1a;“联合解决方案创新是指通过把不同领…...

【python】pytorch包:深度学习(序章)

今日听闻师姐说pytorch实现深度学习要比keras更好用一些&#xff0c;特此记录 Part 0. 机器学习 与 深度学习 的联系与区别 参考B站视频链接 联系 深度学习是机器学习的分支&#xff0c;人工神经网络为基础&#xff0c;对数据的特征进行学习的方法 区别 特征抽取 机器学…...

HTML <acronym> 标签

HTML5 中不支持 <acronym> 标签在 HTML 4 中用于定义首字母缩写词。 实例 标记一个首字母缩写&#xff1a; <acronym title"World Wide Web">WWW</acronym> 浏览器支持 IEFirefoxChromeSafariOpera 所有主流的浏览器均支持 <acronym> …...

python基本数据类型 - 字典集合

引入 在内存中存储的数据可以是不同的数据类型。比如名字可以使用字符串存储&#xff0c;年龄可以使用数字存储&#xff0c;python有6种基本数据类型&#xff0c;用于各种数据的存储&#xff0c;分别是&#xff1a;numbers(数字类型)、string(字符串)、List(列表)、Tuple(元组…...

python数据类型总结

标准数据类型 Python 有以下几种标准数据类型&#xff1a; 整数&#xff08;int&#xff09;&#xff1a;表示整数值&#xff0c;如 1, -5, 0 等。浮点数&#xff08;float&#xff09;&#xff1a;表示小数值&#xff0c;如 3.14, -0.01, 1.0 等。字符串&#xff08;str&…...

TS内置类型总结

typeof 取对象身上的类型 const person {name: ,job: ,age:18 } type p typeof person ->> type p {name: string;job: string;age: number; }keyof取一个类型的属性明作为一个联合类型 const person {name: ,job: ,age: 18 } type p typeof person type k keyof p…...

Spring Cloud Alibaba: Gateway 网关过滤器 GatewayGatewayFilter factory (记录)

目录 AddRequestHeader GatewayFilter factory AddRequestHeadersIfNotPresent GatewayFilter factory AddRequestParameter GatewayFilter Factory AddResponseHeader GatewayFilter Factory CircuitBreaker GatewayFilter factory circuit breaker based on the status…...

Windows Server 2016版本说明

Windows Server 2016 Essentials edition Windows Server 2016 Essentials版是专为小型企业而设计的。它对应于Windows Server的早期版本中的Windows Small Business Server。此版本最多可容纳25个用户和50台设备。它支持两个处理器内核和高达64GB的RAM。它不支持Windows Serve…...

车载红外夜视「升温」

红外夜视赛道&#xff0c;正在升温。 本周&#xff0c;全球车载后视镜头部供应商Gentex宣布&#xff0c;领投以色列热成像技术初创公司ADASKY&#xff0c;后者在B轮融资中拿到了3000万美元。按照计划&#xff0c;Gentex将协助ADASKY将红外夜视技术推向汽车市场。 事实上&#x…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

【 java 虚拟机知识 第一篇 】

目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...

Vue3 PC端 UI组件库我更推荐Naive UI

一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用&#xff0c;前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率&#xff0c;还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库&#xff08;Naive UI、Element …...