在调用Rt-thread的i2c驱动(i2c_bit_ops.c)去实现gpio模拟i2c的过程,在读过程出现了毛刺现象,该毛刺出现在数据位的第8bit后,现通过修改i2c_bit_ops.c源码解决毛刺问题。
写操作:
将i2c_bit_ops.c中i2c_waitack的API进行如下修改,即可消除写操作过程中的毛刺,原因很简单,这里就不在叙述。
原版API代码:
rt_inline rt_bool_t i2c_waitack(struct rt_i2c_bit_ops *ops)
{
rt_bool_t ack;
SDA_H(ops);
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
bit_dbg("wait ack timeout\n");
return -RT_ETIMEOUT;
}
ack = !GET_SDA(ops); /* ACK : SDA pin is pulled low */
bit_dbg("%s\n", ack ? "ACK" : "NACK");
SCL_L(ops);
return ack;
}
修改后:
rt_inline rt_bool_t i2c_waitack(struct rt_i2c_bit_ops *ops)
{
rt_bool_t ack;
//SDA_H(ops); /*修改处*/
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
bit_dbg("wait ack timeout\n");
return -RT_ETIMEOUT;
}
ack = !GET_SDA(ops); /* ACK : SDA pin is pulled low */
bit_dbg("%s\n", ack ? "ACK" : "NACK");
SDA_L(ops); /*修改处*/
SCL_L(ops);
return ack;
}
读操作时:
会在第8bit后出现毛刺现象,因为该过程,主机在等待从机发送ACK或者NACK(最后一个byte),此时,总线处于无人控制阶段,当第8bit为0时,总线将自动去拉高,从而产生毛刺。针对此现象采取操作如下:
读第8bit时若获取SDA数据为0,则直接拉低SDA总线,如果数据为1,则不进行操作,但是这样操作会造成,在下一次写时,出现写失败现象,SDA总线不听使唤,从而导致通信失败,原因如下:
最后一个byte回复NACK时并未对SDA进行拉高,因为原先Rt-thread i2c驱动认为一个byte时,不对SDA进行操作,SDA将自动拉高,但是由于我们提前去拉低了SDA总线,这将导致SDA不会自动去拉高,所以必须手动去拉高,主要修改i2c_readb 与i2c_send_ack_or_nack这两个API,修改如下:
原版代码如下:
static rt_int32_t i2c_readb(struct rt_i2c_bus_device *bus)
{
rt_uint8_t i;
rt_uint8_t data = 0;
struct rt_i2c_bit_ops *ops = bus->priv;
SDA_H(ops);
i2c_delay(ops);
for (i = 0; i < 8; i++)
{
data <<= 1;
if (SCL_H(ops) < 0)
{
bit_dbg("i2c_readb: wait scl pin high "
"timeout at bit %d\n", 7 - i);
return -RT_ETIMEOUT;
}
if (GET_SDA(ops))
data |= 1;
SCL_L(ops);
i2c_delay2(ops);
}
return data;
}
static rt_err_t i2c_send_ack_or_nack(struct rt_i2c_bus_device *bus, int ack)
{
struct rt_i2c_bit_ops *ops = bus->priv;
if (ack)
SET_SDA(ops, 0);
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
bit_dbg("ACK or NACK timeout\n");
return -RT_ETIMEOUT;
}
SCL_L(ops);
return RT_EOK;
}
修改后如下:
static rt_int32_t i2c_readb(struct rt_i2c_bus_device *bus)
{
rt_uint8_t i;
rt_uint8_t data = 0;
struct rt_i2c_bit_ops *ops = bus->priv;
SDA_H(ops);
i2c_delay(ops);
for (i = 0; i < 8; i++)
{
data <<= 1;
if (SCL_H(ops) < 0)
{
bit_dbg("i2c_readb: wait scl pin high "
"timeout at bit %d\n", 7 - i);
return -RT_ETIMEOUT;
}
if(i != 7)
{
if (GET_SDA(ops))
data |= 1;
}
else
{
if (GET_SDA(ops))
data |= 1;
else
SDA_L(ops);
}
SCL_L(ops);
i2c_delay2(ops);
}
return data;
}
static rt_err_t i2c_send_ack_or_nack(struct rt_i2c_bus_device *bus, int ack)
{
struct rt_i2c_bit_ops *ops = bus->priv;
if (ack)
SET_SDA(ops, 0);
else
SDA_H(ops);
i2c_delay(ops);
if (SCL_H(ops) < 0)
{
bit_dbg("ACK or NACK timeout\n");
return -RT_ETIMEOUT;
}
SCL_L(ops);
return RT_EOK;
}