I2c bit banging使用C编程

时间:2014-07-23 16:59:38

标签: c i2c

我正在尝试使用bit banging为I2c编写c代码。我修改了维基代码(http://en.wikipedia.org/wiki/I%C2%B2C)。但我无法得到结果。根据我的理解,维基中的代码不正确。我做了很多改变,但是我所做的一个重大改变,wiki失败告诉了正确的标记/标记该行与/ TBN /。我的代码如下,  //必须定制的特定于硬件的支持功能:

#define I2CSPEED 135
#define SCL P0_0
#define SDA P0_1

void I2C_delay() { volatile int v; int i; for (i=0; i < I2CSPEED/2; i++) v; }
bool read_SCL(void); // Set SCL as input and return current level of line, 0 or 1
bool read_SDA(void); // Set SDA as input and return current level of line, 0 or 1
void clear_SCL(void); // Actively drive SCL signal low
void clear_SDA(void); // Actively drive SDA signal low
void Set_SDA(void);  // Actively drive SDA signal High;
void Set_SCL(void);  // Actively drive SCL signal High

void Set_SCL(void)
{
    //make  P0_0 as OutputPort
    SCL = 1;
}

void Set_SDA(void)
{
    //make  P0_1 as OutputPort
    SDA = 1;
}   

void clear_SCL(void)
{
    //make  P0_0 as OutputPort
    SCL = 0;
}

void clear_SDA(void)
{
    //make  P0_1 as OutputPort
    SDA = 0;
}

bool read_SCL(void)
{
    //make  P0_0 as InputPort
    return SCL;
}

bool read_SDA(void)
{
    //make  P0_0 as InputPort
    return SDA;
}

void i2c_start_cond(void) {

  // set SDA to 1
  Set_SDA();/*TBN*/
  set_SCL();
  // SCL is high, set SDA from 1 to 0.
  I2C_delay();
  clear_SDA();
  I2C_delay();
  I2C_delay();
  clear_SCL();//make SCL Low for data transmission 
  started = true;
}

void i2c_stop_cond(void){
  // set SDA to 0
  clear_SDA();
  I2C_delay();
  // SCL is high, set SDA from 0 to 1
  Set_SCL();/*TBN*/
  I2C_delay();
  Set_SDA();
}

// Write a bit to I2C bus
void i2c_write_bit(bool bit) {
  if (bit) {
    Set_SDA();/*TBN*/
  } else {
    clear_SDA();
  }
  I2C_delay();
  clear_SCL();
}

// Read a bit from I2C bus
bool i2c_read_bit(void) {
  bool bit;
  // Let the slave drive data
  read_SDA();
  I2C_delay();
  // SCL is high, now data is valid
  bit = read_SDA();
  I2C_delay();
  clear_SCL();
  return bit;
}

// Write a byte to I2C bus. Return 0 if ack by the slave.
bool i2c_write_byte(bool send_start,
                    bool send_stop,
                    unsigned char byte) {
  unsigned bit;
  bool nack;
  if (send_start) {
    i2c_start_cond();
  }
  for (bit = 0; bit < 8; bit++) {
    i2c_write_bit((byte & 0x80) != 0);
    byte <<= 1;
  }
  nack = i2c_read_bit();
  if (send_stop) {
    i2c_stop_cond();
  }
  return nack;
}

// Read a byte from I2C bus
unsigned char i2c_read_byte(bool nack, bool send_stop) {
  unsigned char byte = 0;
  unsigned bit;
  for (bit = 0; bit < 8; bit++) {
    byte = (byte << 1) | i2c_read_bit();
  }
  i2c_write_bit(nack);
  if (send_stop) {
    i2c_stop_cond();
  }
  return byte;
}

此代码适用于总线中的一个主控。我请专家审查我的代码并让我知道我的错误。

4 个答案:

答案 0 :(得分:9)

评论中有很多错误信息。

首先,你的read_bit()函数永远不会切换时钟。这可能是你的问题,以及@ user3629249的评论,即主设备在从设备每8位发送一个ACK位之后。您必须在read_byte()函数中解决此问题。

第二:I2C不关心时钟抖动;它只定义了时钟下降时数据必须稳定。在时钟边缘的任何一侧都会有一些纳秒,数据不得改变,但这不是问题。 I2C奴隶不会锁定&#34;到了时钟。实际上,I2C规范没有定义高或低SCL时间的下限。你可以想象在时钟滴答之间等待数天或数年。 SMB确实定义了超时,但这也不是你的问题。

最后:过采样并不适用于I2C。您可以读取该位几次以确保它没有被更改,但正常运行的I2C从器件将不会更改数据,直到SDA信号的上升沿之后的某个时间,距离的数字为数千纳秒。关键的下降边缘。 I2C与通常的串行端口/ UART不同步。

答案 1 :(得分:2)

第一位敲击I2C比比特敲击SPI更复杂。 所以首先我会像25AA1024那样使用SPI EEPROM并尝试我的位冲击技能。

然后尝试像这样的简单的Adadfruit I2C设备 https://www.adafruit.com/products/1855 它只需要I2C输出,您可以轻松地将Arduino代码移植到C或C#。

我知道USB设备Nusbio实现了用C#编写的I2C和SPI位冲击。

https://github.com/madeintheusb/Nusbio.Samples

上查看他们的源代码

答案 2 :(得分:0)

您需要以某种方式实施P0_0P0_1。你标记的电话最终涉及他们,我没有在你的代码中看到它们。

如果您是在真实硬件上进行开发,那么为了影响硬件中的相应引脚,您需要实现P0_0和P0_1宏,代码访问特定控制寄存器以控制这两行的逻辑电平。

答案 3 :(得分:-1)

  

我为我的专用应用程序删除了维基百科代码,   但是我在修复Stop条件后才开始工作。它永远不会   提升SCL所以我在该函数中添加了以下内容:

 // Stop bit setup time, minimum 4us
  set_SCL();  // added this line
  I2C_delay();
  

我现在正在验证正确性,如果是,我将更新维基百科   自己。