Linux i2c子系统应用之Linux ARM嵌入式i2c通信(用i2c设备总线完成i2c从设备寄存器的配置)

一、前言

        本文主要分为三个部分,第一部分,介绍i2c总线应用的背景以及本文编译测试需要的开发环境;第二部分,介绍主要的源码及相关函数接口;第三部分,测试方法以及详细测试结果,i2c从设备的7bit器件地址可以在设备的datasheet查找。文章的最后会给大家分享本文的所有源码

二、开发背景和环境 

        在做嵌入式相关工作时,需要配置i2c从设备的寄存器是常有的事,本文记录了利用i2c总线的去配置i2c从设备的方法。本例是针对i2c子系统来实现的应用程序,在我看来i2c子系统都是标准的,因此跟内核版本以及ARM具体型号没有关系,其中ioctl调用的。其中ARM s3c6410有2个i2c总线,但硬件只引出了i2c-0。

优点:(1)编写代码简单(相对不懂字符驱动架构来说,而且驱动方式需要一个驱动程序和一个应用测试程序),一个应用程序程序可以快速配置寄存器,很快看到结果;

            (2)启动时间比较相比内核驱动的慢,应用程序是在文件系统初始化完成后才能运行;比如,用i2c配置显示模块,要显示屏尽快工作,把配置做在内核驱动比较合适。

缺点 :(1)在应用层运行软件时,i2c从设备需要多种功能操作时(比如修改摄像头的亮度、放大、曝光、分辨率等配置),把每个功能包装成函数模块,相对层次不明显,而且难以管理维护,建议用驱动的实行完成(内核驱动、.ko均可)

             (2)应用层只能调用i2c子系统驱动程序所支持的接口,需要扩展使用内核的其他资源(在必要测试时,但理论很少发生,因内核的存在就是防止应用层随意访问内核的资源),要么修改i2c子系统的驱动,或者自己编写的i2c从设备的设备驱动    。

运行环境:ARM S3C6410平台

交叉编译器:ARM-LINUX-GCC

内核版本:2.6.28.6

三、源码的讲解

         源码部分主要又分为两个部分,第一部分是我们工作中所要应用到的,无非是i2c总线的初始化(打开、关闭操作),还有i2c的读写(write、read操作);第二部分,解决部分疑问(变量、数据结构以及宏定义的来源),选择性了解,由Linux属于开源系统,所以源码中的任何定义都能够找到;

1、应用程序中用户的宏定义

#define I2C_READ_FLAGS 1
#define I2C_WRITE_FLAGS 0
#define I2C_BUS_NAME "/dev/i2c-0" 

2、以下为打开和关闭i2c设备操作,其中包括了检测从设备:

int i2c_init(const char *filename, const int addr) {

	// Check I2C connectivity
	file = open(filename, O_RDWR);
	if (file < 0) {
		printf("Failed to open bus\n");

	}

	// Check connectivity to slave device
	ak = ioctl(file, I2C_SLAVE, addr);
	if (ak < 0) {
		printf("Failed to acquire bus access and/or talk to slave\n");
	}
	return file;
}
void i2c_close(int fb){
	// closes I2C connection
	close(fb);
	return;
}

2、以下函数为用总线i2c去写从设备地址的寄存器,因本文所介绍得i2c从设备每个寄存器地址是8bit(一个字节),而有的i2c从设备寄存器地址为16bit(2个字节),相应地改下work_queue数据包就行,buf存放的寄存器地址和将要向寄存器写的值,flags字段区别读还是写,读的函数同理,凡事多测试、实践就能找到窍门:

static int i2c_write(int i2c_fd, unsigned int reg_address ,unsigned int reg_val){

        struct i2c_rdwr_ioctl_data work_queue;
        int ret;

        work_queue.nmsgs = 1;
        work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs * sizeof(struct i2c_msg));
        if(!work_queue.msgs){
                printf("msgs memery alloc error\n");
                close(i2c_fd);
                return 0;
        }
        if ((work_queue.msgs[0].buf = (unsigned char *)malloc(2 * sizeof(unsigned char))) == NULL){//本文buff是根据char(8位)一个字节分配的空间,可以根据寄存器地址长度自行分配
                printf("buf memery alloc error...\n");
                close(i2c_fd);
                return 0;
        }

        (work_queue.msgs[0]).len = 2;
        (work_queue.msgs[0]).flags = I2C_WRITE_FLAGS;
        (work_queue.msgs[0]).addr = SLAVE_DEVICE_7BIT_ADDRESS;
        (work_queue.msgs[0]).buf[0] =reg_address;
	(work_queue.msgs[0]).buf[1] = reg_val;

        ret = ioctl(i2c_fd, I2C_RDWR, (unsigned long) &work_queue);

        if(ret < 0){
                printf("Error during I2C_RDWR ioctl with error code: %d\n", ret);
                return 0;
        }
	printf("Write Reg:0x%x Value:0x%x\n",reg_address, reg_val);
	free(work_queue.msgs[0].buf);
	free(work_queue.msgs);
}

