Reading & Writing EEPROM but output is WRONG

I am trying to understand how to write and read to an EEPROM. I found examples that write value = 110 but instead of reading the value, it reads the address = 255. I'm stuck on how to change the value. If I get past this bug, I eventually want to learn how to write a string into the memory. Im using MX25L6406E as my EEPROM and Arduino DUE. Shield is attached. Please help me... here's my code

#include "Wire.h"

#define memoryAddress       0x57


#define WP_Pin               9                                     //Write Protect Pin
#define PowerCtrl            11                                    //Shield power 3VDC power control pin
#define RelayCtrl            12
#define SPIPin               10

void setup() 
{
 //3V Power Pin: LOW to Activate
 pinMode(PowerCtrl, OUTPUT);
 digitalWrite(PowerCtrl, LOW);
 
 pinMode(RelayCtrl, OUTPUT);                                     //Enable Relay Control
 digitalWrite(RelayCtrl, HIGH);                                  //Set HIGH to activate 

 pinMode(SPIPin, OUTPUT);
 digitalWrite(SPIPin, LOW);                                     //Set LOW to activate


 //Write Protect OFF
 pinMode(WP_Pin, OUTPUT);
 digitalWrite(WP_Pin, LOW);

  //Connect to I2C bus as master
  Wire.begin();
  
  Serial.begin(9600);

  int address = 0;
  byte val = 110;
  
  writeAddress(memoryAddress, address, val); 
  byte readVal = readAddress(memoryAddress, address);
  
  Serial.print("The returned value is ");
  Serial.println(readVal);                                    //Print in the same line

}

void loop() 
{

}

void writeAddress(byte i2cAddress, int address, byte val)
{
  Wire.beginTransmission(i2cAddress);                  //Begin Transmission to I2C EEPROM
  
/*
 *Since 8 is a literal value, the compiler will not treat it like a decimal value.  
 *Casting the result guarantees that the result value is an int.  
  Wire.Write() is overloaded and one of the overloaded methods takes an int, although ultimately it only sends a single byte.
*/
  Wire.write((int)(address >> 8));                      // MSB
  Wire.write((int)(address & 0xFF));                    // LSB
  
  //Send data to be stored
  Wire.write(val);
  Wire.endTransmission();                               //End Transmission

  delay(5);                                             //Add 5ms delay for EEPROM. Required by EEPROM between writes
}

byte readAddress(byte i2cAddress, int address)
{
  byte rData = 0xFF;                                    //Define the byte for received data
  
  Wire.beginTransmission(i2cAddress);                   //Begin transmission to I2C EEPROM

  Wire.write((int)(address >> 8));                      // MSB
  Wire.write((int)(address & 0xFF));                    // LSB
  Wire.endTransmission();                               //End Transmission

  //Puts the byte of data into EEPROM's buffer

  Wire.requestFrom(i2cAddress, 1);                   //Request one byte of data at current memory address

  rData =  Wire.read();                                 //Read the data and assign to variable

  return rData;                                         //Return the data as the function output
}

(SHIELD_2).pdf (32.6 KB)

Have you run the i2cscanner sketch to confirm that your device is visible on the i2c bus?
Your wire.endTransmission() function also returns a value that would be good to check to see if it was successful or not

Yes, I just did.
image

I also added " Serial.print(rData);" at the end of the readaddress function and turned out to be 255.. still confuse on why...


  Wire.write((int)(address >> 8));                      // MSB
  Wire.write((int)(address & 0xFF));                    // LSB
  Wire.endTransmission();                               //End Transmission

  //Puts the byte of data into EEPROM's buffer

  Wire.requestFrom(i2cAddress, 1);                   //Request one byte of data at current memory address

  rData =  Wire.read();                                 //Read the data and assign to variable
  Serial.print(rData);
  
  return rData;                                         //Return the data as the function output
}

You define your device at address 0x57, but i2c scanner says there is one at 0x56...

I did change it - still 255 output

image

This is an 8MB SPI Flash memory chip. Yes, it offers non-volatile storage but you can't re-program individual bytes. You have to erase a whole Flash page.

EEPROM generally refers to a memory that can erase and re-program individual bytes.

There is no mention of MX25L6406E anywhere in your PDF.

There is an I2C EEPROM called U1. It says AT24C02 which is a 256 byte EEPROM which uses a single byte for address

The schematic looks wrong to me. A0, A1, A2 pins on U1 i.c. are biased at 1.65V which may or may not set the Slave to 0x57. I strongly advise setting A0, A1, A2 to 3.3V which will ensure 0x57.

David.

