Go Down

Topic: Arduino DS1307 with AT24C32 (Read 21661 times) previous topic - next topic

PhantomUK

I recently bought Arduino I2C RTC DS1307 AT24C32 module from ebay:

http://www.ebay.com/itm/ws/eBayISAPI.dll?ViewItem&item=180646747674&ssPageName=ADME:X:AAQ:US:1123


I have successfully managed to set and read the time perfectly but I was wondering how on Earth I make use of the AT24C32?  If I understand it this being on the I2C bus will need a separate address how do I find this or does it some how use the same address as the DS1307 chip?

Regards,

PhantomUK

PaulS

There is a link on the site you posted, to Arduino code, that shows how to use the EEPROM chip. Did you try that?

PhantomUK

I looked at the code and can see how to write to the EEPROM that is available on the DS1307 chip but not the AT24C32 that is included, how do I address this AT24C32 chip or does the EEPROM from the DS1307 just roll over onto the AT24C32?

RandallR

You need to be careful.  When you write, you write only to that page.
I put together a library to help me keep track of things.

AT24Cxx.h
Code: [Select]

/*
* AT24Cxx.h - library for AT24Cxx
*/

#ifndef AT24Cxx_h
#define AT24Cxx_h

class AT24Cxx
{
  // user-accessible "public" interface
  public:
    AT24Cxx();

    static bool isPresent(void);      // check if the device is present
    static int ReadMem(int iAddr, char Buf[], int iCnt);
    static uint8_t WriteMem(int iAddr, uint8_t iVal);
    static uint8_t WriteMem(int iAddr, const char *pBuf, int iCnt);

    static int     ReadStr(int iAddr, char Buf[], int iBufLen);
    static uint8_t WriteStr(int iAddr, const char *pBuf);

  private:
};
#endif


AT24Cxx.cpp
Code: [Select]

#include <Arduino.h>

#include <Wire.h>
#include "AT24Cxx.h"
#define min(a,b) ((a)<(b)?(a):(b))

#define AT24Cxx_CTRL_ID 0x50

AT24Cxx::AT24Cxx()
{
  Wire.begin();
}
//
// PUBLIC FUNCTIONS

bool AT24Cxx::isPresent(void)      // check if the device is present
{
  Wire.beginTransmission(AT24Cxx_CTRL_ID);
  if (Wire.endTransmission() == 0)
    return 1;
  return 0;
}

int AT24Cxx::ReadMem(int iAddr, char Buf[], int iCnt)
{
  int iRead=0, iBytes;
  while (iCnt>0) {
    Wire.beginTransmission(AT24Cxx_CTRL_ID);
#if ARDUINO >= 100
    Wire.write(iAddr>>8);   // Address MSB
    Wire.write(iAddr&0xff); // Address LSB
#else
    Wire.send(iAddr>>8);   // Address MSB
    Wire.send(iAddr&0xff); // Address LSB
#endif
    Wire.endTransmission();

    iBytes = min(iCnt, 128);
    Wire.requestFrom(AT24Cxx_CTRL_ID, iBytes);

    while (Wire.available() && iCnt>0) {
#if ARDUINO >= 100
      Buf[iRead] = Wire.read();
#else
      Buf[iRead] = Wire.receive();
#endif
      iRead++; iCnt--; iAddr++;
    }  /* while */
  }
  return (iRead);
}

int AT24Cxx::ReadStr(int iAddr, char Buf[], int iCnt)
{
  int iRead=0, iBytes;
  char c;
  while (iCnt>0) {
    Wire.beginTransmission(AT24Cxx_CTRL_ID);
#if ARDUINO >= 100
    Wire.write(iAddr>>8);   // Address MSB
    Wire.write(iAddr&0xff); // Address LSB
#else
    Wire.send(iAddr>>8);   // Address MSB
    Wire.send(iAddr&0xff); // Address LSB
#endif
    Wire.endTransmission();

    iBytes = min(iCnt, 128);
    Wire.requestFrom(AT24Cxx_CTRL_ID, iBytes);

    while (Wire.available() && iCnt>0) {
#if ARDUINO >= 100
      c = Wire.read();
#else
      c = Wire.receive();
#endif
      Buf[iRead] = c;
      if (c == '\0') {
        iCnt=0; break;
      }  /* if */
      iRead++; iCnt--; iAddr++;
    }  /* while */
  }
  return (iRead);
}

uint8_t AT24Cxx::WriteMem(int iAddr, uint8_t iVal)
{
  uint8_t iRC=0;
  Wire.beginTransmission(AT24Cxx_CTRL_ID);
#if ARDUINO >= 100
    Wire.write(iAddr>>8);   // Address MSB
    Wire.write(iAddr&0xff); // Address LSB
    Wire.write(iVal);
#else
    Wire.send(iAddr>>8);   // Address MSB
    Wire.send(iAddr&0xff); // Address LSB
    Wire.send(iVal);
#endif
  iRC = Wire.endTransmission();

  return(iRC);
}

// BYTE WRITE:
// A write operation requires two 8-bit data word addresses following the device address word and acknowledgment.
// Upon receipt of this address, the EEPROM will again respond with a zero and then clock in the first 8-bit data
// word. Following receipt of the 8-bit data word, the EEPROM will output a zero and the addressing device, such as
// a microcontroller, must terminate the write sequence with a stop condition. At this time the EEPROM enters an
// internally-timed write cycle, tWR, to the nonvolatile memory. All inputs are disabled during this write cycle and
// the EEPROM will not respond until the write is complete (refer to Figure 2).

