本文主要分为三个部分,第一部分,介绍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 and
Frodo Looijaard */
#ifndef _LINUX_I2C_H
#define _LINUX_I2C_H
#include
/*
* 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
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
/* Some IOCTL commands are defined in */
/* 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盘单独分享给你。