24C02 EEPROMs work differently to 24C32 EEPROMs.
Since this topic came up on AvrFreaks, I write a sketch that should diagnose which chip is mounted e.g. AT24C01 ... AT24C512

I would expect that your board will report AT24C02 @ 0x57

AT24CS_diagnose.ino

#include <Wire.h>

// pagesize =   8: AT24C01, AT24C02
// pagesize =  16: AT24C04, AT24C08, AT24C16
// pagesize =  32: AT24C32, AT24C64
// pagesize =  64: AT24C128, AT24C256
// pagesize = 128: AT24C512, AT24C1024, AT24C1025

// sz: is_ACK(loc + sz)         for 02, 04, 08, 16
// sz: memcmp(loc + sz, loc, n) for 01, 32, 64, 128, 256, 512

char printbuf[80];
#define PRINTF(fmt ...)  { sprintf(printbuf, fmt); Serial.print(printbuf); }
#define address_ACK(slave) write_I2C_buf(slave, NULL, 0)

bool is_24Cxxx = true;   //true for 24C32 - 24C1025
bool is_AT24CS = false;  //true for AT24CS01 - AT24CS128

bool write_I2C_buf(uint8_t slave, uint8_t *buf, int n)
{
    Wire.beginTransmission(slave);
    while (n--) {    //Wire defaults to max 32 bytes
        Wire.write(*buf++);
    }
    return Wire.endTransmission() == 0; //true if good
}

void read_I2C_buf(uint8_t slave, uint8_t *buf, int n)
{
    uint8_t ask;
    while (n) {      //read arbitrary amount (in chunks)
        ask = (n > 16) ? 16 : n;
        n -= ask;
        Wire.requestFrom(slave, ask); // request 16 bytes from slave device #2
        while (Wire.available() && ask--) { // slave may send less than requested
            *buf++ = Wire.read();      // receive a byte as character
        }
    }
}

bool check_24Cxxx(uint8_t slave)
{
    uint8_t wbuf[2] = {0, 0};
    write_I2C_buf(slave, &wbuf[0], 1); //sets location on 1-byte chips
    read_I2C_buf(slave, &wbuf[1], 1);  //reads the first byte
    write_I2C_buf(slave, wbuf, 2); //invokes page-write on 24C01
    bool ret = address_ACK(slave); //24C32 will ACK, 24C01 will NAK
    if (!ret) delay(10); //wait for 24C01 page-write to complete
    return ret;
}

bool write_24Cxxx_buf(uint8_t slave, uint16_t loc, uint8_t *buf, int n)
{
    uint8_t wbuf[n + 2], *p = wbuf;
    if (is_24Cxxx) *p++ = loc >> 8;
    else slave |= ((loc >> 8) & 7);
    *p++ = loc & 0xFF;
    for (int i = 0; i < n; i++) *p++ = buf[i];
    return write_I2C_buf(slave, wbuf, n + 1 + is_24Cxxx);
}

bool read_24Cxxx_buf(uint8_t slave, uint16_t loc, uint8_t *buf, int n)
{
    bool ret = write_24Cxxx_buf(slave, loc, NULL, 0);
    if (!is_24Cxxx) slave |= ((loc >> 8) & 7);
    if (ret) read_I2C_buf(slave, buf, n);
    return ret;
}

void hexdump(uint16_t loc, uint8_t *block, int16_t n)
{
    int16_t cnt;
    char ascbuf[17], *p, wid = 16;
    while (n > 0) {
        PRINTF("%04X:", loc);
        p = ascbuf;
        cnt = n;
        if (cnt > wid) cnt = wid;
        loc += cnt;
        n -= cnt;
        while (cnt--) {
            uint8_t c = *block++;
            *p++ = (c >= ' ' && c < 128) ? c : '.';
            PRINTF(" %02X", c);
        }
        *p = 0;
        PRINTF(" *%s*\r\n", ascbuf);
    }
}

void dump_AT24(uint8_t slave, uint16_t loc, int32_t sz)
{
    uint8_t pagesize = 16, buf[pagesize];
    for (uint32_t ads = loc; sz > 0; ads += pagesize, sz -= pagesize) {
        read_24Cxxx_buf(slave, ads, buf, pagesize);
        hexdump(ads, buf, pagesize);
    }
}

void erase_AT24(uint8_t slave, uint16_t loc, int32_t sz, uint8_t pagesize)
{
    uint8_t buf[pagesize];
    memset(buf, 0xFF, sizeof(buf));
    for (int32_t ads = loc; sz > 0; ads += pagesize, sz -= pagesize) {
        write_24Cxxx_buf(slave, ads, buf, pagesize);
        delay(10);
    }
}

