gpio模拟I2C,驱动pcf8574T

来源:互联网 发布:nba骑士数据 编辑:程序博客网 时间:2024/05/18 12:40

一、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 14static 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

0 0
原创粉丝点击