驱动给应用层提供了open、read、write、ioctl等函数来实现对底层硬件的控制,前三个参数相比其他设备比如uart、spi等使用方法都一样,主要的不同在ioctl里。
/* i2c_dev.h */
/* ioctl函数参数 */
#define I2C_SLAVE 0x0703 /* Use this slave address */
#define I2C_SLAVE_FORCE 0x0706 /* Use this slave address, even if it
is already in use by a driver! */
#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */
#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */
#define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */
#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */
#define I2C_SMBUS 0x0720 /* SMBus transfer */
从上面的参数可以看出来Linux应用层跟I2C通信需要知道1个参数:slave address,即从机地址;从机地址分为7bit和10bit,一般是7bit,具体看从设备的说明,首先需要明白什么是从机地址,下图是7bit的地址构成:
从上图可以看出bit1~bit7是从机地址,bit0是方向位,假设bit0写的时候为0,读的时候为1,bit7~bit1分别是11100010(具体值看从机设备的手册),合起来即:读0xE3,写0xE2;但是从机地址是7bit,因此从机地址是 0xE2 >> 1 = 0x71
由上得出:
从机地址:0x71
写地址:0xE2
读地址:0xE3
下面分析I2C的驱动,先看文件操作函数:
/* i2c_dev.c */
/* I2C驱动函数调用顺序(局部) */
i2c_dev_init => register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); //注册文件操作函数
/* I2C文件操作函数结构体 */
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
/* i2cdev_write函数调用顺序 */
i2cdev_write => i2c_master_send => i2c_transfer => i2c_imx_write
/* i2cdev_read函数调用顺序 */
i2cdev_read => i2c_master_recv => i2c_transfer => i2c_imx_read
从上面的分析可以得出read、write函数的底层实现是因板子不同而不同的,想要使用正确得看看驱动代码,下面以i2c_imx_write为例:
/* i2c消息结构体 */
struct i2c_msg {
__u16 addr; /* slave address */ =>在这里是0x71
__u16 flags; =>写取0,读0 | I2C_M_RD
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */ => 消息长度
__u8 *buf; /* pointer to msg data */ =>消息内容
};
static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
{
int i, result;
dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",
__func__, msgs->addr << 1);
/* write slave address */ =>先发送写地址
writeb(msgs->addr << 1, i2c_imx->base + IMX_I2C_I2DR); =>写地址 = 从机地址 << 1 => 0x71 << 1 = 0xE2
result = i2c_imx_trx_complete(i2c_imx);
if (result)
return result;
result = i2c_imx_acked(i2c_imx);
if (result)
return result;
dev_dbg(&i2c_imx->adapter.dev, "<%s> write data\n", __func__);
/* write data */ => 再发送数据
for (i = 0; i < msgs->len; i++) {
dev_dbg(&i2c_imx->adapter.dev,
"<%s> write byte: B%d=0x%X\n",
__func__, i, msgs->buf[i]);
writeb(msgs->buf[i], i2c_imx->base + IMX_I2C_I2DR);
result = i2c_imx_trx_complete(i2c_imx);
if (result)
return result;
result = i2c_imx_acked(i2c_imx);
if (result)
return result;
}
return 0;
}
由上面分析可以看出,imx6的write/read 驱动函数已经把读写地址发送过了,不需要我们再单独发送了。
使用举例:
#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
const char *i2c_dev = "/dev/i2c-0";
int main()
{
int fd = 0;
unsigned char slave_add = 0xe2;
char i2c_tmp[5] = {0x03,0x00,0x00,0x30,0x33};
char rbuf[20];
int i;
/* 打开设备文件 */
fd = open(i2c_dev,O_RDWR);
if (fd < 0)
{
perror("open i2c device error\n");
}
/* 设置为7bit地址 */
if(ioctl(fd, I2C_TENBIT, 0) < 0)
{
perror("Unable to set I2C_TENBIT");
return 1;
}
/* 设置从机地址 */
if(ioctl(fd, I2C_SLAVE, slave_add >> 1) < 0)
{
perror("Unable to set I2C_SLAVE");
return 1;
}
/* 发送数据 */
write(fd, i2c_tmp, 5);
usleep(12000);
/* 读取数据 */
read(fd,rbuf,20);
for(i = 0; i < 20; i++)
{
printf("----i2c_read-buff:%02x-----\n",rbuf[i]);
}
close(fd);
return 0;
}