gpio模拟I2C,驱动pcf8574T
2015-12-12 11:33
393 查看
一、pcf8574T介绍
查看pcf8574T的数据手册,
A表示读或写,当A为1的时候表示读,当A为0的时候表示写。现把地址控制线,即A2、A1、A0全部接地,可以得到读控制指令为0x41,写控制指令为0x40。
二、I2C介绍
参考:
/article/1389652.html
1、起始和停止时序
2、数据位的传输
也就是在SCL的下降沿将数据位传出。
3、主控制器为写的时候,接收应答
当传输完数据的第8位,第9位要发送一个接收应答信号,将SDA拉高,设为输入模式,在SCL为低电平之前将总线上的数据读取过来,如果为1表示从设备接收数据失败,如果为0表示从设备接收数据成功,可以继续发送下一个字节。
代码片段:
4、主控制器为读的时候,发送应答
第9位发送0,表示接收成功,发送1表示接收失败。如果到最后一个字节后,发送一个NACK信号(1),以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
代码片段:
5、写过程完整数据传输
代码片段:
6、读过程完整数据传输
代码片段:
三、示例代码:
1、驱动
2、应用程序
四、问题
试过用pcf8574读取ds2431的序列号,但初始化就失败了,这是因为这样做就类似于用I2C通讯每控制一个IO空的电平,即约每传输20位就要把单总线通讯的管脚电平拉高或拉低,这样的话就要求I2C的通讯速度要大于单总线通讯的二十多倍(可能更多),才能正常控制ds2431。
参考:
/article/2600940.html
源码下载:
http://pan.baidu.com/s/1qXyQJ3Q
查看pcf8574T的数据手册,
A表示读或写,当A为1的时候表示读,当A为0的时候表示写。现把地址控制线,即A2、A1、A0全部接地,可以得到读控制指令为0x41,写控制指令为0x40。
二、I2C介绍
参考:
/article/1389652.html
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。
参考:
/article/2600940.html
源码下载:
http://pan.baidu.com/s/1qXyQJ3Q
相关文章推荐
- centos 安装phantomjs
- 【万字总结】探讨递归与迭代的区别与联系及如何求解10000的阶层
- chapter 3 -回归试验
- Hibernate映射组件属性xml形式之方式二
- Socket模型详解(转)
- Android中Preference的使用以及监听事件分析
- [Android实例] BLE总结
- 别的程序员是怎么读你的简历的
- Deep Learning论文笔记之(八)Deep Learning最新综述
- 【探究】JavaScript内存回收机制
- 围住神经猫
- 如何给GridView添加RadioButton按钮(如何给GridView添加单选按钮)
- 程序设计的几个基本原则
- java解析xml
- MFC中添加GIF图片
- LeetCodeOJ_3_m_Longest Substring Without Repeating Characters
- springmvc 多文件上传
- 写入服务器的mySql数据库汉字时乱码
- ubuntu 远程 ubuntu
- 绘图 - 7