[置顶] S5PV210 Linux -- IO口模拟I2C总线驱动

驱动程序:

//////////////////////////////////////////////////////////////////////////
 //      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

你可能感兴趣的:(c,linux,struct,IO,Module,byte)