驱动程序:
////////////////////////////////////////////////////////////////////////// // FILE : e2prom.c // DATE : 09/02/2011 // DESCRIPTION : rfid-card i2c driver source // OS : Linux 2.6.35.7 // AUTHOR : light.wu ////////////////////////////////////////////////////////////////////////// // START: 在 SCL 线是高电平时,SDA线从高电平向低电平切换 // STOP: 当 SCL 是高电平时,SDA线由低电平向高电平切换 // 所有主机在 SCL 线上产生它们自己的时钟来传输 IIC 总线上的报文 数据只在时钟的高电平周期有效, // (可理解为传输出去)。数据 SDA 只有在 SCL 为低电平时才可以发生改变。 // ACK 数据传输必须带响应 相关的响应时钟脉冲由主机产生 在响应的时钟脉冲期间 发送器释放 SDA // 线为高 ,在响应的时钟脉冲期间 ,接收器必须将 SDA 线拉低 使它在这个时钟脉冲的高电平期间保持稳定的低电平。 // ////////////////////////////////////////////////////////////////////////// #include<linux/init.h> #include<linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/device.h> #include <linux/gpio.h> #include <linux/slab.h> #include <linux/irq.h> #include <linux/poll.h> #include <linux/delay.h> #include <asm/io.h> #define GPIO_IIC_SCL 0 // S5PV210_GPG3(5), #define GPIO_IIC_SDA 1 // S5PV210_GPG3(6), #define I2C_AT24CXX_ADDRESS 0xb0 #define I2C_AT24C02_MAXSIZE 0xff #define SCL_H { gpio_set_value (S5PV210_GPG3(5), 1) ; } #define SCL_L { gpio_set_value (S5PV210_GPG3(5), 0) ; } #define SDA_H { gpio_set_value (S5PV210_GPG3(6), 1) ; } #define SDA_L { gpio_set_value (S5PV210_GPG3(6), 0) ; } #define SDA_IN { gpio_direction_input (S5PV210_GPG3(6)); } #define SDA_OUT { gpio_direction_output (S5PV210_GPG3(6),1); } #define SDA_OUT0 { gpio_direction_output (S5PV210_GPG3(6),0); } #define WHILE_SDA_HIGH (gpio_get_value (S5PV210_GPG3(6))) #define E2PROM_DEV_NAME "e2prom" static int major = 239 ; module_param( major, int, 0 ) ; unsigned int ByteDelayTimeout = 0x700 ; unsigned int BitDelayTimeout = 0x1000; static void ByteDelay ( void ) ; static void BitDelay ( void ) ; static int e2prom_open ( struct inode *, struct file * ) ; static int e2prom_release ( struct inode *, struct file * ) ; static int e2prom_read ( struct file *, char *, size_t, loff_t * ) ; static int e2prom_write ( struct file *, const char *, size_t, loff_t * ) ; static int e2prom_ioctl ( struct inode *, struct file *, unsigned int,unsigned long ) ; static void InterfaceInit ( void ) ; static void I2C_Start ( void ) ; static void I2C_Stop ( void ) ; static void I2C_Ack ( void ) ; static void I2C_Nack ( void ) ; struct e2prom_private { spinlock_t lock ; int ref_cnt ; unsigned char address ; } ; static struct e2prom_private e2prom_priv ; static struct file_operations e2prom_fops = { .owner = THIS_MODULE, .open = e2prom_open, .read = e2prom_read, .write = e2prom_write, .ioctl = e2prom_ioctl, .release =e2prom_release, } ; static void I2C_Start () { SDA_OUT ; SDA_H ; BitDelay () ; SCL_H ; BitDelay () ; SDA_L ; BitDelay () ; } static void I2C_Stop () { SDA_OUT ; SDA_L ; BitDelay () ; SCL_H ; BitDelay () ; SDA_H ; BitDelay () ; } static void I2C_Ack() { SDA_OUT ; SDA_L ; BitDelay () ; SCL_H ; BitDelay () ; SCL_L ; BitDelay () ; SDA_IN ; BitDelay () ; } static void I2C_Ack1() { int i; SCL_H ; BitDelay () ; SDA_IN ; while((WHILE_SDA_HIGH)&&(i<255))i++; //无应答延时一段时间后默认已经收到 SCL_L ; BitDelay () ; SDA_OUT; BitDelay () ; } static void I2C_Nack () { SDA_OUT ; SDA_H ; BitDelay () ; SCL_H ; BitDelay () ; SCL_L ; BitDelay () ; SCL_H ; } static char Write_I2C_Byte ( char byte ) { char i ; SCL_L ; BitDelay () ; for ( i = 0 ; i < 8 ; i++ ) { if ( (byte & 0x80) == 0x80 ) { SDA_H ; } else { SDA_L ; } BitDelay () ; SCL_H ; BitDelay () ; SCL_L ; BitDelay () ; byte <<= 1 ; } return 1 ; } static char Read_I2C_Byte ( void ) { char i, buff = 0 ; SCL_L ; BitDelay () ; for ( i = 0 ; i < 8 ; i++ ) { SDA_OUT ; SDA_H ; BitDelay () ; SCL_H ; SDA_IN ; BitDelay () ; if ( WHILE_SDA_HIGH ) { buff |= 0x01 ; } else { buff &=~0x01; } if ( i < 7 ) { buff <<= 1 ; } SCL_L ; BitDelay () ; } return buff ; } static void ByteDelay ( void ) { volatile unsigned int dwTimeout ; dwTimeout = ByteDelayTimeout ; while ( --dwTimeout ) { asm ( "nop" ) ; } } static void BitDelay ( void ) { volatile unsigned int dwTimeout ; dwTimeout = BitDelayTimeout ; while ( --dwTimeout ) { asm ( "nop" ) ; } } static void InterfaceInit ( void ) { gpio_direction_output (S5PV210_GPG3(5), 1); // SCL OUT gpio_direction_output (S5PV210_GPG3(6), 1); // SDA OUT gpio_set_value (S5PV210_GPG3(5), 1); gpio_set_value (S5PV210_GPG3(6), 1); ByteDelay () ; ByteDelay () ; ByteDelay () ; } static int e2prom_open ( struct inode *inode_ptr, struct file *fptr ) { printk( "--------------------------------------------\n" ) ; if ( e2prom_priv.ref_cnt != 0 ) { printk ( "%s: exclusive access only\n", E2PROM_DEV_NAME ) ; return -EIO ; } e2prom_priv.ref_cnt++ ; e2prom_priv.address = 0x00 ; fptr->f_op = &e2prom_fops ; fptr->private_data = (void *) &e2prom_priv ; return 0 ; } static int e2prom_release ( struct inode *inode_ptr, struct file *fptr ) { struct e2prom_private *priv = (struct e2prom_private *) fptr->private_data ; priv->ref_cnt-- ; priv->address = 0x00 ; return 0 ; } static int e2prom_read (struct file *fptr, char __user *buffer, size_t count, loff_t * fp) { int i = 0; unsigned char data[100] = {0x00,}; InterfaceInit () ; I2C_Start () ; Write_I2C_Byte ( I2C_AT24CXX_ADDRESS ) ; I2C_Ack1() ; I2C_Start () ; Write_I2C_Byte ( I2C_AT24CXX_ADDRESS + 1 ) ; I2C_Ack1() ; for ( i = 0; i < count; i++ ) { data[i] = Read_I2C_Byte () ; I2C_Ack () ; printk( "0x%x ", data[i] ) ; } Read_I2C_Byte () ; I2C_Nack () ; I2C_Stop () ; copy_to_user ( buffer, (char *) &data, count ); return count; } static int e2prom_write ( struct file *fptr, const char *buffer, size_t size, loff_t * fp ) { int i = 0; unsigned char data[100] = { 0x00, } ; copy_from_user ( (char *) data, buffer, size ); I2C_Start () ; Write_I2C_Byte ( I2C_AT24CXX_ADDRESS ) ; I2C_Ack1 () ; for ( i = 0; i < size; i++ ) { printk( "0x%x ", data[i] ) ; Write_I2C_Byte ( data[i] ) ; I2C_Ack1 () ; } I2C_Stop () ; I2C_Nack () ; I2C_Stop () ; return size; } static int e2prom_ioctl ( struct inode *inode, struct file *fptr, unsigned int cmd, unsigned long arg ) { struct e2prom_private *priv = (struct e2prom_private *) fptr->private_data ; priv->address = (unsigned char)arg ; if ( priv->address >= I2C_AT24C02_MAXSIZE || priv->address < 0x00 ) { printk( "e2prom_ioctl : the address you specificed is overflow e2prom area .\n" ) ; return -EFAULT ; } return 0 ; } static struct class *led_class; static int e2prom_init ( void ) { printk("Real210 LED DRIVER MODULE INIT\n"); int status = 0 ; memset ( &e2prom_priv, 0, sizeof (struct e2prom_private) ) ; InterfaceInit () ; spin_lock_init ( &e2prom_priv.lock ) ; e2prom_priv.ref_cnt = 0 ; e2prom_priv.address = 0x00 ; status = register_chrdev (major, E2PROM_DEV_NAME, &e2prom_fops) ; if ( status < 0 ) { printk ( "e2prom_init : cannot get major number .\n" ) ; return status ; } else if ( major == 0 ) { major = status ; } led_class = class_create(THIS_MODULE, E2PROM_DEV_NAME); //创建节点 if(IS_ERR(led_class)) { printk("Err: failed in Real210-LED class. \n"); return -1; } device_create(led_class, NULL, MKDEV(major, 0), NULL, E2PROM_DEV_NAME); return 0 ; } static void e2prom_exit ( void ) { printk("Real210 LED DRIVER MODULE EXIT\n"); unregister_chrdev ( major, E2PROM_DEV_NAME) ; device_destroy(led_class, MKDEV(major, 0)); class_destroy(led_class); } module_init ( e2prom_init ) ; module_exit ( e2prom_exit ) ; MODULE_DESCRIPTION ( "S5PV210 i2c e2prom driver\n" ) ; MODULE_AUTHOR ( "light" ) ; MODULE_LICENSE ( "GPL" ) ;
Makefile文件:
ifneq ($(KERNELRELEASE),) obj-m := iic_io.o else KDIR := /home/light/miniAndroid/linux-2.6.35.7-android all: make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux- clean: rm -f *.ko *.o *.mod.o *~ *.mod.c *.symvers endif
测试应用程序:
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <linux/types.h> #include <time.h> #include <sys/time.h> #include <stdlib.h> #include "e2prom.h" #define false 0 int main (int argc, char *argv[]) { int fd ,ii,jj,kk; unsigned char dataA[4] = { 0x03, 0x13, 0x13, 0x03 } ; unsigned char dataB[4] = { 0x00, } ; unsigned char address = 0x00 ; fd = open ( "/dev/e2prom", O_RDWR ) ; if ( fd == -1 ) { printf( "Open device e2prom failed .\n" ) ; return false ; } /* if ( ioctl( fd, E2PROM_WRITE_READ, address ) < 0 ) { printf( "ioctl failed .\n" ) ; close ( fd ) ; return false ; } */ if ( write ( fd, (char*)dataA, 4 ) != 4 ) { printf( "Write device e2prom failed .\n" ) ; close ( fd ) ; return false ; } for(ii=10000;ii>0;ii--) { for(jj=10000;jj>0;jj--) { } } //注意延时时间必须得够,否则读不出数据。 if ( read ( fd, dataB, 4 ) != 4 ) { printf( "Read device e2prom failed .\n" ) ; close ( fd ) ; return false ; } for(ii=10000;ii>0;ii--) { for(jj=10000;jj>0;jj--); } if ( read ( fd, dataB, 4 ) != 4 ) { printf( "Read device e2prom failed .\n" ) ; close ( fd ) ; return false ; } printf( "dataB : 0x%x, 0x%x, 0x%x, 0x%x\n", dataB[0], dataB[1], dataB[2], dataB[3] ) ; close ( fd ) ; return EXIT_SUCCESS ; }
Makefile:
CROSSCOMPILE=arm-linux-
call:iictest
iictest:iictest.c
$(CROSSCOMPILE)gcc -g -o iictest iictest.c -static
$(CROSSCOMPILE)strip iictest
clean:
@rm -vf iic-1 *.o *~
注意:
调试时候,必须注意ACK 响应,主机写,从机响应,主机检测,主机读,主机响应,读完了发送不响应。
测试程序需要注意读完之后的延时,如果CPU主频过高,会导致写进去读太快,读不出来数据。
转载自:http://blog.csdn.net/bmbm546/article/details/6965975