linux下IO口模拟I2C的一些总结

之前一直在用I2C接口,由于老是有线程的例子就一直没有去深刻的了解,今天分析了一下在linux下通用GPIO模拟I2C的程序。

I2C的驱动是用杂项设备实现的,这也是一种比较简单的实现方式。经过 misc_register(&mygpioi2c_dev);来注册本身的杂项设备,此函数中会自动建立设备节点,即设备文件。无需mknod指令建立设备文件。由于misc_register()会调用class_device_creat或者device_creat。主设备号也不用管,是最简单的一种驱动了。注册后经过miscdevice结构体关联的file_operations的操做来实现驱动程序的open,read,write接口。 node

[cpp]  view plain copy
  1. static struct file_operations gpioi2c_fops = {  
  2.     .owner      = THIS_MODULE,  
  3.     .ioctl      = gpioi2c_ioctl,  
  4.     .open       = gpioi2c_open,  
  5.     .release    = gpioi2c_close  
  6. };  
具体的操做在IOCTL中实现

[cpp]  view plain copy
  1. int gpioi2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)  
  2. {  
  3.     unsigned int val;  
  4.       
  5.     char device_addr, reg_addr;  
  6.     short reg_val;  
  7.       
  8.       
  9.     switch(cmd)  
  10.     {  
  11.         case GPIO_I2C_READ:  
  12.             val = *(unsigned int *)arg;  
  13.             device_addr = (val&0xff000000)>>24;//设备地址  
  14.             reg_addr = (val&0xff0000)>>16;//设备中读写寄存器地址。对于两字节的地址须要本身去定义数据传送的方法。  
  15.               
  16.             reg_val = gpio_i2c_read(device_addr, reg_addr);  
  17.             *(unsigned int *)arg = (val&0xffff0000)|reg_val;              
  18.             break;  
  19.           
  20.         case GPIO_I2C_WRITE:  
  21.             val = *(unsigned int *)arg;  
  22.             device_addr = (val&0xff000000)>>24;  
  23.             reg_addr = (val&0xff0000)>>16;  
  24.               
  25.             reg_val = val&0xffff;  
  26.             gpio_i2c_write(device_addr, reg_addr, reg_val);  
  27.             break;        
  28.       
  29.         default:  
  30.             return -1;  
  31.     }  
  32.     return 0;  
  33. }  
[cpp]  view plain copy
  1. 下面拿随即读取数据来举例。  
[cpp]  view plain copy
  1. <pre name="code" class="cpp" style="font-family: 宋体, Arial; line-height: 1.5; ">unsigned char gpio_i2c_read(unsigned char devaddress, unsigned char address)  
  2. {  
  3.     int mydata;  
  4.     i2c_start_bit();  
  5.     i2c_send_byte((unsigned char)(devaddress));  
  6.     i2c_receive_ack();  
  7.     i2c_send_byte(address);  
  8.     i2c_receive_ack();    
  9.     i2c_start_bit();  
  10.     i2c_send_byte((unsigned char)(devaddress) | 1);  
  11.     i2c_receive_ack();  
  12.     mydata = i2c_receive_byte();  
  13.     i2c_stop_bit();  
  14. </pre><span style="font-family:宋体,Arial"><span style="line-height:1.5">}<br>  
  15. 其中的规则就是I2C协议的规定.下面是开始信号的模拟</span></span>  
  16. <pre style="font-family:宋体,Arial; line-height:1.5"></pre>  
  17. <pre name="code" class="cpp" style="font-family: 宋体, Arial; line-height: 1.5; ">static void i2c_start_bit(void)  
  18. {  
  19.         DELAY(1);//这里的时序须要本身模拟。用一个for循环就能够  
  20.         i2c_set(SDA | SCL);//sda.scl设为1  
  21.         DELAY(1);  
  22.         i2c_clr(SDA);//在SCL为1时拉低SDA,表示发送开始信号  
  23.         DELAY(2);  
  24. }</pre>  
  25. <pre></pre>  
delay具体多久须要计算,感受能够用定时器实现,以前就写过一个定时1ms的驱动,由于经过linux应用层单步操做时间间隔最好也是10ms,当时写过selest和一些别的方式作过测试,过几天把这部分整理出来。。回到这个驱动上来。。。。
在作模拟时,总结出了一些要点:
1.本身模拟时序时I2C的占空比并非必定的,只要符合其数据在上升沿读取这一规律以及高电平不变数据就行
2.释放SDA就是把SDA拉高
3.应答位谁接受谁产生
4.数据是在上升沿锁存,因此在须要读数据前最好把模拟成CL的GPIO作一下拉低再拉高处理,不要直接拉高。

为何随机读要产生两个开始信号呢,由于随机读须要CPU发送deviceaddress和regaddress,数据流向是cpu->e2prom 而肯定了地址后须要改变数据流向。所以须要重新发送开始信号,为何呢,由于I2C里面的deviceaddress里面包含有读写信息, 即若是写地址是deviceaddress, 则读地址是deviceaddress+1 所以能够这样讲一个开始信号肯定当前是读仍是写,要改变读写,对不起,只能从新开始。下面是IO口模拟写的时序
 
[cpp]  view plain copy
  1. void gpio_i2c_write(unsigned char devaddress, unsigned char address, unsigned char data)  
  2. {  
  3.     i2c_start_bit();  
  4.     i2c_send_byte((unsigned char)(devaddress));  
  5.     i2c_receive_ack();  
  6.     i2c_send_byte(address);  
  7.     i2c_receive_ack();  
  8.     i2c_send_byte(data);   
  9.     i2c_stop_bit();  
  10. }  
能够看到这里只有一次起始,模拟的时候须要注意。以上就是linux模拟I2C驱动的一点心得。
相关文章
相关标签/搜索