3、以下函数为用总线i2c去读从设备地址的寄存器:

static int i2c_read(int i2c_fd, unsigned char reg_address){
        struct i2c_rdwr_ioctl_data work_queue;
        unsigned char val,reg;
        int ret;

        work_queue.nmsgs = 2;
        work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs *sizeof(struct i2c_msg));

        if(!work_queue.msgs){
                printf("Memery alloc error\n");
                close(i2c_fd);
                return 0;
        }

	if ((work_queue.msgs[0].buf = (unsigned char *)malloc(2 * sizeof(unsigned char))) == NULL){
                printf("buf memery alloc error...\n");
                close(i2c_fd);
                return 0;
        }
        val =reg_address;
        (work_queue.msgs[0]).len = 1;
        (work_queue.msgs[0]).flags = I2C_WRITE_FLAGS;
	(work_queue.msgs[0]).addr = SLAVE_DEVICE_7BIT_ADDRESS;
        (work_queue.msgs[0]).buf = &val;

        (work_queue.msgs[1]).len = 1;
        (work_queue.msgs[1]).flags = I2C_READ_FLAGS;
        (work_queue.msgs[1]).addr = SLAVE_DEVICE_7BIT_ADDRESS;
	(work_queue.msgs[1]).buf = &val;


        ret = ioctl(i2c_fd, I2C_RDWR, (unsigned long) &work_queue);
        if(ret < 0){
                perror("Error during I2C_RDWR ioctl with error code:\n");
                return 0;
        }

	printf("Read Reg:0x%x Value:0x%x\n",reg_address, val);
        free(work_queue.msgs);
        return val;
}

以上为用i2c总线配置i2c从设备的接口函数,利用他们编程就足够了;

应用层ioctl函数是如何调用到内核驱动i2c子系统i2c-dev.c中i2cdev_ioctl函数的呢,请看下图,熟悉设备驱动模型就清楚,在此不在细讲,

如果还想了解以上源码中的i2c_rdwr_ioctl_data数据结构 、ioctl函数调用中使用到的宏定义以及ioctl的调用细节,请看下面:

上图展示了I2C_SLAVE宏定义的来源,在内核根目录的arch、drivers并没有搜到,而是在安装的交叉编译器目录下搜索到,而且有好几处都定义了,其实大同小异,从这里可以看出I2C_SLAVE定义与I2C子系统是标准的。平时遇到的多媒体V4l2架构的接口调用也是标准的,许多宏定义也在交叉编译器里定义的;

以下是i2c ioctl调用时所用到的宏定义,其来源于交叉编译器中的头文件:

/usr/local/arm/4.2.2-eabi/usr/include/linux/i2c.h  //I2C_RDWR等宏定义的头文件
/* ------------------------------------------------------------------------- */
/*									     */
/* i2c.h - definitions for the i2c-bus interface			     */
/*									     */
/* ------------------------------------------------------------------------- */
/*   Copyright (C) 1995-2000 Simon G. Vogl

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		     */
/* ------------------------------------------------------------------------- */

/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
   Frodo Looijaard <frodol@dds.nl> */

#ifndef _LINUX_I2C_H
#define _LINUX_I2C_H

#include <linux/types.h>

/*
 * I2C Message - used for pure i2c transaction, also from /dev interface
 */
struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
#define I2C_M_TEN	0x10	/* we have a ten bit chip address	*/
#define I2C_M_RD	0x01
#define I2C_M_NOSTART	0x4000
#define I2C_M_REV_DIR_ADDR	0x2000
#define I2C_M_IGNORE_NAK	0x1000
#define I2C_M_NO_RD_ACK		0x0800
#define I2C_M_RECV_LEN		0x0400 /* length will be first received byte */
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};

/* To determine what functionality is present */

