gpio模拟i2c

i2c写操作

I2C协议规定,总线上数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。起始和结束信号总是由主设备产生(意味着从设备不可以主动通信?所有的通信都是主设备发起的,主可以发出询问的command,然后等待从设备的通信)。

起始和结束信号产生条件:总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件;当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。

在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。起始和结束如图所示:
在这里插入图片描述

#define SDA 12                         //定义SDA所对应的GPIO接口编号  
#define SCL 11                         //定义SCL所对应的GPIO接口编号  
#define OUTP 1                          //表示GPIO接口方向为输出  
#define INP 0                           //表示GPIO接口方向为输入  
#define RETRY_COUNT                3
#define PULL_HIGH				   1
#define PULL_LOW				   0
#define SWI2C_READ			  0
#define SWI2C_WRITE			 1
#define BIT0                    0x00000001

根据传输逻辑,I2C写操作起始条件代码如下:

/* I2C起始条件 */  
bool i2c_start(void)
{  
	bool bStatus = true;	// success status
	bool bRet;	
	//初始化GPIO口  
	gpio_direction_output(SDA, 1);          //设置SDA方向为输出,设置SDA为高电平 
	udelay(20); 
	gpio_direction_output(SCL, 1);         //设置SCL方向为输出,设置SCL为高电平  
	
	udelay(20);

	if ((pin_get_level(SCL) == PULL_LOW) || (pin_get_level(SCL) == PULL_LOW))
	{
		bRet = pin_check_high(SCL);
		if (bRet == false) {
			printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
		}
		bRet = pin_check_high(SDA);
		if (bRet == false) {
			printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
		}
		bStatus = false;
	} 
	else // success
	{
		bRet = pin_check_low(SDA);
		if (bRet == false) {
			printk(KERN_ERR "[%s][%d] pin_check_low fail\n", __FUNCTION__, __LINE__);
		}
		udelay(20);
		bRet = pin_check_low(SCL);
		if (bRet == false) {
			printk(KERN_ERR "[%s][%d] pin_check_low fail\n", __FUNCTION__, __LINE__);
		}
		udelay(20);
	}
	printk(KERN_ERR "i2c_start bStatus %d\n", bStatus);
	
	return bStatus;     //vain
}  

I2C终止条件代码如下:

/* I2C终止条件 */  
void i2c_stop(void)  
{  
	bool bRet;

	gpio_direction_output(SDA, 0);			//设置SDA方向为输出,设置SDA为低电平 
	udelay(20); 

	bRet = pin_check_high(SCL);
	if (bRet == false) {
		printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
	}
	udelay(20);

	bRet = pin_check_high(SDA);
	if (bRet == false) {
		printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
	}
	udelay(20);
	printk(KERN_ERR "i2c_stop\n");
}  

数据传输以字节为单位。主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位, 此时才认为一个字节真正的被传输完成。当然,并不是所有的字节传输都必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备将回传一个否 定应答位。数据传输的过程如图所示:
在这里插入图片描述
I2C写字节代码如下:

bool i2c_write_byte(u8 u8dat, bool isReg)  
{
	bool bRet;
	u8	u8Mask = 0x80;
	int bAck; // acknowledge bit
	int i = 0;
	bool bStatus;
	
	printk(KERN_ERR "i2c_write_byte u8dat 0x%x \n", u8dat);

	for (i = 0; i < 8; i++) {
		if (u8dat & u8Mask)
		{
			bRet = pin_check_high(SDA);
			if (bRet == false) {
				printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
			}
		}
		else
		{
			bRet = pin_check_low(SDA);
			if (bRet == false) {
				printk(KERN_ERR "[%s][%d] pin_check_low fail\n", __FUNCTION__, __LINE__);
			}
		}
		udelay(20);
		bRet = pin_check_high(SCL);
		if (bRet == false) {
			printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
		}
		udelay(20);
		bRet = pin_check_low(SCL);
		if (bRet == false) {
			printk(KERN_ERR "[%s][%d] pin_check_low fail\n", __FUNCTION__, __LINE__);
		}

		if (i == 7) {
			printk(KERN_DEBUG "[%s][%d] release SDA\n", __FUNCTION__, __LINE__);
			bRet = pin_check_high(SDA);
			if (bRet == false) {
				printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
			}
			udelay(20);
		}
		u8Mask >>= 1; // next
	}
	udelay(20);
	unsigned char cRet = i2c_read_ack(isReg);
	if(cRet == 0) {
		bStatus = true;
	} else {
		bStatus = false;
	}
	return bStatus;
}

最后检测ack:

