首页 > 代码库 > S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动

S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动

目录:一. 说明

    二. 驱动程序说明及问题

    三. 案例一

        四. 案例二

一. 说明

      mini210开发板上带了at24c08, 看了linux内核自带的at24.c的驱动程序,编译下载到看发板,读写都行;通过增加一些调试信息,对linux i2c驱动其中的编写方法之一有了一定了解,在我的另外一篇博文有详细说明。但同时对在linux下GPIO模拟i2c产生了兴趣,于是就写这篇博文来记录驱动编写过程中遇到的问题。如果想了解了i2c时序,请google或百度一下。

  本篇博文通过misc驱动模型来编写模拟i2c设备驱动:比如at24c08, pcf8591(AD/DA)。

  本博文是本人原创,如有不足之处,请斧正;

二. 驱动程序说明及议题

  1.1 本驱动使用s3c_gpio_cfgpin来配制硬件引脚寄存器,其实可以使用ioremap函数后,来对硬件寄存器地址访问;如果想详细了解,请看linux驱动初级开发第01篇--I/O内存控制硬件buzzer。

  1.2 pcf8591(AD/DA)驱动程序,基本上没遇到什么问题;但是编写出来的atc08驱动程序,出现了些现在无法解决的问题;

  问题1:只能先读一个字节,再写一个字节;

  问题2: 如果先写一个字节,再读一个字节会读出现错误;但是去掉write_set_at24xx函数中i2c_send_byte(dat)就不会出现读出错,但是不会写入数据。

  问题3:一次写多个字节,就会出现一些字节写出错。

  问题4: 只读就不个会出错,但只写就会出现一些字节写出错

 

三. 案例一

pcf8591.c:

运行结果:

代码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>

#include <asm/uaccess.h>  //其中copy_to*在其中

#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

spinlock_t lock;  //加不加这都 可以

#define DEVICE_NAME "PCF8591"
#define uchar unsigned char
int askflag;
#define  NUM  4                 //接收和发送缓存区的深度
uchar  receivebuf[NUM];    //数据接收缓冲区
uchar    SystemError;                //从机错误标志位

void     set_conOUT_sda(void);
void    set_conOUT_scl(void);
void     set_conIN_sda(void);
void    set_conIN_scl(void);
void     set_data_sda(int i);
void    set_data_scl(int i);
    

//-------------------------------------------------------------------
// 函数名称: i2c_start()
// 函数功能: 启动I2C总线子程序
//-------------------------------------------------------------------
void i2c_start(void)
{ //时钟保持高,数据线从高到低一次跳变,I2C通信开始
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(1);
    set_data_scl(1);
    __udelay(5);
    set_data_sda(0);
    __udelay(5);
    set_data_scl(0);

}
//-------------------------------------------------------------------
// 函数名称: i2c_stop()
// 函数功能: 停止I2C总线数据传送子程序
//-------------------------------------------------------------------
void i2c_stop(void)
{      
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(0);//时钟保持高,数据线从低到高一次跳变,I2C通信停止
    set_data_scl(1);
    __udelay(5);
    set_data_sda(1);
    __udelay(5);
    set_data_scl(0);

}
//------------------------------------------------------------------
// 函数名称: i2c_Init()
// 函数功能: 初始化I2C总线子程序
//------------------------------------------------------------------
void i2c_init(void)
{
       set_conOUT_scl();
    __udelay(1);
    
    set_data_scl(0);
    i2c_stop();    
}  
//-------------------------------------------------------------------
// 函数名称: slave_ack
// 函数功能: 从机发送应答位子程序
//-------------------------------------------------------------------
void slave_ack(void)
{
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(0);
    set_data_scl(1);
    __udelay(5);
    set_data_scl(0);
    
}
//-------------------------------------------------------------------
// 函数名称: slave_noack
// 函数功能: 从机发送非应答位子程序,迫使数据传输过程结束
//-------------------------------------------------------------------
void slave_noack(void)
{
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(1);
    set_data_scl(1);
    __udelay(5);
    set_data_sda(0);
    set_data_scl(0);

}
//-------------------------------------------------------------------
// 函数名称: check_ack
// 函数功能: 主机应答位检查子程序,迫使数据传输过程结束
//-------------------------------------------------------------------
void check_ack(void)
{     
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(1); // 将p1.1设置成输入,必须先向端口写1
    set_data_scl(1);
    askflag = 0;
    __udelay(5);
    set_conIN_sda();
    __udelay(3);
    if ( gpio_get_value(S5PV210_GPE0(3)) == 1)
        askflag = 1;
        // 若SDA=1表明非应答,置位非应答标志askflag
    set_data_scl(0);
   
}
//-------------------------------------------------------------------
// 函数名称: i2c_send_byte
// 入口参数: ch
// 函数功能: 发送一个字节
//-------------------------------------------------------------------
void i2c_send_byte(uchar ch)
 
