精华内容
下载资源
问答
  • verilog实现I2C,可以用于控制标准I2C寄存器; 代码中I2C采用:7bit寄存器地址+寄存器地址+写/读数据模式
  • 一、前言 本文主要分为三个部分,第一部分,介绍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盘单独分享给你。

    展开全文
  • 摘要:本应用笔记介绍如何通过吉比特多媒体串行链路(GMSL) SerDes的远端I2C接口访问16位寄存器地址。  引言  Maxim吉比特多媒体串行链路(GMSL)串行器/解串器(SerDes)系列包括MAX9249、MAX9259、MAX9260、...
  • I2C通信模块 时钟模块 寄存器配置模块,代码有本人的详细注释,看一篇就懂,可以灵活修改
  • I2C寄存器控制

    千次阅读 2019-09-03 22:55:46
    /* 设置寄存器启动传输 */ /* 1. 配置为 master tx mode */ IICCON |= (1<<7); /* TX mode, 在ACK周期释放SDA */ IICSTAT = (1<<4); /* 2. 把从设备地址写入IICDS */ IICDS = msg->...

       

          /* 设置寄存器启动传输 */
          /* 1. 配置为 master tx mode */
        IICCON |= (1<<7); /* TX mode, 在ACK周期释放SDA */
        IICSTAT = (1<<4);
            
        /* 2. 把从设备地址写入IICDS */
        IICDS = msg->addr<<1;
        
        /* 3. IICSTAT = 0xf0 , 数据即被发送出去, 将导致中断产生 */
        IICSTAT = 0xf0;
        

        /* 后续的传输由中断驱动 */

        /* 循环等待中断处理完毕 */

    发送模式:

    IICDS = 第一个字节------> 发送完成---->产生中断,拉低SCL----------->在中断程序中判断状态--------->

    IICDS = 第二个字节------->发送。。。

    接收模式:

    发起传输,接收第一个字节-------->产生中断------->SCL被拉低--------->中断程序中判断

     

     

     

     

    展开全文
  • 海思hisi I2C SPI读写寄存器

    千次阅读 2019-08-20 15:39:25
    I2C 读写命令示例 此操作示例通过 I2C 读写命令实现对 I2C 外围设备的读写操作。 a. 在控制台使用 i2c_read 命令对 I2C 外围设备进行读操作: ~ $ i2c_read <i2c_num> <device_addr> <reg_addr> ...

    I2C 读写命令示例
    此操作示例通过 I2C 读写命令实现对 I2C 外围设备的读写操作。

    a. 在控制台使用 i2c_read 命令对 I2C 外围设备进行读操作:
    ~ $ i2c_read <i2c_num> <device_addr> <reg_addr> <end_reg_addr> <reg_width> <data_width> <reg_step>
    例如读挂载在 I2C 控制器 2 上的 sil9024 设备的 0x8 寄存器:
    ~ $ i2c_read 2 0x72 0x8 0x8 0x1 0x1
    说明:

    i2c_num: I2C 控制器序号(对应《 Hi3516A 专业型 HD IP Camera Soc 用户指南》中的 I2C 控制器 0、 1、 2)
    device_addr:外围设备地址( Hi3516A 只支持 7bit 设备地址)
    reg_addr:读外围设备寄存器操作的开始地址
    end_reg_addr:读外围设备寄存器操作的结束地址
    reg_width:外围设备的寄存器位宽( Hi3516A 支持 8/16bit)
    data_width:外围设备的数据位宽( Hi3516A 支持 8/16bit)
    reg_step:连续读外围设备寄存器操作时递增幅值,默认为 1,即连续读寄存器,读取单个寄存器时不使用该参数

    b. 在控制台使用 i2c_write 命令对 I2C 外围设备进行写操作:
    ~ $ i2c_write <i2c_num> <device_addr> <reg_addr> <value> <reg_width> <data_width>
    例如向挂载在 I2C 控制器 2 上的 sil9024 设备的 0x8 寄存器写入数据 0xa5:
    ~ $ i2c_write 2 0x72 0x8 0xa5 0x1 0x1
    说明:

    i2c_num: I2C 控制器编号(对应《 Hi3516A 专业型 HD IP Camera Soc 用户指南》中的 I2C 控制器 0、 1、 2)
    device_addr:外围设备地址( Hi3516A 的 I2C 控制器只支持 7bit 设备地址)
    reg_addr:写外围设备寄存器操作的地址
    value:写外围设备寄存器操作的数据
    reg_width:外围设备的寄存器位宽( Hi3516A 的 I2C 控制器支持 8/16bit)
    data_width:外围设备的数据位宽( Hi3516A 的 I2C 控制器支持 8/16bit)

    SPI 读写命令示例
    此操作示例通过 SPI 读写命令实现对 SPI 外围设备的读写操作。
    在控制台使用 spi_read 命令对 SPI 外围设备进行读操作:
    ~ $ ssp_read <spi_num> <csn> <dev_addr> <reg_addr> [num_reg] [dev_width] [reg_width] [data_width]
    其中[num_reg] 可以省略,缺省值是 1(表示读 1 个寄存器)。[dev_width] [reg_width] [data_width]可以省略,缺省值都是1(表示1Byte)。
    例如读挂载在 SPI 控制器 0 片选 0 上设备地址为 0x2 的设备的 0x0 寄存器:
    ~ $ ssp_read 0x0 0x0 0x2 0x0 0x10 0x1 0x1 0x1

    说明:

    spi_num: SPI 控制器号(对应《 Hi3516A 专业型 HD IP Camera Soc 用户指南》中的 SPI 控制器0、 1)
    csn:片选号( Hi3516A 的 SPI 控制器 0 有 1 个片选、控制器 1 有 3 个片选)
    dev_addr:外围设备地址
    reg_addr:外围设备寄存器开始地址
    num_reg:读外围设备寄存器个数
    dev_width:外围设备地址位宽(支持 8 位)
    reg_width:外围设备寄存器地址位宽(支持 8 位)
    data_width:外围设备的数据位宽(支持 8 位)

    在控制台使用 spi_write 命令对 SPI 外围设备进行写操作:
    ~ $ ssp_write <spi_num> <csn> <dev_addr> <reg_addr> <data> [dev_width] [reg_width] [data_width]
    其中[dev_width] [reg_width] [data_width]可以省略,缺省值都是 1(表示 1Byte)。
    例如向挂载在 SPI 控制器 0 片选 0 上设备地址为 0x2 的设备的 0x0 寄存器写入数据 0x65:
    ~ $ ssp_write 0x0 0x0 0x2 0x0 0x65 0x1 0x1 0x1

    说明:

    spi_num: SPI 控制器序号(对应《 Hi3516A 专业型 HD IP Camera Soc 用户指南》中的 SPI 控制器 0、 1)
    csn:片选号( Hi3516A 的 SPI 控制器 0 有 1 个片选、控制器 1 有 3 个片选)
    dev_addr:外围设备地址
    reg_addr:外围设备寄存器地址
    data:写外围设备寄存器的数据
    dev_width:外围设备地址位宽(支持 8 位)
    reg_width:外围设备寄存器地址位宽(支持 8 位)
    data_width:外围设备的数据位宽(支持 8 位)
    此 SPI 读写命令仅支持 sensor 的读写操作。
     ———————————————— 
    版权声明:本文为CSDN博主「聚优致成」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_29350001/article/details/52680643

    展开全文
  • ov5640的寄存器配置

    2019-10-15 19:07:41
    基于FPGA的可见光OV5640的寄存器配置,通过SCCB协议对寄存器完成分辨率为1280*960,帧率为15帧,输出数据格式为YUV422,排列方式为YUYVYUYV的配置
  • ADV7611寄存器配置

    2018-11-27 15:44:56
    ADV7611寄存器配置范例
  • 控制ARM12C总线接口需要配置总线控制寄存器(rIICCON)、总线状态寄存器(rIICSTAT)、总线发送接收移位寄存器(rIICDS)和总线地址寄存(rIICADD)这4个寄存器。总线控制寄存器通常在程序开始时配置,包括应答信号和接收
  • 在这里分享一下海思芯片I2C配置方法,希望能对大家使用海思芯片时有帮助,也希望大家多多使用华为海思芯片。 I2C总线简介 首先按照惯例俗套的说一下I2C总线的定义,I2C总线是由Philips公司开发的一种简单、双向...

     

     

    最近美帝从政府层面出手限制华为,作为经常使用华为海思产品的程序猿小编,觉得海思芯片有完整详细的datasheet,配置起来简单,也很容易上手。在这里分享一下海思芯片I2C的配置方法,希望能对大家使用海思芯片时有帮助,也希望大家多多使用华为海思芯片。

    I2C总线简介

    首先按照惯例俗套的说一下I2C总线的定义,I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根分别命名为SDA(串行数据线)和SCL(串行时钟线)的信号线连接不同的器件即可传输信息。

    I2C总线上的器件有主从之分,在嵌入式系统上的主器件一般是嵌入式主芯片(MCU),主器件负责启动、终止数据传送,产生时钟信号。从器件一般是外部设备,像小编所在行业的外部设备包括温度传感器、Tunner、HDMI等。

    I2C主设备配置

    了解了I2C总线的定义和基本原理后,我们怎样做才能让I2C总线正常工作呢?
    首先,我们得配置I2C(本文以Hi3798CV200芯片为例)。
    步骤一:硬件确认,确认连接MCU和外设用的那一组I2C通道。Hi3798CV200 芯片集成5个I2C控制器(一般命名为I2C0~I2C4),实现标准I2C主设备功能,可完成对I2C总线上的从设备的数据发送和接收,这里我们确认的是I2C1。

    步骤二:确认硬件管脚复用情况。因为在嵌入芯片中,绝大多数管脚都是复用的。我们首先查一下芯片datasheet,看该管脚是否是复用的。从下图可以看出来该I2C管脚是复用的:

     

     

    步骤三:配置复用寄存器。从步骤二可以看出,我们使用的I2C1管脚是有复用的,所以我们要配置复用寄存器使当前管脚发挥I2C的功能。查看datasheet得知,控制该组I2C的复用寄存器分别是ioshare_56和ioshare_57。
    我们可以按照下图所示的将寄存器的低三位配置为010,即此管脚就是执行I2C1的数据线功能了。

     

     

    同样的方法配置ioshare_57。

    配置完后我们将记录配置信息相应的分区文件烧录的开发板中。在串口输入如下命令可以查看I2C的信息:

    Hi3798CV200:/ # cat /proc/hisi/msp/i2c                                     
    ---------Hisilicon Standard I2C Info---------
    No.            Rate
    0             0
    1             100000
    2             0
    3             0
    4             0
    

    此信息显示I2C1,速率100K。

    I2C总线调试

    前面一节我们配置了I2C主器件,接下来就是调试该总线,调试之前需要确认从设备在I2C总线上的地址、该设备内部寄存器地址以及寄存器地址长度。
    小编这里用到的是一款温度传感器。其设备地址为0x90,内部寄存器地址是0x0,寄存器地址长度为1个字节。
    海思芯片I2C调试起来很方便,因为它自带了I2C控制命令,包含了读写等操作,省去了自己写调试代码。
    串口命令行下输入i2c_read,就会告诉你读取操作使用方法:

    Hi3798CV200:/ # i2c_read  
    Usage: i2c_read  i2c_channel  device_addr  register_addr  register_addr_len  read_bytes_number
    

    参数含义说明:

    • i2c_channel:I2C通道

    • device_addr:I2C设备地址。

    • register_addr: i2c设备内部寄存器地址。

    • register_addr_len: i2c设备内部寄存器地址长度。

    • read_bytes_number:所需要读取的数据长度。

    于是我们就可以按照上面的方法调试I2C总线了,命令行下输入:

    Hi3798CV200:/ # i2c_read 1 0x90 0 2 2
    

    得到以下结果:

    从上图看出来,我们执行是错误的,那么是不是I2C没有配置好呢?别急,继续检查一遍我们输入的命令,发现第4个参数写成了2,而前面我们介绍的是寄存器地址长度为1个字节。修改命令:

    Hi3798CV200:/ # i2c_read 1 0x90 0 1 2

     

    查看执行结果:

     

    成功啦,成功读取到2个字节内容的数据。到这里,说明我们对I2C的配置是正确的!

    I2C总线的工作状态

    1、I2C开始和停止工作
    开始:当SCL是高电平时,SDA从高电平跳变到低电平,则I2C总线开始工作
    结束:当SCL是高电平时,SDA从低电平跳变到高电平,则I2C总线结束工作,如下图:

     

    2、开始状态后紧跟着主器件会发送一个字节的数据,这个字节数据由7bit从器件地址和1bit读写位组成。
    3、当从器件收到总线上的地址并匹配,发现与自己地址相同时,则在第9个时钟周期回复一个应答反馈信号。
    应答信号如下图:

     

    非应答信号如下图所示:

     

    4、当主器件收到从器件的应答信号后继续发送后面的数据。从器件收到数据后发送ACK则表示继续传输,发送NACK则传输结束。

    留在后面的作业

    小编用示波器抓取了一张I2C总线的波形图,下图黄色为SCL时钟信号,绿色为SDA数据信号。

     

    通过观看这张图结合上面的一些知识,大家能知道这张图上I2C总线做了哪些操作吗?欢迎微信关注下方二维码和我们交流!

     

    展开全文
  • IIC寄存器配置

    2015-04-15 17:07:07
    IIC寄存器配置,read,write
  • STM32寄存器列表 I2C相关寄存器

    千次阅读 2018-03-09 19:15:52
    I2C_CR1(控制寄存器1)15位:SWRST软件复位,当被置位时,I2C处于复位状态,在复位该位前确信I2C的引脚被释放,总线是空的,定义:0(I2C模块不处于复位状态),1(I2C模块处于复位状态) 注:该位可以用于BUSY位为’1’,...
  • IMX290寄存器配置(LVDS)The IMX290LQR-C is a diagonal 6.46 mm (Type 1/2.8) CMOS active pixel type solid-state image sensor with a square pixel array and 2.13 M effective pixels. This chip operates ...
  • 利用I2C总线方式和I2C设备驱动方式配置从设备寄存器,其中设备驱动包括应用程序。有什么疑问,可以看我的微博工作笔记,有相关的测试结果。
  • 硬件I2C与模拟I2C

    万次阅读 多人点赞 2019-03-11 17:48:22
    硬件(固件)I2C是直接调用内部寄存器进行配置;而软件I2C是没有寄存器这个概念的。 软件I2C一般是使用GPIO管脚,用软件控制SCL,SDA线输出高低电平,模拟i2c协议的时序。 例如下面这段I2C的开始和结束信号,我们...
  • 硬件I2C初始化的时候,需要按照实际情况来配置寄存器I2C_Timing,使用该工具计算数值,硬件I2C,时钟配置工具
  • I2C从设备地址设置。
  • 应用层读写i2c从设备寄存器

    千次阅读 2015-09-29 11:59:20
    配置i2c从设备寄存器时往往需要修改驱动中的初始化函数来修改寄存器的值,这样往往需要重新编译内核,其实可以使用i2c驱动提供给应用层的接口函数ioctl来在命令行修改寄存器,只需要编写一个类似i2c测试程序的程序...
  • 货号STC-7099,注意我之前网上找了好几个不同版本的配置表试了都不行,后来根据货号找的原厂同型号要的配置。下载之前最好匹配货号是否一致。文件内容就一数组。
  • 在Android手机中通过i2c读取芯片寄存器(含i2c-tools)tags: Android Linux i2c driver需求:同事甩来一个某品牌的手机,Android系统,需要把里面某个芯片的寄存器配置参数值读出来。折腾了两天(坑爹的网络以及...
  • 学习DSP外设之I2C配置和应用

    千次阅读 2016-10-26 16:34:23
    一、I2C的协议这里就不赘述了,随便搜一下就可以了解。 二、I2C的初始化 1.Enable I2C clock configure the PSC register(if it is driven by PSC)PSC相关说明可以查看手册 2. Place I2C in reset set IRS =0 in ...
  • I2C_Timing_Configuration

    2016-09-07 21:32:13
    I2C_Time 寄存器配置工具,主要针对stm32F1 stm32f0系列的I2C配置
  • 1. 配置I2C使能寄存器EN中关闭I2C使能; 2. 配置标准速度I2C时钟高电平寄存器SS_SCL_HCNT; 3. 配置标准速度I2C时钟低电平寄存器SS_SCL_LCNT; 4. 配置数据保持时间寄存器SDA_HOLD; 5. 配置目标地址寄存器TAR,值为从...
  • 根据 ug1182,zcu102 板卡上 PS 连接 2 个 I2C 接口,分别是 I2C0(MIO 14-15) 和 I2C1(MIO 16-17)。 本试验使用 I2C1 通过 zcu102 板卡的 FMC HPC1 接口连接外设,PS 作为 I2C Master,外设作为 I2C Slave。 I2C ...
  • 应用层读写取i2c设备代码可读写8位,16位设备地址小工具及源码 平常的I2C只能读8位,此工具可读写8,16位寄存器地址
  • wm8960寄存器配置

    热门讨论 2015-03-28 16:43:31
    wm8960寄存器配置表,并包含通道连线图,经调试后的记录,用于声卡驱动开发。
  • STM32_IIC程序_寄存器

    2018-05-02 14:46:17
    2,串口1(波特率:9600,PA9/PA10连接在板载USB转串口芯片CH340上面) 3,ALIENTEK 2.8/3.5/4.3/7寸TFTLCD模块(通过GPIO驱动,连接关系见lcd.h) 4,按键KEY0(PC5)/KEY_UP(PA0,也称之为WK_UP) 5,24C02(IIC连接在PC11/...
  • STM32F030_I2C详细配置说明

    万次阅读 2017-04-11 14:59:04
    STM32F030_I2C详细配置说明本文主要总结STM32F030_I2C的相关功能与源代码分享。 I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域...
  • 文章内容根据野火学习教程进行整理,仅仅是学习记录。 开发板: 野火STM32F429-...先找电路图,看看这个I2C设备挂载在I2C1、I2C2、I2C3的哪一个总线上,以及接的是哪两个GPIO。 SDA挂载在I2C1总线,接的是PB7引...
  • stm32 i2c通信 [操作寄存器+库函数]

    千次阅读 2016-08-30 21:29:24
    I2C总线是由NXP(原PHILIPS)公司设计,有十分简洁的物理层定义,其特性如下: 只要求两条总线线路:一条串行数据线SDA,一条串行时钟线SCL; 每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 64,004
精华内容 25,601
关键字:

i2c寄存器配置