linux下IO口模拟I2C的一些总结
2012-12-17 16:51
281 查看
以前一直在用I2C接口,因为总是有线程的例子就一直没有去深入的了解,今天分析了一下在linux下通用GPIO模拟I2C的程序。
I2C的驱动是用杂项设备实现的,这也是一种比较简单的实现方式。通过 misc_register(&mygpioi2c_dev);来注册自己的杂项设备,此函数中会自动创建设备节点,即设备文件。无需mknod指令创建设备文件。因为misc_register()会调用class_device_creat或者device_creat。主设备号也不用管,是最简单的一种驱动了。注册后通过miscdevice结构体关联的file_operations的操作来实现驱动程序的open,read,write接口。
具体的操作在IOCTL中实现
其中的规则就是I2C协议的规定.下面是开始信号的模拟
[/code]
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口模拟写的时序
可以看到这里只有一次起始,模拟的时候需要注意。以上就是linux模拟I2C驱动的一点心得。
I2C的驱动是用杂项设备实现的,这也是一种比较简单的实现方式。通过 misc_register(&mygpioi2c_dev);来注册自己的杂项设备,此函数中会自动创建设备节点,即设备文件。无需mknod指令创建设备文件。因为misc_register()会调用class_device_creat或者device_creat。主设备号也不用管,是最简单的一种驱动了。注册后通过miscdevice结构体关联的file_operations的操作来实现驱动程序的open,read,write接口。
static struct file_operations gpioi2c_fops = { .owner = THIS_MODULE, .ioctl = gpioi2c_ioctl, .open = gpioi2c_open, .release = gpioi2c_close };
具体的操作在IOCTL中实现
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&0xff0000)>>16;//设备中读写寄存器地址。对于两字节的地址需要自己去定义数据传送的方法。 reg_val = gpio_i2c_read(device_addr, reg_addr); *(unsigned int *)arg = (val&0xffff0000)|reg_val; break; case GPIO_I2C_WRITE: val = *(unsigned int *)arg; device_addr = (val&0xff000000)>>24; reg_addr = (val&0xff0000)>>16; reg_val = val&0xffff; gpio_i2c_write(device_addr, reg_addr, reg_val); break; default: return -1; } return 0; }
下面拿随即读取数据来举例。
unsigned char gpio_i2c_read(unsigned char devaddress, unsigned char address) { int mydata; i2c_start_bit(); i2c_send_byte((unsigned char)(devaddress)); i2c_receive_ack(); i2c_send_byte(address); i2c_receive_ack(); i2c_start_bit(); i2c_send_byte((unsigned char)(devaddress) | 1); i2c_receive_ack(); mydata = i2c_receive_byte(); i2c_stop_bit();}
其中的规则就是I2C协议的规定.下面是开始信号的模拟
[/code]
static void i2c_start_bit(void) { DELAY(1);//这里的时序需要自己模拟。用一个for循环就可以 i2c_set(SDA | SCL);//sda.scl设为1 DELAY(1); i2c_clr(SDA);//在SCL为1时拉低SDA,表示发送开始信号 DELAY(2); }
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口模拟写的时序
void gpio_i2c_write(unsigned char devaddress, unsigned char address, unsigned char data) { i2c_start_bit(); i2c_send_byte((unsigned char)(devaddress)); i2c_receive_ack(); i2c_send_byte(address); i2c_receive_ack(); i2c_send_byte(data); i2c_stop_bit(); }
可以看到这里只有一次起始,模拟的时候需要注意。以上就是linux模拟I2C驱动的一点心得。
相关文章推荐
- stm32F103 模拟I2C mpu6050收到数据全为0,或者地址为209,104,0x68,0xD0的一些解决办法总结
- 笔记四:linux下IO口模拟实现I2C协议
- linux gpio模拟i2c的使用/用GPIO模拟I2C总线-2 .
- ARM Linux 内核gpio模拟I2C
- IO模拟I2C代码
- 总结Linux中一些实用性的shell技巧
- linux 文件IO和标准IO的一些实现细节
- linux自己总结的一些命令
- 一些关于linux的总结
- 学习笔记 (Linux和mysql的一些命令和总结) [第十课]
- 【Linux开发】linux设备驱动归纳总结(五):2.操作硬件——IO内存
- linux下一些常用命令总结
- linux下模拟丢包,延时命令总结
- Centos-Linux的一些问题总结
- 在Linux中Oracle安装成功后,首次启动使用时,会出现的一些问题总结和解决办法
- socket io管理模型总结(Linux、Windows)
- 一些实用的linux命令总结
- 嵌入式Linux内存管理的一些知识点总结
- linux一些机制的总结(精)