{
      unsigned char  n=8;     // 向SDA上发送一位数据字节,共八位
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);

    spin_lock(&lock);
    while(n--) {
        if((ch&0x80) == 0x80)    // 若要发送的数据最高位为1则发送位1
        {
               set_data_sda(1);  // 传送位1
            set_data_scl(1);
            __udelay(5);
            set_data_scl(0);
        }
        else
        {  
            set_data_sda(0); // 否则传送位0
            set_data_scl(1);
            __udelay(5);
            
            set_data_scl(0);
        }
            ch = ch<<1;    // 数据左移一位
    }
    spin_unlock(&lock);
}
//-------------------------------------------------------------------
// 函数名称: i2c_receive_byte
// 返回接收的数据
// 函数功能: 接收一字节子程序
//-------------------------------------------------------------------
uchar i2c_receive_byte(void)
{
    uchar  n=8;     // 从SDA线上读取一上数据字节,共八位
    uchar tdata=http://www.mamicode.com/0;
    int temp;

    spin_lock(&lock);
    
    while(n--) {
        set_conOUT_sda();
        set_conOUT_scl();
        __udelay(1);
        
        set_data_sda(1);
        set_data_scl(1);

       tdata =http://www.mamicode.com/tdata<<1; //左移一位
       set_conIN_sda();
       __udelay(3);
       
       temp = gpio_get_value(S5PV210_GPE0(3));
           if( temp == 1)
          tdata = http://www.mamicode.com/tdata|0x01; // 若接收到的位为1,则数据的最后一位置1
        else
          tdata = http://www.mamicode.com/tdata&0xfe; // 否则数据的最后一位置0
        //printk("read_data_bit: %d", temp); //++
        set_data_scl(0);
     }
     spin_unlock(&lock);
     printk("read_data: %d\n", tdata);
     return(tdata);
}

//-------------------------------------------------------------------
// 函数名称: adc_pcf8591
// 入口参数: controlbyte控制字
// 函数功能: 连续读入4路通道的A/D转换结果到receivebuf
//-------------------------------------------------------------------
void adc_pcf8591(uchar controlbyte)
{
    uchar  receive_da,i=0;

    i2c_start();

    i2c_send_byte(0x90);    //控制字
    check_ack();
    __udelay(5);
    if(askflag == 1)  //++
    {
        SystemError = 1;
        return;
    }

    i2c_send_byte(controlbyte);    //控制字
    check_ack();
    __udelay(5);
    if(askflag == 1)//++
    {
        SystemError = 1;
        return;
    }

    i2c_start();                //重新发送开始命令
       i2c_send_byte(0x91);    //控制字
    check_ack();
    __udelay(5);
    if(askflag == 1) // ++
    {
        SystemError = 1;
        return;
    }
    
    i2c_receive_byte();   //空读一次,调整读顺序
    slave_ack();        //收到一个字节后发送一个应答位
    __udelay(5);
    while(i<4)
    {  
      receive_da=i2c_receive_byte();
      receivebuf[i++]=receive_da;
      slave_ack();       //收到一个字节后发送一个应答位
    }
    slave_noack();       //收到最后一个字节后发送一个非应答位
    i2c_stop();
}

void set_conIN_sda(void)
{
    s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_INPUT);
    //gpio_set_value(S5PV210_GPE0(3), 1);    
}

void set_conIN_scl(void)
{
    s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_INPUT);
    //gpio_set_value(S5PV210_GPE0(3), 1);    
}


void set_conOUT_sda(void)
{
    s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_OUTPUT);
}

void set_conOUT_scl(void)
{
    s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_OUTPUT);
}


void set_data_sda(int i)
{
    if (i == 0) {
        gpio_set_value(S5PV210_GPE0(3), 0);    
    }    else if(i == 1) {
        gpio_set_value(S5PV210_GPE0(3), 1);    
    }
}

