cancel
Showing results for 
Search instead for 
Did you mean: 

I2C problems

brj
Associate II
Posted on January 28, 2009 at 11:49

I2C problems

21 REPLIES 21
brj
Associate II
Posted on May 17, 2011 at 12:30

Hello All !

I have been trying to perform read/write operations on a 24C64 (16 bit internal addressing) eeprom via I2C. At first i built my own interrupt driven driver, but was having a lot of problems with interrupts coming in strange timing and locking the I2C peripheral, so I tried using the I2C example 5 instead (of course modified to support 16 bit internal addressing).

However here I saw that the pulse timing for SDA/SCL added an additional clock pulses between receiving data and before the stop condition was sent. I cant explain why they are there ?? and I also suspect this is what makes the peripheral hang.

I have attached a scope picture showing the entire Read 1 byte operation.

Sequence explained:

1. Send Start

2. Send device adr 0xA2 with write transmission bit

3. Send eeprom MSB address 0x00

4. Send eeprom LSB address 0x00

5. Resend Start

6. Send Device adr 0xA2 with read bit

7. Receive data 0x44, master NACK's this to terminate further reading

8. Additional 9 clocks are issued (from the master)

9. Stop condition

This is not normal I2C behaviour as far as I know ?

I was also having additional clocks when using interrupts.

In that case the first byte was read, but starting a new read sequence would make the appl loop between interrupts an my state machine. If I made my JLINK break somewhere in the loop and then restarted it it would suddenly complete the sequence and return valid data ??

I tried both I2C1 and I2C2 with the same result.

Has anyone here seen this kind of behaviour ?

Best regards

Brian

brj
Associate II
Posted on May 17, 2011 at 12:30

Found a workaround ! After each Read/Write operation perform

/* Work around !!!!! Deinitialize and reinitialize I2C2 peripheral */

I2C_DeInit(I2C_DEVICE);

ReInitI2cDriver();

This does not remove the extra clock pulses, and as such not an acceptable solution. But one can get on with the project ....

Also is there a way to clear the interrupt flags, they are not cleared automatically when executing the ISR. E.g if the AF (ack failed) bit is set it will continuosly jump to the ISR, if you dont Disable interrupts and reinitialize the I2C peripheral.

/BRJ

lanchon
Associate III
Posted on May 17, 2011 at 12:30

hi,

a while back I used the I2C. unfortunately the ref manual on the I2C is more like a bunch of howtos than a description of what the hardware is. but reading the manual and the examples gave me an idea of what the hardware was and where it wasn't being used correctly. I remember a few things:

I remember a couple of bugs in the code you mention, one involving the positioning of the stop.

I remember I didn't like the way of doing things of the firmware library, which was on the whole different from what the ref man recommended, so I wrote my own driver. it's posted here somewhere. it's limited to what I needed: master mode using 7-bit addressing, polled and blocking. I've used it for many millions of transactions without any problems.

I also remember that the macrocell has a design flaw that causes the need to provide hard realtime handling of the I2C events in order to receive data correctly in the general case. it has to do with the configuration for ACK generation on receive not being shadow-buffered, and goes something like this...

if you can't guarantee the processing of receptions withing an I2C byte timeframe (you shouldn't need to since I2C provides flow control), then:

-you can safely drive a transaction in which you receive a single byte.

-probably you can safely receive 3 bytes or more, provided that you stall the transaction for a moment just before the last byte, which is not optimal.

-you can't safely receive two bytes; there's no way to ACK the first but not the second unless your code is given realtime guarantees. (this is because you can't stall reception just before the second byte.)

since my driver could be preempted by higher priority processes at any time (think interrupts if you're not using processes) I decided to limit receptions to a single byte, since that was all I needed.

support for 3 or more bytes with stalling could be added. or if execution time is guaranteed, support for 2 or more bytes without stalling could be added. it could also be used as a base for interrupt driven code.

brj
Associate II
Posted on May 17, 2011 at 12:30

hi Lanchon,

Thanks for your insight.

Well I too am seing the same problem reading 2 bytes, where the second is also ACK'ed (should have been NACK'ed). After the second byte i still see 18 clock pulses and data is being NACK'ed. The two bytes I want are correct.

But I guess playing safe is better, and limiting reading only 1 byte in a read sequence.

What is ST's opinion on this issue ??

lanchon
Associate III
Posted on May 17, 2011 at 12:30

I haven't contacted them.

I think the extra byte you get is a problem from the demo software. try my code if you want to check (it's c++ but there's a build environment posted here, or convert it, it's really very short).

the two byte problem can be solved if you commit to answer the interrupts promptly.

lanchon
Associate III
Posted on May 17, 2011 at 12:30

but I didn't check that there was a problem with two bytes, I just guessed there would be because in my view there's no safe way to obtain the behavior.

brj
Associate II
Posted on May 17, 2011 at 12:30

I will have a look at your code.

My driver is interrupt driven, and I still get the extra byte.

viktor3
Associate II
Posted on May 17, 2011 at 12:30

With this routine you don´t get extra pulses :

#define EEPROM_ADDRESS 0xA0

s16 rd_i2c_n(I2C_TypeDef *I2C, u8* buffer, u8 addr, u16 len)

{

I2C->CR1 |= (1<

while(!check_event(I2C, 0x00030001));

if(len == 1) I2C->CR1 &= ~(1<

else I2C->CR1 |= (1<

I2C->DR = EEPROM_ADDRESS;

while(!check_event(I2C, 0x00070082));

I2C->DR = addr;

while(!check_event(I2C, 0x00070084));

I2C->CR1 |= (1<

while(!check_event(I2C, 0x00030001));

I2C->DR = EEPROM_ADDRESS | 1;

while(!check_event(I2C, 0x00030002));

while(len)

{

if(len == 1) I2C->CR1 |= (1<

while(!check_event(I2C, 0x00030040));

if(len == 2) I2C->CR1 &= ~(1<

*buffer++ = I2C->DR;

len--;

}

return 0;

}

nikhil23
Associate II
Posted on May 17, 2011 at 12:30

i am working with I2c eprom 24c16 type, at first i am using example5 of ST 's i2c examples.

But what happening is i cannot able to write on two consecutive steps using I2C_EE_BufferWrite function. it hangs after writing for the first time.

but what is strange is if on next i read eprom using I2C_EE_BufferRead function and wait for I2C_EE_WaitEepromStandbyState i works perfect. but in the application say i have to log data in two consecutive steps that will not work....

(i am not using interrupts)

what can be possible bug?