文章目录
- 前言
- 一、交叉编译i2c_tool
- 二、板子上使用i2c_tool
- 三、为什么不需要编写驱动也能够访问到对应设备
- 四、命令行使用i2_tool操作AP3216模块
- 五、使用i2c_tool代码操作IIC设备
- 六、相关函数讲解
- 1.open_i2c_dev
- 2.int set_slave_addr
- 七、具体代码编写
- 总结
前言
本篇文章将带大家学习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工具还是非常强大的,大家有必要掌握一下。