0

I have been trying to program an AT24C16 EEPROM and have set up a circuit as per the attached image.

enter image description here

I have been using the following Arduino code:

#include <Wire.h>    
 
#define disk1 0x50    //Address of 24LC256 eeprom chip
 
void setup(void)
{
  Serial.begin(9600);
  Wire.begin();  
 
  unsigned int address = 10;
 
  writeEEPROM(disk1, address, 72);
  Serial.println(readEEPROM(disk1, address), DEC);
}
 
void loop(){}
 
void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data ) 
{
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.write(data);
  Serial.println(Wire.endTransmission());
 
  delay(5);
}
 
byte readEEPROM(int deviceaddress, unsigned int eeaddress ) 
{
  byte rdata = 0xFF;
  
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Serial.println(Wire.endTransmission());
 
  Wire.requestFrom(deviceaddress,1);
 
  if (Wire.available()) rdata = Wire.read();
 
  return rdata;
}

Unfortunately I only ever receive '255' in the serial monitor suggesting that no data has been written.

The code was from this tutorial which explains how the wiring on pins 1-3 governs the address of the chip.

6
  • See what values your .endTransmission() calls are returning. You may want to use the I2CScanner sketch to at least make sure it is showing up on the bus.
    – timemage
    Jan 28, 2021 at 17:16
  • Hi have updated code so it prints out the .endTransmission() calls and get a '2' returned from both which according to wire doc is '2:received NACK on transmit of address' Jan 28, 2021 at 17:44
  • I thought I should print the endTransmission call for both writeEEPROM() and readEEPROM() ? - will update question when i know where to put print statement... Jan 28, 2021 at 17:53
  • 1
    Ok, the updated code returns '0' for the read and write endTransmission calls. Jan 28, 2021 at 18:06
  • 1
    Same thing. Ive tried different addresses and different values and they always seem to be the same result.. Jan 28, 2021 at 18:23

2 Answers 2

1

Well, I think I see part of what is happening. I'm not sure to what degree this is an Arduino problem, but it may be of some use to someone wanting to interface this chip with an Arduino. So, here's what I see:

You're writing to the chip just fine so far as I2C is concerned. But the content of what you're writing is not exactly correct to the chip itself.

You have:

Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8));   // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB

Which is intuitive, but unfortunately not how the AT24C16 apparently works. To be clear, the tutorial you've referenced uses the 24LC256, which does use the addressing scheme above. But you're not using that chip.

The chips in the series covered by the AT24LC16 datasheet need progressively larger EEPROM addresses for the larger memory sizes. As the size of the chip increases they are effectively stealing configurable bits from the A0, A1, A2 pins and repurposing them as part of the EEPROM address encoded into the I2C address. You'll see that the for up to the 2Kbit/256byte/8-bit addressable EEPROM, all of A0, A1, A2 can be used to set the I2C address (to have multiple of these chips). By the time you work your way up to the 16kbit/2kbyte/11-bit addressable chip, all of the A0, A1, A2 are specified as no connect; the datasheet says that can be connected to ground. The reason for doing this probably just for controlling noise.

The chip expects only the lower 8 bits of the EEPROM word address to be sent in the data portion of the I2C write (or "dummy" write). But the bits in excess of 8-bits, the upper 3-bits for your 11-bit addressable, are expected to be encoded into the I2C address itself, like so:

const uint8_t i2c_base_address = 0x50;
const uint8_t upper_three_eeprom_adress_bits = (eeaddress >> 8) & 0x7;
const uint8_t i2c_address = i2c_base_address | upper_three_eeprom_adress_bits;
Wire.beginTransmission(i2c_address);
Wire.write(eeaddress & 0xFF); // address LSB

The datasheet speaks of "BYTE WRITE" where single bytes per I2C write transactions are written to EEPROM. And it speaks of "PAGE WRITE" where an entire page payload is sent, 16-bytes for your model. Because you are sending two bytes as part of data portion of the I2C write, you are doing neither of these things. The chip's notion of "current address" is updated differently for "byte" vs "page" writes.

If I had guess, they didn't want there to be a qualitative difference in handling 1 vs 2 byte I2C write transactions or alternately between 15 (page size minus one) vs 16 (page size) byte I2C write transactions. So, they just decided anything in the middle ground between a single byte write and an entire page write should be ignored, so no EEPROM write operation carried out.

2
  • Thank you so much for taking the time to respond in such depth and clarity. I have updated my code and it appears to work! Shall I post my code as another answer? The comment section wont allow it... Jan 29, 2021 at 11:13
  • That sounds reasonable to me. Yeah, comments aren't really intended for answer material anyway, let alone code of any significant size.
    – timemage
    Jan 29, 2021 at 16:15
2

In response to the answer from @timemage I updated my code which now allows me to read and write to the EEPROM chip as intended. Here is the code just in case anyone else wants it quickly:

#include <Wire.h>

#define disk1 0x50    //Address of eeprom chip

void setup(void)
{
  Serial.begin(9600);
  Wire.begin();

  unsigned int address0 = 0;

  writeEEPROM(disk1, address0, 1);

  Serial.println(readEEPROM(disk1, address0), DEC);
}

void loop() {}

void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data )
{

  const uint8_t i2c_base_address = 0x50;
  const uint8_t upper_three_eeprom_adress_bits = (eeaddress >> 8) & 0x7;
  const uint8_t i2c_address = i2c_base_address | upper_three_eeprom_adress_bits;
  Wire.beginTransmission(i2c_address);
  Wire.write(eeaddress & 0xFF); // address LSB

  Wire.write(data);
  Wire.endTransmission();

  delay(5);
}

byte readEEPROM(int deviceaddress, unsigned int eeaddress )
{
  byte rdata = 0xFF;


  const uint8_t i2c_base_address = 0x50;
  const uint8_t upper_three_eeprom_adress_bits = (eeaddress >> 8) & 0x7;
  const uint8_t i2c_address = i2c_base_address | upper_three_eeprom_adress_bits;
  Wire.beginTransmission(i2c_address);
  Wire.write(eeaddress & 0xFF); // address LSB
  
  Wire.endTransmission();

  Wire.requestFrom(deviceaddress, 1);

  if (Wire.available()) rdata = Wire.read();

  return rdata;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.