i2c_tool的使用

举报
yd_274589494 发表于 2023/11/21 10:12:36 2023/11/21
【摘要】 @TOC 前言本篇文章将带大家学习i2c_tool这个工具,有了这个工具无需驱动程序我们也可以访问到iic设备。 一、交叉编译i2c_tool首先需要得到i2c_tool的源码:在终端执行下面的命令:git clone git://git.kernel.org/pub/scm/utils/i2c-tools/i2c-tools.git修改makefile中的工具链:这里需要修改为arm的工具...

@TOC


前言

本篇文章将带大家学习i2c_tool这个工具,有了这个工具无需驱动程序我们也可以访问到iic设备。

一、交叉编译i2c_tool

首先需要得到i2c_tool的源码:
在终端执行下面的命令:

git clone git://git.kernel.org/pub/scm/utils/i2c-tools/i2c-tools.git

修改makefile中的工具链:
在这里插入图片描述
这里需要修改为arm的工具链,这样我们才能到板子上面使用。

执行make:
在这里插入图片描述
编译生成了include文件夹和lib文件夹:
在这里插入图片描述
将include文件夹中的头文件和lib文件夹中的动静态库都拷贝到系统目录下:

查看系统目录的路径:

echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -

在这里插入图片描述
拷贝到对应的目录:
拷贝头文件:

在这里插入图片描述
拷贝库文件:
在这里插入图片描述
把对应的库拷贝到板子上:
在这里插入图片描述

二、板子上使用i2c_tool

使用i2cdetect -l 命令可以检测到板子上有多少个i2c控制器:
在这里插入图片描述
使用i2cdetect -y -a 0命令可以检测到对应i2c总线上挂载的设备:
在这里插入图片描述
在这里插入图片描述
这里的UU代表内核中已经有了这个驱动,显示的是数值的话就代表内核中没有这个驱动。

三、为什么不需要编写驱动也能够访问到对应设备

我们的正常思维都是一个设备需要有一个对应的驱动程序,那么这个i2c_tool到底是怎么样操作到设备的呢?

这里使用百问网的一张图片来解释说明:
在这里插入图片描述
1.Linux 内核已经提供了 i2c-dev 模块,它允许用户空间应用程序通过特定的文件系统接口进行 I2C 通信。这个模块负责处理底层的 I2C 总线传输,并将读写请求转发到相应的 I2C 设备上。因此,基于 i2c-dev 模块的工具(如 i2c_tool)可以直接利用这个模块提供的接口进行硬件访问。

2.文件系统接口:i2c-dev 模块将 I2C 总线和设备映射为文件系统中的特殊文件。通过打开这些文件,并使用读写操作对其进行访问,用户空间应用程序可以与 I2C 设备进行通信。i2c_tool 就是通过读写这些特殊文件实现对硬件模块的操作。

3.统一的接口:通过使用 i2c-dev 模块提供的文件系统接口,i2c_tool 可以以统一的方式与不同的 I2C 设备进行通信。无论是操作传感器、存储器、显示器还是其他类型的设备,只需提供正确的设备地址和命令,并在 i2c_tool 中执行适当的读写操作即可。

四、命令行使用i2_tool操作AP3216模块

AP3216是一种集成了环境光传感器(ALS)、红外光传感器(IR)和距离传感器(PS)的数字化模块。它可用于测量环境光强度、接近物体距离和红外光反射强度等应用。

以下是一些关于AP3216模块的基本特性和功能:

具有三个传感器:

环境光传感器(ALS):测量环境光强度,并提供数字输出结果。
红外光传感器(IR):测量红外光强度,并提供数字输出结果。
距离传感器(PS):测量物体与传感器之间的距离,并提供数字输出结果。

下面我们使用i2c_tool来直接操作AP3216:

操作步骤:
复位:往寄存器 0 写入 0x4
使能:往寄存器 0 写入 0x3
读光强:读寄存器 0xC、0xD 得到 2 字节的光强
读距离:读寄存器 0xE、0xF 得到 2 字节的距离值

这样的话就可以将具体的数值读取出来了:
在这里插入图片描述

五、使用i2c_tool代码操作IIC设备

使用命令行来操作IIC设备是非常简单的,但是有的时候也需要我们会使用i2c_tool的源码来对IIC设备进行操作:

流程图(来自百问网):

下面这些函数的原型可以在i2c_tool源码中查看到:
在这里插入图片描述

六、相关函数讲解

1.open_i2c_dev

i2cbus 是要打开的 I2C 总线的编号,filename 是传入的缓冲区,用于存储设备文件路径。size 是缓冲区的大小,quiet 是一个标志,指示是否抑制错误输出。

int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet);

2.int set_slave_addr

函数返回类型为 int,接收的参数包括 file(文件描述符),address(要设置的从设备地址)和 force(强制设置地址的标志)。

int set_slave_addr(int file, int address, int force);

七、具体代码编写


#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include "i2cbusses.h"