// PAGE WRITE:
// The 32K/64K EEPROM is capable of 32-byte page writes. A page write is initiated the same way as a byte write, but
// the microcontroller does not send a stop condition after the first data word is clocked in. Instead, after the EEPROM
// acknowledges receipt of the first data word, the microcontroller can transmit up to 31 more data words. The EEPROM
// will respond with a zero after each data word received. The microcontroller must terminate the page write sequence
// with a stop condition (refer to Figure 3).

// The data word address lower 5 bits are internally incremented following the receipt of each data word. The higher
// data word address bits are not incremented, retaining the memory page row location. When the word address, internally
// generated, reaches the page boundary, the following byte is placed at the beginning of the same page. If more than 32
// data words are transmitted to the EEPROM, the data word address will "roll over" and previous data will be overwritten.
uint8_t AT24Cxx::WriteMem(int iAddr, const char *pBuf, int iCnt)
{
  uint8_t iBytes, iRC=0;

// Writes are restricted to a single 32 byte page.  Therefore. if a write spans a page
// boundry we must split the write.

  while (iCnt > 0) {
    iBytes = min(iCnt, BUFFER_LENGTH-2);
    int iCurPage = iAddr & ~((int)0x1f);
    if (iAddr+iBytes > iCurPage+32) { // Number of bytes is too large
      iBytes = (iCurPage+32) - iAddr;
    }

    Wire.beginTransmission(AT24Cxx_CTRL_ID);
#if ARDUINO >= 100
    Wire.write( highByte(iAddr) ); // Address MSB
    Wire.write( lowByte(iAddr) );  // Address LSB
    Wire.write((uint8_t*)pBuf, iBytes);
#else
    Wire.send( highByte(iAddr) );   // Address MSB
    Wire.send( lowByte(iAddr) ); // Address LSB
    Wire.send(pBuf, iBytes);
#endif
    Wire.endTransmission();
    iRC  +=(int)iBytes;
    iCnt -=(int)iBytes;
    iAddr+=(int)iBytes;
    pBuf +=(int)iBytes;
    delay(50);  // Give the EEPROM time to write its data
  }  /* while */

  return(iRC);
}

uint8_t AT24Cxx::WriteStr(int iAddr, const char *pBuf)
{
  uint8_t iRC=0;
  int iCnt = strlen(pBuf);

  iRC = WriteMem(iAddr, pBuf, iCnt+1); // Write the NULL terminator
  return(iRC);
}


I hope this helps.

Krodal

#4
Apr 16, 2012, 08:14 pm Last Edit: Apr 16, 2012, 09:03 pm by Krodal Reason: 1
RandallR, Is this the only place where you publish you code? You could add it to the Playground.
I like your code, because it handles the page boundery for me.
But is the 50ms delay necessary ?
If it is omitted, is the Wire.beginTransmission() waiting for the device to be ready ?

I have done a test with the 24C64A.
I used ReadMem and WriteMem with a buffer.
Without delay: not good, the first read after writing is wrong. Writing was good.
With 5ms delay: good
With 1ms delay: not good.

If there are wrong read bytes, the return value is still normal. So the return value doesn't show an error.
I removed the delay of 50ms and added a delay of 5ms outside the loop, just before the return. That result is also good.

So it is not the writing that needs a delay, but only the first read after writing.
I don't know what to test else, and I don't understand the delay.

RandallR

Yes, I am sort of still working on it.  But it looked like someone needed the code.

Also, I do not know how to post to the "Playground"

Krodal

The Playground is a wiki, and this a forum.
It has a totally different style of writing.
There are already few pages at "I2C EEPROM" http://arduino.cc/playground/Main/InterfacingWithHardware#Storage
If you click 'edit' on top of that page, you can see the format. At the "!!!I2C EEPROM"-section you can add a page in the same format. If you save it, you can go to you new created page by clicking on it, and start writing your page.

Krodal

#7
Apr 22, 2012, 11:01 am Last Edit: Apr 22, 2012, 11:09 am by Krodal Reason: 1
RandallR, I think I found the problem.

I checked the return values of the Wire library, and was able to generate some errors.

Without the delay of 50ms, the device could be busy. In that case, every Wire.endTransmission() should be checked for an error. Both while writing and reading. And if an error occured, some time (5ms) should be waited before trying again.
That would increase the code size, but reading and writing would be fast.

A much simpler solution is to change your delay in the loop to 5ms, and add also a delay of 5ms to the other WriteMem() function (the byte write version).

Also other examples at the Playground use a delay:
This one http://arduino.cc/playground/Code/I2CEEPROM24LC512 uses a delay of 5ms very often.
This one http://arduino.cc/playground/Code/I2CEEPROM24C1024 uses a delay of 5ms after writing a byte.
This one http://arduino.cc/playground/Main/LibraryForI2CEEPROM uses a delay of 5ms after writing a few bytes in a page.
This one http://arduino.cc/playground/Code/I2CEEPROMBANK uses a delay of 5ms after writing a byte.

Testing: with those two delays of 5ms, eveything is okay. I can't generate any error. So that's what I'm using now.

I assume that 5ms is enough, since the datasheet of the AT24C64A says that writing is finished within 5ms. I don't know how it is for other I2C EEPROMs.

Go Up