0

PIC: PIC32MX564F128L

EEPROM: 24AA16

I've put together some code for the PIC, in C, to read & write to an external EEPROM, via I2C. When I use these methods to write a single byte, then read it back again for verification, it works. If I change the write location, it works, so I presume the addressing is working, and the byte I read back matches the write, so that appears to work too. All good so far!

I then extended the code to use the same routine 100 times, writing different bytes in adjacent locations. I.E. Write values 0 to 99 in Locations x to x + 99. I then proceeded to read back the 100 locations to verify the writes, and this is where it goes wrong.

It would appear, from various tests, that the write method performs all the writes in the same location, as the read method gets the last value (99) from the first location, and nothing (0xFF) from the other locations!

The scope shows that the clock is good, and the data line toggling, which I expected as the single write works.

Debug on the PIC shows that the addresses being used by the methods are sequential, so I'm a little stumped as to why all the writes are being performed in the same location! I can change this location, but it always just uses the first byte for all values.

Anyone know what could be wrong?

My code is here, and the entry point is testEeprom():

// ----------------------------------------------------------------------------
#include "eeprom.h"
//#define _PLIB_DISABLE_LEGACY
#include <plib.h>
//// ----------------------------------------------------------------------------
#define SYS_CLOCK                   (80000000L)
#define GetSystemClock()            (SYS_CLOCK)
#define GetPeripheralClock()        (SYS_CLOCK / 2)
#define GetInstructionClock()       (SYS_CLOCK)
#define I2C_CLOCK_FREQ              100000

