i2c 编程接口

2023-11-10

1、通信接口

i2c发送或者接收一次数据都以数据包 struct i2c_msg 封装

struct i2c_msg {
	__u16 addr;     // 从机地址
	__u16 flags;    // 标志
#define I2C_M_TEN   0x0010  // 十位地址标志
#define I2C_M_RD    0x0001  // 接收数据标志
	__u16 len;      // 数据长度
	__u8 *buf;      // 数据指针
};

其中addr为从机地址;flags则是这次通信的标志,发送数据为0,接收数据则为 I2C_M_RD;len为此次通信的数据字节数;buf 为发送或接收数据的指针。在设备驱动中我们通常调用 i2c-core 定义的接口 i2c_master_send 和 i2c_master_recv 来发送或接收一次数据。

int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
	int ret;
	struct i2c_adapter *adap=client->adapter;  // 获取adapter信息
	struct i2c_msg msg;                        // 定义一个临时的数据包

	msg.addr = client->addr;                   // 将从机地址写入数据包
	msg.flags = client->flags & I2C_M_TEN;     // 将从机标志并入数据包
	msg.len = count;                           // 将此次发送的数据字节数写入数据包
	msg.buf = (char *)buf;                     // 将发送数据指针写入数据包

	ret = i2c_transfer(adap, &msg, 1);         // 调用平台接口发送数据

	/* If everything went ok (i.e. 1 msg transmitted), return #bytes
	   transmitted, else error code. */
	return (ret == 1) ? count : ret;           // 如果发送成功就返回字节数
}
EXPORT_SYMBOL(i2c_master_send);

i2c_master_send 接口的三个参数:client 为此次与主机通信的从机,buf 为发送的数据指针,count 为发送数据的字节数。

int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
{
	struct i2c_adapter *adap=client->adapter;  // 获取adapter信息
	struct i2c_msg msg;                        // 定义一个临时的数据包
	int ret;

	msg.addr = client->addr;                   // 将从机地址写入数据包
	msg.flags = client->flags & I2C_M_TEN;     // 将从机标志并入数据包
	msg.flags |= I2C_M_RD;                     // 将此次通信的标志并入数据包
	msg.len = count;                           // 将此次接收的数据字节数写入数据包
	msg.buf = buf;

	ret = i2c_transfer(adap, &msg, 1);         // 调用平台接口接收数据

	/* If everything went ok (i.e. 1 msg transmitted), return #bytes
	   transmitted, else error code. */
	return (ret == 1) ? count : ret;           // 如果接收成功就返回字节数
}
EXPORT_SYMBOL(i2c_master_recv);

i2c_master_recv 接口的三个参数:client 为此次与主机通信的从机,buf 为接收的数据指针,count 为接收数据的字节数。我们看一下 i2c_transfer 接口的参数说明:

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
其中 adap 为此次主机与从机通信的适配器;msgs 为通信的数据包,这里可以是单个或多个数据包; num 用于指定数据包的个数,如果大于1则表明将进行不止一次的通信。通信一次就需要寻址一次,如果需要多次通信就需要多次寻址,前面2个接口都是进行一次通信,所以 num 为1;有的情况下我们要读一个寄存器的值,就需要先向从机发送一个寄存器地址然后再接收数据,这样如果想自己封装一个接口就需要将 num 设置为2。接口的 返回值如果失败则为负数,如果成功则返回传输的数据包个数。比如读一个寄存器的接口可以按照如下方式封装:

static int read_reg(struct i2c_client *client, unsigned char reg, unsigned char *data)
{
	int ret;

	struct i2c_msg msgs[] = {
		{
			.addr	= client->addr,
			.flags	= 0,
			.len	= 1,
			.buf	= &reg,  // 寄存器地址
		},
		{
			.addr	= client->addr,
			.flags	= I2C_M_RD,
			.len	= 1,
			.buf	= data,  // 寄存器的值
		},
	};

	ret = i2c_transfer(client->adapter, msgs, 2);  // 这里 num = 2,通信成功 ret = 2
	if (ret < 0)
		tp_err("%s error: %d\n", __func__, ret);

	return ret;
}

还可调用前面所述的接口封装:

static unsigned char read_reg(struct i2c_client *client, unsigned char reg)
{
    unsigned char buf;

    i2c_master_send(client, &reg, 1);  // 发送寄存器地址
    i2c_master_recv(client, &buf, 1);  // 接收寄存器的值

    return 	buf;
}
2、reset 接口

最近因为平台的i2c总线经常发生死锁,用逻辑分析仪检测发现通常为SDA和SCL都被拉低,于是在i2c-core中加入了reset机制,总体思路如下:

