# I2C EEPROM bit-banging: Writes fine, but only if first bit is not set

I am currently working on an I2C EEPROM project using bit-banging to drive the SDA and SCL lines.

My read function works fine but whenever I write any byte with a leading "1", I always read FF back; even if the byte has been programmed with something else before. Leading "0" is perfect. It is not my read routine; as I can see on the scope it returns FF.

I am looking for suggestions on why this might be. Is there any obvious I could miss which could cause the problem? [I cannot post the code - company confidential... :(]

Every waveform I look at meets the spec exactly. I am decoupling the EEPROM. My pull ups are 2.2k so within spec. I'm clocking at about 500 Hz in this prototype. The chip is sending ACKs to each of my bytes so it recognises them. But it just doesn't work...

I am using a Microchip 24LC256.

Simplified writing algorithm for one byte:

wait
SDA low
SCL low
wait
for each bit
if bit is set:   SDA high
if bit is unset: SDA low
wait
SCL high
wait
wait
SCL low
wait
wait
SDA high
SCL high
wait
wait
check ACK status
SDA low
SCL low
wait
return ACK status


Simplified reading algorithm for one byte:

wait
SCL low
SDA high
for each bit (8 bits)
SCL high
wait
wait
SCL low
wait
check and store received bit
wait
do a NACK or ACK depending on if it is the last byte

• @Justin - I think he's saying that writing the value 0x7F to any address works, but writing 0x80 to any address doesn't work. – Rocketmagnet Aug 3 '12 at 13:49
• It's stuff like this that makes me hate I2C. – Rocketmagnet Aug 3 '12 at 13:50
• I've got a crazy hunch. In your for each bit code, are you inadvertantly sign-extending with a shift right operation? If you are then your leading one will eventually leave you with a 0xFF after 7 shift operations. – vicatcu Aug 3 '12 at 16:52
• The irony, here, is the "company confidential" code. It is valuable to them. Everyone else here shares code that works. What sets this company's code apart from the others is that it doesn't work. – gbarry Aug 9 '12 at 21:37
• It's hard to imagine why a company so desperately needs to keep some I2C bit banging code confidential. There's so much of it around on the internet. – Rocketmagnet Aug 10 '12 at 11:06

You're reading the data after clock is low again. You'll have to do that between making the clock high and making it low. After the clock is low the slave is allowed to change the data line, not while it's high.

So reading should be like this:

wait
SCL low
SDA high
for each bit (8 bits)
SCL high                      <--------
wait
check and store received bit  <--------
wait
SCL low                       <--------
wait
wait
do a NACK or ACK depending on if it is the last byte

• That's a good point; I'll fix that. However, my data still shows as all-ones (FF) on my scope so my read can't be the problem... :( – Thomas O Aug 3 '12 at 13:04

The problem in the end turned out to be that I was inadvertently sending a STOP condition under some conditions due to mangled timing. I gave up on using the scope and got out the logic analyser, and was able to fix the problem in 15 minutes as it highlighted the STOP which should not have been there. I will choose who to give the bounty to based on the most helpful answer. Thank you for all solutions.

• Glad you resolved it by "verify the write timing" – user11355 Aug 10 '12 at 10:08
• I told you that this would be solved by looking at the waveform. – Rocketmagnet Aug 10 '12 at 11:10
• @Rocketmagnet I have always been looking at the waveform, but had never noticed it before. – Thomas O Aug 10 '12 at 15:40
• Yes, but you did not show us the waveform, despite us repeatedly asking you for it. You could have had this problem solved days ago. – Rocketmagnet Aug 10 '12 at 16:08
• @Rocket - I agree. I wish I had a camera available at the time. The Tek DPO I was using had a floppy drive but no floppy. I WOULD have posted a picture if I could have. – Thomas O Aug 13 '12 at 7:37

OK your scope proves the 1st byte coming in to the PIC is bad so it's not the PIC read function.

Did you verify the write timing is OK at the receiving end?

Does this fail in both modes below?

- Byte mode sequential
- Page mode Sequential


The spec shows "The Most Signiﬁcant Bit (MSB) ‘b7’ is sent ﬁrst" This also is coincident when b7=1 that the entire byte is read back as FF. So either it is not written and only erased (fault condition) when b7=1 , or it reads back bad as FF regardless of the prior content. Since every write is a byte wide erase before write, could it still be a bad write or a bad read or the timing of the 1st byte is different.

Suggestion: Verify the PTC signal during a write /read to ensure normal operation.

There is an option of using an external clock for timing the length of an E/W cycle using PTC. Have you tried to you use this?

tE/W cycle time

• internal oscillator 7ms typ
• external clock 4 ~ 10 ms min~max

Does it pass this criteria?

Sounds like it could be a couple things:

1. What else is on the bus? Could there be a bus contention with another device that is being held in reset or uninitialized?
2. Are you correctly changing the direction of the I/O pin? If it is working fine in the output case, you could have inadvertently forgotten to change the direction of the pin to input and will always read 0xFF. The pin could be left as an output driving the bus while you're to read from it.
3. Do you have internal pull-ups on the pin itself and/or on the I/O lines? Microcontrollers usually give a range of resistance and not a fixed value. You might want to disable the pull-ups on the micro and just use the discrete ones on the bus as you can get a more precise pull-up resistance from discrete components.
4. Clock polarity - Are you sure you're measuring on the right edge/phase between the clock/data? You could be clocking out what looks great to you on the scope, but if the phase is out of line all your EEPROM will see is 0xFFs (and would most likely return the same as it's probably an invalid command/condition).
• 1. Just the EEPROM and MCU. 2. Yes, I believe I am, as the EEPROM is able to hold the SDA/SCL low. 3. There are 2.2k 5% pull ups on the board adjacent to the EEPROM. – Thomas O Aug 3 '12 at 13:36
• for #2, are you sure the EEPROM is the one holding the bus low. Does the EEPROM have any conditions in the datasheet where it will return all 0xFFs? See my edits above too. – Joel B Aug 3 '12 at 13:41
• #4. The EEPROM is "ACK"ing my requests and works with some words, but not all. – Thomas O Aug 3 '12 at 14:51

I submitted this as a comment above, but my confidence in the answer has been quietly growing in the deep recesses of my mind so I'm promoting it to an answer.

I've got a crazy hunch that this is almost certainly a low level software bug relating to signedness of some variables. In your for each bit code, are you inadvertantly sign-extending with a shift right operation? If you are then your leading one will eventually leave you with a 0xFF after 7 shift operations.

Steven alluded to this in a comment, but have you witnessed the sanctity of your write operations on an osilloscope, or do you only presume that they work based on half the read backs looking good? If you haven't tried looking at the write operation of the value 0xAA, that might be a good thing to try.

If you can provide the actual code of your inner loop and associated variable declarations we might be able to spot a bug.

• My writes are good; I can see this on the scope. Another weirdness: Addresses with leading MSB are okay. Only data causes issues! I think about posting the code soon. – Thomas O Aug 6 '12 at 7:43
• In support of this answer: if this is C code, change all 'char' declarations to 'unsigned char' and try again. – Wouter van Ooijen Aug 9 '12 at 19:35