/*   
I2C读取ACK信号(写数据时使用)  
返回值 :0表示ACK信号有效;非0表示ACK信号无效  
*/  
unsigned char i2c_read_ack(bool isReg)  
{  
	unsigned char ret = 0;
	bool bRet = false;
	int bAck;
	int i = 0;
	
	udelay(20);
	gpio_direction_input(SDA);
	udelay(20);
	bRet = pin_check_high(SCL);
	if (bRet == false) {
		printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
	}
	udelay(20);
	
	for (i = 0; i < 10000; i++) {
		bAck = pin_get_level(SDA);
		if (bAck == 1) {
			ret = 1;
		} else {
			printk(KERN_ERR "i2c_read_ack bAck %d\n", bAck);
			ret = 0;
			break;
		}
		udelay(20);
	}
	
	bRet = pin_check_low(SCL);
	if (bRet == false) {
		printk(KERN_ERR "[%s][%d] pin_check_low fail\n", __FUNCTION__, __LINE__);
	}
	udelay(20);
	gpio_direction_output(SDA, 1);
	return ret;
} 

主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。向指定设备发送数据的格式如图所示:(每一最小包数据由9bit组成,8bit内容+1bit ACK, 如果是地址数据,则8bit包含1bit方向)在这里插入图片描述

bool IIC_AccessStart(u8 u8SlaveID, u8 trans_t)
{
	u16 u16Retries = RETRY_COUNT;
	u8 addr = 0;

	if (trans_t == SWI2C_READ) // check i2c read or write
	{
		addr = (u8SlaveID << 1) | 1;
	}
	else
	{
		addr = (u8SlaveID << 1) | 0;
	}

	while (u16Retries--)
	{
		if (i2c_start() == false)
		{
			continue;
		}
		if (i2c_write_byte(addr, false) == true )  // check acknowledge
		{
			return true;
		}
		i2c_stop();
	}
	return false;
}

I2C写操作代码如下:

/*  
I2C写操作  
addr:目标设备地址  
buf:写缓冲区  
len:写入字节的长度  
*/  
int gpio_i2c_write(unsigned short addr, unsigned char* buf, int len)  
{
	int bRet = 1;
	u16 u16Retries = RETRY_COUNT;
	int i = 0;

	while (u16Retries--)
	{
		if (IIC_AccessStart( addr, SWI2C_WRITE) == false)
		{
			if( u16Retries )
				continue;
			else {
				goto fail;
			}
		}

		if (i2c_write_byte(buf[0],true) == false)
		{
			goto fail;
		}
		len--;
		buf++;
		
		while(len) // loop of writting data
		{
			len--;
			
			if (i2c_write_byte(buf[0],false) == false) {
				
				goto fail;
			}
			buf++; // next byte pointer
		}

		break;
	}
	bRet = 0;

fail:
	i2c_stop();
    return bRet;
}

I2C读操作

/*  
I2C读操作  
addr:目标设备地址  
buf:读缓冲区  
len:读入字节的长度  
*/  
void gpio_i2c_read(unsigned short addr, unsigned char reg, unsigned char* buf, int len)  
{ 
	int bRet = 1;
	u16 u16Retries = RETRY_COUNT;
	int i = 0;

	while (u16Retries--)
	{
		if (IIC_AccessStart( addr, SWI2C_WRITE) == false)
		{
			if( u16Retries )
				continue;
			else {
				goto fail;
			}
		}
		if (i2c_write_byte(reg,true) == false)
		{
			goto fail;
		}
		i2c_stop();

		//restart 
		if (IIC_AccessStart( addr, SWI2C_READ) == false)
		{
			if( u16Retries )
				continue;
			else {
				goto fail;
			}
		}

		for (i = 0; i < len-1; i++) {
			buf[i] = i2c_read_byte();
			i2c_send_ack(0);
		}
		buf[len-1] = i2c_read_byte();
		i2c_send_ack(1);

	}
fail:
	i2c_stop();
    return bRet;
}  

I2C读字节代码如下:

/* I2C字节读 */  
unsigned char i2c_read_byte(void)  
{  
	int i;  
	unsigned char r = 0; 
	bool bRet;
	
	gpio_direction_input(SDA);           //设置SDA方向为输入
	for (i=7; i>=0; i--) {  
		bRet = pin_check_high(SCL);
		if (bRet == false) {
			printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
		}
		udelay(20); 
		
		r = (r << 1) | pin_get_level(SDA);      //从高位到低位依次准备数据进行读取  
		
		bRet = pin_check_low(SCL);
		if (bRet == false) {
			printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
		}
		udelay(20);  
	}
	
	return r;  
}  

