最近在用I2C通信时,遇到的问题。
本帖最后由 chen~chen 于 2018-8-30 15:48 编辑场景:stm32用I2C对一外设模块通信,外设模块要求:1)通信速度最大100Kbps,2)外设工作于slaver模式,IIC从机地址0x30.
程序运行正常,通信正常,但是在用示波器查I2C的SCL,SDA线的波形是发现在读数据时(如图1所示的1是开始读),
外设向单片机通信时的第一个SCL信号应是先低电平4us,再高电平4us,但实际上高电平只有2us,。(波形如图2所示)
u8 IIC_Wait_Ack1(void)//等待应答。
{
u8 ucErrTime=0;
SDA_IN_FMS();
IIC_SDA_FMS=1;delay_us(3);
IIC_SCL_FMS=1;delay_us(3);
while(READ_SDA_FMS)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop1();
return 1;
}
}
IIC_SCL_FMS=0;
return 0;
}
u8 IIC_Read_Byte1(u8 ack)//读操作
{
unsigned char i,receive=0;
SDA_IN_FMS();
for(i=0;i<8;i++ )
{
IIC_SCL_FMS=0;
delay_us(4);
IIC_SCL_FMS=1;
receive<<=1;
if(READ_SDA_FMS)receive++;
delay_us(4);
}
if (!ack)
IIC_NAck1();
else
IIC_Ack1();
return receive;
}
u8 FMS_Read_data(u8 ReadAddr )//读数据
{
u8 temp=0;
IIC_Start1();
IIC_Send_Byte1(0X30); //从机地址写
IIC_Wait_Ack1();
IIC_Send_Byte1(ReadAddr);//寄存器地址
IIC_Wait_Ack1();
IIC_Start1();
IIC_Send_Byte1(0X31); //从机地址读
IIC_Wait_Ack1();
temp=IIC_Read_Byte1(0);
IIC_Stop1();
return temp;
}
当我在temp=IIC_Read_Byte1(0); 前面加一个延时delay_us(3)时,读的第一个脉冲就基本上完整了。(图三)
我对u8 FMS_Read_data(u8 ReadAddr)程序进行分析,在等待应答时SCL先置高,等待SDA输入低电平,应答成功,则SCL置低电平。而后紧接着就进入IIC_Read_Byte1(0)函数,SCL被置低4us(实际运行时间大于4us),而后置高4us,但是实际上只有2us,而其后的第二个读脉冲的低电平为4.8us与其他的都一样,所以收到影响的只是第一个读脉冲信号。可能是外设模块在反应需要几us的时间,虽然IIC_Read_Byte1(0)在操作,但是信号被无视。等外设模块准备好输出时,IIC_Read_Byte1(0)后面的波形才被正常显示出来,也没有影响到其他波形。这只是我自己的假设,请大佬指点一二,谢谢。
本帖最后由 chen~chen 于 2018-8-31 12:42 编辑
我又将temp=IIC_Read_Byte1(0); 前面的延时分别加到了6us,10us。波形正常,
1)此为6us时,其他都基本和delay_us(3)时一样,只是第一个读脉冲前的低电平延长到12.4us
2)此为10us时,其他都基本和delay_us(3)时一样,只是第一个读脉冲前的低电平延长到16.4us
所以认为在读操作时,外设需要一个准备时间,此时I2C仍在运行,但是要到外设准备好了之后才能看到波形(这就是前面读的SCL信号的高电平为4us,实际只有2us,的原因吧)。
读不可能有什么延时的 本帖最后由 ayb_ice 于 2018-9-3 09:00 编辑
一看时序不够严谨,正常操作时SCL为低,时序开始先高后低,结束时就为低
再有,一般先输出数据再转为输出模式,可以避免出现错误脉冲信号
// 接收一个字节
// ack : 应答信号ACK或NACK
// 返回: 接收的字节
// 注: 软件模拟方式无法判断是否成功或失败,所以总是成功
//
u8 IIC_Receive(bit bAck)
{
SDA_INPUT();
u32 uiTmp = 0;
for (u32 i=0; i<8; i++)
{
uiTmp <<= 1;
SCL_1();
IIC_DelaySclHigh();
vu32 uiSda = SDA_VALUE;
SCL_0();
if (uiSda)
{
uiTmp++;
}
IIC_DelaySclLow();
}
//___________________________________
// 主机应答从机
SDA_OUTPUT();
SDA_OUT(bAck);
IIC_DelaySda();
SCL_1();
IIC_DelayStd();
SCL_0();
IIC_DelayStd();
SDA_1(); // 接收结束时GPIO_I2C_SDA=1
return uiTmp;
}
ayb_ice 发表于 2018-9-3 08:56
一看时序不够严谨,正常操作时SCL为低,时序开始先高后低,结束时就为低
再有,一般先输出数据再转为输出 ...
一般先输出数据再转为输出模式,可以避免出现错误脉冲信号,这句话,能不能给个程序例子?方便理解 chen~chen 发表于 2018-9-5 16:42
一般先输出数据再转为输出模式,可以避免出现错误脉冲信号,这句话,能不能给个程序例子?方便理解 ...
上面的代码不是有吗
// 主机应答从机
SDA_OUTPUT();
SDA_OUT(bAck); ayb_ice 发表于 2018-9-5 16:44
上面的代码不是有吗
// 主机应答从机
这个不是先转为输出模式,在输出数据吗? chen~chen 发表于 2018-9-5 17:03
这个不是先转为输出模式,在输出数据吗?
确实搞错了,其实不影响结果,但波形会好看些
这个代码是先输出数据,再切换模式
void i2c_start(void)
{
SCL_0();
i2c_delay_std();
SDA_1();
SDA_OUTPUT();
SDA_1();
i2c_delay_std();
SCL_1();
i2c_delay_std();
SDA_0();
i2c_delay_std();
SCL_0(); // SCL = 0, SDA = 1
i2c_delay_std();
SDA_1();
} 本帖最后由 chen~chen 于 2018-9-5 17:20 编辑
ayb_ice 发表于 2018-9-5 17:07
确实搞错了,其实不影响结果,但波形会好看些
这个代码是先输出数据,再切换模式
谢谢,这种其实不是先输出数据。应是先设置电平,然后再设定为输出模式,避免了脉冲上升时间,波形会好看。要不然,你都可以输出数据了,再设置成输出模式,有什么意义呢?我曾经看到一个程序有这样的写法。
chen~chen 发表于 2018-9-5 17:18
谢谢,这种其实不是先输出数据。应是先设置电平,然后再设定为输出模式,避免了脉冲上升时间,波形会好看 ...
理解正确 chen~chen 发表于 2018-9-5 17:18
谢谢,这种其实不是先输出数据。应是先设置电平,然后再设定为输出模式,避免了脉冲上升时间,波形会好看 ...
理解正确
页:
[1]