GPIO模拟I2C实现

GPIO模拟I2C实现

代码实现

1、启动I2C传输

static void start_i2c(void)
{
	SDA_H;   
	CyDelayUs(IO_DELAY);
	SCL_H;
	CyDelayUs(IO_DELAY_5X); 
	SDA_L;   	
	CyDelayUs(IO_DELAY_3X);        
	SCL_L;   
	CyDelayUs(IO_DELAY_2X);
}

2、写数据命令

static uint8_t  i2c_send_byte(uint8_t data)
{
	uint8_t bit_cnt;
    uint8_t size = 0;

	for(bit_cnt = 0; bit_cnt < 8; bit_cnt++) 
	{
		if((data<<bit_cnt) & 0x80)
		{
			SDA_H;   
		}
		else
		{ 
			SDA_L;                
		}
		CyDelayUs(IO_DELAY);
		SCL_H;             
		CyDelayUs(IO_DELAY_2X);         
		SCL_L; 
        CyDelayUs(IO_DELAY);
	}
    
    //释放SDA总线,等待从设备应答
	if(wait_r_ack() != 0)
	{ 
		size = 0;
	}	 
	else
	{
		size = 1;
	}

    return size;
}

等待从设备响应

static uint8_t r_ack(void)			//接收应答信号函数
{
    uint8_t ack;				    //定义一个位变量,来暂存应答状态。

    CyDelayUs(IO_DELAY);
	SCL_H;				            //拉高时钟线。
	CyDelayUs(IO_DELAY);
	ack = I2C_SDA_READ;				//读取应答信号的状态。
	CyDelayUs(IO_DELAY);
	SCL_L;				            //拉低时钟线。
	CyDelayUs(IO_DELAY);
	return ack;				        //返回应答信号的状态,0表示应答,1表示非应答。
}

static uint8_t wait_r_ack(void)
{
	uint8_t ack;
	int32_t i;
    uint8_t max_count = 3;
    
	ack = 1;
	for(i = 0; i < max_count; i++)
	{
		ack = r_ack();
		if(ack == 0)
		{
			break;
		}
        
		if(i == max_count - 1)
		{
			return 1;
		}
	}
	return 0;
}

3、停止I2C数据传输

static void stop_i2c(void)
{
	SDA_L;    
	CyDelayUs(IO_DELAY);
	SCL_H; 
	CyDelayUs(IO_DELAY_5X);
	SDA_H;  
	CyDelayUs(IO_DELAY_2X);
}

4、读取从设备数据

static uint8_t  recv_byte(void)
{
	uint8_t retc = 0;
	uint8_t bit_cnt;
    
	for (bit_cnt = 0; bit_cnt < 8; bit_cnt++)
	{
		SCL_H;            
		CyDelayUs(IO_DELAY);
		retc = retc<<1;
		if (I2C_SDA_READ != 0)
		{
			retc = retc | 0x1;
		}					
		CyDelayUs(IO_DELAY);
        SCL_L;
        CyDelayUs(IO_DELAY_3X);
	}
    
	return retc;
}

5、读取多字节数据

uint8_t i2c_read_byte(uint8_t slave, uint8_t sub_addr, uint8_t *buffer, uint8_t len)
{
	uint8_t i;
    uint8_t size;
    
    if(buffer == NULL || len == 0)
    {
        return 0;
    }
    
	start_i2c();                 
	size = i2c_send_byte(slave);              
	if(size == 0)
	{
		stop_i2c();
		*buffer = 0x00;
		return size;
	}
    
	size = i2c_send_byte(sub_addr);             
	if (size == 0)
	{
		stop_i2c();
		*buffer = 0x00;
		return  size;
	}
    
	start_i2c();			
	size = i2c_send_byte(slave+1);
	if (size == 0)
	{
		stop_i2c();
		*buffer = 0x00;
		return  size;
	}
    
	for (i = 0; i < len - 1; i++)
	{   
		*buffer = recv_byte();
        ack_i2c(0);
		buffer++;
	}
    
	*buffer = recv_byte();
    //释放SDA总线
    ack_i2c(1);
	stop_i2c();                 
	return  len;
}

问题

1、I2C波形显示SDA没有完全下拉到地,只有在clk下拉到地之后,才被完全下拉。

答案:确认IO的驱动模式,有可能是由于模式选择不正确引起的,需要确定IO口是否支持开漏下拉,由外部上拉电阻,默认电平为高。

2、I2C从设备没有反应,如下图:
在这里插入图片描述
答:从示波器的波形来看,从设备无法将SDA总线拉低,可能是从设备的驱动力不够,这种原因很可能是SDA的IO模式设置成了强力推挽模式导致。将模式修改为开漏下拉模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值