void test_AT24xxx(uint8_t slave)
{
    uint16_t loc = 0x80 - 8;
    uint16_t page = loc & ~127;
    uint8_t buf[128];
    uint8_t msg[] = "page boundary cross";
    int msglen = strlen((char*)msg);
    int pagesize = 8;
    //erase_AT24(slave, 0, 2048, pagesize);
    write_24Cxxx_buf(slave, loc, msg, msglen);
    delay(10);   // typical AT24C01 is 5ms
    read_24Cxxx_buf(slave, page, buf, 128);
    hexdump(page, buf, 128);
    for (pagesize = 8; pagesize <= 128; pagesize <<= 1) {
        if (pagesize == 8 && memcmp(buf + 128 - pagesize, "ossry cr", 8) == 0) break;
        else if (memcmp(buf + 128 - pagesize, "ndary cross", 11) == 0) break;
    }
    if (pagesize > 128) pagesize = -1;
    uint8_t mirror[8];
    uint32_t sz;
    for (sz = 128; sz <= 65536 ; sz <<= 1) {
        bool ret = read_24Cxxx_buf(slave, loc + sz, mirror, 8);
        if (ret == false) break;
        if (memcmp(mirror, buf + loc - page, 8) == 0) break;
    }
    PRINTF("page size = %d mirror size = %ld e.g. AT24C%s%02d\r\n",
           pagesize, sz, is_AT24CS ? "S" : "", (int)(sz / 128));
    if (is_AT24CS) {
        dump_AT24(slave + 8, is_24Cxxx ? 0x0800 : 0x80, 16);
    }
    //dump_AT24(slave, 0, 1024); // i.e 24C08
}

void setup()
{
    Serial.begin(9600);
    Wire.begin();        // join i2c bus (address optional for master)
    PRINTF("diagnose 24Cxx devices\r\n");
    for (uint8_t slave = 0x50; slave < 0x58; slave++) {
        bool found = address_ACK(slave);
        delay(1);
        if (found) {
            is_24Cxxx = check_24Cxxx(slave);
            is_AT24CS = address_ACK(slave + 0x08);
            PRINTF("I2C Device found @ 0x%02X %s%s\r\n",
                   slave, is_24Cxxx ? " 2-byte loc" : " AT24C01.16",
                   is_AT24CS ? " with SerialNo" : "");
            if (!is_24Cxxx && slave > 0x50 && slave < 0x54)
                continue; //skip 24C08 extras
            test_AT24xxx(slave);
        }
    }
}

void loop()
{
}
1 Like

Thanks David. MX25L6406E is part of another assembly (attached) - part number 1430998. This goes on top of J6 designator. Unfortunately, I cannot show the whole schematic.

U1 is not what I am testing.

U1 is not what I am testing.

Go on. Which U1 do you want to test ?

Please quote the part number. e.g. MX25L6406E or AT24C02

You need to use a Flash library e.g. "SPI_Flash.h" or "SerialFlash.h" for Flash memory chips
And there are libraries that can treat your 8MB Flash as a filesystem.

Writing to a tiny I2C EEPROM is very different to using an 8MB Flash memory.
You can just use "Wire.h". Personally, I find that any specific "24Cxx" libraries are more trouble than they are worth.

David.

I'm using MX25L6406E. Reference designator 'U1' from the picture has part number 1430998 (that's my EEPROM MX25L6406E).

Or maybe I'm doing it all wrong. MX25L6406E is an SPI device while my code is for I2C......

There are several Arduino libraries for SPI Flash memory chips.
Search for "SPI Flash" or "Winbond".

Macronix make your MX25L6406E chip. You should find that there are 64Mb chips from many different manufacturers. And they all obey the industry standard 25xxx SPI commands. So you don't need to worry if the library was originally written for Adesto, Atmel, Macronix, Microchip, Winbond, ...

What do you actually want to do with this MX25L6406E chip ?
8MB is much smaller capacity than say a 16GB microSD card. But a single SMD chip is more robust than a microSD socket. However you have to manage your own wear levelling, file formats, ... Generally by an Arduino library.

David.

Will you show the picture of your EEPROM? Is it like this:
exq
Can you show the connection diagram ythat ou have made with your Arduino?
Is it 5V or 3.3V that you have connected with your EEPROM?
Have you read the data sheets of the memory chip to see the communication protocol?
Is the EEPROM be connected with SPI Port or I2C Bus?

