I2C Library Problem

17 Aug 2011

In an infinite loop in the main function I am continually reading four bytes from a M24C64-W I2C EEPROM using pins P27 and P28. It seems like some of the reads send a third byte with the address and it is always 80H. Here is my code:

// address in big-endian format
Buffer[0] = (Address >> 8) & 0xFF;
Buffer[1] = Address & 0xFF;
    
while(eeprom.write(EEPROMAddress, Buffer, 2, true));
eeprom.read(EEPROMAddress, Data, Length, false);

As you can see this writes two bytes (the address) to the EEPROM, then sends a repeated start, then reads a set of bytes from the device.

Here is what happens some of the time:

/media/uploads/ajayre/i2cproblem.png

It shows the address (0x0C00) and instead of the repeated start the microcontroller sends a third byte. Fortunately I have the writing disabled on the EEPROM so it NACKs the byte, but this causes the read to fail.

Here is a capture of the previous read of this same address, which was successful and occurred 388.58us before the failed read:

/media/uploads/ajayre/i2cok.png

As you can see the 16-bit address 0x0C00 is sent and then the four bytes 0x06, 0x00, 0x00, 0x00 are successfully read.

Any ideas what the problem might be? Is the source code to the I2C library available somewhere?

thanks, Andy

13 Aug 2011

I tried the v29 beta version of the mbed library. There were no warnings or errors during compilation and linking, but here is the result:

/media/uploads/ajayre/i2cv29beta.png

I2C appears completely broken. Switching back to v28 of the mbed library and it's back to working (albeit with the problem I described above).

Also when adding the beta library there is no way of expanding it and seeing the descriptions of the modules.

Andy

13 Aug 2011

Has the 'Length' variable been initialised correctly? The read function may behave strangely when 'Length' is 0 or negative. What seems to be an additional write of 80h could be a failed read() operation. Maybe 'Length' becomes invalid after the first succesful read(). Try setting Length to the correct value just before the read().

Wim

13 Aug 2011

Thanks for the great suggestion. I was full of hope but unfortunately it didn't help. I changed my code to:

while(eeprom.write(EEPROMAddress, Buffer, 2, true));
eeprom.read(EEPROMAddress, Data, 4, false);

And it still sends an extra write byte instead of the repeated start.

Andy

14 Aug 2011

Hi Andy,

Thanks for the great explanation of your problem.

I notice your write function is in a while loop, presumably to retry if the write fails? It might be worth seeing if it is a repeated call to write that is causing this.

We'll also see if we can reproduce it our end and get you an explanation if you don't get there first, or a fix if this is something to do with transaction failures and repeated start combinations not being handled properly.

Simon

14 Aug 2011

Simon,

Yes, the while loop is to poll for completion of any previous actual write. My test code only has a single actual write to the EEPROM. Then it reads like crazy in a while loop. If I space out the read (with "write" of address) by 200us I still see the same issue.

Andy

17 Aug 2011

Hi Andy,

I'll get someone on it now to try and reproduce the problem, ideally by building the same setup; if we have the part, hopefully it won't take too long (but we may have to put one on order, or ask you to do some tests if not).

Simon

17 Aug 2011

I think it should be possible to reproduce the problem by using another mbed to emulate the EEPROM - the issue is in the master so it would just need to mimic reading/writing of data.

Andy

17 Aug 2011

Hi Andy! May I have the datasheet of the EEPROM?

17 Aug 2011
17 Aug 2011

Hi Andy,

Can you try to write a data in your write operation to see if there is also the problem ? Perhaps the chip is waiting for a data after 0x50 0x0c 0x00.

17 Aug 2011

Hi Andy,

You say you are using the 24C64, but unless it is actually a 24C64-Wxx or a 24C64-Txx it is not rated to work at 3.3V

17 Aug 2011

It's a -W and I have it connected to the 3.3V regulated output of the mbed.

The master is supposed to send a repeated start after writing 0x50, 0x0C, 0x00 but it doesn't - it sends another byte of data from somewhere unknown. Write operations appear to work fine.