void set_data_scl(int i)
{
    if (i == 0) {
        gpio_set_value(S5PV210_GPE0(4), 0);    
    }    else if(i == 1) {
        gpio_set_value(S5PV210_GPE0(4), 1);    
    }
}

static int pcf8591_open(struct inode *inode, struct file *filp)
{
    printk (KERN_INFO "Device opened\n");
    spin_lock_init(&lock);
    return 0;
}


 /*读取数据*/
static int pcf8591_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
    printk("pcf8591_read\n");
    int i;
    int v[4]; //存实际的电压值
    i2c_init();                  //I2C总线初始化
    adc_pcf8591(0x04);

    if(SystemError == 1)      //有错误,重新来
    {
          printk("SystemError: %d\n", SystemError);
          i2c_init();                  //I2C总线初始化
        adc_pcf8591(0x04);
    }   
      
    for(i=0;i<4;i++)    
    {
        printk("AD_%d: %d\t\n", i, receivebuf[i]); //显示通道0    
        v[2]=receivebuf[i]/51;   //AD值转换为3位BCD码,最大为5.00V。
        v[2]=v[2];     //转换为ACSII码
        v[3]=receivebuf[i]%51;   //余数暂存
        
        v[3]=v[3]*10;    //计算小数第一位
        v[1]=v[3]/51;
        v[1]=v[1];     //转换为ACSII码
        
        v[3]=v[3]%51;
        v[3]=v[3]*10;    //计算小数第二位
        v[0]=v[3]/51;                                                                             //
        v[0]=v[0];  //转换为ACSII码
        
        printk("AD_%d: %d.%d%dV\t\n", i, v[2], v[1], v[0]);
    }
    copy_to_user(buffer, receivebuf, sizeof(receivebuf));
    return 0;
}
/*写命令,在此置空*/
static int pcf8591_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
    return 0;
}

static int pcf8591_release(struct inode *inode,struct file *filp)
{
    printk (KERN_INFO "device closed\n");
    return 0;
}

static long pcf8591_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    return 0;
}

static struct file_operations pcf8591_fops = {
    .owner            = THIS_MODULE,
    .open    =    pcf8591_open,
    .read = pcf8591_read,
    .write = pcf8591_write,
    .unlocked_ioctl    = pcf8591_ioctl,
    .release = pcf8591_release,
};

static struct miscdevice pcf8591_dev = {
    .minor            = MISC_DYNAMIC_MINOR,
    .name            = DEVICE_NAME,
    .fops            = &pcf8591_fops,
};

static int __init pcf8591_dev_init(void) {
    int ret;
    int i;

    for (i = 3; i < 5; i++) {
        ret = gpio_request(S5PV210_GPE0(i), "PCF8591");
        if (ret) {
            printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
                S5PV210_GPE0(i), ret);
            return ret;

            s3c_gpio_cfgpin(S5PV210_GPE0(i), S3C_GPIO_OUTPUT);
            gpio_set_value(S5PV210_GPE0(i), 1);
        }
    }

    ret = misc_register(&pcf8591_dev);

    printk(DEVICE_NAME"\tinitialized\n");

    return ret;
}

static void __exit pcf8591_dev_exit(void) {
    int i;

    for (i = 0; i < 1; i++) {
        gpio_free(S5PV210_GPE0(3));
        gpio_free(S5PV210_GPE0(4));
    }

    misc_deregister(&pcf8591_dev);
}

