Go Down

Topic: Arduino Uno and ATMEL memory chip using SPI (Read 7 times) previous topic - next topic


You must be using a different chip than the AT25DF641 you originally used if 239 is the ID. Without knowing what you are using I can't check and see if the opcodes you are using are correct.

Also, I think your
Code: [Select]
myByte = SPI.transfer(0x255); is incorrect as that is a hex 255 not a decimal 255. I think you would want
Code: [Select]
myByte = SPI.transfer(0xFF);


Yes, I should have included the memory chip I am using now in my post. Its a winbond: http://www.winbond.com.tw/NR/rdonlyres/591A37FF-007C-4E99-956C-F7EE4A6D9A8F/0/W25Q64BV.pdf

This chip requires three address bytes of B00000000 after the read manufacturer id command.

And thanks sark, I didnt catch that.

Nick Gammon

Based on the ID you gave (239 = 0b11101111) and looking up this page:


I am guessing you have a NEXCOM chip of some sort. Possibly something like the NX25P40.

Their data sheet does indeed say you get the manufacturer ID with 0x90, you then clock in 3 lots of zero, and on the fourth byte it responds with the manufacturer ID, and then the device ID.

As to why that only sometimes works? Well maybe read out the device ID as well. Maybe the chip is sending it, but you haven't clocked it out, so it is waiting? Just a guess. Try adding in the extra transfer and see what happens.


Your more recent post says you are now using the W25Q64BV ... I see you can query the manufacturer ID using 0x9F (as well as 0x90 which requires the 3 extra bytes) so maybe try that.

Nick Gammon

I got the Atmel AT25DF041A chip today, and after quite a struggle, got it to program. :)

Some of the challenges were:

  • Soldering the 8-SOIC SMD device without destroying it ;)

  • Getting the buffer chip to work

  • Understanding the AT25DF041A documentation

  • Working out how to disable write-protect, and sector protection

  • Reading the status register to know when the chip wasn't busy any more

Anyway, the code below works. I used the Atmel AT25DF041A "4-megabit 2.3-volt or 2.7-volt Minimum SPI Serial Flash Memory" with the SN54AHC125 "Quadruple Bus Buffer Gates with 3-state outputs" as a 3.3 to 5v buffer.

The buffer and EEPROM chip were powered off the 3.3V Arduino output pin.

I didn't need to do custom SPI software, the standard library worked fine.

To avoid spurious data on the SPI lines I wired up pin 9 to the buffer chip "3-state enable" pins so that the buffer could be enabled in an orderly way. This is brought low to allow communication with the EEPROM.

Code: [Select]

// Written by Nick Gammon
// 10th March 2011

#include <SPI.h>

#define CHIP_SELECT 10   // for EEPROM
#define BUFFER_ENABLE 9  // for SN54AHC125 buffer

// AT25DF041A EEPROM commands

// reading
#define ReadArray             0x0B
#define ReadArrayLowFrequency 0x03

// programming
#define BlockErase4Kb       0x20
#define BlockErase32Kb      0x52
#define BlockErase64Kb      0xD8
#define ChipErase           0x60
#define ByteProgram         0x02
#define SequentialProgram   0xAD

// protection
#define WriteEnable           0x06
#define WriteDisable          0x04
#define ProtectSector         0x36
#define UnProtectSector       0x39
#define ReadSectorProtection  0x3C

// status
#define ReadStatus 0x05
#define WriteStatus 0x01

// miscellaneous
#define ReadManufacturer     0x9F
#define DeepPowerDown        0xB9
#define ResumeFromPowerDown  0xAB

// wait until chip not busy
void notBusy ()
  digitalWrite (CHIP_SELECT, LOW);
  SPI.transfer (ReadStatus);       
  // wait until busy bit cleared
  while (SPI.transfer (0) & 1)
  digitalWrite (CHIP_SELECT, HIGH); 
}  // end notBusy

// enable writing
void writeEnable ()
notBusy ();

digitalWrite (CHIP_SELECT, LOW);
SPI.transfer (WriteEnable);       
digitalWrite (CHIP_SELECT, HIGH); 
}  // end of writeEnable

// read device status
byte readStatus (void)
digitalWrite (CHIP_SELECT, LOW);
SPI.transfer (ReadStatus);       
byte status = SPI.transfer (status);       
digitalWrite (CHIP_SELECT, HIGH); 
return status;
}  // end of readStatus

// write status register
void writeStatus (const byte status)
   writeEnable ();
   notBusy ();  // wait until ready
   digitalWrite (CHIP_SELECT, LOW);
   SPI.transfer (WriteStatus);       
   SPI.transfer (status);       
   digitalWrite (CHIP_SELECT, HIGH); 
}  // end of writeStatus

// send a command to the EEPROM followed by a 3-byte address
void sendCommandAndAddress (const byte command, const unsigned long addr)
  SPI.transfer (command);       
  SPI.transfer ((addr >> 16) & 0xFF);       
  SPI.transfer ((addr >> 8) & 0xFF);       
  SPI.transfer (addr & 0xFF);       
}  // end of sendCommandAndAddress

// write len (max 256) bytes to device

// Note that if writing multiple bytes the address plus
//  length must not cross a 256-byte boundary or it will "wrap"
void writeEEPROM (const unsigned long addr, byte * data, byte len)
  // now write to it
  writeEnable ();
  notBusy ();  // wait until ready
  digitalWrite (CHIP_SELECT, LOW);
  sendCommandAndAddress (ByteProgram, addr);
  for ( ; len ; --len)
    SPI.transfer (*data++);       
  digitalWrite (CHIP_SELECT, HIGH); 
  notBusy ();
} // end of writeEEPROM

// write one byte to device
void writeEEPROM (unsigned long addr, byte data)
  writeEEPROM (addr, &data, 1);
} // end of writeEEPROM

// read len bytes from device
void readEEPROM (const unsigned long addr, byte * data, unsigned int len)
  notBusy ();  // wait until ready
  digitalWrite (CHIP_SELECT, LOW);
  sendCommandAndAddress (ReadArray, addr);

  SPI.transfer (0);  // clock in "don't care" byte

  for ( ; len ; --len)
   *data++ = SPI.transfer (0);       
  digitalWrite (CHIP_SELECT, HIGH); 
}  // end of readEEPROM

// erase a 4Kb block of bytes which contains addr
void eraseEEPROM (const unsigned long addr)
  writeEnable ();

  notBusy ();  // wait until ready
  digitalWrite (CHIP_SELECT, LOW);
  sendCommandAndAddress (BlockErase4Kb, addr);
  digitalWrite (CHIP_SELECT, HIGH); 
}  // end of eraseEEPROM

// show device info and status
void info ()
  notBusy (); // wait until ready
  digitalWrite (CHIP_SELECT, LOW);
  SPI.transfer (ReadManufacturer);       
  Serial.print ("Manufacturer: ");
  Serial.println (SPI.transfer (0), HEX);
  Serial.print ("Device ID Part 1: ");
  Serial.println (SPI.transfer (0), HEX);
  Serial.print ("Device ID Part 2: ");
  Serial.println (SPI.transfer (0), HEX);
  Serial.print ("Extended Information Length: ");
  Serial.println (SPI.transfer (0),HEX);

  digitalWrite (CHIP_SELECT, HIGH);
  Serial.print ("Status: ");
  Serial.println (readStatus (), HEX);

} // end of info

void setup ()
  Serial.begin (9600);
  SPI.begin ();
  // enable the SN54AHC125 output enable peons (more work?)
  pinMode (BUFFER_ENABLE, OUTPUT);  // buffer chip enable
  digitalWrite (BUFFER_ENABLE,LOW);  // enable

  // global unprotect
  writeStatus (0);
  Serial.println ("Status:");
  info ();
  // test: write a string to address 0x1000

#define TESTADDRESS 0x1000

  byte hello [] = "Hello, World!";
  writeEEPROM (TESTADDRESS, hello, sizeof hello);
  // read back to confirm
  byte test [sizeof hello] = { 0 } ;
  readEEPROM (TESTADDRESS, test, sizeof test);

  Serial.println ((char *) test);  // display to confirm
}  // end of setup

void loop()

}  // end of loop

This is basically a demo program, but incorporates routines to write up to 256 bytes and read up to 65536 bytes. There is a lot of complexity in the chip itself which would have obscured the demo if I tried to incorporate it all, like sector protection, writing pages, erasing blocks of various sizes and so on.

One point which wasn't initially obvious is that you have to keep enabling write-enable. Every write operation cancels it (as a safety precaution no doubt). So to disable software protection you first do a write-enable. Then to erase some memory you have to first do a write-enable again. And then to write to the memory you have to first do a write-enable.


Awesome! Got mine working with your code Nick, thanks A LOT!

Now to get the sequential program mode to work so I don't have to mess with write enabled every single time.  :smiley-roll-sweat:

Go Up