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模式设置成了强力推挽模式导致。将模式修改为开漏下拉模式。