Note that I have ported my code to use the I2C driver library from NXP (using Keil uVision) and it works fine. I believe that eliminates any hardware issues.

Andy

17 Aug 2011

I'm using a 24LC01 EEPROM on I2C port 2. This worked fine using the mbed library functions of January 2011, but not with the library of June or July 2011.

I found the required pin function were either not being set, or were being overwritten by some other initialization code. I added a couple of lines to fix this that ran after all other initialization had been done and the EEPROM functions worked.

I2C i2c(P0_10, P0_11); sda, scl I2C2

LPC_PINCON->PINSEL0 = 0x00A00050

void I2C2_init(void) { LPC_PINCON->PINSEL0 |= (1<<21); LPC_PINCON->PINSEL0 |= (1<<23); }

18 Aug 2011

Hi Andy,

After some tests with this chip: 24lc256, the code below is working well. It's not the same chip as yours but the characteristics are the same.

#include "mbed.h"

Serial pc(USBTX, USBRX);
I2C eeprom(p28, p27);

#define EEPROMAddress  0xa0
#define Address  0x0000

char Buffer[2];
char Data[4];


int main() {
    pc.printf("\x1B[2J");
    pc.printf("\x1B[H");
    pc.printf("hello!\r\n");
    Buffer[0] = (Address >> 8) & 0xFF;
    Buffer[1] = Address & 0xFF;
    while (1) {
        if (eeprom.write(EEPROMAddress, Buffer, 2, true))
            pc.printf("fail\r\n");
        eeprom.read(EEPROMAddress, Data, 4, false);
        //pc.printf("Data: %d %d %d %d\r\n", Data[0], Data[1], Data[2], Data[3]);
    }
}

There is nothing special concerning the hardware part except the two pull-up resistors on sda and scl.

During the last two hours, the program was running without fail (even the v30 beta library).

You can see on this capture that all is ok: /media/uploads/samux/eeprom.png

You said that the problem appears randomly. Can you try to see if there is a specific procedure which causes the problem ? Have you tried with another chip ? I have ordered exactly the same chip as yours but I have not received it yet...

18 Aug 2011

^nice program there

what software are you using? and hardware?

18 Aug 2011

I'm don't think your test proves that the problem I described isn't occurring. I only saw it when I read back what was previously written and compared it, while keeping the /WC pin low during reads If the compare failed I raised an output pin which I used to trigger my logic analyser. I never checked for errors returned from the eeprom.read or eeprom.write functions.

As I explained in my original post the I2C library sends a third byte sometimes after writing of the address. If you look at the datasheet for the device this translates into a write of one byte into the EEPROM, corrupting the contents there. Therefore you must look for evidence of data corruption and data corruption can only happen when /WC is low. The value written seems to always be 80H.

As I stated in an earlier post, my hardware works fine with the NXP I2C driver library when compiled using Keil RealView and uVision, so I don't see how it can be a hardware issue.

I don't know how often the corrupted write operation occurred, but it always occurred within seconds of starting the firmware.

Andy

18 Aug 2011

Hi Andy,

Can you post all your code to see if I can reproduce your problem ?

19 Aug 2011

Code sent. Here is the schematic diagram.

/media/uploads/ajayre/eepromschematic.png

Andy

19 Aug 2011

Hi Andy,

I did a test with this program:

#include "mbed.h"

Serial pc(USBTX, USBRX);
I2C eeprom(p28, p27);
DigitalOut trigger(p22);
DigitalOut w_dis(p21);

#define EEPROMAddress  0xa0

char Buffer[2];
char Data[4];
char write[3];
int Address = 0;

char data_to_write = 0xbb;