module_init(pcf8591_dev_init);
module_exit(pcf8591_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

 

四. 案例二

at24c08.c:

运行结果:

代码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>

#include <asm/uaccess.h>  //其中copy_to*在其中

#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

spinlock_t lock;  //加不加这都 可以

#define DEVICE_NAME "at24xx"
#define uchar unsigned char
int askflag;
#define  NUM  4                 //接收和发送缓存区的深度
uchar  receivebuf[NUM];    //数据接收缓冲区
uchar    SystemError;                //从机错误标志位

void     set_conOUT_sda(void);
void    set_conOUT_scl(void);
void     set_conIN_sda(void);
void    set_conIN_scl(void);
void     set_data_sda(int i);
void    set_data_scl(int i);
    

//-------------------------------------------------------------------
// 函数名称: i2c_start()
// 函数功能: 启动I2C总线子程序
//-------------------------------------------------------------------
void i2c_start(void)
{ //时钟保持高,数据线从高到低一次跳变,I2C通信开始
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(1);
    set_data_scl(1);
    __udelay(1);
    set_data_sda(0);
    __udelay(5);
    set_data_scl(0);
    __udelay(1);
}
//-------------------------------------------------------------------
// 函数名称: i2c_stop()
// 函数功能: 停止I2C总线数据传送子程序
//-------------------------------------------------------------------
void i2c_stop(void)
{      
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(0);//时钟保持高,数据线从低到高一次跳变,I2C通信停止
    set_data_scl(1);
    __udelay(5);
    set_data_sda(1);
    __udelay(5);
    set_data_sda(0);
    set_data_scl(0);

}
//------------------------------------------------------------------
// 函数名称: i2c_Init()
// 函数功能: 初始化I2C总线子程序
//------------------------------------------------------------------
void i2c_init(void)
{
       set_conOUT_scl();
    __udelay(1);
    
    set_data_scl(0);
    i2c_stop();    
}  
//-------------------------------------------------------------------
// 函数名称: slave_ack
// 函数功能: 从机发送应答位子程序
//-------------------------------------------------------------------
void slave_ack(void)
{
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(0);
    set_data_scl(1);
    __udelay(5);
    set_data_scl(0);
    
}
//-------------------------------------------------------------------
// 函数名称: slave_noack
// 函数功能: 从机发送非应答位子程序,迫使数据传输过程结束
//-------------------------------------------------------------------
void slave_noack(void)
{
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(1);
    set_data_scl(1);
    __udelay(5);
    set_data_sda(0);
    set_data_scl(0);

}
//-------------------------------------------------------------------
// 函数名称: check_ack
// 函数功能: 主机应答位检查子程序,迫使数据传输过程结束
//-------------------------------------------------------------------
void check_ack(void)
{     
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);
    
    set_data_sda(1); // 将p1.1设置成输入,必须先向端口写1
    set_data_scl(1);
    askflag = 0;
    __udelay(5);
    set_conIN_sda();
    __udelay(3);
    if ( gpio_get_value(S5PV210_GPE0(3)) == 1)
        askflag = 1;
        // 若SDA=1表明非应答,置位非应答标志askflag
    set_data_scl(0);
   
}
//-------------------------------------------------------------------
// 函数名称: i2c_send_byte
// 入口参数: ch
// 函数功能: 发送一个字节
//-------------------------------------------------------------------
void i2c_send_byte(uchar ch)
 
{
      unsigned char  n=8;     // 向SDA上发送一位数据字节,共八位
    set_conOUT_sda();
    set_conOUT_scl();
    __udelay(1);

    spin_lock(&lock);
    while(n--) {
        if((ch&0x80) == 0x80)    // 若要发送的数据最高位为1则发送位1
        {
               set_data_sda(1);  // 传送位1
            set_data_scl(1);
            __udelay(5);
            set_data_scl(0);
        }
        else
        {  
            set_data_sda(0); // 否则传送位0
            set_data_scl(1);
            __udelay(5);
            
            set_data_scl(0);
        }
            ch = ch<<1;    // 数据左移一位
    }
    spin_unlock(&lock);
}
//-------------------------------------------------------------------
// 函数名称: i2c_receive_byte
// 返回接收的数据
// 函数功能: 接收一字节子程序
//-------------------------------------------------------------------
uchar i2c_receive_byte(void)
{
    uchar  n=8;     // 从SDA线上读取一上数据字节,共八位
    uchar tdata=http://www.mamicode.com/0;
    int temp;

    spin_lock(&lock);
    
    while(n--) {
        set_conOUT_sda();
        set_conOUT_scl();
        __udelay(1);
        
        set_data_sda(1);
        set_data_scl(1);

       tdata =http://www.mamicode.com/tdata<<1; //左移一位
       set_conIN_sda();
       __udelay(3);
       
       temp = gpio_get_value(S5PV210_GPE0(3));
           if( temp == 1)
          tdata = http://www.mamicode.com/tdata|0x01; // 若接收到的位为1,则数据的最后一位置1
        else
          tdata = http://www.mamicode.com/tdata&0xfe; // 否则数据的最后一位置0
        //printk("read_data_bit: %d", temp); //++
        set_data_scl(0);
     }
     spin_unlock(&lock);
     printk("read_data: %d\n", tdata);
     return(tdata);
}


//***************************************************
//函数功能:向AT24xx中指定地址写入数据
//入口参数:add(地址), dat(储存待写入的数据)
//***************************************************
void write_set_at24xx(uchar add, uchar dat)
{
    i2c_start();
    i2c_send_byte(0xA0);
    __udelay(1);
    check_ack();
    if(askflag == 1)  //++
    {
        SystemError = 1;
        printk("write_set_01_SystemError: %d\n", SystemError);
        return;
    } else {
        SystemError = 0;
    }
    i2c_send_byte(add);
    __udelay(1);
    check_ack();
    if(askflag == 1)  //++
    {
        SystemError = 1;
        printk("write_set_02_SystemError: %d\n", SystemError);
        return;
    } else {
        SystemError = 0;
    }
    i2c_send_byte(dat);
    __udelay(1);
    check_ack();
    if(askflag == 1)  //++
    {
        SystemError = 1;
        printk("write_set_03_SystemError: %d\n", SystemError);
        return;
    } else {
        SystemError = 0;
    }
    i2c_stop();
    __udelay(5);
}


//***************************************************
//函数功能:从AT24Cxx中的当前地址读取数据
//出口参数:x (储存读出的数据)
//***************************************************
uchar read_current_at24xx()
{
    unsigned char x;
    i2c_start();               //开始数据传递
    i2c_send_byte(0xA1);   //选择要操作的AT24Cxx芯片,并告知要读其数据
    __udelay(5);
    check_ack();
    
    if(askflag == 1)  //++
    {
        SystemError = 1;
        printk("read_current_01_SystemError: %d\n", SystemError);
        return;
    } else {
        SystemError = 0;
    }
    x=i2c_receive_byte();         //将读取的数据存入x
    __udelay(3);
    
    i2c_stop();                //停止数据传递
    return x;              //返回读取的数据
}

//***************************************************
//函数功能:从AT24Cxx中的指定地址读取数据
//入口参数:set_addr
//出口参数:x
//***************************************************
unsigned char read_set_at24xx(unsigned char set_addr)
// 在指定地址读取
{
    uchar temp;
    i2c_start();                      //开始数据传递
    i2c_send_byte(0xA0);       //选择要操作的AT24Cxx芯片,并告知要对其写入数据
    __udelay(5);
    check_ack();
    
    if(askflag == 1)  //++
    {
        SystemError = 1;
        printk("read_set_01_SystemError: %d\n", SystemError);
        return;
    } else {
        SystemError = 0;
    }
    i2c_send_byte(set_addr);       //写入指定地址
    __udelay(1);
    check_ack();
    
    temp = read_current_at24xx();
    
    return(temp);        //从指定地址读出数据并返回
    
}

void set_conIN_sda(void)
{
    s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_INPUT);
    //gpio_set_value(S5PV210_GPE0(3), 1);    
}

void set_conIN_scl(void)
{
    s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_INPUT);
    //gpio_set_value(S5PV210_GPE0(3), 1);    
}


void set_conOUT_sda(void)
{
    s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_OUTPUT);
}

void set_conOUT_scl(void)
{
    s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_OUTPUT);
}


void set_data_sda(int i)
{
    if (i == 0) {
        gpio_set_value(S5PV210_GPE0(3), 0);    
    }    else if(i == 1) {
        gpio_set_value(S5PV210_GPE0(3), 1);    
    }
}

void set_data_scl(int i)
{
    if (i == 0) {
        gpio_set_value(S5PV210_GPE0(4), 0);    
    }    else if(i == 1) {
        gpio_set_value(S5PV210_GPE0(4), 1);    
    }
}

static int at24xx_open(struct inode *inode, struct file *filp)
{
    printk (KERN_INFO "Device opened\n");
    spin_lock_init(&lock);
    return 0;
}


 /*读取数据*/
