一、简介
i2c设备驱动有两种模式:一种是用户模式设备驱动,这种驱动依赖于i2c子系统中i2c-dev驱动,这种驱动对应用程序员的要求很高,要求应用程序员了解硬件的一些东西,了解时序、地址等;另一种是普通的设备驱动,应用程序员在使用的时候就像读写文件一样。
下文介绍怎么在应用层通过设备节点操作IIC。
二、操作步骤
1、内核配置里的dev-interface选项
make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
Device Drivers --->
<*> I2C support --->
<*> I2C device interface
选上此项,更新内核镜像重启系统后,应就可以在/dev目录下出现i2c-*的设备文件,应用程序就是通过这些设备文件调用控制器的。
设备文件的命名方式, 如序号为0的控制器,对应的设备文件应为/dev/i2c-0.
2、打开要调用的控制器设备文件
int fd = open("/dev/i2c-0", O_RDWR);
3、设置超时和传输失败时的重试次数(非必须)
ioctl(fd, I2C_TIMEOUT, 10);
ioctl(fd, I2C_RETRIES, 2);
4、准备好需要传输的数据.
应用层操作IIC是以数据包进行交流的,对应的结构体:
//include/linux/i2c-dev.h
/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
struct i2c_msg __user *msgs; /* pointers to i2c_msgs */
__u32 nmsgs; /* number of i2c_msgs */
};
//include/linux/i2c.h
struct i2c_msg {
__u16 addr; /* iic从机地址 */
__u16 flags; //iic的标志,见如下宏定义 1为读操作,0为写操作(写的没有宏定义,但是可以直接用)
#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; /* 单位为字节 */
__u8 *buf; /* 读操作的时候存取读的信息,写操作要提前给buffer赋值 */
};
5、调用API函数读写数据
ioctl(file, I2C_RDWR, struct i2c_rdwr_ioctl_data *msgset);
调用此ioctl函数类似设备驱动里调用i2c_transfer函数。调用一次,只会在完成时发出一个停止信号,不管发出了多少个i2c_msg。 而且每一个i2c_msg都会有一个开始信号.
三、实例:
/*
C library for reading/writing I2C slave device registers from Raspberry Pi 1 Model B
*/
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
// Terrible portability hack between arm-linux-gnueabihf-gcc on Mac OS X and native gcc on raspbian.
#ifndef I2C_M_RD
#include <linux/i2c.h>
#endif
typedef unsigned char u8;
int i2c_fd = -1;
const char *i2c_fname = "/dev/i2c-1";
// Returns a new file descriptor for communicating with the I2C bus:
int i2c_init(void) {
if ((i2c_fd = open(i2c_fname, O_RDWR)) < 0) {
char err[200];
sprintf(err, "open('%s') in i2c_init", i2c_fname);
perror(err);
return -1;
}
return i2c_fd;
}
void i2c_close(void) {
close(i2c_fd);
}
// Write to an I2C slave device's register:
int i2c_write(u8 slave_addr, u8 reg, u8 data) {
int retval;
u8 outbuf[2];
struct i2c_msg msgs[1];
struct i2c_rdwr_ioctl_data msgset[1];
outbuf[0] = reg;
outbuf[1] = data;
msgs[0].addr = slave_addr;
msgs[0].flags = 0;
msgs[0].len = 2;
msgs[0].buf = outbuf;
msgset[0].msgs = msgs;
msgset[0].nmsgs = 1;
if (ioctl(i2c_fd, I2C_RDWR, &msgset) < 0) {
perror("ioctl(I2C_RDWR) in i2c_write");
return -1;
}
return 0;
}
// Read the given I2C slave device's register and return the read value in `*result`:
int i2c_read(u8 slave_addr, u8 reg, u8 *result) {
int retval;
u8 outbuf[1], inbuf[1];
struct i2c_msg msgs[2];//这个地方要封两个包。因为要先写入需要读的地址,然后才是读操作
struct i2c_rdwr_ioctl_data msgset[1];
msgs[0].addr = slave_addr;
msgs[0].flags = 0;//表示写
msgs[0].len = 1;
msgs[0].buf = outbuf;
msgs[1].addr = slave_addr;
msgs[1].flags = I2C_M_RD | I2C_M_NOSTART;//读
msgs[1].len = 1;
msgs[1].buf = inbuf;
msgset[0].msgs = msgs;
msgset[0].nmsgs = 2;
outbuf[0] = reg;
inbuf[0] = 0;
*result = 0;
if (ioctl(i2c_fd, I2C_RDWR, &msgset) < 0) {
perror("ioctl(I2C_RDWR) in i2c_read");
return -1;
}
*result = inbuf[0];
return 0;
}
ref:
Pololu - 12.8. Example I²C code for Linux in C
i2c-dev.h - include/linux/i2c-dev.h - Linux source code (v3.3) - Bootlin
【北京迅为】嵌入式学习之Linux驱动篇_哔哩哔哩_bilibili
https://gist.github.com/JamesDunne/9b7fbedb74c22ccc833059623f47beb7
[Linux驱动炼成记] 01-用户空间操作IIC - 云+社区 - 腾讯云
Linux应用程序直接调用I2C控制器实现的设备驱动_jklinux的博客-程序员ITS301_i2c控制器驱动 - 程序员ITS301
i2c驱动之调用ioctl函数进行读写at24c08_luckywang1103的专栏-CSDN博客_i2c_rdwr_ioctl_data