posts - 34, comments - 90, trackbacks - 0, articles - 0
      由于公司的需要,在写I2C程序,原来从头到尾自己写一个IIC驱动是一件很简单的事情,但想完美的我还是想把我的驱动和内核的I2C子系统连接起来,I2C本身很简单,S3C2410无操作系统不用300行就搞定,但I2C子系统却把这么简单的代码变得非常庞大,非常难懂,结构错综复杂。
      关于I2C子系统,我转载的文章Linux I2C核心、总线与设备驱动[转] 已经说得很请楚,这里只作一些补充。

一、master_xfer,以及i2c_msg标志位
      其实抛开子系编本身其它部份,实现I2C的主要作用代码就是algorithm里的master_xfer方法。这个方法就是我们无操作系统时的的I2C读写函数(它用参数来区分读和写)。分析这些代码,最好是读内核的i2c-algo-bit.c文件,这个文件就是用模拟的方法来实现I2C总线,因为不和其它I2C控制芯片相关,所以比较好理解。i2c-algo-bit.c其中的master_xfer函数bit_xfer函数如下:
 1static int bit_xfer(struct i2c_adapter *i2c_adap,
 2            struct i2c_msg msgs[], int num)
 3{
 4    struct i2c_msg *pmsg;
 5    struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
 6    
 7    int i,ret;
 8    unsigned short nak_ok;
 9
10    i2c_start(adap);
11    for (i=0;i<num;i++{
12        pmsg = &msgs[i];
13        nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; 
14        if (!(pmsg->flags & I2C_M_NOSTART)) {
15            if (i) {
16                i2c_repstart(adap);
17            }

18            ret = bit_doAddress(i2c_adap, pmsg);
19            if ((ret != 0&& !nak_ok) {
20                DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: NAK from device addr %2.2x msg #%d\n"
21                    ,msgs[i].addr,i));
22                return (ret<0? ret : -EREMOTEIO;
23            }

24        }

25        if (pmsg->flags & I2C_M_RD ) {
26            /* read bytes into buffer*/
27            ret = readbytes(i2c_adap, pmsg);
28            DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: read %d bytes.\n",ret));
29            if (ret < pmsg->len ) {
30                return (ret<0)? ret : -EREMOTEIO;
31            }

32        }
 else {
33            /* write bytes from buffer */
34            ret = sendbytes(i2c_adap, pmsg);
35            DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: wrote %d bytes.\n",ret));
36            if (ret < pmsg->len ) {
37                return (ret<0? ret : -EREMOTEIO;
38            }

39        }

40    }

41    i2c_stop(adap);
42    return num;
43}

其实也并不复杂
      1)i2c_start函数发start信号
      2)i2c_repstart函数发重复start信号
      3)bit_doAddress函数写器件地址
      4)readbytes函数读N字节
      5)writebytes函数写N字节

其中的每一个函数都不复杂,都是设置或读取scl和sda线。
bit_xfer函数参数只要是i2c_msg序列msgs。这个结构请看Linux I2C核心、总线与设备驱动[转] 或自己Google吧,里面放着要这个函数完成的任务。我们的最主要任务是i2c_msg的一些标志位,这些标识位网上都没有提到。我来解释一下,错了大家请原谅。

I2C_M_IGNORE_NAK:
      设置这个标志意味当前i2c_msg忽略I2C器件的ack和nack信号。
I2C_M_NOSTART:      
      设置这个标志意味当前i2c_msg不发送start信号。注意,其实调用bit_xfer的一开始就已经发了start信号了(程序第10行),这个标记无非就是标志是否发送地址第18行。其次,如果一个i2c_msg没有定义I2C_M_NOSTART而且又不是msgs序列里的第一个i2c_msg,则回发送重复start信号,我想这就是这个标志起这个名的原因。我们可以猜想,
      1.msgs序列第一个数据必须是地址,同时必须不定义这个标志位
      2.在进行读数据,要从写操作转变为读操作时,会发重复start信号和器件地址时,必须不定义这个标志位
      3.其它情况下一的i2c_msg必须定义这个标志
      以上只是我看完这个函数的理解,不一定正确。同时1和2总结下来就是发器件地址(注意,是器件地址,不是像EEPROM那样的EEPROM地址,这个地址是当数据发的)时会不设置I2C_M_NOSTART, 发数据时就设置I2C_M_NOSTART这个标志。
