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 注册:i2c_add_driver 2.1.3 注销:i2c_del_driver 2.1.4 module_i2c_driverÿ…...
[C++] 动态内存与智能指针
众所周知,C五大内存区:全局数据区(静态区)、代码区、栈区、堆区、常量区。 全局数据区(静态区):存放全局变量,静态数据和常量; 代码区:存放所有类成员函数和非成员函数代码,函数体的二进制代码。…...
多态的原理
有了虚函数,会在类的对象增加一个指针,该指针就是虚函数表指针_vfptr;虚表本质就是函数指针数组,虚表里面存放着该对象的虚函数的地址; 派生类继承有虚函数基类的对象模型 子类继承父类的虚表指针时,是对父类的虚表指针进行了拷…...
RK3588平台开发系列讲解(内存篇)Linux 伙伴系统数据结构
平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、 页二、区三、内存节点沉淀、分享、成长,让自己和他人都能有所收获!😄 📢Linux 系统中,用来管理物理内存页面的伙伴系统,以及负责分配比页更小的内存对象的 SLAB 分配器了。 本篇将介绍伙伴系统相关数据结…...
Windows(MFC/C++)上进程间通讯的几种简单又实用的方法
前段时间,做了一个项目,涉及数据传输。项目实现方式有很多种,但不同的实现方式,对数据的传输方法不同,且各有优缺点。 下文就不同情况来如何选择数据传输(通讯)方式。 先说说需求,模块A获取测试数据&#…...
嘉兴桐乡会计考证培训-备考中级职称有必要报班吗?
备考中级会计职称有必要报班吗?其实,备考报班不能说是必需的,但听课学习确实是节省时间的一种方式,根据同学们的反馈,自学所花费的时间远远多于跟着老师学。上元教育就整理了一些学员报班之前走过的弯路 报班之前 在2…...
java元注解和自定义注解的区别
Java的元注解和自定义注解是两个不同的概念。 元注解是Java内置的一组用于修饰其他注解的注解,包括Retention、Target、Inherited和Documented。它们可以控制被修饰的注解的保留策略、目标范围、是否继承等属性,并且可以在编写自定义注解时使用。 Retent…...
技术到底是什么
背景 我发了朋友圈:做了个奇怪的梦,梦见被离职了,理由竟然是:你技术太菜了 我补充评论:我还没想明白怎么回事,就醒了。有点遗憾的是:想再努力反驳两句,结果没机会了… 很多人评论…...
什么CRM客户管理系统最好?
产业互联网背景下,企业数字化转型日渐深化。毋庸置疑,客户是企业的命脉,企业发展的关键便是以客户为中心,为客户创造价值,并不断实现企业的可持续性增长,而这也是每个企业永不落幕的主题。 一套优秀的CRM客…...
吴军《计算之魂》读后感
前言 断断续续,终于完成了这本书的第一次通读,记录下自己的一些想法。 先说一个小故事。前段时间家里买了一个小鱼缸,问我有没有办法让水自动循环,但不想用电。没有好的想法,去小某书上搜了下,好多案例教…...
CSS进阶
01-复合选择器 定义:由两个或多个基础选择器,通过不同的方式组合而成。 作用:更准确、更高效的选择目标元素(标签)。 后代选择器 后代选择器:选中某元素的后代元素。 选择器写法:父选择器 …...
金兰组织 | 2023金兰解决方案集经营管理篇正式发布
为助力企业创新管理、提质增效,人大金仓携手金兰组织成员单位,于近期发布多项经营管理领域的联合解决方案,共享创新应用成果。 /人大金仓高级副总裁宋瑞/ 人大金仓高级副总裁宋瑞在致辞中表示:“联合解决方案创新是指通过把不同领…...
【python】pytorch包:深度学习(序章)
今日听闻师姐说pytorch实现深度学习要比keras更好用一些,特此记录 Part 0. 机器学习 与 深度学习 的联系与区别 参考B站视频链接 联系 深度学习是机器学习的分支,人工神经网络为基础,对数据的特征进行学习的方法 区别 特征抽取 机器学…...
HTML <acronym> 标签
HTML5 中不支持 <acronym> 标签在 HTML 4 中用于定义首字母缩写词。 实例 标记一个首字母缩写: <acronym title"World Wide Web">WWW</acronym> 浏览器支持 IEFirefoxChromeSafariOpera 所有主流的浏览器均支持 <acronym> …...
python基本数据类型 - 字典集合
引入 在内存中存储的数据可以是不同的数据类型。比如名字可以使用字符串存储,年龄可以使用数字存储,python有6种基本数据类型,用于各种数据的存储,分别是:numbers(数字类型)、string(字符串)、List(列表)、Tuple(元组…...
python数据类型总结
标准数据类型 Python 有以下几种标准数据类型: 整数(int):表示整数值,如 1, -5, 0 等。浮点数(float):表示小数值,如 3.14, -0.01, 1.0 等。字符串(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…...
车载红外夜视「升温」
红外夜视赛道,正在升温。 本周,全球车载后视镜头部供应商Gentex宣布,领投以色列热成像技术初创公司ADASKY,后者在B轮融资中拿到了3000万美元。按照计划,Gentex将协助ADASKY将红外夜视技术推向汽车市场。 事实上&#x…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
