Getting JEDEC Device ID from SPI Flash IC

Hello,

I recently got a project where I need to get the device ID/Manufacturer ID from a MX25L640E flash IC. I search for similar examples but nothing is teaching me how to do it. I know the basic concept of SPI, but implementing it to a code is a little hard in my perspective. Im using an Arduino Due for this project and read the datasheet. I understood what I need to do without coding it (ie CS needs to be low to select the chip) but I need more background in regards to communication between arduino and the chip.

Any examples you could refer me to or even what steps should I take to accomplish this? Im not going anywhere with my researching skills.

MX25L6406E, 3V, 64Mb, v1.5 (1).pdf (2.4 MB)

Here is an example I wrote that should bring you close to a solution. I did not test it because I do not have such a device, but I followed the datasheet. I am not certain about the SPI mode you might need to adapt that. I chose one that might work and 1MHz SPI clock. That should also work in most cases. You need to change the SPI_CS_PIN to whatever you used on your Arduino.

#include <SPI.h>

SPISettings spiSettings( 1000000, MSBFIRST, SPI_MODE0 );
#define SPI_CS_PIN                          10
#define SPI_DUMMY_BYTE                      0x00
#define MX25_REMS_COMMAND                   0x90
#define MX25_REMS_ADDRESS_MANUFACTURER_ID   0x00
#define MX25_REMS_ADDRESS_DEVICE_ID         0x01


void setup()
{
  pinMode( SPI_CS_PIN, OUTPUT );
  Serial.begin( 9600 );
  while ( !Serial );
  
  SPI.begin();
  printMX25REMS();
}


void loop()
{
}

// See MX25L6406E DATASHEET
// P/N: PM1577 REV. 1.5, JUN. 13, 2012
// 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 );
  
  SPI.transfer( MX25_REMS_COMMAND );
  SPI.transfer( SPI_DUMMY_BYTE );
  SPI.transfer( SPI_DUMMY_BYTE );
  SPI.transfer( MX25_REMS_ADDRESS_MANUFACTURER_ID );

  uint8_t manufacturerID = SPI.transfer( SPI_DUMMY_BYTE );
  uint8_t deviceID = SPI.transfer( SPI_DUMMY_BYTE );

  digitalWrite( SPI_CS_PIN, HIGH );
  SPI.endTransaction();

  Serial.print( "MX25 Manufacturer ID: 0x" );
  Serial.println( manufacturerID, HEX );
  Serial.print( "MX25 Device ID: 0x" );
  Serial.println( deviceID, HEX );
}

If you have any questions let me know.

Thank you for giving me an idea of what to do. Now, I somehow understand how the code works in relation to the SPI concept. The next thing I need to research is what output should I expect. Currently, the output of the program is 0x0 for both device ID and Manufacturer ID.

For those who might search for the same topic and couldn't figure it out like me, below is a 'revised' code with comments to ease the pain of self-teaching.

/* 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. 
*/

#include <SPI.h>

SPISettings spiSettings( 1000000, MSBFIRST, SPI_MODE0 );    // SPI_MODE0:    CPOL = 0 | CPHA = 0 | Output Edge = Falling | Data Capture = Rising
#define SPI_CS_PIN                          10              //Is this the correct CS# pin?
#define SPI_DUMMY_BYTE                      0x00
#define MX25_REMS_COMMAND                   0x90
#define MX25_REMS_ADDRESS_MANUFACTURER_ID   0x00
#define MX25_REMS_ADDRESS_DEVICE_ID         0x01
#define PowerControl_PIN                      11            //Shield power 3VDC power control pin


void setup()
{
  pinMode(PowerControl_PIN, OUTPUT);
  digitalWrite(PowerControl_PIN, LOW);                      //Set it LOW to activate 3V based on Shield Schematic

  pinMode( SPI_CS_PIN, OUTPUT);
  Serial.begin( 9600 );
  while ( !Serial );
  
  SPI.begin();
  printMX25REMS();
}


void loop()
{

}

