谈谈个人在用GPIO模拟IIC过程中对IIC的理解
前两天在配置一款数字陀螺仪的IIC时序,分别实现了使用芯片自带IIC模块配置和使用GPIO模拟,下面谈谈个人对IIC的一些理解。
K60上使用LPLD底层库实现GPIO模拟IIC的代码如下,该代码是用于对GY_50数字陀螺仪的读写
SCL为1期间SDA从1变为0表示开始信号
//开始信号
void GY_50_Start(void)
{
LPLD_GPIO_Output_b(GY_50_SDA_PORT_ID,GY_50_SDA_PORT_NUM,1);
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID,GY_50_SCL_PORT_NUM,1);
GY_50_Delay(1);
LPLD_GPIO_Output_b(GY_50_SDA_PORT_ID,GY_50_SDA_PORT_NUM,0);
GY_50_Delay(1);
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID,GY_50_SCL_PORT_NUM,0);
}
SCL为1期间SDA从1变为0表示结束信号
//停止信号
void GY_50_Stop(void)
{
LPLD_GPIO_Output_b(GY_50_SDA_PORT_ID, GY_50_SDA_PORT_NUM,0);
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID, GY_50_SCL_PORT_NUM,1);
GY_50_Delay(1);
LPLD_GPIO_Output_b(GY_50_SDA_PORT_ID, GY_50_SDA_PORT_NUM,1);
GY_50_Delay(1);
}
向数据线上传送一位数据表示有应答或者无应答
//发送应答信号(0:ACK 1:NAK)
void GY_50_SendAck(uint8 ack)
{
ack= ack?1:0;
LPLD_GPIO_Output_b(GY_50_SDA_PORT_ID, GY_50_SDA_PORT_NUM,ack);
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID, GY_50_SCL_PORT_NUM,1);
GY_50_Delay(1);
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID, GY_50_SCL_PORT_NUM,0);
GY_50_Delay(1);
}
从数据线上读取一位数据查看是否应答
//接收应答信号(0:ACK 1:NAK)
uint8 GY_50_ReceiveAck(void)
{
uint8 data_temp=0;
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID, GY_50_SCL_PORT_NUM,1);
GY_50_Delay(1);
SetGpioPortIO(GY_50_SDA_PORT_ID,GY_50_SDA_PORT_PIN,DIR_INPUT);
data_temp = LPLD_GPIO_Input_b(GY_50_SDA_PORT_ID, GY_50_SDA_PORT_NUM);
SetGpioPortIO(GY_50_SDA_PORT_ID,GY_50_SDA_PORT_PIN,DIR_OUTPUT);
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID, GY_50_SCL_PORT_NUM,0);
GY_50_Delay(1);
return data_temp;
}
按照协议要求,在SCL为低期间改变数据,在SCL为高期间保持数据,并保持一定时间,保证对方读取完毕
//向IIC发送一个字节的数据
void GY_50_SendByte(uint8 data)
{
static uint8 i,data_temp;
for(i=0;i<8;i++)
{
data_temp = data&0x80;//取最高位
data <<= 1;
data_temp = data_temp?1:0;
LPLD_GPIO_Output_b(GY_50_SDA_PORT_ID, GY_50_SDA_PORT_NUM,data_temp);
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID, GY_50_SCL_PORT_NUM,1);
GY_50_Delay(1);
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID, GY_50_SCL_PORT_NUM,0);
GY_50_Delay(1);
}
GY_50_ReceiveAck();
}
根据协议要求,在SCL为高期间读取数据,在SCL为低期间让对方发送数据,SCL为低要保持一定时间,保证数据线上的数据稳定
//从IIC接收一个数据
uint8 GY_50_ReceiveByte(void)
{
static uint8 i,data_temp=0;
LPLD_GPIO_Output_b(GY_50_SDA_PORT_ID, GY_50_SDA_PORT_NUM,1);
SetGpioPortIO(GY_50_SDA_PORT_ID,GY_50_SDA_PORT_PIN,DIR_INPUT);
for(i=0;i<8;i++)
{
data_temp <<= 1;
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID, GY_50_SCL_PORT_NUM,1);
GY_50_Delay(1);
data_temp |= LPLD_GPIO_Input_b(GY_50_SDA_PORT_ID, GY_50_SDA_PORT_NUM);
LPLD_GPIO_Output_b(GY_50_SCL_PORT_ID, GY_50_SCL_PORT_NUM,0);
GY_50_Delay(1);
}
SetGpioPortIO(GY_50_SDA_PORT_ID,GY_50_SDA_PORT_PIN,DIR_OUTPUT);
//GY_50_Delay(1);
return data_temp;
}
根据协议要求,串行传送信号
//单字节写入
void GY_50_WriteByte(uint8 add, uint8 data)
{
GY_50_Start();
GY_50_SendByte(GY_50_ADDRESS);
GY_50_SendByte(add);
GY_50_SendByte(data);
GY_50_Stop();
}
根据协议要求,串行传送信号和指令
//单字节读取
uint8 GY_50_ReadByte(uint8 add)
{
uint8 data_temp;
GY_50_Start();
GY_50_SendByte(GY_50_ADDRESS);
GY_50_SendByte(add);
GY_50_Start();
GY_50_SendByte(GY_50_ADDRESS|0x01);
data_temp = GY_50_ReceiveByte();
GY_50_SendAck(1);
GY_50_Stop();
return data_temp;
}
该函数在总线周期为50M的K60芯片上大约延迟4us(k=1)
//延时函数
void GY_50_Delay(uint8 k)
{
uint16 i,j;
for(i=0;ifor(j=0;j<200;j++)
{
asm("nop");
}
}
}
希望以上对大家的学习有所帮助
注:个人才疏学浅,难免有疏漏之处,还望批评指正