本帖最后由 xiaob135 于 2014-5-11 16:22 编辑
今天下了一天雨,无聊写点代码吧。
发现网上都是io口模拟i2c主机的代码,很少有模拟i2c从机的。所以写一个贡献出来。对于学习i2c的时序还是挺有帮助的。
需要两个带中断的io口,必须支持上升沿和下降沿中断。
typedef enum
{
I2C_SLAVE_IDLE,
I2C_SLAVE_ADD,//write iic add
I2C_SLAVE_REG,//write the register add
I2C_SLAVE_WRITE,//master write and slave read
I2C_SLAVE_READ,//master read and slave write
I2C_SLAVE_BUSY
}e_I2C_SLAVE_MODE;
static e_I2C_SLAVE_MODE i2c_slave_mode = I2C_SLAVE_IDLE;
static unsigned char I2C_DATA_TEMP = 0;
static unsigned char i2c_slave_reg_p = 0;//the register add
static unsigned char i2c_slave_data_p = 0;//
void i2c_slave_scl_h( void )
{
I2C_SDA_INT_EN();
switch( i2c_slave_mode )
{
case I2C_SLAVE_ADD:
case I2C_SLAVE_REG:
case I2C_SLAVE_WRITE:
I2C_DATA_TEMP <<= 1;
if( I2C_SDA_IN() )
I2C_DATA_TEMP ++;
i2c_slave_data_p++;
break;
default:break;
}
}
void i2c_slave_scl_l( void )
{
I2C_SDA_INT_DIS();
if( i2c_slave_data_p > 8 )
{
i2c_slave_data_p = 0;
I2C_SDA_H();//end ack;
return;
}
I2C_SCL_L();//slow the i2c speed
switch( i2c_slave_mode )
{
case I2C_SLAVE_ADD:
if( i2c_slave_data_p == 8 )
{
if( I2C_DATA_TEMP >> 1 == I2C_ADD )
{
I2C_SDA_L();//ack
if( I2C_DATA_TEMP & 0x01 )//read
{
i2c_slave_mode = I2C_SLAVE_READ;
}
else
i2c_slave_mode = I2C_SLAVE_REG;//write regster add
}
else
i2c_slave_mode = I2C_SLAVE_BUSY;//nack
}
break;
case I2C_SLAVE_REG:
if( i2c_slave_data_p == 8 )
{
I2C_SDA_L();//ack
i2c_slave_reg_p = I2C_DATA_TEMP;
i2c_slave_mode = I2C_SLAVE_WRITE;
}
break;
case I2C_SLAVE_WRITE:
if( i2c_slave_data_p == 8 )
{//
if( I2C_SLAVE_WRITE_BYTE( i2c_slave_reg_p++, I2C_DATA_TEMP ) )
{
I2C_SDA_L();
}
else
{
i2c_slave_mode = I2C_SLAVE_BUSY;
}
}
break;
case I2C_SLAVE_READ:
if( i2c_slave_data_p < 8 )
{
if( I2C_DATA_TEMP & 0x80 )//output a bit
I2C_SDA_H();
else
I2C_SDA_L();
I2C_DATA_TEMP <<= 1;
i2c_slave_data_p++;
}
else if( i2c_slave_data_p == 8 )
{
if( I2C_SLAVE_READ_BYTE(i2c_slave_reg_p++, &I2C_DATA_TEMP) )
{
I2C_SDA_L();
}
else
{
i2c_slave_mode = I2C_SLAVE_BUSY;
}
}
break;
default:break;
}
I2C_SCL_H();
}
void i2c_slave_sda_h( void )
{
if( I2C_SCL_IN() )
{
I2C_SCL_INT_DIS();
i2c_slave_mode = I2C_SLAVE_IDLE;
}
}
void i2c_slave_sda_l( void )
{
switch( i2c_slave_mode )
{
case I2C_SLAVE_IDLE:
if( I2C_SCL_IN() )
{
I2C_SCL_INT_EN();
i2c_slave_mode = I2C_SLAVE_ADD;
i2c_slave_data_p = 0;
}
break;
default:break;
}
}
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
去年做过一个产品,51单片机,处理起来很费时。最后没办法,检测到起始位后,关闭中断,直接用nop的方式来延时,直到通信结束退出。
主机SCK速率不能超过30K。
一周热门 更多>