I2C_M_NO_RD_ACK:
      这个标识表示在正行读操作时不去ACK,我不知道其它芯片如果,如果是AT24C04则一定不能设这个标志位了。
(下面三个标志为均为bit_doAddress函数使用,结合上面的说明,也就是这时I2C_M_NOSTART一定没有设置。)
I2C_M_RD:
      表示这是一个读操作,默认是把相应的位置1
I2C_M_REV_DIR_ADDR:
      表示把读写标志位反转,也就是读是把相应位置0
I2C_M_TEN:
      表示这个器件的器件地址是10Bit的。一定要搞清,这是器件地址,不是指EEPROM的ROM地址。24C02等芯片真正的器件地址只有4位永远有效(0xA),低4位用来放其它东西了(根据容量有可能是器件地址的低3位,或ROM地址的高3位)。也是说,无论什么容量,这类器件的地址只是器件地址我们只选7位模式(内核只区分10位模式和其它模式)


Feedback

# re: Linux I2C 驱动阅读的碰到的一些网上没有提到的东西  回复  更多评论   

2009-04-11 13:39 by 初学都
楼主,你好。我看了你那篇《Linux I2C核心、总线与设备驱动[转]》,让我明白了不少,不过我还是有些不明白的地方,想请教一下。
我挂的I2C设备是只可以写的,不能读,所以只有一个写从机地址,可是当我调用内核的I2C驱动时发现,写操作执行后,在示波器上看到,在开始信号之后,它先发送完从机地址并接收到应答信号,可之后立即出现了一个停止信号(我后面还有数据要发的呀),然后又开始发送从机地址,并在收到应答后又出现一个停止位,如此反复;
在我下面的程序运行时,只在示波器上抓到四组这样“开始—从机地址—应答—停止”的信号,却没有看到我接下来要发数据,同时还打印出“Remote I/O error”的错误,我先把我的程序贴一下,希望能得到你的指点,谢谢

#define I2C_TENBIT 0x704
#define I2C_SLAVE 0x703
#define CHIP_ADDR 0x30 >> 1 // 7 bits for fm31256 address
#define IO_DEV "/dev/i2c-0"

main(int argc, char *argv[])
{
unsigned char REG_WRITE=0X30; /*写地址*/
unsigned char data=0xf4;
int ret;
ret=0;

if ((fd = open(IO_DEV, O_RDWR)) < 0)
perror("open i2c_adpter error");
else //设置I2C访问模式
{
if(ioctl(fd,I2C_TENBIT,0))
perror("set i2c 7bits address mode error");
if(ioctl(fd,I2C_SLAVE,CHIP_ADDR))
perror("set device address error");
}

if((ret = write (fd, REG_WRITE, 1))!= 1)//发从机地址
{
perror("set address error"); //这里打印Remote I/O error
}
else
{
if (write (fd, &data, 1))!= 1)//发送数据,但在示波器上没有 看到date的值
perror("write date error"); //这里打印Remote I/O error
}
return 0;
}

# re: Linux I2C 驱动阅读的碰到的一些网上没有提到的东西  回复  更多评论   

2009-04-27 21:16 by 猫头鹰
我想,发送4次是因为I2C核心的重发,因些可以看到,核心已经在每次发送时检测到错误了,4次失败后就给应用报错了。我不知你用的是什么CPU,很可能是I2C驱动的问题。你可能多printk一下。

# re: Linux I2C 驱动阅读的碰到的一些网上没有提到的东西  回复  更多评论   

2010-10-19 10:32 by 小魚
感謝您的分享,給現在的我非常大的啟發和幫助。
只有注册用户登录后才能发表评论。