Linux下I2C设备通信

刚入门Linux设备I2C通信,感觉很迷惑,不管是看书,看视频,还是网上查,都是千篇一律的资料,抄来抄去,还都是过时的,很TM 恶心。

比如说我现在在做的,在应用程序中去读取EEPROM的数据,问题到现在还没解决(2018/8/1:现在已经解决了),但是至少已经有了一点点的认识,虽然使用的是网上说的方法,但是第一,方法太多太杂,第二,说的不清不楚。

下面先说下自己的一些心得吧,后续有了再加进来。

一.

很多情况下都不需要像网上说的那样累赘,因为我们很多人都是在平台的基础上去进行开发,所以像i2c_adapter和i2c_algorithm这类的驱动我们根本不用写,我们写的最多的应该是(I2C)设备驱动,也就是把某一个设备接入到Linux系统中,然后写这个设备的驱动。

二.

(I2C)设备驱动也有很多种写法,Linux3.x的内核中使用dts去代替原先的platform_data(好像是这样),这样一来,设备驱动的编写方式就有了改动,一个网页是这样,一个网页又是那样,有的甚至网页上都没有,对于刚入门的学者,也许他们拿到的内核版本和网上的版本有什么区别,他们根本就不知道,当然也不知道应该怎么写。

三.

以读取EEPROM数据为例,有很多方法都可以。比如说接下来说的这种

第一种:

dts,(eeprom)设备驱动都不需要写,只需要在应用程序中通过I2C总线对设备地址进行传输就可以了。如下:

	
#include <stdio.h>
#include <linux/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#define I2C_RETRIES 0x0701
#define I2C_TIMEOUT 0x0702
#define I2C_RDWR 0x0707 
/*********参数定义与内核一致,路径include/linux/i2c-dev.h*******/
 
struct i2c_msg
{
	unsigned short addr;
	nsigned short flags;
#define I2C_M_TEN 0x0010
#define I2C_M_RD 0x0001
	unsigned short len;
	unsigned char *buf;
};
 
struct i2c_rdwr_ioctl_data
{
	struct i2c_msg *msgs;
	int nmsgs; 
        /* nmsgs这个数量决定了有多少开始信号,对于“单开始时序”,取1*/
};
 
int main()
{
	int fd,ret;
	struct i2c_rdwr_ioctl_data e2prom_data;
	fd=open("/dev/i2c-0",O_RDWR);
        /*
        */dev/i2c-0是在注册i2c-dev.c后产生的,代表一个可操作的适配器。如果不使用i2c-dev.c
        *的方式,就没有,也不需要这个节点。
        */
	if(fd<0)
	{
		perror("open error");
	}
	e2prom_data.nmsgs=2; 
        /*
        *因为操作时序中,最多是用到2个开始信号(字节读操作中),所以此将
        *e2prom_data.nmsgs配置为2
        */
	e2prom_data.msgs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));
	if(!e2prom_data.msgs)
	{
		perror("malloc error");
		exit(1);
	}
	ioctl(fd,I2C_TIMEOUT,1);/*超时时间*/
	ioctl(fd,I2C_RETRIES,2);/*重复次数*/
 
	/***write data to e2prom**/
	e2prom_data.nmsgs=1;
	(e2prom_data.msgs[0]).len=2; //1个待写入的寄存器地址和1个数据
	(e2prom_data.msgs[0]).addr=0x50;//e2prom 设备地址
	(e2prom_data.msgs[0]).flags=0; //write
	(e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);
	(e2prom_data.msgs[0]).buf[0]=0x10;// e2prom 待写入寄存器的地址
	(e2prom_data.msgs[0]).buf[1]=0x58;//the data to write 
 
        ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);
	if(ret<0)
	{
		perror("ioctl error1");
	}
	sleep(1);
 
        /******read data from e2prom*******/
	e2prom_data.nmsgs=2;
	(e2prom_data.msgs[0]).len=1; //1个待写入的寄存器地址
	(e2prom_data.msgs[0]).addr=0x50; // e2prom 设备地址
	(e2prom_data.msgs[0]).flags=0;//write
	(e2prom_data.msgs[0]).buf[0]=0x10;//e2prom数据地址,即寄存器地址
	(e2prom_data.msgs[1]).len=1;//1个待读出的数据
	(e2prom_data.msgs[1]).addr=0x50;// e2prom 设备地址 
	(e2prom_data.msgs[1]).flags=I2C_M_RD;//read
	(e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的地址。
 	(e2prom_data.msgs[1]).buf[0]=0;//初始化读buffer
 
        ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);
	if(ret<0)
	{
		perror("ioctl error2");
	}
	printf("buff[0]=%x/n",(e2prom_data.msgs[1]).buf[0]);
        /***打印读出的值,没错的话,就应该是前面写的0x58了***/
	close(fd);
	return 0;
}

2018/8/1:继续添加另一种方法(第二种):

unsigned char p_tof_eeprom_double[128*1024];//全局变量,用于存储标定数据

int iic1_open()
{
	int fd;
	fd=open("/dev/i2c-1",O_RDWR);
    if(fd<0)
    {
            perror("/dev/i2c-1 open error\n");
            return -1;
    }
    return fd;
}

//读操作先发Slaveaddr_W+Regaddr_H+Regaddr_L 3个字节来告诉设备操作器件及两个byte参数
//然后发送Slaveaddr_R读数据
static int iic_read(int fd, unsigned char buff[], int reg_addr, int count)
{
    int ret;
    char sendbuffer[2];
    sendbuffer[0]=reg_addr>>8;
    sendbuffer[1]=reg_addr;
    write(fd,sendbuffer,2);
	ret=read(fd,buff,count);
//	printf("read %d byte at 0x%x\n", ret, reg_addr);
	return ret;
}
//在写之前,在数据前加两个byte的参数,根据需要解析
static int iic_write(int fd, unsigned char buff[], int reg_addr, int count)
{
        int ret;
        static char sendbuffer[100];
        memcpy(sendbuffer+2, buff, count);
        sendbuffer[0]=reg_addr>>8;
        sendbuffer[1]=reg_addr;
        ret=write(fd,sendbuffer,count+2);
//        printf("write %d byte at 0x%x\n", (ret-2), reg_addr);
        return ret;
}

void save_file_to_sdcard(void *p_in,unsigned int size)
{
	char save_file[100];
	char *p_save = (char *)p_in;

	sprintf(save_file, "/mnt/sdcard/tof_calib_data.usb");
	FILE *out_file = fopen(save_file,"wb");
	printf("------------------------dump begin----------------------------\n");
	fwrite(p_save, size, 1, out_file);
	fclose(out_file);
	printf("------------------------dump over----------------------------\n");
}

int Read_Cailb_Data(int slave_address,int register_address)
{
	int fd,ret,i,j;
    unsigned char *p1=p_tof_eeprom_double;
	fd=iic1_open();
	ret = ioctl(fd,I2C_TENBIT,0);   //7bit slave address
	for(j=0;j<2;j++)
	{
		ret = ioctl(fd,I2C_SLAVE_FORCE,slave_address);    //设置I2C从设备地址[6:0]
		for(i=0;i<8;i++)
		{
			ret=iic_read(fd,p1,register_address+i*8*1024,8*1024);
//			printf("%d bytes read:\n",ret);
			p1+=8*1024;
		}
		slave_address++;
	}
	printf("=====Read Calibration data  success!======\n");
	close(fd);
	return 0;
}

int Control_Register(int slave_address,int register_address,int flag,int value)
{
	int fd,ret;
    unsigned char buf[2];
	fd=iic1_open();
	ret = ioctl(fd,I2C_TENBIT,0);   //7bit slave address
	ret = ioctl(fd,I2C_SLAVE_FORCE,slave_address);    //设置I2C从设备地址[6:0]

	if(flag==0)//read
	{
		ret=iic_read(fd,buf,register_address,2);
//		printf("======buf[0] is 0x%x==========\n",buf[0]);
//		printf("======buf[1] is 0x%x==========\n",buf[1]);
		printf("=====Read Register data  success!======\n");
	}
	else if(flag==1)//write
	{
	    buf[0]=value>>8;
	    buf[1]=value;
		ret=iic_write(fd,buf,register_address,2);
//		printf("======buf[0] is 0x%x==========\n",buf[0]);
//		printf("======buf[1] is 0x%x==========\n",buf[1]);
		printf("=====Write Register data  success!======\n");
	}
	else
		printf("Control Register error!\n");

	close(fd);
	return 0;
}

int test()
{
	//	read calib
	Read_Cailb_Data(0x56,0);

	//	save file to sdcard
//	save_file_to_sdcard(p_tof_eeprom_double,128*1024);

	//	read register
//	Control_Register(0x3d,0x9835,0,NULL);

	//	write register
//	Control_Register(0x3d,0x9835,1,0x0312);

	return 0;
}

首先要说明,这种方法也不需要在dts及设备驱动中做任何的添加及修改,只需要在应用层添加这些函数即可。

其次,这种方法很通用,不管是eeprom还是sensor的寄存器数值,都可以进行随机读写,比第一种方法通用的多。

 

当然,还有其他的方法,比如说通过sysfs的方式去读取,或者其他方式,但是这些方式可能需要编写eeprom的设备驱动。我现在也不太懂,等我弄懂了再来添加。

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值