#define I2C_FUNC_I2C			0x00000001
#define I2C_FUNC_10BIT_ADDR		0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
#define I2C_FUNC_SMBUS_HWPEC_CALC	0x00000008 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK		0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE	0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE	0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA	0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA	0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA	0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA	0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL	0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA	0x01000000
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2	 0x10000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */

#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
                             I2C_FUNC_SMBUS_WRITE_BYTE)
#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
                                  I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
                                  I2C_FUNC_SMBUS_WRITE_WORD_DATA)
#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
                                   I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
                                  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
#define I2C_FUNC_SMBUS_I2C_BLOCK_2 (I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 | \
                                    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2)

#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | \
                             I2C_FUNC_SMBUS_BYTE | \
                             I2C_FUNC_SMBUS_BYTE_DATA | \
                             I2C_FUNC_SMBUS_WORD_DATA | \
                             I2C_FUNC_SMBUS_PROC_CALL | \
                             I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
                             I2C_FUNC_SMBUS_I2C_BLOCK)

/*
 * Data for SMBus Messages
 */
#define I2C_SMBUS_BLOCK_MAX	32	/* As specified in SMBus standard */
union i2c_smbus_data {
	__u8 byte;
	__u16 word;
	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
	                       /* and one more for user-space compatibility */
};

/* smbus_access read or write markers */
#define I2C_SMBUS_READ	1
#define I2C_SMBUS_WRITE	0

/* SMBus transaction types (size parameter in the above functions)
   Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */
#define I2C_SMBUS_QUICK		    0
#define I2C_SMBUS_BYTE		    1
#define I2C_SMBUS_BYTE_DATA	    2
#define I2C_SMBUS_WORD_DATA	    3
#define I2C_SMBUS_PROC_CALL	    4
#define I2C_SMBUS_BLOCK_DATA	    5
#define I2C_SMBUS_I2C_BLOCK_DATA    6
#define I2C_SMBUS_BLOCK_PROC_CALL   7		/* SMBus 2.0 */


/* ----- commands for the ioctl like i2c_command call:
 * note that additional calls are defined in the algorithm and hw
 *	dependent layers - these can be listed here, or see the
 *	corresponding header files.
 */
				/* -> bit-adapter specific ioctls	*/
#define I2C_RETRIES	0x0701	/* number of times a device address      */
				/* should be polled when not            */
                                /* acknowledging			*/
#define I2C_TIMEOUT	0x0702	/* set timeout - call with int		*/


/* this is for i2c-dev.c	*/
#define I2C_SLAVE	0x0703	/* Change slave address			*/
				/* Attn.: Slave address is 7 or 10 bits */
#define I2C_SLAVE_FORCE	0x0706	/* Change slave address			*/
				/* Attn.: Slave address is 7 or 10 bits */
				/* This changes the address, even if it */
				/* is already taken!			*/
#define I2C_TENBIT	0x0704	/* 0 for 7 bit addrs, != 0 for 10 bit	*/

#define I2C_FUNCS	0x0705	/* Get the adapter functionality */
#define I2C_RDWR	0x0707	/* Combined R/W transfer (one stop only)*/
#define I2C_PEC		0x0708	/* != 0 for SMBus PEC                   */

#define I2C_SMBUS	0x0720	/* SMBus-level access */

/* ----- I2C-DEV: char device interface stuff ------------------------- */
#endif /* _LINUX_I2C_H */

其中i2c_rdwr_ioctl_data数据结构同宏定义一样,来源于交叉编译器的头文件中

/usr/local/arm/4.2.2-eabi/usr/include/linux/i2c-dev.h //i2c_rdwr_ioctl_data数据定义的头文件
/*
    i2c-dev.h - i2c-bus driver, char device interface

    Copyright (C) 1995-97 Simon G. Vogl
    Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef _LINUX_I2C_DEV_H
#define _LINUX_I2C_DEV_H

#include <linux/types.h>

/* Some IOCTL commands are defined in <linux/i2c.h> */
/* Note: 10-bit addresses are NOT supported! */

/* This is the structure as used in the I2C_SMBUS ioctl call */
struct i2c_smbus_ioctl_data {
	__u8 read_write;
	__u8 command;
	__u32 size;
	union i2c_smbus_data *data;
};

/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
	struct i2c_msg *msgs;	/* pointers to i2c_msgs */
	__u32 nmsgs;			/* number of i2c_msgs */
};

#define  I2C_RDRW_IOCTL_MAX_MSGS	42

#endif /* _LINUX_I2C_DEV_H */

再来看ioctl的函数接口,在内核驱动i2c子系统的i2c-dev.c文件里面,以下只是展示调用ioctl细节的相关部分