(1)在i2c.driver和i2c.adapter的结构中加入reset接口,即每一个i2c设备都可以注册reset函数,每条i2c总线都有相应的reset接口

(2)当发生死锁时,首先根据i2c-timeout的信息获取当前通信的设备地址和总线编号,然后依次执行当前总线下所有i2c设备的reset函数,再尝试发送是否成功;如果总线仍然处于死锁状态则执行i2c.adapter的reset函数;如果总线还是处于死锁状态就重启机器;总共3层reset机制

(3)i2c.driver的reset函数一般操作设备的reset pin或者电源(需要根据硬件设计进行相应操作)

(4)i2c.adapter的reset函数首选进行SCL的模拟解锁方案,然后再是操作整个总线上设备的电源(需要根据硬件设计进行相应操作)

(5)重启是最后的一层机制,此时无法恢复设备的正常使用就只能重启了

因为i2c.adapter层的需要,在i2c-core中加入了遍历当前总线所有设备并执行设备reset函数的接口i2c_reset_device:

/**
 * i2c_reset_device - reset I2C device when bus dead
 * @adapter: the adapter being reset
 * @addr: the device address
 */
static int __i2c_reset_device(struct device *dev, void *addrp)
{
	struct i2c_client *client = to_i2c_client(dev);
	int addr = *(int *)addrp;

	if (client && client->driver && client->driver->reset)
		return client->driver->reset();

	return 0;
}

int i2c_reset_device(struct i2c_adapter *adapter, int addr)
{
	return device_for_each_child(&adapter->dev, &addr, __i2c_reset_device);
}
EXPORT_SYMBOL(i2c_reset_device);

需要注意的是i2c.driver的reset函数返回值需要为0,不然device_for_each_child不会继续后面的遍历。用GPIO模拟SCL解锁的参考代码如下:

static int i2c_reset_adapter(void)
{
	int counter = 0;

	gpio_request(I2C_BUS_DATA, "gpioxx");
	gpio_request(I2C_BUS_CLK, "gpioxx");
	/* try to recover I2C bus */
	gpio_direction_input(I2C_BUS_DATA);

	if (!__gpio_get_value(I2C_BUS_DATA)) {
		while((!__gpio_get_value(I2C_BUS_DATA)) && ++counter < 10)
		{
			udelay(5);
			gpio_direction_output(I2C_BUS_CLK, 1);
			udelay(5);
			gpio_direction_output(I2C_BUS_CLK, 0);
		}
		i2c_err("try to recover i2c bus, retry times are %d\n",counter);
		if (counter < 10) {
			udelay(5);
			gpio_direction_output(I2C_BUS_DATA, 0);
			udelay(5);
			gpio_direction_output(I2C_BUS_CLK, 1);
			udelay(5);
			gpio_direction_output(I2C_BUS_DATA, 1);
			msleep(10);
		} else {
			i2c_err("try to recover i2c bus failed!\n");
		}
	}

	gpio_free(I2C_BUS_DATA);
	gpio_free(I2C_BUS_CLK);

	return 0;
}

3、i2c_detect 接口

最近在看一个 wifi 驱动的时候发现了另外一种注册 i2c 设备的方式,其实也是属于动态注册的一种,不过内核支持得更好,可以用内核提供的宏来实现。该方法以 struct i2c_client_address_data 结构为基础,我们先来看下这个结构的组成:

struct i2c_client_address_data {
	const unsigned short *normal_i2c;      /* 常规探测地址 */
	const unsigned short *probe;           /* 常规的通道和地址 */
	const unsigned short *ignore;          /* 过滤的通道和地址 */
	const unsigned short * const *forces;  /* 强制的通道和地址 */
};
这个结构定义了 4 个指向不同数组的指针,数组元素都以 I2C_CLIENT_END 结尾,其中 normal_i2c 指向的数组保存的是常规探测地址,而且该数组中的地址如果和 ignore 数组中的地址相同则会被过滤掉,常见定义如下:
static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};  /* 该数组定义了3个可能的设备地址 */
其他 3 个数组的元素依次是通道和地址成对的组合,而且元素个数最好定义为偶数个即最后两个 I2C_CLIENT_END。在探测过程中,i2c_detect 函数会遍历系统中所有的 i2c 通道,在每个通道中依次探测地址,顺序是  forces -> probe -> normal_i2c,比如在探测 forces 里面的通道和地址时,会先比较当前通道和数组里面定义的通道是否一致,不一致则不会继续探测该对地址,如果定义的是  ANY_I2C_BUS 则任意通道都会探测该对地址,接着探测地址会调用 driver->detect 接口,在该接口中一般会去读取设备的某个寄存器值以判断设备是否存在,如果设备存在则返回 0 值,如果不存在则返回  -ENODEV,无论返回 0 值或者 -ENODEV 都会继续后面的探测,如果返回其他错误值则会结束整个探测过程。一个常见的例子如下:
static const struct i2c_device_id wifi_core_i2c_id[] = {{WIFI_CORE_I2C_DEVNAME, 0}, {}};
MODULE_DEVICE_TABLE(i2c, wifi_core_i2c_id);

