gpio模擬I2C,驅動pcf8574T


一、pcf8574T介紹

查看pcf8574T的數據手冊,

clip_image001

A表示讀或寫,當A為1的時候表示讀,當A為0的時候表示寫。現把地址控制線,即A2、A1、A0全部接地,可以得到讀控制指令為0x41,寫控制指令為0x40。

二、I2C介紹

參考:

http://blog.csdn.net/ce123_zhouwei/article/details/6882221

1、起始和停止時序

clip_image002

2、數據位的傳輸

clip_image002[4]

也就是在SCL的下降沿將數據位傳出。

3、主控制器為寫的時候,接收應答

當傳輸完數據的第8位,第9位要發送一個接收應答信號,將SDA拉高,設為輸入模式,在SCL為低電平之前將總線上的數據讀取過來,如果為1表示從設備接收數據失敗,如果為0表示從設備接收數據成功,可以繼續發送下一個字節。

clip_image001[5]

代碼片段:

// 接收應答信號 
static int i2c_recv_ack(void)
{
    int tmp;
    //SDA=1;
    gpio_direction_output(sda_pin, 1);
    //SCL=1;
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //F0=SDA;
    gpio_direction_input(sda_pin);
    tmp = gpio_get_value(sda_pin);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_direction_output(scl_pin, 0);
    //delay_us(5);
    udelay(5);
    //if(F0==1) return 1;
    if (tmp == 1) return 1;

    return 0;
}

4、主控制器為讀的時候,發送應答

第9位發送0,表示接收成功,發送1表示接收失敗。如果到最后一個字節后,發送一個NACK信號(1),以通知被控發送器結束數據發送,並釋放SDA線,以便主控接收器發送一個停止信號P。

clip_image002[6]

代碼片段:

// 發送應答信號
static void i2c_send_ack()
{
    //SDA=0;
    gpio_direction_output(sda_pin, 0);
    //SCL=1;
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //SDA=0;
    gpio_set_value(sda_pin, 0);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_set_value(scl_pin, 0);
    //delay_us(5);
    udelay(5);
}

static void i2c_send_noack()
{    
    //SDA=1;
    gpio_direction_output(sda_pin, 1);
    //SCL=1;
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //SDA=1;
    gpio_set_value(sda_pin, 1);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_set_value(scl_pin, 0);
    //delay_us(5);
    udelay(5);
}

5、寫過程完整數據傳輸

代碼片段:

// 控制PCF8574引腳電平
static int pcf8574_write(u8 val)
{
      int acktmp = 1; 
      i2c_start();
      i2c_write_byte(0x40);//寫控制指令 0x20<<1 R/W
      acktmp = i2c_recv_ack();
      if (acktmp == 1) {
        PRK("i2c_recv_ack fail\n");
        //return -1;
    }
      i2c_write_byte(val);                   
      acktmp = i2c_recv_ack();
      if (acktmp == 1) {
        PRK("i2c_recv_ack fail\n");
        //return -1;
    }
      i2c_stop();
      if (acktmp == 1) return -1;
      return 0;
}

6、讀過程完整數據傳輸

代碼片段:

// 讀出PCF8574引腳電平
static u8 pcf8574_read()
{    
    int acktemp = 1;
    u8 rddata = 0;
    i2c_start();
    i2c_write_byte(0x41);//讀控制指令
    i2c_send_ack();
    rddata = i2c_read_byte();
    i2c_send_noack();
    i2c_stop();
    return rddata;
}

三、示例代碼:

1、驅動