/opt/kernel-s3c6410/htx-linux-2.6.28-*******-20190114/drivers/i2c/i2c-dev.c //i2cdev_ioctl函数定义的文件
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct i2c_client *client = (struct i2c_client *)file->private_data;
	unsigned long funcs;

	dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
		cmd, arg);

	switch ( cmd ) {
	case I2C_SLAVE:
	case I2C_SLAVE_FORCE:
		/* NOTE:  devices set up to work with "new style" drivers
		 * can't use I2C_SLAVE, even when the device node is not
		 * bound to a driver.  Only I2C_SLAVE_FORCE will work.
		 *
		 * Setting the PEC flag here won't affect kernel drivers,
		 * which will be using the i2c_client node registered with
		 * the driver model core.  Likewise, when that client has
		 * the PEC flag already set, the i2c-dev driver won't see
		 * (or use) this setting.
		 */
		if ((arg > 0x3ff) ||
		    (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
			return -EINVAL;
		if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
			return -EBUSY;
		/* REVISIT: address could become busy later */
		client->addr = arg;
		return 0;
	case I2C_TENBIT:
		if (arg)
			client->flags |= I2C_M_TEN;
		else
			client->flags &= ~I2C_M_TEN;
		return 0;
	case I2C_PEC:
		if (arg)
			client->flags |= I2C_CLIENT_PEC;
		else
			client->flags &= ~I2C_CLIENT_PEC;
		return 0;
	case I2C_FUNCS:
		funcs = i2c_get_functionality(client->adapter);
		return put_user(funcs, (unsigned long __user *)arg);

	case I2C_RDWR:
		return i2cdev_ioctl_rdrw(client, arg);

	case I2C_SMBUS:
		return i2cdev_ioctl_smbus(client, arg);

	case I2C_RETRIES:
		client->adapter->retries = arg;
		break;
	case I2C_TIMEOUT:
		client->adapter->timeout = arg;
		break;
	default:
		/* NOTE:  returning a fault code here could cause trouble
		 * in buggy userspace code.  Some old kernel bugs returned
		 * zero in this case, and userspace code might accidentally
		 * have depended on that bug.
		 */
		return -ENOTTY;
	}
	return 0;
}

其中的读写函数ioctl都会执行

case I2C_RDWR:
        return i2cdev_ioctl_rdrw(client, arg);

下面我们查看i2cdev_ioctl_rdrw函数调用

/opt/kernel-s3c6410/htx-linux-2.6.28-*******-20190114/drivers/i2c/i2c-dev.c //i2cdev_ioctl_rdrw函数定义的文件
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
		unsigned long arg)
{
	struct i2c_rdwr_ioctl_data rdwr_arg;
	struct i2c_msg *rdwr_pa;
	u8 __user **data_ptrs;
	int i, res;

	if (copy_from_user(&rdwr_arg,
			   (struct i2c_rdwr_ioctl_data __user *)arg,
			   sizeof(rdwr_arg)))
		return -EFAULT;

	/* Put an arbitrary limit on the number of messages that can
	 * be sent at once */
	if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
		return -EINVAL;

	rdwr_pa = (struct i2c_msg *)
		kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
		GFP_KERNEL);
	if (!rdwr_pa)
		return -ENOMEM;

	if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
			   rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
		kfree(rdwr_pa);
		return -EFAULT;
	}

	data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
	if (data_ptrs == NULL) {
		kfree(rdwr_pa);
		return -ENOMEM;
	}

	res = 0;
	for (i = 0; i < rdwr_arg.nmsgs; i++) {
		/* Limit the size of the message to a sane amount;
		 * and don't let length change either. */
		if ((rdwr_pa[i].len > 8192) ||
		    (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {
			res = -EINVAL;
			break;
		}
		data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
		rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
		if (rdwr_pa[i].buf == NULL) {
			res = -ENOMEM;
			break;
		}
		if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],
				   rdwr_pa[i].len)) {
				++i; /* Needs to be kfreed too */
				res = -EFAULT;
			break;
		}
	}
	if (res < 0) {
		int j;
		for (j = 0; j < i; ++j)
			kfree(rdwr_pa[j].buf);
		kfree(data_ptrs);
		kfree(rdwr_pa);
		return res;
	}

	res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
	while (i-- > 0) {
		if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
			if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
					 rdwr_pa[i].len))
				res = -EFAULT;
		}
		kfree(rdwr_pa[i].buf);
	}
	kfree(data_ptrs);
	kfree(rdwr_pa);
	return res;
}

