2

I am having odd behavior with Energia's Wire library, so I figured that I would attempt to do i2c myself.

I'm writing my own (bit-bang) implementation if i2c and keeping it pretty simple. For now, I just want to detect a particular i2c device. Here's my basic algorithm.

  1. Start the message by driving SDA from High to Low while SCL stays high.
  2. Send 7 bit address one bit at a time by driving SDA to high or low while SCL is low and then changing SCL to high so the slave device will read each bit.
  3. Send 1 bit write or read.
  4. Receive Ack from the slave.
  5. End the message by driving SDA from Low to High while SCL stays high.

I know the above may be a bit oversimplified, but I just wanted to get things in context. I'm having trouble with step 4 and knowing the particular timing as when to release and re-aquire control of the SDA line during an Ack.

So, after the last read/write bit has been set on SDA and the SCL set to HIGH, I need to know what the order of operations is in order to get an Ack from the slave.

I've tried every combination I can think of, but it doesn't seem to be working properly. It would seem that I should do the following:

setMode(SDA, INPUT);                //Release the SDA line to the slave device
digitalWrite(SCL,LOW);              //Pulse the SCL line
digitalWrite(SCL,HIGH);
int ack = digitalRead(SDA) == LOW;  //Read from the SDA line
setMode(SDA, OUTPUT);               //Seize control of the SDA line

The sequence above doesn't seem to be working. My digital analyzer isn't recognizing my communications as it should.

Any corrections in my assumptions or adjustments to the code above would be greatly helpful.

For Further Clarification:

I'm using a development environment called 'Energia'. It is a fork of Arduino and is used to 'simplify' the process of programming of TI MSP430x processors. Anyway, it doesn't seem to have any concept of 'OpenCollector' mode.

It's either Input, Output, or Input_Pullup. I'm wondering if Input_Pullup isn't their version of OpenCollector mode. I'll try that.. There is an interesting discussion of Input, Output, Input_Pullup here:

Energia Constants

Curtis
  • 341
  • 6
  • 18
  • 2
    You don't "release control of SDA". Both pins must be open-collector, which means your device only drives low. Instead of driving high, it simply goes high-Z, allowing the pull-up resistors to pull the line high, **unless** another device drives low (slave sends ACK). It sounds like you need to read up on I2C. – DoxyLover Jan 13 '17 at 20:23
  • 2
    If, by chance, you can't do open-collector, the alternative is to set the pin output state to low and then switch pin modes between output for 0 and input for 1. – DoxyLover Jan 13 '17 at 20:26
  • What do you mean with "doesn't seem to be working"? You need to show an oscilloscope trace. – CL. Jan 13 '17 at 20:37
  • So I guess digitalWrite(SDA, LOW); tells my app that I want the pin to be low when I'm controlling it. pinMode(SDA, OUTPUT) is me taking control of the pin (forcing it to low), and pinMode(SDA, INPUT) is me releasing control of the pin (allowing to to go to HIGH via the pull-up resisters) ? I'll give that a shot. Thanks DoxyLover !!! Put your suggestion as an answer and if it works, I'll mark it as THE correct answer!! – Curtis Jan 13 '17 at 21:48

2 Answers2

2

Most of your problem is probably going to be due to the fact that you are setting these lines high instead of letting them float. SDA and SCL go high because there is a resistor pulling them up to +V, not because they are driven high. Many people don't realize that the clock line is also intended to be operated this way.

There's something else about the way you describe your logic that makes me think that while you see SCL transition to low as the event that latches the data, you seem to think that you return SCL to high afterwards. I encourage you to think of the clock event more like this:

  • set up the data
  • raise the clock
  • wait an appropriate bit time
  • lower the clock

The way you are doing it, you can get into trouble in the following way:

  • you write SDA--let's say it's low.
  • you raise the clock, so SCL is now high
  • you release SDA because you want to read it.
  • SDA goes high if there's no ACK present

Surprise! You just created a STOP condition without realizing it.

gbarry
  • 8,615
  • 21
  • 31
  • Yes, that's exactly what's happening, according to my digital analyzer... I get to the ACK bit and then I see a stop and a start happen. – Curtis Jan 13 '17 at 21:49
  • 1
    If none of the slaves on an I2C bus use clock stretching, there isn't really any downside to having the master drive the clock actively in both directions. Of course, if slaves do use clock stretching, the master will not only need to use a passive pullup when releasing the clock, but also wait for the clock to actually go high every time it's released, *and* allow for the fact that slave devices aren't required to put valid data on SDA until they're going to release SCL. – supercat Jan 13 '17 at 21:54
  • 1
    Those who drive SCL tend to "learn" they can always do it, especially if they only use EEPROMs, temp sensors, and anything without a microcontroller inside. Then, one day, they find out otherwise! So, I like to remind people what the original intent was. – gbarry Jan 14 '17 at 01:39
1

Whenever a bit is transmitted, the writer drives SDA (or not) immediately after SCL goes low, and the reader reads the state of SDA when SCL goes high. (SCL going high might be delayed by the slave with clock stretching; you have to wait for SCL actually going high.)

Your code must pulse SCL for long enough so that the slave has enough time to set the value (at least 4.7 µs).

Switching SDA to output mode might pull it low if that was the last previous value. This is not allowed while SCL is high (unless you actually want to do a repeated start).

CL.
  • 18,161
  • 5
  • 40
  • 67