#define I2C_BUS      0
#define AP3216C_ADDR 0x1e

static int fd;

int ap3216c_init(void)
{
	char buf[100];
	int err;
	struct i2c_rdwr_ioctl_data rdwr;
	struct i2c_msg msgs[1];
	int nmsgs_sent;
	
	fd = open_i2c_dev(I2C_BUS, buf, sizeof(buf), 0);
	if (fd < 0)
	{
		printf("can not open i2c bus %d\n", I2C_BUS);
		return fd; /* err */
	}

	err = set_slave_addr(fd, AP3216C_ADDR, 1);
	if (err)
	{
		printf("can not set slave addr 0x%x\n", AP3216C_ADDR);
		return err;
	}

	/* reset ap3216c */
	msgs[0].addr  = AP3216C_ADDR;
	msgs[0].flags = 0;      /* 写:0, 读:I2C_M_RD */
	msgs[0].len   = 2;
	msgs[0].buf   = buf;
	buf[0] = 0;
	buf[1] = 4;

	rdwr.msgs = msgs;
	rdwr.nmsgs = 1;
	nmsgs_sent = ioctl(fd, I2C_RDWR, &rdwr);
	if (nmsgs_sent != 1)
	{
		printf("can not reset ap3216c\n");
		return -1;  /* err */
	}

	/* enable ap3216c */
	msgs[0].addr  = AP3216C_ADDR;
	msgs[0].flags = 0;      /* 写:0, 读:I2C_M_RD */
	msgs[0].len   = 2;
	msgs[0].buf   = buf;
	buf[0] = 0;
	buf[1] = 3;

	rdwr.msgs = msgs;
	rdwr.nmsgs = 1;
	nmsgs_sent = ioctl(fd, I2C_RDWR, &rdwr);
	if (nmsgs_sent != 1)
	{
		printf("can not enable ap3216c\n");
		return -1;  /* err */
	}
	
	return 0;
}

int ap3216c_read_light(void)
{
	struct i2c_rdwr_ioctl_data rdwr;
	struct i2c_msg msgs[2];
	int nmsgs_sent;

	char buf_tx[1];
	char buf_rx[2];

	int light;

	/* 发送寄存器地址 */
	msgs[0].addr  = AP3216C_ADDR;
	msgs[0].flags = 0;      /* 写:0, 读:I2C_M_RD */
	msgs[0].len   = 1;
	msgs[0].buf   = buf_tx;
	buf_tx[0] = 0xc;

	msgs[1].addr  = AP3216C_ADDR;
	msgs[1].flags = I2C_M_RD;      /* 写:0, 读:I2C_M_RD */
	msgs[1].len   = 2;
	msgs[1].buf   = buf_rx;

	rdwr.msgs = msgs;
	rdwr.nmsgs = 2;
	nmsgs_sent = ioctl(fd, I2C_RDWR, &rdwr);
	if (nmsgs_sent != 2)
	{
		printf("can not read ap3216c light\n");
		return -1;  /* err */
	}

	light = (buf_rx[1]<<8) | buf_rx[0];
	
	return light;
}

int ap3216c_read_distance(void)
{
	struct i2c_rdwr_ioctl_data rdwr;
	struct i2c_msg msgs[2];
	int nmsgs_sent;

	char buf_tx[1];
	char buf_rx[2];

	int distance;

	/* 发送寄存器地址 */
	msgs[0].addr  = AP3216C_ADDR;
	msgs[0].flags = 0;		/* 写:0, 读:I2C_M_RD */
	msgs[0].len   = 1;
	msgs[0].buf   = buf_tx;
	buf_tx[0] = 0xe;

	msgs[1].addr  = AP3216C_ADDR;
	msgs[1].flags = I2C_M_RD;	   /* 写:0, 读:I2C_M_RD */
	msgs[1].len   = 2;
	msgs[1].buf   = buf_rx;

	rdwr.msgs = msgs;
	rdwr.nmsgs = 2;
	nmsgs_sent = ioctl(fd, I2C_RDWR, &rdwr);
	if (nmsgs_sent != 2)
	{
		printf("can not read ap3216c light\n");
		return -1;	/* err */
	}

	distance = ((buf_rx[1] & 0x3F)<<4) | (buf_rx[0] & 0xf);
	
	return distance;
}





#include <stdio.h>
#include <unistd.h>
#include "ap3216c_lib.h"

int main(int argc, char **argv)
{
	int err; 
	int light, distance;
	int cnt = 0;
	
	err = ap3216c_init();
	if (err)
	{
		printf("ap3216c_init err : %d\n", err);
	}

	while (1)
	{
		light = ap3216c_read_light();	
		distance = ap3216c_read_distance();
		printf("%03d: light = %d, distance = %d\n", cnt, light, distance);
		cnt++;
		sleep(5);
	}


	return 0;
}


总结

这篇文章我们就讲解到这里,i2c_tool工具还是非常强大的,大家有必要掌握一下。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。