Shop OBEX P1 Docs P2 Docs Learn Events
Simple EEPROM read/write — Parallax Forums

Simple EEPROM read/write

ypapelisypapelis Posts: 99
edited 2013-04-19 19:10 in Propeller 1
Shouldn't the following piece of code read/write bytes to the upper 32k of the EEPROM, provided it is a 64k EEPROM?

I am getting back 0xFFs, no matter what the address
#include <propeller.h> 
#include <i2c.h>
static I2C_COGDRIVER i2c;
static I2C* pI2c;
static int scl = 28;
static int sda = 29;

int EEPROM_Init()
{
     pI2c = i2cOpen(&i2c, scl, sda, 400000);
     if ( pI2c == 0 ) {
         return -1;
     }
     return 0;
}

uint8_t EEPROM_ReadByte(int addr)
{
     uint8_t  data[2] = { (addr >> 8) + 0x80, addr & 0xFF };
     uint8_t  value;
      i2cWrite(pI2c, 0xA0, data, 2, 0);
     i2cRead(pI2c, 0xA1, &value, 1, 1); 
     return value;
}

void EEPROM_WriteByte(int addr, uint8_t value)
{
     uint8_t  data[2] = { (addr >> 8) + 0x80, addr & 0xFF };
      i2cWrite(pI2c, 0xA0, data, 2, 0);
     i2cWrite(pI2c, 0xA0, &value, 1, 1);
}
 