static unsigned short wifi_core_force[] = {ANY_I2C_BUS, WIFI_CORE_ADDR, I2C_CLIENT_END, I2C_CLIENT_END};
static const unsigned short * const wifi_core_forces[] = {wifi_core_force, NULL};

static struct i2c_client_address_data wifi_core_addr_data = {
	.forces		= wifi_core_forces,
};

static int wifi_core_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
{
	strcpy(info->type, WIFI_CORE_I2C_DEVNAME);  /* 如果探测成功则在detect接口中至少要设置设备名称 */
	return 0;
}

static struct i2c_driver wifi_core_driver = 
{
	.probe = wifi_core_probe,
	.remove = wifi_core_remove,
	.detect = wifi_core_detect,
	.shutdown = wifi_shutdown,
	.driver.name = WIFI_CORE_I2C_DEVNAME,
	.id_table = wifi_core_i2c_id,
	.address_data = &wifi_core_addr_data,
};

i2c_add_driver(&wifi_core_driver)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

i2c 编程接口 的相关文章

  • Yolov5的安装配置及实现目标检测

    本文内容 本文主要介绍anaconda下yolov5的安装配置方法 以及如何实现目标检测 目录 一 yolov5的下载安装 一 环境配置 二 下载yolov5 二 yolov5测试 一 图片测试 二 视频测试 三 调用手机摄像头实现实时检测
  • 如何使用 Docker 部署 FreeGPT-WebUI:一个简单的教程

    目录 1 FreeGPT WebUI 项目简介 2 安装 Docker 3 从 Docker Hub 拉取 FreeGPT WebUI 镜像 4 使用 Docker 运行 FreeGPT WebUI 应用程序 5 访问 FreeGPT We
  • python菜鸟学习Day1(数据类型)

    第一节 数据类型 int 整型 python2中有int long python3中只有int float 浮点型 string 字符串型 bool 布尔型 变量命名 字母 数字 下划线 数据不能开头 大小写敏感 不能和系统关键字 函数名
  • [2020.1.19][udev] 金步国udev

    金步国index里有4条udev相关的 1 udevadm 2 udev 3 systemd udevd service udevd守护进程 4 udev conf 1 udevadm 1 udevadm info options devp
  • linux 安装nginx+php +mysql 安装

    安装make make命令是GNU的工程化编译工具 用于编译众多相互关联的源代码问价 以实现工程化的管理 提高开发效率 安装PCRE库 g wget yum y install gcc gcc c wget pcre pcre devel
  • IP 地址解析与子网划分教程

    数据来源 一 简单局域网的构成 局域网 一般称为内网 简单局域网的构成 交换机 网线 PC 其他IT终端 交换机 用来组建内网的局域网的设备 交换机并不能让电脑上网 需要通过路由器 路由器 路由器可以为局域网自动分配IP和虚拟拨号 电脑需要
  • 高精度减法详解

    高精度减法详解 建议先学习高精度加法 高精度加法详解 当减数 被减数 差特别大时 大到无法用long long来存储 外面就需要使用高精度算法解决 算法基本思想 存储就是和加法差不多 都是用数组解决问题 计算就使用小学竖式计算即可 算法描述
  • 设计模式-桥接模式(Bridge)

    文章目录 前言 一 桥接模式的概念 二 桥接模式的实现 三 桥接模式的优缺点 1 优点 2 缺点 前言 桥接模式 Bridge Pattern 是一种结构型设计模式 用于将抽象部分和实现部分分离 使它们可以独立地变化 这种分离允许你将一个类
  • 【精】【PDF链接转图片】- Java用pdfbox将PDF的URL转换并压缩成图片,解决“口口口”乱码问题

    业务场景 做一个开电子发票的业务 中税返回我们一个pdf的url 这个url在web端是可以显示的 移动端 ios可以正常显示 安卓显示为是否要下载 产品邀请发票预览需让用户第一时间看到 不应该有下载的场景出现 解决方案 将PDF转化图片流
  • http://www.clamav.org/

    url http www clamav org url Clam AntiVirus is an open source GPL anti virus toolkit for UNIX designed especially for e m
  • 发送邮件验证码 php,PHP(ThinkPHP5.0) + PHPMailer 进行邮箱发送验证码

    GitHub下载最新版第三方类库PHPMailer php 第一步 html 打开网址https github com PHPMailer PHPMailer 下载PHPMailer PHPMailer 须要 PHP 的 sockets 扩
  • 柔性数组简介:

    个人主页 勇敢的小牛儿 推荐专栏 C语言知识点 座右铭 敢于尝试才有机会 今日鸡汤 我们应该尽可能的花精力 做到有多牛 而不是用很多无用的努力 让自己显得有多牛 一 柔性数组简介 1 柔性数组首先是一个数组 2 柔性数组之所以叫柔性数组是因
  • ATL 和 MFC 字符转换宏

    ATL 和 MFC 字符转换宏 ATL3 0 ATL3 0中的W2T T2W等一系列宏很方便 但一定要小心 它们从栈上分配内存 直到调用它的函数返回前 该内存不会被释放 如果在一个循环中 这类宏被你反复调用几万次时 你将不可避免地产生sta
  • ElementUi常用的属性及官方解释比较模糊的知识点

    1 Dialog对话框 1 close on click modal true 官方文档解释 是否可以通过点击 modal 关闭 Dialog 默认是true 其实它的意思就是点击空白处弹框可关闭 经过尝试这个空白处指的是弹框外的空白处 不
  • 【保姆级教程】Marktext配合Github图床使用

    写在前面 这些天在跟着李宏毅老师的网课进度 补一补机器学习和深度学习的相关内容 从头开始归档markdown文档手写笔记 由于之前大火的Typora笔记软件收费 推荐学生党使用免费开源的Marktext作为平替 下载链接如下 marktex
  • 大专生出身?mysql面试题常问

    正文 下文中截图来源于朋友一个pdf版本的面经 把所以知识点的答案整理了下来 耗费他至少1个月时间 在本文最后部分把这个pdf分享给大家 觉得有用的麻烦点赞关注走一波 谢谢 面经中有他的知识点的答案 如下图示例 非常详细 文末有领取方式 1
  • 支付宝碎屏险究竟是怎么回事?靠谱么?

    由于有很多人看这个文章 所以个人补充一些内容 1 支付宝碎屏险一次就是保一年的 只有支付方式为月付或者年付 连续包月是陷阱 无论你选什么套餐 在保险条款里面都是保12个月的 已经理赔的不允许退保 所以即使你已经理赔 人家不再给你修了 你也要
  • qDebug打印出来路径的时候遇到的问题

    具体参考博客 https blog csdn net u011283226 article details 101382747 当遇到路径打印的时候 正确的路径打印是 qDebug noquote lt lt qDebug lt lt di

