一、pcf8574T介紹
查看pcf8574T的數據手冊,
A表示讀或寫,當A為1的時候表示讀,當A為0的時候表示寫。現把地址控制線,即A2、A1、A0全部接地,可以得到讀控制指令為0x41,寫控制指令為0x40。
二、I2C介紹
參考:
http://blog.csdn.net/ce123_zhouwei/article/details/6882221
1、起始和停止時序
2、數據位的傳輸
也就是在SCL的下降沿將數據位傳出。
3、主控制器為寫的時候,接收應答
當傳輸完數據的第8位,第9位要發送一個接收應答信號,將SDA拉高,設為輸入模式,在SCL為低電平之前將總線上的數據讀取過來,如果為1表示從設備接收數據失敗,如果為0表示從設備接收數據成功,可以繼續發送下一個字節。
代碼片段:
// 接收應答信號 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。
代碼片段:
// 發送應答信號 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
源碼下載: