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.
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.
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.
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.
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;
Relay Control needs to be HIGH
CS Pin: 10
Yes, there is a board for MX25L640. Unfortunately, I can't show it for privacy consideration.
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
}
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.