Comments

  • 4x5n4x5n Posts: 745
    edited 2012-12-12 16:33
    Quick question about the pointer pI2c. I see that you declared it but I don't see where you allocated any memory for it to point to. Is that correct?
  • ypapelisypapelis Posts: 99
    edited 2012-12-12 19:08
    4x5n wrote: »
    Quick question about the pointer pI2c. I see that you declared it but I don't see where you allocated any memory for it to point to. Is that correct?

    The pI2c pointer is returned by the call to EEPROM_Init, which calls the i2c device driver initialization. The actual structure is inside the driver, so I don't think this is the problem.
  • 4x5n4x5n Posts: 745
    edited 2012-12-12 19:14
    ypapelis wrote: »
    The pI2c pointer is returned by the call to EEPROM_Init, which calls the i2c device driver initialization. The actual structure is inside the driver, so I don't think this is the problem.

    Gotcha. If I had a dollar everytime I got caught with that one. :innocent:
  • jazzedjazzed Posts: 11,803
    edited 2012-12-13 00:26
    Did you look at the markup in i2c.h ?
    /**
     * @brief Write to an I2C device
     *
     * @details Write to an I2C device at the specified address.
     * The address should be the device address in bits 7:1 and
     * a zero in bit 0. If count is zero only the address byte
     * will be sent. Set the stop parameter to TRUE to cause an
     * I2C stop sequence to be emitted after the data. Setting it
     * to FALSE omits the stop sequence.
     *
     * @param dev I2C device to close
     * @param address I2C address in bits 7:1, zero in bit 0
     * @param buffer Address of the buffer containing data to write
     * @param count Number of bytes of data to write
     * @param stop TRUE to send a stop sequence after the data
     *
     * @returns 0 on success, -1 on failure.
     *
     */
    static inline int i2cWrite(I2C *dev, int address, uint8_t *buffer, int count, int stop)
    {
        return (*dev->ops->write)(dev, address, buffer, count, stop);
    }
    
    
    /**
     * @brief Read from an I2C device
     *
     * @details Read from an I2C device at the specified address.
     * The address should be the device address in bits 7:1 and
     * a zero in bit 0. Set the stop parameter to TRUE to cause an
     * I2C stop sequence to be emitted after the data. Setting it
     * to FALSE omits the stop sequence.
     *
     * @param dev I2C device to close
     * @param address I2C address in bits 7:1, zero in bit 0
     * @param buffer Address of the buffer to receive data
     * @param count Number of bytes of data to receive
     * @param stop TRUE to send a stop sequence after the data
     *
     * @returns 0 on success, -1 on failure.
     *
     */
    static inline int i2cRead(I2C *dev, int address, uint8_t *buffer, int count, int stop)
    {
        return (*dev->ops->read)(dev, address, buffer, count, stop);
    }
    
  • ypapelisypapelis Posts: 99
    edited 2012-12-13 02:57
    Hi Jazzed,

    Being an educator myself, I appreciate what you are trying to do. However, I have been away from the propeller universe for a while working on numerous other projects (all involving numerous details and facts of their own) and my old mind shifts out data as new information is shifted in. A year ago I would probably be able to pick up on the nuance of your answer, but at this point I can't.

    I have seen the header file and the ony thing I came up with is that I should not set the 'Read' bit myself. This didn't work. I quickly looked through the EEPROM data sheet and I think I am sending the right sequence to write (EEPROM device address sent by the driver, 2 byte address plus 0x8000 to hit the upper 64k; no stop sequence, followed by the byte to write); also tried sending all three bytes (upper addr, low addr, byte to write) in one i2cWrite sequence to avoid the repeated start of the second call. Similarly for the read. And I realize that I am not polling the eeprom after writes, but I am using pretty large delays between writes at this point.

    I could load the spin code that works, check out the clock/data lines with the scope and then do the same for the C program to figure out the mistake, but was hoping for something quicker.
  • David BetzDavid Betz Posts: 14,516
    edited 2012-12-13 05:44
    ypapelis wrote: »
    The pI2c pointer is returned by the call to EEPROM_Init, which calls the i2c device driver initialization. The actual structure is inside the driver, so I don't think this is the problem.
    Actually, i2cOpen call just returns its first argument cast to a generic I2C pointer. There is no internal storage in the driver.
  • David BetzDavid Betz Posts: 14,516
    edited 2012-12-13 05:54
    ypapelis wrote: »
    Shouldn't the following piece of code read/write bytes to the upper 32k of the EEPROM, provided it is a 64k EEPROM?
    Are you sure you're using the right address for the eeprom? The code you included in your post is almost identical to the code used by the COG loader in the PropGCC library.

    Also, if you're using the boot eeprom you should probably use the i2cBootOpen() function since some boards don't have pullups on the boot eeprom. The code in the boot version of the i2c driver actively drives both pins.
  • ypapelisypapelis Posts: 99
    edited 2012-12-13 07:18
    David Betz wrote: »
    Actually, i2cOpen call just returns its first argument cast to a generic I2C pointer. There is no internal storage in the driver.
    Thanks for the clarification, but I think the main point still remains that this should not be an issue, I think.
  • ypapelisypapelis Posts: 99
    edited 2012-12-13 07:20
    David Betz wrote: »
    Are you sure you're using the right address for the eeprom? The code you included in your post is almost identical to the code used by the COG loader in the PropGCC library.

    Also, if you're using the boot eeprom you should probably use the i2cBootOpen() function since some boards don't have pullups on the boot eeprom. The code in the boot version of the i2c driver actively drives both pins.

    Unless I am missing something, 0xA0 is the address of the on-board eeprom, and I am positive that the board I am using has pull-ups so there is no need to drive the pins.
  • jazzedjazzed Posts: 11,803
    edited 2012-12-13 10:55
    Glad you found the API info. I didn't have much time yesterday and I noticed you were not getting any help. That's the best I could do given the circumstances.

    I'm able to use your read routine to fetch values from both 0x0000 and 0x8000 in a 64KB device. The values in address 0 of a previously programmed EEPROM should be 0x00, 0xb4, 0xc4, 0x04. If the upper 64KB of the EEPROM has never been programmed the values will be 0xff for address 0x8000+.

    Can you verify the bytes at address 0,1,2,3 ?

    Here's the read I ended up using after a bit. Notice i don't add 0x8000 to the address and use 0xA0 in both ID parameters.
    uint8_t EEPROM_ReadByte(int addr)
    {
        uint8_t  data[2] = { (addr >> 8), addr & 0xFF };
        uint8_t  value;
        i2cWrite(pI2c, 0xA0, data, 2, 0);
        i2cRead(pI2c, 0xA0, &value, 1, 1); 
        return value;
    }
    
  • ypapelisypapelis Posts: 99
    edited 2012-12-18 05:52
    jazzed wrote: »
    Glad you found the API info. I didn't have much time yesterday and I noticed you were not getting any help. That's the best I could do given the circumstances.

    I'm able to use your read routine to fetch values from both 0x0000 and 0x8000 in a 64KB device. The values in address 0 of a previously programmed EEPROM should be 0x00, 0xb4, 0xc4, 0x04. If the upper 64KB of the EEPROM has never been programmed the values will be 0xff for address 0x8000+.

    Can you verify the bytes at address 0,1,2,3 ?

    Here's the read I ended up using after a bit. Notice i don't add 0x8000 to the address and use 0xA0 in both ID parameters.
    uint8_t EEPROM_ReadByte(int addr)
    {
        uint8_t  data[2] = { (addr >> 8), addr & 0xFF };
        uint8_t  value;
        i2cWrite(pI2c, 0xA0, data, 2, 0);
        i2cRead(pI2c, 0xA0, &value, 1, 1); 
        return value;
    }
    

    Hi Jazzed,

    I did confirm that bytes at 0, 1, 2, 3 come across correctly; code works for upper 32k also. It was the read code that did not work.

    I compared what it does to the good-old i2cObject and realized that the C version of the i2cWrite routine sends the device address each time it is called, as a result all the 'data' has to be sent in one call, not two like I was doing earlier. The code below works now!

    In case someone wants to use this code, keep in mind that the EEPROM_WriteByte function does not check if the EEPROM is ready to receive more data so fast sequential writes may fail. Lazy solution to that is to put delays after each write; proper solution is to check if the EEPROM is ready, by checking for the ack bit.
    void EEPROM_WriteByte(int addr, uint8_t value) {
          uint8_t  data[3] = { (addr >> 8) & 0xFF, addr & 0xFF, value };
          i2cWrite(pI2c, 0xA0, data, 3, 1);
     }
    
  • ypapelisypapelis Posts: 99
    edited 2012-12-21 15:17
    Following up on the EEPROM read/write code, if I use I2C_SIMPLE, it does not work. Any ideas on that?

    It would be nice to be able to avoid using a COG after using the i2c simply to read some config variables from the eeprom.

    So this works:
    static int scl = 28;
    static int sda = 29;
    
    static I2C_COGDRIVER i2c;  
    static I2C* pI2c;
    
    int EEPROM_Init()
    {
        pI2c = i2cOpen(&i2c, scl, sda, 400000);   
        return pI2c == 0;
    }
    

    but this does not
    static int scl = 28;
    static int sda = 29;
    
    static I2C_SIMPLE i2c;
    static I2C* pI2c; 
    
    int EEPROM_Init()
    {
         pI2c = simple_i2cOpen(&i2c, scl, sda);
         return pI2c == 0;
    }
    
    

    Another thought, is it possible to shutdown the i2c driver and free the cog? I can see which cogs are free before and which are free after and use cogstop to force the shutdown but I was wondering if there is a 'nicer' way to do that.
  • jazzedjazzed Posts: 11,803
    edited 2012-12-21 15:40
    David Betz wrote these libraries and he is travelling. It may be a day before he answers.
    I have not used the code other than testing some examples.
  • David BetzDavid Betz Posts: 14,516
    edited 2012-12-27 20:02
    ypapelis wrote: »
    Following up on the EEPROM read/write code, if I use I2C_SIMPLE, it does not work. Any ideas on that?

    It would be nice to be able to avoid using a COG after using the i2c simply to read some config variables from the eeprom.

    So this works:
    static int scl = 28;
    static int sda = 29;
    
    static I2C_COGDRIVER i2c;  
    static I2C* pI2c;
    
    int EEPROM_Init()
    {
        pI2c = i2cOpen(&i2c, scl, sda, 400000);   
        return pI2c == 0;
    }
    

    but this does not
    static int scl = 28;
    static int sda = 29;
    
    static I2C_SIMPLE i2c;
    static I2C* pI2c; 
    
    int EEPROM_Init()
    {
         pI2c = simple_i2cOpen(&i2c, scl, sda);
         return pI2c == 0;
    }
    
    

    Another thought, is it possible to shutdown the i2c driver and free the cog? I can see which cogs are free before and which are free after and use cogstop to force the shutdown but I was wondering if there is a 'nicer' way to do that.
    You can shut down the COG by calling i2cClose. I'm not sure why you're having trouble with simple_i2cOpen. I'll have to look at it. Unfortunately, I'm in the middle of something else right now. I'll try to get to it sometime soon. In the meantime, could you post the entire program you're having trouble with?

    Thanks,
    David
  • David BetzDavid Betz Posts: 14,516
    edited 2013-04-19 19:10
    Sorry to take so long to answer this. The code below works for me. I think the main problem with the original code was splitting the write into two calls to i2cWrite. That caused two start conditions and two EEPROM address bytes to be sent instead of just one.
    #include <stdio.h>
    #include <stdlib.h>
    #include <propeller.h> 
    #include <i2c.h>
    
    static I2C_COGDRIVER i2c;
    static I2C* pI2c;
    static int scl = 28;
    static int sda = 29;
    
    #define EEPROM_ADDR	0xA0
    
    void chk(char *fcn, int sts)
    {
        if (sts != 0) {
            printf("%s failed: %d\n", fcn, sts);
    	exit(1);
        }
    }
    
    int EEPROM_Init()
    {
         pI2c = i2cOpen(&i2c, scl, sda, 400000);
         if ( pI2c == 0 ) {
             return -1;
         }
         return 0;
    }
    
    uint8_t EEPROM_ReadByte(int addr)
    {
         uint8_t  data[2] = { addr >> 8, addr & 0xFF };
         uint8_t  value;
         chk("i2cWrite", i2cWrite(pI2c, EEPROM_ADDR, data, 2, 0));
         chk("i2cRead", i2cRead(pI2c, EEPROM_ADDR, &value, 1, 1)); 
         return value;
    }
    
    void EEPROM_WriteByte(int addr, uint8_t value)
    {
         uint8_t  data[3] = { addr >> 8, addr & 0xFF, value };
         chk("i2cWrite", i2cWrite(pI2c, EEPROM_ADDR, data, 3, 1));
    }
    
    uint8_t show_byte(int addr)
    {
        uint8_t byte = EEPROM_ReadByte(addr);
        printf("[%04x]: %02x\n", addr, byte);
        return byte;
    }
    
    int main(void)
    {
        uint8_t byte;
        int addr;
    
        chk("EEPROM_Init", EEPROM_Init());
    
        show_byte(0x0000);
        show_byte(0x0001);
        show_byte(0x0002);
        show_byte(0x0003);
        printf("before ");
        byte = show_byte(0x8000);
        EEPROM_WriteByte(0x8000, byte + 1);
        sleep(2);
        printf("after  ");
        show_byte(0x8000);
        
        printf("\n");
        for (addr = 0x8000; addr < 0x8020; ++addr)
            show_byte(addr);
    
        return 0;
    }
    
Sign In or Register to comment.