![委屈](http://static.blog.csdn.net/xheditor/xheditor_emot/default/wronged.gif)
大家都知道, I2C总线仅仅使用SCL、SDA这两根信号线就实现了设备之间的数据交互,极大地简化了对硬件资源和PCB板布线空间的占用。因此,I2C总线应用非常的广泛。而工作中,我需要通过I2C实现Hi35XX控制视频芯片CX25828,我是这样做的,首先,把I2C当做一个字符设备,然后用2个GPIO管脚模拟数据线和时钟线,一个管脚控制方向,实现电平的高低控制,这样就可以模拟I2C通信了(CPU与设备间的通信)。
1.当做一个字符设备
看到这个,相信大家一定很熟悉了,对了,这就是我们入门时再熟悉不过的东东了,上层可直接对dev下的“gpioi2c”的这个节点open、write、read、close(不过,就目前这个,无read、write接口,只有ioctl了),接下来,你懂的。(在网上有很多比较成熟的I2C驱动,也是同我这个原理实现的,改改GPIO口地址就可以了)。
- <SPAN style="FONT-SIZE: 14px">//打开和关闭设备
- int gpioi2c_open(struct inode * inode, struct file * file)
- {
- return 0;
- }
- int gpioi2c_close(struct inode * inode, struct file * file)
- {
- return 0;
- }
- //最重要的结构体,文件描述符指针,上层直接ioctl就可通过底层调用gpioi2c_ioctl函数。
- //当然,可以有其他的成员,如read、write等。
- static struct file_operations gpioi2c_fops =
- {
- .owner = THIS_MODULE,
- .ioctl = gpioi2c_ioctl,
- .open = gpioi2c_open,
- .release = gpioi2c_close
- };
- static struct miscdevice gpioi2c_dev =
- {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "gpioi2c",//在dev下可以找到这个节点,上层就是通过open、read/write这个节点而对设备操作的。
- .fops = &gpioi2c_fops,
- };
- //导入导出
- static int __init gpio_i2c_init(void)
- {
- int ret;
- printk("hello dvr iic\n");
- //注册设备
- ret = misc_register(&gpioi2c_dev);
- if(0 != ret)
- return -1;
- i2c_set(SCL | SDA);
- return 0;
- }
- static void __exit gpio_i2c_exit(void)
- {
- //卸载设备
- misc_deregister(&gpioi2c_dev);
- }
- //导入导出内核设备
- module_init(gpio_i2c_init);
- module_exit(gpio_i2c_exit);
- MODULE_INFO(build, UTS_VERSION);
- MODULE_LICENSE("GPL");</SPAN>
//打开和关闭设备
int gpioi2c_open(struct inode * inode, struct file * file)
{
return 0;
}
int gpioi2c_close(struct inode * inode, struct file * file)
{
return 0;
}
//最重要的结构体,文件描述符指针,上层直接ioctl就可通过底层调用gpioi2c_ioctl函数。
//当然,可以有其他的成员,如read、write等。
static struct file_operations gpioi2c_fops =
{
.owner = THIS_MODULE,
.ioctl = gpioi2c_ioctl,
.open = gpioi2c_open,
.release = gpioi2c_close
};
static struct miscdevice gpioi2c_dev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = "gpioi2c",//在dev下可以找到这个节点,上层就是通过open、read/write这个节点而对设备操作的。
.fops = &gpioi2c_fops,
};
//导入导出
static int __init gpio_i2c_init(void)
{
int ret;
printk("hello dvr iic\n");
//注册设备
ret = misc_register(&gpioi2c_dev);
if(0 != ret)
return -1;
i2c_set(SCL | SDA);
return 0;
}
static void __exit gpio_i2c_exit(void)
{
//卸载设备
misc_deregister(&gpioi2c_dev);
}
//导入导出内核设备
module_init(gpio_i2c_init);
module_exit(gpio_i2c_exit);
MODULE_INFO(build, UTS_VERSION);
MODULE_LICENSE("GPL");
杂项设备(misc device):
在 Linux 内核的include\linux\miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主设备号10 ,一起归于misc device,其实misc_register就是用主设备号10调用register_chrdev()的。也就是说,misc设备其实也就是特殊的字符设备。
misc_device是特殊的字符设备。注册驱动程序时采用misc_register函数注册,此函数中会自动创建设备节点,即设备文件。无需mknod指令创建设备文件。因为misc_register()会调用class_device_create()或者device_create()。
2.对字符设备的控制:ioctl函数
提供给上层的接口,arg参数其实是一个地址,这个地址里面必须存放三个信息:设备地址+寄存器地址+data。通过设备地址找到设备,然后通过寄存器地址配置相应的寄存器。关于每次通信的信息量,这要看上层接口函数ioctl:int ioctl(int file_operations, int cmd, void *addr)时arg地址内存放的内容了,如果内容是int类型即4个字节,就如下。如果这个地址内存放的是结构体,而且sizeof这个结构体的大小为12byte(即设备地址、寄存器地址、以及数据各占4个字节),就不需要移动了,直接填进去。
- <SPAN style="FONT-SIZE: 14px">int gpioi2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
- {
- unsigned int val;
- char device_addr, reg_addr;
- short reg_val;
- switch(cmd)
- {
- case GPIO_I2C_READ:
- val = *(unsigned int *)arg;
- device_addr = (val&0xff000000)>>24;
- reg_addr = (val&0xffff00)>>8;
- reg_val = gpio_i2c_read(device_addr, reg_addr);
- *(unsigned int *)arg = (val&0xffffff00)|reg_val;
- break;
- case GPIO_I2C_WRITE:
- val = *(unsigned int *)arg;
- device_addr = (val&0xff000000)>>24;
- reg_addr = (val&0xffff00)>>8;
- reg_val = val&0xff;
- gpio_i2c_write(device_addr, reg_addr, reg_val);
- break;
- default:
- return -1;
- }
- return 0;
- }
- </SPAN>
int gpioi2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned int val;
char device_addr, reg_addr;
short reg_val;
switch(cmd)
{
case GPIO_I2C_READ:
val = *(unsigned int *)arg;
device_addr = (val&0xff000000)>>24;
reg_addr = (val&0xffff00)>>8;
reg_val = gpio_i2c_read(device_addr, reg_addr);
*(unsigned int *)arg = (val&0xffffff00)|reg_val;
break;
case GPIO_I2C_WRITE:
val = *(unsigned int *)arg;
device_addr = (val&0xff000000)>>24;
reg_addr = (val&0xffff00)>>8;
reg_val = val&0xff;
gpio_i2c_write(device_addr, reg_addr, reg_val);
break;
default:
return -1;
}
return 0;
}
3.对设备的读写:read、write
CX25828中,寄存器的地址为16位,设备地址为8位,所有,I2C每次只能read、write1个字节的数据。。总线空闲时,上拉电阻使SDA和SCL线都保持高电平。当 SCL 稳定在高电平时,SDA 由高到低的变化将产生一个开始位,而由低到高的变化则产生一个停止位。开始位和停止位都由 I2C 主设备产生。在选择从设备时,如果从设备采用 7 位地址,则主设备在发起传输过程前,需先发送1字节的地址信息,前 7 位为设备地址,最后 1 位为读写标志。之后,每次传输的数据也是 1 个字节,从 MSB 位开始传输。每个字节传完后,在SCL的第9个上升沿到来之前,接收方应该发出 1 个 ACK 位。SCL上的时钟脉冲由I2C主控方发出, 在第8个时钟周期之后。
- <SPAN style="FONT-SIZE: 14px">//读1个字节(其实可以2,或者4个字节,这个地方是实现读写功能以及决定字节数的,IIC支持)
- unsigned short gpio_i2c_read(unsigned char devaddress, unsigned int address)
- {
- unsigned short rxdata=0
- unsigned int tmp=0;
- int dev_addr=devaddress;
- //发送设备地址:8位
- i2c_start_bit();
- i2c_send_byte((unsigned char)(dev_addr));
- i2c_receive_ack();
- //寄存器地址:16位
- tmp=address&0xff00;
- tmp=tmp>>8;
- i2c_send_byte((unsigned char)tmp);
- i2c_receive_ack();
- tmp=address&0xff;
- i2c_send_byte((unsigned char)tmp);
- i2c_receive_ack();
- //发送:读状态
- i2c_start_bit();
- i2c_send_byte((unsigned char)(dev_addr) | 1);
- i2c_receive_ack();
- //接收数据:8bit
- rxdata = i2c_receive_byte();
- //停止接收数据
- i2c_stop_bit();
- rxdata=rxdata&0xff;
- return rxdata;
- }
- //写一个字节
- void gpio_i2c_write(unsigned char devaddress, unsigned int address, unsigned int data)
- {
- int tmp;
- unsigned short devaddr=devaddress&0xfe;//在手册中可以看到,读写状态占1bit,读为1,写为0.
- //设备地址:8bit
- i2c_start_bit();
- i2c_send_byte((unsigned char)(devaddr));
- i2c_receive_ack();//ACK
- //寄存器地址:16bit
- tmp=address&0xff00;
- tmp=tmp>>8;
- i2c_send_byte((unsigned char)tmp);
- i2c_receive_ack();
- tmp=address&0xff;
- i2c_send_byte((unsigned char)tmp);
- i2c_receive_ack();
- //发送数据:8bit
- tmp=data&0xff;
- i2c_send_byte((unsigned char)tmp);
- i2c_receive_ack();
- //停止接收数据
- i2c_stop_bit();
- }</SPAN>
//读1个字节(其实可以2,或者4个字节,这个地方是实现读写功能以及决定字节数的,IIC支持)
unsigned short gpio_i2c_read(unsigned char devaddress, unsigned int address)
{
unsigned short rxdata=0
unsigned int tmp=0;
int dev_addr=devaddress;
//发送设备地址:8位
i2c_start_bit();
i2c_send_byte((unsigned char)(dev_addr));
i2c_receive_ack();
//寄存器地址:16位
tmp=address&0xff00;
tmp=tmp>>8;
i2c_send_byte((unsigned char)tmp);
i2c_receive_ack();
tmp=address&0xff;
i2c_send_byte((unsigned char)tmp);
i2c_receive_ack();
//发送:读状态
i2c_start_bit();
i2c_send_byte((unsigned char)(dev_addr) | 1);
i2c_receive_ack();
//接收数据:8bit
rxdata = i2c_receive_byte();
//停止接收数据
i2c_stop_bit();
rxdata=rxdata&0xff;
return rxdata;
}
//写一个字节
void gpio_i2c_write(unsigned char devaddress, unsigned int address, unsigned int data)
{
int tmp;
unsigned short devaddr=devaddress&0xfe;//在手册中可以看到,读写状态占1bit,读为1,写为0.
//设备地址:8bit
i2c_start_bit();
i2c_send_byte((unsigned char)(devaddr));
i2c_receive_ack();//ACK
//寄存器地址:16bit
tmp=address&0xff00;
tmp=tmp>>8;
i2c_send_byte((unsigned char)tmp);
i2c_receive_ack();
tmp=address&0xff;
i2c_send_byte((unsigned char)tmp);
i2c_receive_ack();
//发送数据:8bit
tmp=data&0xff;
i2c_send_byte((unsigned char)tmp);
i2c_receive_ack();
//停止接收数据
i2c_stop_bit();
}
4.模拟数据线时钟线以及需要调用的一些函数
- <SPAN style="FONT-SIZE: 14px">#include "gpio_i2c.h"
- #define GPIO_0_BASE 0x20150000
- #define GPIO_0_DIR IO_ADDRESS(GPIO_0_BASE + 0x400) //GPIO方向控制
- #define SCL (1 << 1) /* GPIO 0_1 */
- #define SDA (1 << 0) /* GPIO 0_0 */
- #define GPIO_I2C_SDA_REG IO_ADDRESS(GPIO_0_BASE + 0x4)//数据线GPIO管脚
- #define GPIO_I2C_SCL_REG IO_ADDRESS(GPIO_0_BASE + 0x8)//时钟线GPIO管脚
- #define GPIO_I2C_SCLSDA_REG IO_ADDRESS(GPIO_0_BASE + 0xc)
- #define HW_REG(reg) *((volatile unsigned int *)(reg))
- #define DELAY(us) time_delay_us(us)
- //拉低数据线/时钟线
- static void i2c_clr(unsigned char whichline)
- {
- unsigned char regvalue;
- //拉低时钟线
- if(whichline == SCL)
- {
- //方向:输入/输出
- regvalue = HW_REG(GPIO_0_DIR);
- regvalue |= SCL;
- HW_REG(GPIO_0_DIR) = regvalue;
- //置零
- HW_REG(GPIO_I2C_SCL_REG) = 0;
- return;
- }
- else if(whichline == SDA)
- {
- regvalue = HW_REG(GPIO_0_DIR);
- regvalue |= SDA;
- HW_REG(GPIO_0_DIR) = regvalue;
- HW_REG(GPIO_I2C_SDA_REG) = 0;
- return;
- }
- else if(whichline == (SDA|SCL))
- {
- regvalue = HW_REG(GPIO_0_DIR);
- regvalue |= (SDA|SCL);
- HW_REG(GPIO_0_DIR) = regvalue;
- HW_REG(GPIO_I2C_SCLSDA_REG) = 0;
- return;
- }
- else
- {
- printk("Error input.\n");
- return;
- }
- }
- //拉高数据线/时钟线
- static void i2c_set(unsigned char whichline)
- {
- unsigned char regvalue;
- //拉高时钟线
- if(whichline == SCL)
- {
- regvalue = HW_REG(GPIO_0_DIR);
- regvalue |= SCL;
- HW_REG(GPIO_0_DIR) = regvalue;
- HW_REG(GPIO_I2C_SCL_REG) = SCL;
- return;
- }
- else if(whichline == SDA)
- {
- regvalue = HW_REG(GPIO_0_DIR);
- regvalue |= SDA;
- HW_REG(GPIO_0_DIR) = regvalue;
- HW_REG(GPIO_I2C_SDA_REG) = SDA;
- return;
- }
- else if(whichline == (SDA|SCL))
- {
- regvalue = HW_REG(GPIO_0_DIR);
- regvalue |= (SDA|SCL);
- HW_REG(GPIO_0_DIR) = regvalue;
- HW_REG(GPIO_I2C_SCLSDA_REG) = (SDA|SCL);
- return;
- }
- else
- {
- printk("Error input.\n");
- return;
- }
- }
- //延时usec微秒
- void time_delay_us(unsigned int usec)
- {
- int i,j;
- for(i=0; i<usec * 5; i++)
- {
- for(j=0; j<64; j++)
- {
- ;
- }
- }
- }
- //开始IIC通信
- static void i2c_start_bit(void)
- {
- DELAY(1);
- //拉高双线,拉低数据线,开始发送数据。
- i2c_set(SDA | SCL);
- DELAY(1);
- i2c_clr(SDA);
- DELAY(2);
- }
- //停止IIC通信
- static void i2c_stop_bit(void)
- {
- //时钟响应
- DELAY(1);
- i2c_set(SCL);
- DELAY(1);
- i2c_clr(SCL);
- //数据线为高,数据线由低->高:结束通信
- DELAY(1);
- i2c_clr(SDA);
- DELAY(1);
- i2c_set(SCL);
- DELAY(1);
- i2c_set(SDA);
- DELAY(1);
- }
- //读取数据:1bit
- static unsigned char i2c_data_read(void)
- {
- unsigned char regvalue;
- regvalue = HW_REG(GPIO_0_DIR);
- regvalue &= (~SDA);
- HW_REG(GPIO_0_DIR) = regvalue;
- DELAY(1);
- regvalue = HW_REG(GPIO_I2C_SDA_REG);
- if((regvalue&SDA) != 0)
- return 1;
- else
- return 0;
- }
- //发送数据:1byte
- static void i2c_send_byte(unsigned char c)
- {
- int i;
- //屏蔽中断
- local_irq_disable();
- for(i=0; i<8; i++)
- {
- DELAY(1);
- i2c_clr(SCL);
- DELAY(1);
- //发送1(这个是重点哦<IMG alt=大笑 src="http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif">)
- if(c & (1<<(7-i)))
- i2c_set(SDA);
- else//发送0
- i2c_clr(SDA);
- DELAY(1);
- i2c_set(SCL);
- DELAY(1);
- i2c_clr(SCL);
- }
- DELAY(1);
- local_irq_enable();
- }
- //接收数据:1byte
- static unsigned char i2c_receive_byte(void)
- {
- int j=0;
- int i;
- unsigned char regvalue;
- local_irq_disable();
- for(i=0; i<8; i++)
- {
- DELAY(1);
- i2c_clr(SCL);
- DELAY(2);
- i2c_set(SCL);
- regvalue = HW_REG(GPIO_0_DIR);
- regvalue &= (~SDA);
- HW_REG(GPIO_0_DIR) = regvalue;
- DELAY(1);
- if(i2c_data_read())
- j+=(1<<(7-i));
- DELAY(1);
- i2c_clr(SCL);
- }
- local_irq_enable();
- DELAY(1);
- return j;
- }
- //响应(每发送一个字节,都要响应),返回值为0:成功。
- static int i2c_receive_ack(void)
- {
- int nack;
- unsigned char regvalue;
- DELAY(1);
- regvalue = HW_REG(GPIO_0_DIR);
- regvalue &= (~SDA);//数据线置零
- HW_REG(GPIO_0_DIR) = regvalue;
- DELAY(1);
- i2c_clr(SCL);
- DELAY(1);
- i2c_set(SCL);
- DELAY(1);
- nack = i2c_data_read();
- DELAY(1);
- i2c_clr(SCL);
- DELAY(1);
- if(nack == 0)
- return 1;
- return 0;
- }</SPAN>
#include "gpio_i2c.h"
#define GPIO_0_BASE 0x20150000
#define GPIO_0_DIR IO_ADDRESS(GPIO_0_BASE + 0x400) //GPIO方向控制
#define SCL (1 << 1) /* GPIO 0_1 */
#define SDA (1 << 0) /* GPIO 0_0 */
#define GPIO_I2C_SDA_REG IO_ADDRESS(GPIO_0_BASE + 0x4)//数据线GPIO管脚
#define GPIO_I2C_SCL_REG IO_ADDRESS(GPIO_0_BASE + 0x8)//时钟线GPIO管脚
#define GPIO_I2C_SCLSDA_REG IO_ADDRESS(GPIO_0_BASE + 0xc)
#define HW_REG(reg) *((volatile unsigned int *)(reg))
#define DELAY(us) time_delay_us(us)
//拉低数据线/时钟线
static void i2c_clr(unsigned char whichline)
{
unsigned char regvalue;
//拉低时钟线
if(whichline == SCL)
{
//方向:输入/输出
regvalue = HW_REG(GPIO_0_DIR);
regvalue |= SCL;
HW_REG(GPIO_0_DIR) = regvalue;
//置零
HW_REG(GPIO_I2C_SCL_REG) = 0;
return;
}
else if(whichline == SDA)
{
regvalue = HW_REG(GPIO_0_DIR);
regvalue |= SDA;
HW_REG(GPIO_0_DIR) = regvalue;
HW_REG(GPIO_I2C_SDA_REG) = 0;
return;
}
else if(whichline == (SDA|SCL))
{
regvalue = HW_REG(GPIO_0_DIR);
regvalue |= (SDA|SCL);
HW_REG(GPIO_0_DIR) = regvalue;
HW_REG(GPIO_I2C_SCLSDA_REG) = 0;
return;
}
else
{
printk("Error input.\n");
return;
}
}
//拉高数据线/时钟线
static void i2c_set(unsigned char whichline)
{
unsigned char regvalue;
//拉高时钟线
if(whichline == SCL)
{
regvalue = HW_REG(GPIO_0_DIR);
regvalue |= SCL;
HW_REG(GPIO_0_DIR) = regvalue;
HW_REG(GPIO_I2C_SCL_REG) = SCL;
return;
}
else if(whichline == SDA)
{
regvalue = HW_REG(GPIO_0_DIR);
regvalue |= SDA;
HW_REG(GPIO_0_DIR) = regvalue;
HW_REG(GPIO_I2C_SDA_REG) = SDA;
return;
}
else if(whichline == (SDA|SCL))
{
regvalue = HW_REG(GPIO_0_DIR);
regvalue |= (SDA|SCL);
HW_REG(GPIO_0_DIR) = regvalue;
HW_REG(GPIO_I2C_SCLSDA_REG) = (SDA|SCL);
return;
}
else
{
printk("Error input.\n");
return;
}
}
//延时usec微秒
void time_delay_us(unsigned int usec)
{
int i,j;
for(i=0; i<usec * 5; i++)
{
for(j=0; j<64; j++)
{
;
}
}
}
//开始IIC通信
static void i2c_start_bit(void)
{
DELAY(1);
//拉高双线,拉低数据线,开始发送数据。
i2c_set(SDA | SCL);
DELAY(1);
i2c_clr(SDA);
DELAY(2);
}
//停止IIC通信
static void i2c_stop_bit(void)
{
//时钟响应
DELAY(1);
i2c_set(SCL);
DELAY(1);
i2c_clr(SCL);
//数据线为高,数据线由低->高:结束通信
DELAY(1);
i2c_clr(SDA);
DELAY(1);
i2c_set(SCL);
DELAY(1);
i2c_set(SDA);
DELAY(1);
}
//读取数据:1bit
static unsigned char i2c_data_read(void)
{
unsigned char regvalue;
regvalue = HW_REG(GPIO_0_DIR);
regvalue &= (~SDA);
HW_REG(GPIO_0_DIR) = regvalue;
DELAY(1);
regvalue = HW_REG(GPIO_I2C_SDA_REG);
if((regvalue&SDA) != 0)
return 1;
else
return 0;
}
//发送数据:1byte
static void i2c_send_byte(unsigned char c)
{
int i;
//屏蔽中断
local_irq_disable();
for(i=0; i<8; i++)
{
DELAY(1);
i2c_clr(SCL);
DELAY(1);
//发送1(这个是重点哦
)
if(c & (1<<(7-i)))
i2c_set(SDA);
else//发送0
i2c_clr(SDA);
DELAY(1);
i2c_set(SCL);
DELAY(1);
i2c_clr(SCL);
}
DELAY(1);
local_irq_enable();
}
//接收数据:1byte
static unsigned char i2c_receive_byte(void)
{
int j=0;
int i;
unsigned char regvalue;
local_irq_disable();
for(i=0; i<8; i++)
{
DELAY(1);
i2c_clr(SCL);
DELAY(2);
i2c_set(SCL);
regvalue = HW_REG(GPIO_0_DIR);
regvalue &= (~SDA);
HW_REG(GPIO_0_DIR) = regvalue;
DELAY(1);
if(i2c_data_read())
j+=(1<<(7-i));
DELAY(1);
i2c_clr(SCL);
}
local_irq_enable();
DELAY(1);
return j;
}
//响应(每发送一个字节,都要响应),返回值为0:成功。
static int i2c_receive_ack(void)
{
int nack;
unsigned char regvalue;
DELAY(1);
regvalue = HW_REG(GPIO_0_DIR);
regvalue &= (~SDA);//数据线置零
HW_REG(GPIO_0_DIR) = regvalue;
DELAY(1);
i2c_clr(SCL);
DELAY(1);
i2c_set(SCL);
DELAY(1);
nack = i2c_data_read();
DELAY(1);
i2c_clr(SCL);
DELAY(1);
if(nack == 0)
return 1;
return 0;
}
5.上层调用
- /***** IIC写数据 ********************************************************************************************/
/***** IIC写数据 ********************************************************************************************/
- //参数分别为设备地址、寄存器地址、值。
//参数分别为设备地址、寄存器地址、值。
- //如果writeval为结构体(最好12字节),而非unsiged long类型,就不需要移动了(底层驱动支持情况下)
- void IIC_Write(int devAddr, int regAddr, int val)
- {
- unsigned long writeval, ret;
- writeval = (((devAddr & 0xff) << 24) | ((regAddr & 0xffff) << 8) | (val & 0xff));
- ret = ioctl(i2cfd, I2C_WRITE, &writeval);
- if(0 > ret)
- {
- _ERROR("write iic ERROR");
- }
- }
- /***** IIC读数据 ********************************************************************************************/
- unsigned int IIC_Read(int devAddr, int regAddr)
- {
- unsigned long readval, ret;
- readval = (((devAddr & 0xff) << 24) | ((regAddr & 0xffff) << 8));
- ret = ioctl(i2cfd, I2C_READ, &readval);
- if(0 > ret)
- {
- _ERROR("read iic ERROR");
- }
- readval &= 0xff;
- return readval;
- }