目录:一. 说明

    二. 驱动程序说明及问题

    三. 案例一

        四. 案例二

一. 说明

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=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 =tdata<<1;            //左移一位
       set_conIN_sda();
       __udelay(3);
       
       temp = gpio_get_value(S5PV210_GPE0(3));
           if( temp == 1)
          tdata = tdata|0x01;   // 若接收到的位为1,则数据的最后一位置1
        else
          tdata = 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=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 =tdata<<1;            //左移一位
       set_conIN_sda();
       __udelay(3);
       
       temp = gpio_get_value(S5PV210_GPE0(3));
           if( temp == 1)
          tdata = tdata|0x01;   // 若接收到的位为1,则数据的最后一位置1
        else
          tdata = 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.");

转载于:https://www.cnblogs.com/minglicnblogs/p/3967431.html

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

  1. linux源码gpio模拟i2c,linux内核gpio模拟i2c实例.doc

    linux内核gpio模拟i2c实例.doc linux内核GPIO模拟I2C实例2010-10-11作者:cvip302814来源:cvip302814的blog前言:在许多情况下,我们并没有足够的 ...

  2. STM32模拟I2C时序读写EEPROM精简版

    平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线+外部EEPROM(不需要上拉电阻) 工程介绍:主要文件在USER组中,bsp_i2c_ee.c,bsp_i2c_ee.h ...

  3. GPIO模拟I2C程序实现

    GPIO模拟I2C程序实现. I2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA).I2C是总线结构,1个 ...

  4. Linux I2C子系统分析之(一) ----- 用GPIO模拟I2C总线

    在drivers/i2c/busses下包含各种I2C总线驱动,如S3C2440的I2C总线驱动i2c-s3c2410.c,使用GPIO模拟I2C总线的驱动i2c-gpio.c,这里只分析i2c-gp ...

  5. 外设驱动库开发笔记8:GPIO模拟I2C驱动

    I2C总线简单方便,是我们经常使用的一种总线.但有时候我们的MCU没有足够多的I2C控制器来实现我们的应用,所幸我可以使用普通的GPIO引脚来模拟低速的I2C总线通信.这一节我们就来实现使用软件通过普 ...

  6. STM32 通用GPIO模拟I2C实现

    STM32 通用GPIO模拟I2C实现 通用GPIO模拟I2C通信实现样例 1 GPIO初始化 #ifdef HW_I2C1 //硬件I2C初始化//PA8-I2C1_SCLGPIO_StructIn ...

  7. i2c 驱动五:gpio模拟i2c

    有关linux的i2c相关文章有一下几篇,他们互相关联,应该一同看: - i2c 驱动一:简介 - i2c 驱动二:devfs文件系统 - i2c 驱动三:自己实现设备和驱动分离 - i2c 驱动四: ...

  8. MTK6592 GPIO模拟I2C

    虽然Linux内核支持该功能,但是MTK并不支持I2C-GPIO功能,官方说法是硬件I2C接口已经够多,不需要软件模拟,并且用GPIO模拟I2C会影响系统性能... 所以,要做的,就是将被MTK阉割的 ...

  9. GPIO模拟I2C通信协议(二)

    GPIO模拟2C读写E2PROM 1 E2PROM简介 2 AT24C28的读写逻辑 2.1 单字节写入 (BYTE WRITE) 2.2 页写入 (PAGE WRITE) 2.3 读取当前地址 (C ...

最新文章

  1. [原创]软件测试思维方式
  2. OpenCV_图像平滑
  3. Vue PDF文件预览vue-pdf
  4. hibenate5.1配置mysql_hibernate5.2的基本配置方法(详解)
  5. python 学习笔记(十二) 文件和序列化
  6. pip install 包的安装位置
  7. php 如何生成exe文件怎么打开,如何把PHP转成EXE文件
  8. DataGridView添加一行数据、全选、取消全选、清空数据、删除选中行
  9. 【Python】浮点数计算时的不准确性以及如何进行精确计算
  10. sql增删改查_5分钟GET全栈开发 - 增删改查的取经之路
  11. JUC编程入门(高并发)
  12. 安卓使用MediaPlayer播放RTSP无画面的解决办法
  13. 英语模板作文,词组,句子,句型,开头
  14. Spring Cloud技术栈简述
  15. 开源APM性能检测系统技术选型与架构实战
  16. USB转SPI芯片操作FLASH--CH347应用
  17. JAVA基础案例教程——模拟物流快递系统程序设计
  18. 如何生成EAN13流水号条形码
  19. 从头构建自己的Linux系统
  20. bzoj4564: [Haoi2016]地图 仙人掌的圆方树 莫队 分块

热门文章

  1. WS-Eventing、WS-Transfer Web服务标准
  2. 银行交易系统 TiDB 在线缩容迁移
  3. hiveserver或者hive启动出现Expected authority at index 7问题解决
  4. wine的sys文件具体位置
  5. 【机器学习】逻辑回归特征的离散化与交叉
  6. linux是否有免安装程序,在线Ubuntu Linux系统,免安装体验Linux系统
  7. oracle中的 where 1=1 和where 11
  8. textarea 输入框限制字数
  9. RT-Flash imxrt 系列rt1052 rt1060量产神器宣传
  10. 用来枚举属性的对象工具函数