Thank you. I will change my code and post it here in a few days. The whole objective with the MX25L6406E chip is a functional test. I have to do the following:

  1. Read Device ID: I have this code already. I posted a question in this forum and someone helped me understand it.
  2. Write a string and read it. Arduino is connected to an LCD which will output the prewritten string and will show it to the user. For now, I want to write "110" and learn how to write a string later.

In using Arduino Due with the backpack (shield). The shield's schematic is attached in the past comments. The chip is connected to the shield's J6 reference designator.

Show how you did this. e.g. with third party library calls or just with SPI calls.

Then we can show you how to write and read data from this Flash chip.

Note that Flash memory is organised in large pages e.g. 8kB. You have to erase a whole page if you want to set a single bit in a single byte. And obviously re-write all the other bytes back to the (freshly erased) page.

"110" is a string.
110 is a decimal number
0110 is an octal number
0x110 is a hexadecimal number

David.

Show how you did this. e.g. with third party library calls or just with SPI calls.

Here it is, David. This is the code for "reading device ID of EEPROM" Im going to work on the #2 test function this weekend.

/* NOTES
Manufacturer ID for Macronix and the Device ID are shifted out on the falling edge of SCLK with 
most significant bit (MSB) first as shown in Figure 26. 

SHIELD PERPECTIVE:
MISO_1/BIOS SDO: Pin 108 Arduino DUE
MOSI_1/BIOS SDI: Pin 109 Arduino DUE
SCK_1/BIOS_SCL:  Pin 110 Arduino DUE 
*/

#include <SPI.h>
#include "Adafruit_LiquidCrystal.h"
#include "Wire.h"
#define SPI_DUMMY_BYTE                      0x00
#define MX25_REMS_COMMAND                   0x90
//#define MX25_REMS_ADDRESS_MANUFACTURER_ID   0x00            //Not in use. Datasheet page 21
#define MX25_REMS_ADDRESS_DEVICE_ID         0x01
#define PowerControl                          11            //Shield power 3VDC power control pin
#define RelayCtrl                             12           //Relay Control SPI pin MOSI & SCK. 
#define SPI_CS_PIN                            10           //Chip Select Pin
#define ModuleDetectPin                       55           //Module insertion detect pin

SPISettings spiSettings( 100000, MSBFIRST, SPI_MODE0 );    // SPI_MODE0:    CPOL = 0 | CPHA = 0 | Output Edge = Falling | Data Capture = Rising
// Connect via i2c, default address #0 (A0-A2 not jumpered)
Adafruit_LiquidCrystal lcd(0);

void setup()
{
  pinMode(PowerControl, OUTPUT);
  digitalWrite(PowerControl, HIGH);                           //Set it LOW to activate 3V based on Shield Schematic
   
  pinMode(RelayCtrl, OUTPUT);                                 //Enable Relay Control
  digitalWrite(RelayCtrl, LOW);                               //Set HIGH to activate 
  
  pinMode(SPI_CS_PIN, OUTPUT);
  digitalWrite(SPI_CS_PIN, HIGH);                             //Set LOW to activate

  
  Serial.begin( 9600 );
  while ( !Serial );                                          //Adding this line makes the board pause until you open the serial port, so you get to see that initial bit of data.
  
  SPI.begin();
  lcd.setBacklight(HIGH);
}


void loop()
{
 lcd.begin(16, 2);                                       //set up the LCD's number of rows and columns: 
 digitalWrite(PowerControl, LOW);                        //Turn ON 3V   
 digitalWrite(RelayCtrl, HIGH);                          //Set HIGH to turn relay ON (U2) and connect MOSI and SPI Clock
 printMX25REMS();
 delay(5000);
}

// See MX25L6406E DATASHEET
// Page 21 and 43
// Figure 26. Read Electronic Manufacturer & Device ID (REMS) Sequence (Command 90)