// 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
  
  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 deviceID = SPI.transfer( SPI_DUMMY_BYTE );
  uint8_t manufacturerID = SPI.transfer( SPI_DUMMY_BYTE );

  digitalWrite( SPI_CS_PIN, HIGH );                           //Instruction complete based on manuf datasheet
  SPI.endTransaction();

  Serial.print( "MX25 Manufacturer ID: 0x" );
  Serial.println( deviceID, HEX );
  Serial.print( "MX25 Device ID: 0x" );
  Serial.println( manufacturerID, HEX );
}type or paste code here

That suggests there is no output from the device. It is unlikely that information is zero.

Do you have a schematic of your system (hand drawn is OK)?
Are you using a shield with the device on it? Do you have a datasheet, schematic or any other information about it?
You will need to confirm the CS pin first. Then I would try SPI_MODE3.

Yes, pin 10 is correct for CS pin looking at Arduino DUE. Im missing connections/data lines from my code. Looking at the pinouts for MX25L6406E / U1:

  1. SDO
  2. SDI
  3. SCL
    How do you implement this?
    These images are part of the schematic for MX25L640 and the shield I am using...



SDO, SDI and SCL are terms for the SPI bus lines. There are two common naming schemes for SPI.

SDO - Serial Data Out and SDI - Serial Data In and SCL is the Clock. This naming convention requires you to cross the signals SDO from one side (e.g. Master) to to the SDI of the other side (Slave) and the other way around.

The second is MOSI - Master Out Slave In and MISO - Master In Slave Out and SCK is the clock again. With this naming convention you need to connect MOSI to MOSI and MISO to MISO and SCK.

So on the Master (e.g. your Arduino)

  • MOSI = SDO and MISO = SDI

on the Slave

  • MOSI = SDI and MISO = SDO

SPI does not require any external components on the bus lines.

If you have a shield I would have expected the pins to be connected correctly.

Do you have a link to the documentation of your shield? The images are hard to read (getting old) and do not show the physical location.

Here is the shield I'm using:

(SHIELD_2).pdf (32.6 KB)

Thanks for the file.

There is no MX25L640E on the schematic. However there are multiple SPI connections going to J6.

Is there another smaller board on the shield? Is the MX25L640E on that board? If yes, what is it?

Additionally, the SPI pins ( MOSI and SCK ) are routed trough the relay. That relay is controlled trough a pin connected to J9 (top right corner).

You need to find out:

  • which of the SPI connections is the MX25L640E
  • select the correct CS line (see schematic)
  • check whether you need to pull the RELAY_CTRL high or low to the get relay into the right state for the connection to work.
    -- I suspect you need to pull the pin high to enable the relay and therefore close the connection to connect your Arduino MOSI and SCK to the device/board on J6.
    -- you can test whether the connection between COM1 and NO1 is closed or open when the board is not powered.

@Klaus_K Thank you so much. I'm getting close enough to my end goal. Added LCD display as well.. The only error I have is the device ID outputs 17 instead of 16 as defined in the datasheet of MX25L640 page 22.

Your Questions;

  1. Relay Control needs to be HIGH
  2. CS Pin: 10
  3. Yes, there is a board for MX25L640. Unfortunately, I can't show it for privacy consideration.
  4. Shield's pin number is not consistent with Arduino Due. I followed Arduino's pin numbering instead.
/* 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
#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( SPI_DUMMY_BYTE );
  uint8_t manufacturerID = SPI.transfer( SPI_DUMMY_BYTE );

  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 
}

Output:

I got it!!!! I was using MX25L12835F as my UUT. Not the one we talked about. Thanks again!

One last question: Why my output stays the same even if I change "SPI_DUMMY_BYTE" to MX25_REMS_ADDRESS_DEVICE_ID or MX25_REMS_COMMAND?

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( SPI_DUMMY_BYTE );
  uint8_t manufacturerID = SPI.transfer( SPI_DUMMY_BYTE );

Dummy bytes are just to make the master create clock cycles on the SPI interface clock line. There are two cases in for your devices. The value of the dummy byte is ignored.

  • When you read data, the master must send a dummy byte and therefore create clock cycles to tell the slave when it needs to put each bit onto the MISO line.
  • The device internal system seems to need some additional clock cycles in some cases to do some internal work e.g., making data ready. This probably saves internal logic and make the device simpler and cheaper to produce.