当前位置: 首页 > 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…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...

高分辨率图像合成归一化流扩展

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 1 摘要 我们提出了STARFlow&#xff0c;一种基于归一化流的可扩展生成模型&#xff0c;它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流&#xff08;TARFlow&am…...

命令行关闭Windows防火墙

命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)​方法二:CMD命令…...

计算机系统结构复习-名词解释2

1.定向&#xff1a;在某条指令产生计算结果之前&#xff0c;其他指令并不真正立即需要该计算结果&#xff0c;如果能够将该计算结果从其产生的地方直接送到其他指令中需要它的地方&#xff0c;那么就可以避免停顿。 2.多级存储层次&#xff1a;由若干个采用不同实现技术的存储…...