int main() {
    trigger = 0;
    w_dis = 1;
    
    
    pc.printf("\x1B[2J");
    pc.printf("\x1B[H");
    pc.printf("hello!\r\n");
    
    while (1) {
    
    
        Buffer[0] = (Address >> 8) & 0xFF;
        Buffer[1] = Address & 0xFF;
    
        write[0] = (Address >> 8) & 0xFF;
        write[1] = Address & 0xFF;
        write[2] = data_to_write;
    
        //write 
        w_dis = 0;
        while(eeprom.write(EEPROMAddress, write, 3, false));
        
        
        //read at the same address
        w_dis = 1;
        while(eeprom.write(EEPROMAddress, Buffer, 2, true));
        while(eeprom.read(EEPROMAddress, Data, 4, false));
        
        //test if the value read is the value written
        if(Data[0] != write[2])
        {
            pc.printf("echec\r\n");
            trigger = 1;
            while(1);
        }
        
        //change address and data
        Address += 10;
        data_to_write += 5;
    }
}

This is working fine for me. Can you try this code ?

19 Aug 2011

Again, sorry, but I don't think that reproduces what I was doing. I performed a single write (as I mentioned earlier) and then read as fast as possible inside a while loop. It could be that the first read after a write always works, but perhaps the 100th (for example) read after a write fails.

The example code I sent you demonstrated this arrangement.

You need to test with v28 of the mbed library otherwise you won't be able to tell the difference between not being able to reproduce it or if it was already fixed in v30 of the library.

Finally correct me if I am wrong, but your code appears to disable writing in the EEPROM during the read sequence (w_dis = 1). You are looking for read operations that are being converted into write operations when they shouldn't be. If /WC is high then you will never see the issue... Notice how I have tied /WC low in my schematic diagram.

Andy

19 Aug 2011

Hi,

Ok I am doing several writings... It's not exactly what you are doing.

Quote:

Finally correct me if I am wrong, but your code appears to disable writing in the EEPROM during the read sequence (w_dis = 1). You are looking for read operations that are being converted into write operations when they shouldn't be. If /WC is high then you will never see the issue... Notice how I have tied /WC low in my schematic diagram.

You are doing this in your code... on p26. But on the schematic, this pin is not connected. But the behaviour is the same if I connect this pin to the ground.

In your code there are these two lines:

    while(eeprom.write(EEPROMAddress, Buffer, 2, true))
    eeprom.read(EEPROMAddress, Data, Length, false);

It seems to me that there is a ";" missing at the end of the while.

Finally, I took your code (with your mbed library: 28) and I have added a ";" after the while. First, I have reduced the frequency at 100KHz. There is no problem at this frequency. Until 300KHz there is no problem for me. After, I observe this third byte 0x80...

I did a test program with your two "key functions" to write or read the eeprom and it works for me even at 400kHz.

#include "mbed.h"

Serial pc(USBTX, USBRX);
I2C eeprom(p28, p27);
DigitalOut trigger(p22);
DigitalOut w_dis(p21);

#define EEPROMAddress  0xa0
#define uint32 unsigned long
#define uint16 unsigned short
#define byte char

void Read( uint16 Address, byte * Data, int length);
void Write( uint16 Address, byte * Data, int length);

char Buffer[2];
char Data[4];


void Read (
    uint16 Address,                                        // address to start reading from
    byte *Data,                                            // location to store data
    int Length                                             // number of bytes to read
    )
{
    // address in big-endian format
    Buffer[0] = (Address >> 8) & 0xFF;
    Buffer[1] = Address & 0xFF;
    
    while(eeprom.write(EEPROMAddress, Buffer, 2, true));
    eeprom.read(EEPROMAddress, Data, Length, false);
}


void Write
    (
    uint16 Address,                                        // address to start writing to
    byte *Data,                                            // data to write
    int Length                                             // number of bytes to write
    )
{
    // enable writing
    //w_dis = 0;

    // address in big-endian format
    Buffer[0] = (Address >> 8) & 0xFF;
    Buffer[1] = Address & 0xFF;

    // data
    for (int i = 0; i < Length; i++)
    {
        Buffer[2 + i] = Data[i];
    }
    
    // write
    while(eeprom.write(EEPROMAddress, Buffer, Length + 2, false));

    // disable writing
    //w_dis = 1;
}