从上面可以看到,除了读写数据的包装、内核与用户空间数据拷贝的调用外,传输最终调用的i2c_transfer()函数接口,与设备驱动最底层调用的接口一致,最后都回归i2c子系统。

四、程序的测试:

1、在PC Linux上用交叉编译器编译程序,

以下是测试的main函数和编译过程

int main(int argc, char *argv[]){

	int ret;
	int temp, tmp;	
	unsigned char  value, address,readValue;
	fp = i2c_init(I2C_BUS_NAME,SLAVE_DEVICE_7BIT_ADDRESS);

	if(fp < 0){
		printf("open i2c failed!\n");	
	}
	
	if( (argc == 4) && (strcmp(argv[1],"write") == 0)){

		if((argv[2][0] == '0') && (argv[2][1] == 'x')){
			sscanf(argv[2],"0x%x",&tmp);
			address = tmp & 0xff;
		}

		if((argv[3][0] == '0') && (argv[3][1] == 'x')){
                        sscanf(argv[3],"0x%x",&temp);
			value = temp & 0xff;
		}

		i2c_write(fp, address, value);
		i2c_close(fp);

		return 0;

	}else if( (argc == 3) && (strcmp(argv[1],"read") == 0)){
		if((argv[2][0] == '0') && (argv[2][1] == 'x')){
                        sscanf(argv[2],"0x%x",&tmp);
                        address = tmp & 0xff;
                }
		i2c_read(fp, address);
		i2c_close(fp);

	}else if( (argc == 2) && (strcmp(argv[1],"on") == 0)){
		printf("=============on start===========\n");
		ch7026_power_on(fp);
		printf("=============on end===========\n");
		i2c_close(fp);

	}else if( (argc == 2) && (strcmp(argv[1],"off") == 0)){
		printf("=============off start===========\n");
		ch7026_power_off(fp);
		printf("=============off end===========\n");
		i2c_close(fp);

	}else if( (argc == 2) && (strcmp(argv[1],"readAll") == 0)){
		ch7026_read_status(fp);
		i2c_close(fp);
	}else{
		print_usage();
	}
}

static void print_usage(void)
{
        printf("usage:./ch7026_i2c_bus [commad]......\n");
        printf("./ch7026_i2c_bus on --------------------------Write some regs to trun on slave device\n");
        printf("./ch7026_i2c_bus off--------------------------Write some regs to turn off slave device\n");
        printf("./ch7026_i2c_bus write [address] [value]------Write only one reg\n");
        printf("./ch7026_i2c_bus read [address]-------------- Read only one reg\n");
        printf("./ch7026_i2c_bus readAll----------------------Read all regs\n");
        printf("For example:\n");
        printf("./ch7026_i2c_bus on\n");
        printf("./ch7026_i2c_bus off\n");
        printf("./ch7026_i2c_bus write 0x02 0x03\n");
        printf("./ch7026_i2c_bus read 0x02\n");
        printf("./ch7026_i2c_bus readAll\n");
}

2、然后把程序拷贝到开发板上进行测试:

首先读取寄存器的初始值,再向寄存器写值,由于有操作重复的寄存器,仔细对照就能看到被修改的寄存器。

最后把所有寄存器的值再读回来,仔细和写的寄存器的操作对照,就发现生效了,PS:注意重复操作的寄存器

结果如下,可以看到读写i2c从设备的0x06寄存器成功:

Makefile内容:

CC=/usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-gcc #更换成自己平台的交叉编译器
default:
        $(CC) ch7026_i2c_bus.c -o ch7026_i2c_bus
        cp ch7026_i2c_bus  /mnt/hgfs/upload/
clean:
        rm ch7026_i2c_bus -rf

       在读写寄存器的时候,要注意所要操作的寄存器的权限(是否可读写),另外有些i2c器件的初始化,需要按一定顺序写某些寄存器,有时候单独操作某几个寄存器会引发所有寄存器的值错乱。

      最后我把i2c总线方式和i2c设备驱动方式配置从设备的源码整理在一起上传到资源区(https://download.csdn.net/download/psy6653/11014339),

        写了这么多本想设置2个下载积分安慰下自己的,但不知道怎么的系统默认设置为5分(但修改不了),实在抱歉。不过没有关系,有多的积分的朋友就赞助下哈,没积分的朋友可以给我留言,我会用wan盘单独分享给你。

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值