随机推荐

  • 6-9.Vue-router之编程式导航

    Vue router 编程式导航 vue中实现导航可以由
  • 3D形状分割:在ShapeNet数据集上使用PointNet++进行3D形状分割任务

    在本篇博客中 我们将探讨如何使用PointNet 模型在ShapeNet数据集上进行3D形状分割任务 3D形状分割是计算机视觉和深度学习领域的一个重要研究方向 可以用于识别3D点云数据中的不同部分 PointNet 是一种基于点云数据的深度
  • OSI模型与TCP\IP协议

    目录 一 分层 1 1 分层原因 1 2 OSI七层模型 二 TCP IP 2 1 TCP IP协议族的组成 模型层 物理层 网络层 传输层 应用层 三 数据封装过程 五 PDU协议数据定义 六 设备与各层对应关系 七 各层间通信 一 分层
  • dos bat批量创建软链接

    windows 下 要将 train2017 val2017 两个目录下的图片并入一个目录 images 用 mklink 创建软链接 1 可以不用额外空间 win10 也可以写 sh 脚本用 ln s 但效果似乎同 copy 因为用 ln
  • uni-app实战教程

    一 准备 下载HBuilderX编辑器 前往下载 注册百度AI账号 创建应用获得Appid和Secret 前往注册 百度AI通用物体识别文档 前往查阅 Uni App文档 前往查阅 HTML5 文档 前往查阅 HTML5 文档 前往查阅 二
  • IDEA插件分享(实用推荐)

    1 SequenceDiagram 序列图插件 查看方法内部的调用其他的序列图 使用方法 选中对应的方法 右击选择 SequenceDiagram 或者右上角点击SequenceDiagramtu bi 2 Maven Search 快速搜
  • 万户协同办公平台ezoffice未授权访问漏洞

    文章目录 0x01 前言 0x02 漏洞描述 0x03 影响范围 0x04 漏洞环境 0x05 漏洞复现 1 构造POC 2 进行MD5值解密 3 尝试进行登录 0x06 复现建议 0x01 前言 本次测试仅供学习使用 如若非法他用 与本文
  • [论文分享] TREX: Learning Execution Semantics from Micro-Traces for Binary Similarity

    TREX Learning Execution Semantics from Micro Traces for Binary Similarity Kexin Pei Columbia University Zhou Xuan Univer
  • HashMap的扩容机制、ConcurrentHashMap的原理

    HashMap的扩容机制 ConcurrentHashMap的原理 n 1 hash 相当于hash n public V put K key V value return putVal hash key key value false t
  • Android性能调优工具TraceView介绍

    转自 http www trinea cn android android traceview 本文主要介绍Android性能调优工具TraceView的使用及通过其确定性能点 目前性能优化专题已完成以下部分 性能优化总纲 性能问题及性能调
  • WIN10软件开机自启动设置(基于win10系统,不依赖第三方软件)

    开机启动项设置教程 第一步 win R打开运行窗口 输入命令msconfig 回车 第二步打开系统配置 切换到 启动 选项卡 第三步打开任务管理器 第四步单击选中想要禁止或者开启的软件 已启动代表开机时软件会自动启动 已禁用代表开机是软件不
  • “Intel VT-x处于禁用状态”怎么解决(图形化)

    Intel VT x处于禁用状态 如何解决 开启虚拟机时弹出这个提示 说明电脑的虚拟化没有开启 只有开启它 才能运行虚拟机 网上大多数 Intel VT x处于禁用状态 解决方案都是开机按Fn系列键进入BIOS设置 可是博主试了好多次 电脑
  • WEB攻击与防御

    这里列举一些常见的攻击类型与基本防御手段 XSS攻击 跨站脚本 Cross site scripting 简称XSS 把JS代码注入到表单中运行例如在表单中提交含有可执行的JS的内容文本 如果服务器端没有过滤或转义这些脚本 而这些脚本由通过
  • 判断带头结点的循环双链表是否对称

    题目 设计一个算法 用于判断带头结点的循环双链表是否对称 分析 循环双链表的特点是 当前结点方便找到前后节点 且尾指针指向第一个结点 对称性 判断第一个结点和最后一个结点的值是否相等 如果相等 再判断第二个结点和倒数第二个结点 以此类推 从
  • [论文阅读] (18)英文论文Model Design和Overview如何撰写及精句摘抄——以系统AI安全顶会为例

    娜璋带你读论文 系列主要是督促自己阅读优秀论文及听取学术讲座 并分享给大家 希望您喜欢 由于作者的英文水平和学术能力不高 需要不断提升 所以还请大家批评指正 非常欢迎大家给我留言评论 学术路上期待与您前行 加油 前一篇介绍CCS2019的P
  • 电子设计大赛作品_电子设计大赛

    为了进一步提高学生对电子和科技的兴趣 培养学生的动手能力和想象能力 增强学生的团队合作意识 提高学生分析和解决问题的能力 现决定开展电子设计大赛 电子设计大赛详情 一 参赛对象 全体全日制在校大学生 1 3人自由组队 并指定队长一名 可自由
  • 华为OD机试 - 简易内存池(Java)

    题目描述 请实现一个简易内存池 根据请求命令完成内存分配和释放 内存池支持两种操作命令 REQUEST和RELEASE 其格式为 REQUEST 请求的内存大小 表示请求分配指定大小内存 如果分配成功 返回分配到的内存首地址 如果内存不足
  • java-map-put方法源码分析

    HashMap是由数组 链表和红黑树组成的数据结构 而其中put方法可以算的上HashMap中的核心方法 这个方法给我们展示了HashMap的大部分精髓 我们首先来看一下map的核心变量 transient Node
  • 2022年一起努力应对互联网寒冬吧,5G音视频时代还不学NDK开发吗

    前言 找工作还是需要大家不要紧张 有我们干这一行的接触人本来就不多 难免看到面试官会紧张 主要是因为怕面试官问的问题到不上来 那时候不要着急 答不上了的千万不然胡扯一些 直接就给面试官说这块我还没接触到 以后如果工作当中遇到的话我可以很快的
  • i2c 编程接口

    1 通信接口 i2c发送或者接收一次数据都以数据包 struct i2c msg 封装 struct i2c msg u16 addr 从机地址 u16 flags 标志 define I2C M TEN 0x0010 十位地址标志 def