int main() {

    char write0[4] = {0x02, 0x11, 0x78, 0x05};
    char write1[4] = {0x06, 0xaa, 0xe4, 0xae};
    char write2[4] = {0x08, 0x1d, 0x78, 0x59};

    pc.printf("\x1B[2J");
    pc.printf("\x1B[H");
    pc.printf("hello!\r\n");

    trigger = 0;
    
    eeprom.frequency(400000);    
    
    Write(100, write0, 4);
    Write(200, write1, 4);
    Write(300, write2, 4);
    
    while (1) {
    
        Read(100, Data, 4);
        for(int i = 0; i < 4; i++)
            if(Data[i] != write0[i])
            {
                trigger = 1;
                pc.printf("fail1\r\n");
                while(1);
            }
        
        
        Read(200, Data, 4);
        for(int i = 0; i < 4; i++)
            if(Data[i] != write1[i])
            {
                trigger = 1;
                pc.printf("fail2\r\n");
                while(1);
            }
            
        Read(300, Data, 4);
        for(int i = 0; i < 4; i++)
            if(Data[i] != write2[i])
            {
                trigger = 1;
                pc.printf("fail3\r\n");
                while(1);
            }
    }
}

This code is exactly what you are doing. 3 writings and then I read as fast as I can.

19 Aug 2011

In my code I added the EEPROM write disable as a workaround for this issue. I should have given you an older revision, sorry.

Samuel Mokrani wrote:

Finally, I took your code (with your mbed library: 28) and I have added a ";" after the while. First, I have reduced the frequency at 100KHz. There is no problem at this frequency. Until 300KHz there is no problem for me. After, I observe this third byte 0x80...

OK, so you are able to reproduce the problem, but I am still unsure what the cause/solution is. Sorry for being dense.

Could it be the fact that I am using timer 0 and timer 1 interrupts and toggling a pin while using the I2C library at 400kHz?

Andy

19 Aug 2011

I am able to reproduce the problem with your code. With the code in my previous post, there is no problem even at 400kHz. Can you try in your code to reduce the number of pointers and call functions to begin.

Try also my code to see if it works with your chip.

19 Aug 2011

Right, I have pinned it down. Your code works on my hardware once I change the w_dis pin to p26. However, I can then make it break in the way I described in my first post by added the following interrupt handler:

// timer 1 interrupt
// called every 18ms
extern "C" void TIMER1_IRQHandler()
{
    // MR0 match
    if (LPC_TIM1->IR & 1)
    {
        // clear interrupt
        LPC_TIM1->IR |= 1;
    }
}

and then inserting the following after the line that sets the EEPROM frequency to 400kHz:

// enable power to timer 1
LPC_SC->PCONP |= (1UL << 2);
// timer 1 clock = cclk/4
LPC_SC->PCLKSEL0 &= ~(3UL << 4);
// set up timer 1 interrupt
NVIC_SetPriority(TIMER1_IRQn, 1);
NVIC_EnableIRQ(TIMER1_IRQn);
// pclk = 24MHz, divide down to 1kHz
LPC_TIM1->PR = 24000 - 1;
// match register to generate an interrupt every 18 ms
LPC_TIM1->MR0 = 18 - 1;
// interrupt and reset on match 0
LPC_TIM1->MCR = 3;
// timer mode
LPC_TIM1->CTCR = 0;
// start timer 1
LPC_TIM1->TCR = 1;

My guess is that the mbed I2C library when running at 400kHz is somehow upset by the timer interrupt executing.

Andy

19 Aug 2011

Have you tried with the Ticker class of the mbed library to execute a function every 18ms ?

Is the problem appear at the beginning of the program or there are some successful readings/writings before ? Can you try in your interrupt handler to change the value of a gpio and to see the state of this gpio when a reading is not correct.

Is your program working with a lower frequency ?

19 Aug 2011

Why is the I2C library not compatible with the timer 1 interrupt? The NXP example I2C driver doesn't use a timer at all.

I don't want to use a slower frequency with I2C. Is there a limitation in the mbed library that stops 400kHz from working reliably?

As I am no longer using the mbed compiler and library I think I will leave it at this point. You now have a small program that reproduces the issue and hopefully it will be possible for you to get to the bottom of it. I don't think there is much more for me to add at this stage as I don't know what is going on inside the I2C library. Thanks!!

Andy