#define EEPROM_I2C_BUS              I2C1
#define EEPROM_ADDRESS              0x50        // 0b1010000 Serial EEPROM address
#define EEPROM_STORAGE_LOCATION     0x0100
#define DATA_ARRAY_SIZE             100
// ----------------------------------------------------------------------------
UINT8               EEPROM_Data_Array[DATA_ARRAY_SIZE];
UINT8               EEPROM_Data_Array_RX[DATA_ARRAY_SIZE];
BOOL                EEPROM_Success;
I2C_7_BIT_ADDRESS   SlaveAddress;
// ----------------------------------------------------------------------------
void i2c_wait(unsigned int cnt)
{
    while (--cnt)
    {
        Nop();
        Nop();
    } // while
}
// ----------------------------------------------------------------------------
BOOL StartTransfer(BOOL restart)
{
    I2C_STATUS status;

    // Send the Start (or Restart) signal
    if (restart)
    {
        I2CRepeatStart(EEPROM_I2C_BUS);
    }
    else
    {
        // Wait for the bus to be idle, then start the transfer
        while (!I2CBusIsIdle(EEPROM_I2C_BUS));

        if (I2CStart(EEPROM_I2C_BUS) != I2C_SUCCESS)
        {
            debugTrace("I2C Error: Bus collision during transfer Start\r\n");
            return FALSE;
        }
    }

    // Wait for the signal to complete
    do
    {
        status = I2CGetStatus(EEPROM_I2C_BUS);
    }
    while (!(status & I2C_START));

    return TRUE;
}
// ----------------------------------------------------------------------------
BOOL TransmitOneByte(UINT8 data)
{
    // Wait for the transmitter to be ready
    while (!I2CTransmitterIsReady(EEPROM_I2C_BUS));

    // Transmit the byte
    if (I2CSendByte(EEPROM_I2C_BUS, data) == I2C_MASTER_BUS_COLLISION)
    {
        debugTrace("I2C Error: I2C Master Bus Collision\r\n");
        return FALSE;
    }

    // Wait for the transmission to finish
    while (!I2CTransmissionHasCompleted(EEPROM_I2C_BUS));

    return TRUE;
}
// ----------------------------------------------------------------------------
void StopTransfer(void)
{
    I2C_STATUS status;

    // Send the Stop signal
    I2CStop(EEPROM_I2C_BUS);

    // Wait for the signal to complete
    do
    {
        status = I2CGetStatus(EEPROM_I2C_BUS);
    }
    while (!(status & I2C_STOP));
}
// ----------------------------------------------------------------------------
void initIcd()
{
    UINT32 actualClock;

    EEPROM_Success = TRUE;
    // Set the I2C baudrate
    actualClock = I2CSetFrequency(EEPROM_I2C_BUS, GetPeripheralClock(), I2C_CLOCK_FREQ);
    if (abs(actualClock - I2C_CLOCK_FREQ) > I2C_CLOCK_FREQ / 10)
    {
        debugTrace("Error: I2C1 clock frequency (%u) error exceeds 10%%.\r\n", (unsigned) actualClock);
    }

    // Enable the I2C bus
    I2CEnable(EEPROM_I2C_BUS, TRUE);
}
// ----------------------------------------------------------------------------
// Send the data to EEPROM to program one location
// ----------------------------------------------------------------------------
void txIcd(unsigned int address, unsigned char value)
{
    BOOL Acknowledged;
    int Index;
    UINT8 i2cData[4];
    int DataSz;

    EEPROM_Success = TRUE;

    // Initialize the data buffer
    I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, EEPROM_ADDRESS, I2C_WRITE);
    i2cData[0] = SlaveAddress.byte;
    i2cData[1] = address >> 8; // EEPROM location to program (high address byte)
    i2cData[2] = address & 0xFF; // EEPROM location to program (low address byte)
    i2cData[3] = value; // EEPROM location to program (low address byte)
    DataSz = 4;

    // Start the transfer to write data to the EEPROM
    if (!StartTransfer(FALSE))
    {
        debugTrace("I2C Error: Can't Start Transfer TX (1)\r\n");
        return;
    }

    // Transmit all header
    Index = 0;
    while (EEPROM_Success && (Index < DataSz))
    {
        // Transmit a byte
        if (TransmitOneByte(i2cData[Index]))
        {
            // Advance to the next byte
            Index++;

            // Verify that the byte was acknowledged
            if (!I2CByteWasAcknowledged(EEPROM_I2C_BUS))
            {
                debugTrace("I2C Error: Sent byte was not acknowledged (1)\r\n");
                EEPROM_Success = FALSE;
            }
        }
        else
        {
            EEPROM_Success = FALSE;
        }
    }

    // End the transfer (hang here if an error occured)
    StopTransfer();
    if (!EEPROM_Success)
    {
        debugTrace("I2C Error: No Success on TX (1)\r\n");
        return;
    }

    // Wait for EEPROM to complete write process, by polling the ack status.
    Acknowledged = FALSE;
    do
    {
        // Start the transfer to address the EEPROM
        if (!StartTransfer(FALSE))
        {
            debugTrace("I2C Error: Can't Start Transfer TX (2)\r\n");
            return;
        }

        // Transmit just the EEPROM's address
        if (TransmitOneByte(SlaveAddress.byte))
        {
            // Check to see if the byte was acknowledged
            Acknowledged = I2CByteWasAcknowledged(EEPROM_I2C_BUS);
        }
        else
        {
            EEPROM_Success = FALSE;
        }

        // End the transfer (stop here if an error occured)
        StopTransfer();
        if (!EEPROM_Success)
        {
            debugTrace("I2C Error: No Success on TX (2)\r\n");
            return;
        }
    }
    while (Acknowledged != TRUE);
}
// ----------------------------------------------------------------------------
// Read the data back from the EEPROM.
// ----------------------------------------------------------------------------
unsigned char rxIcd(unsigned int address)
{
    UINT8 i2cbyte;
    UINT8 i2cData[3];
    int DataSz;
    int Index;

    EEPROM_Success = TRUE;

    // Initialize the data buffer
    I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, EEPROM_ADDRESS, I2C_WRITE);
    i2cData[0] = SlaveAddress.byte;
    i2cData[1] = address >> 8; // EEPROM location to program (high address byte)
    i2cData[2] = address & 0xFF; // EEPROM location to program (low address byte)
    DataSz = 3;

    // Start the transfer to write data to the EEPROM
    if (!StartTransfer(FALSE))
    {
        debugTrace("I2C Error: Can't Start Transfer TX (1)\r\n");
        return;
    }

    // Transmit all header
    Index = 0;
    while (EEPROM_Success && (Index < DataSz))
    {
        // Transmit a byte
        if (TransmitOneByte(i2cData[Index]))
        {
            // Advance to the next byte
            Index++;
        }
        else
        {
            EEPROM_Success = FALSE;
        }

        // Verify that the byte was acknowledged
        if (!I2CByteWasAcknowledged(EEPROM_I2C_BUS))
        {
            debugTrace("I2C Error: Sent byte was not acknowledged (1)\r\n");
            EEPROM_Success = FALSE;
        }
    }

    // Restart and send the EEPROM's internal address to switch to a read transfer
    if (EEPROM_Success)
    {
        // Send a Repeated Started condition
        if (!StartTransfer(TRUE))
        {
            debugTrace("I2C Error: Can't Start Transfer TX (4)\r\n");
            return;
        }

        // Transmit the address with the READ bit set
        I2C_FORMAT_7_BIT_ADDRESS(SlaveAddress, EEPROM_ADDRESS, I2C_READ);

        if (TransmitOneByte(SlaveAddress.byte))
        {
            // Verify that the byte was acknowledged
            if (!I2CByteWasAcknowledged(EEPROM_I2C_BUS))
            {
                debugTrace("I2C Error: Sent byte was not acknowledged (3)\r\n");
                EEPROM_Success = FALSE;
            }
        }
        else
        {
            EEPROM_Success = FALSE;
        }
    }

    // Read the data from the desired address
    if (EEPROM_Success)
    {
        if (I2CReceiverEnable(EEPROM_I2C_BUS, TRUE) == I2C_RECEIVE_OVERFLOW)
        {
            debugTrace("I2C Error: I2C Receive Overflow\r\n");
            EEPROM_Success = FALSE;
        }
        else
        {
            while (!I2CReceivedDataIsAvailable(EEPROM_I2C_BUS));

            i2cbyte = I2CGetByte(EEPROM_I2C_BUS);
        }
    }

    // End the transfer (stop here if an error occured)
    StopTransfer();
    if (!EEPROM_Success)
    {
        debugTrace("I2C Error: No Success on TX (3)\r\n");
        return;
    }

    return i2cbyte;
}
// ----------------------------------------------------------------------------
void testEeprom(void)
{
    unsigned int i;
    BOOL failed = FALSE;
    unsigned char value;

    // Create & Write 100 byte array
    for (i = 0; i < DATA_ARRAY_SIZE; i++)
    {
        EEPROM_Data_Array[i] = i;

        initIcd();
        txIcd(EEPROM_STORAGE_LOCATION + i, EEPROM_Data_Array[i]);
        StopTransfer();
        I2CEnable(EEPROM_I2C_BUS, FALSE);
    } // for i

    // Read back & varify data against array
    for (i = 0; i < DATA_ARRAY_SIZE; i++)
    {
        initIcd();
        value = rxIcd(EEPROM_STORAGE_LOCATION + i);
        if (EEPROM_Data_Array[i] != value)
        {
            failed = TRUE;
            break;
        }
        StopTransfer();
        I2CEnable(EEPROM_I2C_BUS, FALSE);
    } // for i

    if (failed == FALSE)
    {
        debugTrace("I2C SUCCESS\r\n");
    }
    else
    {
        debugTrace("I2C FAILED %d: %d != %d\r\n", i, value, EEPROM_Data_Array[i]);
    }

    while(1);
}
// ----------------------------------------------------------------------------
Dave
  • 1,696
  • 4
  • 23
  • 47
  • 1
    How does the hardware differentiate the packet telling it to write a byte from the packet telling it to read a byte? It appears that the only difference in `i2cData[]` in `txIcd()` and `rxIcd()` is the length of `i2cData[]`. Is that enough for the hardware to figure out the difference between the two possible but opposite requests? Or does it just consume the requested address from `i2cData[]` and then the following write (also embedded in `i2cData[]`) or read does the rest? – Alexey Frunze Oct 05 '12 at 10:14
  • The I2C_FORMAT_7_BIT_ADDRESS has a bit set to I2C_WRITE or I2C_READ to determine data direction. – Dave Oct 05 '12 at 12:16
  • But you have `I2C_WRITE` in both places, is that right? – Alexey Frunze Oct 05 '12 at 12:29
  • Yes. In each case, TX & RX, the initial values written included the hardware address, and the address in EEPROM that you want to access. In the RX handler, once this address is written, you'll see a change to I2C_READ to read from that location. – Dave Oct 05 '12 at 12:45

0 Answers0