void printMX25REMS()
{  
  SPI.beginTransaction( spiSettings );
  digitalWrite(SPI_CS_PIN, LOW);                            //Instruction is iniated by driving the CS# pin low based on manuf datasheet. Chip Select/Slave Slect

  SPI.transfer( MX25_REMS_COMMAND );                        //Shift the instruction code to 90h
  SPI.transfer( SPI_DUMMY_BYTE );                           //Followed by two dummy bytes
  SPI.transfer( SPI_DUMMY_BYTE );
  SPI.transfer( MX25_REMS_ADDRESS_DEVICE_ID );                //Followed by one byte address (A7~A0). If the one-byte address is initially set to 01h, then the device ID will be read first and then followed by the Manufacturer ID.

  //uint8_t: unsigned integer of 8bits length
  uint8_t deviceID = SPI.transfer( MX25_REMS_COMMAND );
  uint8_t manufacturerID = SPI.transfer( MX25_REMS_COMMAND );

  SPI.endTransaction();
  delay(1000);   

  /*  IF & ELSE statement will wait till SCLK initiates */
  /*  square wave pattern from the initial power cycle  */

  if(deviceID < 0xFF)
  {
    Serial.print("Device ID: 0x" );
    Serial.println(deviceID, HEX );
    lcd.setCursor(0,0);
    lcd.print("DeviceID is:");                           // Print a message to the LCD.
    lcd.setCursor(0,1);                                  // set the cursor to column 0, line 1
    lcd.print(deviceID, HEX);
    delay(2000);
  }
  else
  {
    Serial.print( "Device ID:" );
    Serial.println(" reading...wait ");
    lcd.setCursor(0,0);
    lcd.print("DeviceID is:");                           // Print a message to the LCD.
    lcd.setCursor(0,1);                                  // set the cursor to column 0, line 1
    lcd.print(" reading...");
    delay(2000);
  }

  if(manufacturerID < 0xFF)
  {
    Serial.print( "Manuf ID: 0x" );
    Serial.println( manufacturerID, HEX );
    lcd.setCursor(0,0);
    lcd.print("Manuf ID is:");                           // Print a message to the LCD.
    lcd.setCursor(0,1);                                  // set the cursor to column 0, line 1
    lcd.print(manufacturerID, HEX);
    delay(2000);
  }
  else
  {
    Serial.print("Manuf ID:");
    Serial.println(" reading...wait");
    lcd.setCursor(0,0);
    lcd.print("Manuf ID is:");                           // Print a message to the LCD.
    lcd.setCursor(0,1);                                  // set the cursor to column 0, line 1
    lcd.print(" reading...");
    delay(2000);
  } 
  digitalWrite( SPI_CS_PIN, HIGH );                           //Instruction complete based on manuf datasheet 
}

Look at Figure 26. Read Identification (RDID) Sequence (Command 9F) in the MX25L6406E datasheet

It shows the complete SPI sequence. Including where you set /CS active.
So it is nothing more than

     CS_low;
     SPI.transfer(0x9F);       //JEDEC ID command
     id[0] = SPI.transfer(0);  //0xEF manufacturer
     id[1] = SPI.transfer(0);  //0x20 memory type
     id[2] = SPI.transfer(0);  //0x17 memory capacity
     CS_high;

In fact most of the Read commands are pretty simple. e.g. a command byte, followed by command arguments, followed by reading some bytes.
The Write commands are more complex. You have to use commands in the correct order e.g. WREN

I suggest that you study the command list in the datasheet. Especially the details about Write enable, protection etc.

You can experiment with reading ID, blocks of memory etc. Just with SPI calls.
But it is wise to use a respected library when you want to erase and write data.
Of course the write procedure can also be implemented with a few SPI calls. Which you can try for yourself after you have learned how to use the appropriate "SerialFlash.h" or "SPI_flash" library.

David.

Hi @david_prentice,

I am experimenting on SPI in DUE. Why can't I access the SPCR and SPDR? Its giving me a compile error even if I have "#include <SPI.h>

char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}

void setup()
{
  Serial.begin(9600);
  SPI.begin(10);
  pinMode(PowerControl, OUTPUT);
    digitalWrite(PowerControl, LOW);                           //Set it LOW to activate 3V based on Shield Schematic
  pinMode(RelayCtrl, OUTPUT);                                 //Enable Relay Control
    digitalWrite(RelayCtrl, HIGH);                               //Set HIGH to activate 
  
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device
  //SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/4 rate (fastest)
  SPCR = (1<<SPE)|(1<<MSTR);            //  SPCR Control Register
  clr = SPSR;                           //  SPSR Status Register
  clr = SPDR;                           //  SPDR Data Register

Writing_Reading5:70:9: error: 'SPDR' was not declared in this scope
clr = SPDR; // SPDR Data Register

Those are the registers used in the 8 bit avr architecture like the AT328.

The Arduino Due is based on the Atmel SAM3X8E ARM Cortex-M3 CPU which is a 32-bit ARM core microcontroller. There is different nomenclature and values for the spi registers on the device.

Apples and Oranges.

Is there someone who could give me a sample code that will work with MX25L6406E EEPROM using SPI from Arduino Due. Im breaking my head here and I'm still very confused. I am not able to understand how to use commands in the datasheet of MX25L6406E EEPROM such as WREN, READ, and WRITE. Then, implementing it on SPI.transfer().

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.