Go Down

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

Nick Gammon


Code: [Select]

void loop ()
  {
  sensorValue = analogRead (analogInPin);
  writeEEPROM (address, (byte *) &sensorValue, sizeof sensorValue);
 
  byte test2 [2]
  readEEPROM (address, test2, sizeof test2);
  Serial.println ((char *) test2);
 
  addr += sizeof sensorValue;
  Serial.print("analog pin: ");Serial.print (sensorValue, DEC);Serial.print('\n',BYTE); //debug
  Serial.print("address: ");Serial.print (address, DEC);Serial.print('\n',BYTE); //debug
  }  // end of loop


I'm confused on how to read the int after it has been written. I had the test2 byte in there just so I could try to read something, but all I got out was garbage.


When writing we passed the address of sensorValue (ie. &sensorValue) so you have to do the same thing when reading back. That is:

Code: [Select]
int test;
readEEPROM (address, &test, sizeof test);


That reads back into the 2-byte integer in the same way we wrote the data tout.

sark86

So now I'm trying to figure out how to read data back after it has been powered off. I execute the readEEPROM command right in the setup function before anything else has been executed.
Code: [Select]

int reading;
for (int I=0;I<100;I=I+2)
  {
    readEEPROM (I, &reading, sizeof reading);
    Serial.println (reading);
  }

The results I get back are all 255 (hex FF). I know these addresses were previously written to with different values, and I'm not sure what to do from here. Any ideas?

Nick Gammon

Does it work before being powered off? Can you post all your code please?

sark86

Yah, it seems to be working fine, except every time it gets powered on it will read 255 for all the values in the for loop in the setup function. Then it continues to the loop function and works fine. I have a potentiometer on the analog pin to change the values that are written to make sure the values are actually changing when getting written. When I power it off and back on I would expect those values that were written previously to be read out right away, but that's not happening.

Code: [Select]

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

#include <SPI.h>

#define CHIP_SELECT 10   // for EEPROM

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

int address = 0;
int analogInPin = A6;
int sensorValue = 0;
int reading;

// 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, int * 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 (115200);
  SPI.begin ();

  // global unprotect
  writeStatus (0);
 
  Serial.print('\n',BYTE);
  Serial.println ("Status:");
  info ();

  for (int I=0;I<100;I=I+2)
  {
    readEEPROM (I, &reading, sizeof reading);
    Serial.println (reading);
  } 
}  // end of setup

void loop ()
  {
   
  eraseEEPROM (address);
  sensorValue = analogRead (analogInPin);
  writeEEPROM (address, (byte *) &sensorValue, sizeof sensorValue);
 
  readEEPROM (address, &reading, sizeof reading); 
  Serial.println (reading);
 
  address += sizeof sensorValue;
  }  // end of loop


Nick Gammon

I'm a little troubled by this:

Code: [Select]
void loop ()
  {
   
  eraseEEPROM (address);
  sensorValue = analogRead (analogInPin);
  writeEEPROM (address, (byte *) &sensorValue, sizeof sensorValue);
 
  readEEPROM (address, &reading, sizeof reading); 
  Serial.println (reading);
 
  address += sizeof sensorValue;
  }  // end of loop


Your main loop is going to take a reading and write it to the EEPROM, very rapidly. These chips are not designed for billions of writes. I would have expected you to place a delay (eg. a second) before writing another reading. That way your chip is written to much more slowly. I can't see on their spec sheet how many writes or erases it can tolerate, but it wouldn't be infinite.

The second issue is this line:

Code: [Select]
  eraseEEPROM (address);


According to the comments in my code:

Code: [Select]
// 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


That erase 4Kb every time (in other words, through your main loop you erase all 4 Kb of the initial memory, every time you take a reading. I take it this is unintentional?

The flash chips are designed so that you have to erase "pages" of data back to 0xFF, and then you write to them, which changes some 1s to 0s. You can't erase individual bytes.

So you really need to design in such a way that, when you change to a new 4Kb block, you erase it first. And then don't do it again until you move onto another 4Kb block.

This also relates to the issue of writing very quickly. Without some pause, you will have written to all the chip, erased, the data, and written over it, probably every minute or so.

You may also want to consider keeping track of the current position (to write to). Another recent post had the poster writing his current position to addresses 0/1, so he read that in at setup, to find where to write his next reading to. You might want to do something like that.

Go Up