I2C发送ACK应答代码如下:

/* I2C发出ACK信号(读数据时使用) */  
int i2c_send_ack(unsigned char flag)  
{  
	bool bRet;
	
	if (flag == 0) {
		gpio_direction_output(SDA, 0);
	} else {
		gpio_direction_output(SDA, 1);
	}
	udelay(20);

	bRet = pin_check_high(SCL);
	if (bRet == false) {
		printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
	}
	udelay(20);
	bRet = pin_check_low(SCL);
	if (bRet == false) {
		printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
	}
	udelay(20);

	//释放SDA
	bRet = pin_check_high(SDA);
	if (bRet == false) {
		printk(KERN_ERR "[%s][%d] pin_check_high fail\n", __FUNCTION__, __LINE__);
	}
}  

模拟i2c替换linux的i2c控制器

static int i2c_master_reg8_write(const struct i2c_client *client, const char addr, const char val)
{
	unsigned char buf[2];
	
	buf[0] = addr+0x80;
	buf[1] = val;
	gpio_i2c_write(client->addr, buf, 2);
	return OK;
/*
	struct i2c_adapter *adap=client->adapter;
	int ret;
	char buf[2];
    
	buf[0] = addr + 0x80;
	buf[1] = val;
    
    struct i2c_msg msg[] = 
	{
		{
			.addr = client->addr,
			.len = 2,
			.buf = buf,
			.flags = 0|I2C_M_NORMAL_SPEED,
		},
	};
    
	ret = i2c_transfer(adap, msg, 1); 
    return (ret == 1)?OK : ERR;
*/
}
static int i2c_master_nbytes_write(const struct i2c_client *client, const char addr, const char *buf, int count)
{
	char *msgBuf = NULL;
	msgBuf =  (char *)kzalloc((count +1) * sizeof(char), GFP_KERNEL);
	if (NULL == msgBuf)
	{
		printk(KERN_ERR "Alloc space failed. \n");
		return -1;
	}
	msgBuf[0] = addr+0x80;
	memcpy(msgBuf+1, buf, count);
	gpio_i2c_write(client->addr, msgBuf, count+1);
	
	kfree(msgBuf);
	return OK;
/*
	struct i2c_adapter *adap=client->adapter;
	int ret;
	char *msgBuf = NULL;
	
	struct i2c_msg msg[1] = 
	{
		{
			.addr = client->addr,
			.len = count+1,
			.flags = 0|I2C_M_NORMAL_SPEED,
		},
	};

	msgBuf =  (char *)kzalloc((count +1) * sizeof(char), GFP_KERNEL);
	if (NULL == msgBuf)
	{
		printk(KERN_ERR "Alloc space failed. \n");
		return -1;
	}
	msgBuf[0] = addr + 0x80;
	memcpy(msgBuf+1, buf, count);
	msg[0].buf = msgBuf;
	
	ret = i2c_transfer(adap, msg, 1); 
	kfree(msgBuf);
    return (ret == 1)?OK : ERR;
*/	
}
static int i2c_master_reg8_recv(const struct i2c_client *client, char reg, char *buf)
{
	gpio_i2c_read(client->addr, reg, buf, 1);
	return OK;

/*
    struct i2c_adapter *adap=client->adapter;
    int ret;
		
	struct i2c_msg msg[2] = 
	{
		{
			.addr = client->addr,
			.len = 1,
			.buf = &reg,
			.flags = 0,
		},
		{
			.addr = client->addr,
			.flags = I2C_M_RD,
			.len = 1,
			.buf = buf,
		},
	};
	ret = i2c_transfer(adap, msg, 2); 
    return (ret == 2)?OK : ERR;
*/
}
static int i2c_master_nbytes_read(const struct i2c_client *client, char reg, char *buf, int count)
{
	gpio_i2c_read(client->addr, reg, buf, count);
	return OK;
/*
    struct i2c_adapter *adap=client->adapter;
    int ret;
		
	struct i2c_msg msg[2] = 
	{
		{
			.addr = client->addr,
			.len = 1,
			.buf = &reg,
			.flags = 0,
		},
		{
			.addr = client->addr,
			.flags = I2C_M_RD,
			.len = count,
			.buf = buf,
		},
	};
    printk(KERN_ERR "i2c_master_nbytes_read[%d] 0x%x 0x%x\n", __LINE__, client->addr, reg);
	ret = i2c_transfer(adap, msg, 2); 
    return (ret == 2)?OK : ERR;
*/
}

版权声明:本文为mars_Vessalius原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。