static int at24xx_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
    printk("at24xx_read\n");
    int i;
    uchar read_dat;
    //i2c_init();
    //write_set_at24xx(5, ‘A‘);
    i2c_init();
    read_dat = read_set_at24xx(5);
    printk("add_%d: %d\t", 5, read_dat);
    
    //i2c_init();
    for (i=‘a‘; i<‘z‘; i++) {
        i2c_init();
        write_set_at24xx(i, i+1); //在24c08的地址2中写入数据sec    
        if (SystemError == 1) {
            i2c_init();
            write_set_at24xx(i, i);    
        }/*
        if (SystemError == 1) {
            i2c_init();
            write_set_at24xx(i, i);    
        }
        if (SystemError == 1) {
            i2c_init();
            write_set_at24xx(i, i);    
        }*/
        __udelay(5);
    }
    __udelay(9);

    for (i=‘a‘; i<‘z‘; i++) {
        i2c_init();
        read_dat = read_set_at24xx(i);
        printk("add_%d: %c\t",i, read_dat);
        __udelay(5);
    }

    printk("\n");

    for (i=2; i< 10; i++) {
        i2c_init();
        read_dat = read_set_at24xx(i);
        printk("add_%d: %c\t",i, read_dat);
        __udelay(5);
    }
    
    copy_to_user(buffer, receivebuf, sizeof(receivebuf));
    return 0;
    /*printk("at24xx_read\n");
    int i;
    int v[4]; //存实际的电压值
    i2c_init();                  //I2C总线初始化
    adc_at24xx(0x04);

    if(SystemError == 1)      //有错误,重新来
    {
          printk("SystemError: %d\n", SystemError);
          i2c_init();                  //I2C总线初始化
        adc_at24xx(0x04);
    }   
      
    for(i=0;i<4;i++)    
    {
        printk("AD_%d: %d\t\n", i, receivebuf[i]); //显示通道0    
        v[2]=receivebuf[i]/51;   //AD值转换为3位BCD码,最大为5.00V。
        v[2]=v[2];     //转换为ACSII码
        v[3]=receivebuf[i]%51;   //余数暂存
        
        v[3]=v[3]*10;    //计算小数第一位
        v[1]=v[3]/51;
        v[1]=v[1];     //转换为ACSII码
        
        v[3]=v[3]%51;
        v[3]=v[3]*10;    //计算小数第二位
        v[0]=v[3]/51;                                                                             //
        v[0]=v[0];  //转换为ACSII码
        
        printk("AD_%d: %d.%d%dV\t\n", i, v[2], v[1], v[0]);
    }
    copy_to_user(buffer, receivebuf, sizeof(receivebuf));
    return 0;
    */
}
/*写命令,在此置空*/
static int at24xx_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
    printk("at24xx_write\n");
    char b[10];
    char *a = b;//kmalloc(100, GFP_KERNEL);
    int i;
    int len;
    len = sizeof(buffer);
    copy_from_user(a,buffer, count);
    for (i=0; i < count; i++) {
        i2c_init();
        write_set_at24xx(i+2, *(a++));
    }
    printk("len: %s\n", a);
    return count;
}

static int at24xx_release(struct inode *inode,struct file *filp)
{
    printk (KERN_INFO "device closed\n");
    return 0;
}

static long at24xx_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    return 0;
}

static struct file_operations at24xx_fops = {
    .owner            = THIS_MODULE,
    .open    =    at24xx_open,
    .read = at24xx_read,
    .write = at24xx_write,
    .unlocked_ioctl    = at24xx_ioctl,
    .release = at24xx_release,
};

static struct miscdevice at24xx_dev = {
    .minor            = MISC_DYNAMIC_MINOR,
    .name            = DEVICE_NAME,
    .fops            = &at24xx_fops,
};

static int __init at24xx_dev_init(void) {
    int ret;
    int i;

    for (i = 3; i < 5; i++) {
        ret = gpio_request(S5PV210_GPE0(i), "PCF8591");
        if (ret) {
            printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
                S5PV210_GPE0(i), ret);
            return ret;

            s3c_gpio_cfgpin(S5PV210_GPE0(i), S3C_GPIO_OUTPUT);
            gpio_set_value(S5PV210_GPE0(i), 1);
        }
    }

    ret = misc_register(&at24xx_dev);

    printk(DEVICE_NAME"\tinitialized\n");

    return ret;
}

static void __exit at24xx_dev_exit(void) {
    int i;

    for (i = 0; i < 1; i++) {
        gpio_free(S5PV210_GPE0(3));
        gpio_free(S5PV210_GPE0(4));
    }

    misc_deregister(&at24xx_dev);
}

module_init(at24xx_dev_init);
module_exit(at24xx_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动