/*
 * Copyright (c) 2015 tingpan
 * Copyright 2012-2015 Senscom.cn
 *    tingpan <smbx-ztbz@cnblog.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>  //混雜設備
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/delay.h>  //mdelay
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_gpio.h>

#include <linux/kfifo.h>
#include <linux/interrupt.h>
#include <linux/irq.h>

#include <linux/types.h>  //u8

#include <linux/ioctl.h>


#define PCF8574_DEBUG    1

#if (PCF8574_DEBUG == 1)
#define PRK(...) printk(__VA_ARGS__)
#else 
#define PRK(...) 
#endif

#define DRV_NAME    "pcf8574"
#define DRV_DESC    "use i2c to extend i/o" 
#define DRV_VERSION    "0.1.0"

//#define PCF8574_NODE_NAME DRV_NAME

#define scl_pin 17
#define sda_pin 14

static struct mutex pcf8574_lock;

static DEFINE_MUTEX(pcf8574_lock);

//pcf8574
// 發送I2C啟動位 
static void i2c_start(void)
{
    //sda_pin=1; 
    gpio_direction_output(sda_pin, 1);
    //scl_pin=1; 
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //SDA=0; 
    gpio_set_value(sda_pin, 0);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_set_value(scl_pin, 0);
    //delay_us(5);
    udelay(5);
}

// 發送I2C停止位 
static void i2c_stop(void)
{
    //SDA=0;
    gpio_direction_output(sda_pin, 0);
    //SCL=1;
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //SDA=1;
    gpio_set_value(sda_pin, 1);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_set_value(scl_pin, 0);
    //delay_us(5);
    udelay(5);
}

// 發送BIT0  
static void i2c_send_bit_0(void)
{
    //SDA=0;
    gpio_direction_output(sda_pin, 0);
    //SCL=1;
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_set_value(scl_pin, 0);
    //delay_us(5);
    udelay(5);
}

// 發送BIT1 
static void i2c_send_bit_1(void)
{
    //SDA=1;
    gpio_direction_output(sda_pin, 1);
    //SCL=1;
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_set_value(scl_pin, 0);
    //delay_us(5);
    udelay(5);
}

// 接收應答信號 
static int i2c_recv_ack(void)
{
    int tmp;
    //SDA=1;
    gpio_direction_output(sda_pin, 1);
    //SCL=1;
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //F0=SDA;
    gpio_direction_input(sda_pin);
    tmp = gpio_get_value(sda_pin);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_direction_output(scl_pin, 0);
    //delay_us(5);
    udelay(5);
    //if(F0==1) return 1;
    if (tmp == 1) return 1;

    return 0;
}

// 發送應答信號
static void i2c_send_ack()
{
    //SDA=0;
    gpio_direction_output(sda_pin, 0);
    //SCL=1;
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //SDA=0;
    gpio_set_value(sda_pin, 0);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_set_value(scl_pin, 0);
    //delay_us(5);
    udelay(5);
}

static void i2c_send_noack()
{    
    //SDA=1;
    gpio_direction_output(sda_pin, 1);
    //SCL=1;
    gpio_direction_output(scl_pin, 1);
    //delay_us(5);
    udelay(5);
    //SDA=1;
    gpio_set_value(sda_pin, 1);
    //delay_us(5);
    udelay(5);
    //SCL=0;
    gpio_set_value(scl_pin, 0);
    //delay_us(5);
    udelay(5);
}

// 寫一個字節
static void i2c_write_byte(u8 data)
{
    u8 i;
    for (i=0; i<8; i++) {
        if ((data<<i) & 0x80)
                i2c_send_bit_1();
            else
                i2c_send_bit_0();
    }
}

// 接收一個字節
static u8 i2c_read_byte(void)
{
    u8 data = 0;
    u8 i;
    int tmp;
    for (i=0; i<8; i++) {
        //SDA=1;  
        gpio_direction_output(sda_pin, 1);  
        //SCL=1;   
        gpio_direction_output(scl_pin, 1); 
        //delay_us(5);
        udelay(5);
        //F0=SDA;
        gpio_direction_input(sda_pin);
        tmp = gpio_get_value(sda_pin);
        //delay_us(5);
        udelay(5);
        //SCL=0;
        gpio_set_value(scl_pin, 0);
        if (tmp == 1) {
            data = data<<1;
            data = data | 0x01;
        } else
            data = data<<1;
    }
    return data;
}

// 控制PCF8574引腳電平
static int pcf8574_write(u8 val)
{
      int acktmp = 1; 
      i2c_start();
      i2c_write_byte(0x40);//寫控制指令 0x20<<1 R/W
      acktmp = i2c_recv_ack();
      if (acktmp == 1) {
        PRK("i2c_recv_ack fail\n");
        //return -1;
    }
      i2c_write_byte(val);                   
      acktmp = i2c_recv_ack();
      if (acktmp == 1) {
        PRK("i2c_recv_ack fail\n");
        //return -1;
    }
      i2c_stop();
      if (acktmp == 1) return -1;
      return 0;
}

// 讀出PCF8574引腳電平
static u8 pcf8574_read()
{    
    int acktemp = 1;
    u8 rddata = 0;
    i2c_start();
    i2c_write_byte(0x41);//讀控制指令
    i2c_send_ack();
    rddata = i2c_read_byte();
    i2c_send_noack();
    i2c_stop();
    return rddata;
}

static ssize_t op_pcf8574_read(struct file *file, char __user * dat, 
                                    size_t len, loff_t *loff)
{
    int err = 0;
    u8 result;
    err = !access_ok(VERIFY_WRITE, (void __user *)dat, _IOC_SIZE(len));//用到len,分配指定大小空間
    if (err) {
        PRK(KERN_INFO " access not allowed!\n");
        return -EFAULT;
    }    
    result = pcf8574_read();
    __copy_to_user(dat, &result, 1);
    return 0;
}

static ssize_t op_pcf8574_write(struct file *file, char __user * dat, 
                                    size_t len, loff_t *loff)
{
    u8 wrdata = 0;
    if(!copy_from_user(&wrdata, dat, len))
    {
        if (!pcf8574_write(wrdata)) {
            PRK("op_sensorid_write %d success!\n",wrdata);
            return 0;
        } else {
            PRK("op_sensorid_write %d fail!\n",wrdata);
            return -1;
        }
        
    }  
    else
         return -1;
}

static int op_pcf8574_open(struct inode *inode, struct file *file)
{
    int err;

    err = gpio_request(scl_pin, "scl_pin");//管腳申請
    if (err) {
        PRK("[%d]gpio_request scl_pin failed.\n", __LINE__);
        return -1;
    }
    gpio_direction_output(scl_pin, 1);//該管腳設為輸出,且輸出為高電平

    err = gpio_request(sda_pin, "sda_pin");//管腳申請
    if (err) {
        PRK("[%d]gpio_request sda_pin failed.\n", __LINE__);
        return -1;
    }
    gpio_direction_output(sda_pin, 1);//該管腳設為輸出,且輸出為高電平
    
    PRK(KERN_INFO " op_pcf8574_open\n");
    return 0;
}

static int op_pcf8574_release(struct inode *inode, struct file *file)
{
    gpio_free(sda_pin);//釋放IO口
    gpio_free(scl_pin);//釋放IO口
    
    PRK(KERN_INFO " op_pcf8574_release\n");

    return 0;
}

static const struct file_operations pcf8574_fops =
{
    .owner   = THIS_MODULE,
    
    .open    = op_pcf8574_open,
    .read    = op_pcf8574_read,
    .write    = op_pcf8574_write,
    .release = op_pcf8574_release,
};

static struct miscdevice pcf8574_miscdev =
{
    //次設備號,驅動注冊時,如果次號指定MISC_DYNAMIC_MINOR,則進行動態分配。
    .minor = MISC_DYNAMIC_MINOR,
    .name = DRV_NAME,//設備名稱,將在/dev文件夾下顯示
    .fops = &pcf8574_fops,
};
static int __init pcf8574_init(void)//放后面,因為 misc_register 要包含前面的內容
{
    int ret;

    ret = misc_register(&pcf8574_miscdev);
    if (ret) {
        printk("misc_register error\n");
        return ret;
    }
    
    printk(KERN_INFO DRV_NAME " ver " DRV_VERSION" init\n");

    return 0;
}

module_init(pcf8574_init);

static void __exit pcf8574_exit(void)
{
    int ret;

    ret = misc_deregister(&pcf8574_miscdev);//注銷
    if (ret) {
        printk("misc_deregister error\n");
        return ;
    }

    printk(KERN_INFO DRV_NAME " ver " DRV_VERSION" exit\n");
}
module_exit(pcf8574_exit);

MODULE_DESCRIPTION(DRV_DESC);//描述
MODULE_VERSION(DRV_VERSION);//版本
MODULE_AUTHOR("tingpan <smbx-ztbz@cnblogs.com>");//作者
MODULE_LICENSE("GPL v2");//協議

2、應用程序

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/time.h>

#include <linux/types.h>

#include <syslog.h>

#include <uci.h>

#include <time.h>
#include <math.h>

#include <sys/select.h>

#include <pthread.h>

#define PCF8574_DTEST 1

#if (PCF8574_DTEST == 1)
#define PRT(...) printf(__VA_ARGS__)
#else 
#define PRT(...) 
#endif

#define USAGE_MESSAGE \
    "Usage: pcf8574-dtest MODE [data]\n"  \
    "MODE is r or w\n"  \
    "data is the value want to write when MODE is w\n"
    
int main(int argc, char **argv)
{
    int fd,ret;
    unsigned char data = 0;
    //if (argc<2 || (argv[1]=='r')) {
        //PRT(USAGE_MESSAGE);
        //return -1;
    //}
    //memset(data, 0, sizeof(data));
    //PRT("Compile Time %s %s\n", __DATE__, __TIME__);//打印出最后的編譯日期
    fd = open("/dev/pcf8574", O_RDWR);              //打開設備文件   
    if (fd < 0) {  
        perror("Open device file err:");  
        close(fd);  
        return -1;  
    }  

    if(argc > 2 && !strcmp(argv[1], "w")) {
        data  = atoi(argv[2]); 
        ret = write(fd, &data, 1); //第三個參數沒用上,先固定為1
        if (ret) perror("write"); 
    } else if (argc > 1 && !strcmp(argv[1], "r")) {
        ret = read(fd, &data, 1);
        if (ret) perror("write"); 
        PRT("read data is %d\n",data);
    } else {
        PRT(USAGE_MESSAGE);
        return -1;
    }
    return 0;
}

四、問題

試過用pcf8574讀取ds2431的序列號,但初始化就失敗了,這是因為這樣做就類似於用I2C通訊每控制一個IO空的電平,即約每傳輸20位就要把單總線通訊的管腳電平拉高或拉低,這樣的話就要求I2C的通訊速度要大於單總線通訊的二十多倍(可能更多),才能正常控制ds2431。

參考:

http://blog.csdn.net/xukai871105/article/details/18273653

源碼下載:

http://pan.baidu.com/s/1qXyQJ3Q


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM