Pages: [1] 2   Go Down
Author Topic: Arduino Uno and ATMEL memory chip using SPI  (Read 6792 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey all.

I've been working hard the past 2 weeks trying to get my Arduino to successfully and consistently write/read from memory using SPI but I am having problems. I used the tutorial code for SPIEEPROM to get me started and have made the proper modifications for the memory chip I am using. I'm using the Arduino Uno and the Atmel AT25DF641 chip (data sheet here: http://www.atmel.com/dyn/resources/prod_documents/doc3680.pdf).

For the connections I used pins 13,12,11,10 from the Arduino to the chip (on a breadboard), and also tied the WP to Vcc.

Here's the code that I have so far:

Code:
int DATAOUT =  11;//MOSI
int DATAIN =  12;//MISO
int SPICLOCK =  13;//sck
int SLAVESELECT =  10;//ss
 
byte eeprom_output_data;
byte clr;
int address=0;
char buffer [128];

void setup()
{
  
  Serial.begin(9600);
  
  initPins();  //initialize data pins
  
  digitalWrite(SLAVESELECT,HIGH); //disable device
  
  DDRB = (1<<DDB5)|(1<<DDB3)|(1<<DDB2);  // set DDRB register
  
  delay(1000);      // delay
  
  // CPOL = 0 base value of clock is zero (SPI Mode0)
  // CPHA = 0 data captured on rising edge(SPI Mode0)
  // Arduino Master Mode
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
  clr=SPSR;
  clr=SPDR;
  
  delay(1000);    // delay
  
  fill_buffer();    // fill a char array in code
  
  digitalWrite(SLAVESELECT,HIGH);
  digitalWrite(SLAVESELECT,LOW);   // start operation HIGH->LOW
  spi_transfer((char)6);                 //write enable
  digitalWrite(SLAVESELECT,HIGH);  //end operation LOW->HIGH  
  
  delay(1000);  // wait 1 second
  
  digitalWrite(SLAVESELECT,LOW); //start operation HIGH->LOW
  spi_transfer((char)2);       // write command
  spi_transfer((char)0);       //first address byte (MSB)
  spi_transfer((char)0);       //second address byte (next byte)
  spi_transfer((char)0);       //third address byte (LSB)
  
  // Loop sending each char in char array to memory chip
  for (int I=0;I<128;I++)
  {
    Serial.print("Sending "); Serial.print(buffer[I], DEC);
    Serial.println(" to memory.");
    spi_transfer(buffer[I]); //write data byte
    delay(3);
  }
 
  digitalWrite(SLAVESELECT,HIGH); //end operation LOW->HIGH
  
  delay(3000); // wait 3 seconds
  
  Serial.print('h',BYTE);
  Serial.print('i',BYTE);
  Serial.print('\n',BYTE);//debug
}

void loop()
{
  
  eeprom_output_data = read_eeprom(address);
  
  Serial.print("Data: ");Serial.print(eeprom_output_data,DEC);Serial.print(" from Address: ");Serial.println(address,DEC);
    
  address++;
  
  if(address == 128)
    address=0;
 
 delay(500);
 
}

void fill_buffer()
{
  for (int I=0;I<128;I++)
  {
    buffer[I]=I;
  }
}

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


byte read_eeprom(int EEPROM_address)
{
  int data;
  
  digitalWrite(SLAVESELECT,LOW);
  spi_transfer((char)11);                       //transmit read opcode
  spi_transfer((char)(EEPROM_address>>8)); // first address byte
  spi_transfer((char)(EEPROM_address>>8)); // second address byte
  spi_transfer((char)(EEPROM_address));   // third address byte
  spi_transfer((char)(B11111111)); // dummy byte
  data = spi_transfer((char)(B11111111)); // generate extra clock signals for data
  digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
  
  delay(10);
  
  return data;
}

void initPins()
{
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
}


My output is:
Code:
Data: 0 from Address: 0
Data: 0 from Address: 1
.
.

I get 0's for each address despite having the loop that writes to memory. Does anything in the code stick out as wrong? Any help is appreciated!!
« Last Edit: February 17, 2011, 08:16:49 am by truce11 » Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 544
Posts: 27352
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Don't you to print eeprom_output_data after the read, and not testdata?

eeprom_output_data = read_eeprom(address);
 
  Serial.print("Data: ");Serial.print(testdata,DEC);Serial.print(" from Address: "); Serial.println(address,DEC);
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

0
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Don't you to print eeprom_output_data after the read, and not testdata?

eeprom_output_data = read_eeprom(address);
 
  Serial.print("Data: ");Serial.print(testdata,DEC);Serial.print(" from Address: "); Serial.println(address,DEC);


Yes, I apologize; I accidentally posted an earlier version of my code that gave me an error (I'll change the original post). However, the output I posted is what I get even when I print eeprom_output_data.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So I've still been working on it to no avail. Any chance it's a timing issue? Maybe I need to throw some delays in there. I'm just not sure what the problem is; it seems as if I'm doing everything in the proper order.
Logged

Toronto, ON
Offline Offline
Full Member
***
Karma: 10
Posts: 233
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I haven't looked at any of the data sheets in a while, but if I remember correctly, when I was using the DataFlash AT45 parts way back when, there is a clock polarity issue - you will have to play around with the clock polarity/phase on the master SPI port to match.  So, if the AT25 parts use the same SPI hardware, you may face this as well.

Again, I'm not 100% certain of what's going on, but I thought I'd pass on my findings from a while back.

b
Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 495
Posts: 19032
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Why not use the SPI library?

Anyway, your problem is either writing or reading, agreed?

Now if the reading part is correct, then check out this from the data sheet:

Quote
The Byte/Page Program command allows anywhere from a single byte of data to 256-bytes of data to be programmed into previously erased memory locations. An erased memory location is one that has all eight bits set to the logical “1” state (a byte value of FFh).

You read back zero, which doesn't meet the condition of being the value 0xFF. Therefore it won't write to it.
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 9
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've been trying to do the same thing...with different hardware.

truce: I've tried your code on a nano 3.0 and atmel AT25DF041A (datasheet - http://www.atmel.com/dyn/resources/prod_documents/doc3668.pdf)

I'm using the same pins on the arduino and a logic level converter (http://www.sparkfun.com/products/8745) between the eeprom and the nano.

The results I'm getting are:
Code:
Data: 255 from Address: 0
Data: 255 from Address: 1
Data: 255 from Address: 2
Data: 255 from Address: 3
...

Nick: because I am reading 0xFF, do you have any other ideas what might be going on?

bhagman: i've tried both supported modes (0 and 3)
Code:
//CPOL = 0 base value of clock is zero (SPI Mode0)
//CPHA = 0 data captured on rising edge(SPI Mode0)
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
and
Code:
//CPOL = 1 base value of clock is high (SPI Mode3)
//CPHA = 1 data captured on falling edge(SPI Mode3)
SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPOL)|(1<<CPHA)|(1<<SPR1)|(1<<SPR0);

both to no avail...

anybody have any ideas?
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 495
Posts: 19032
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Nick: because I am reading 0xFF, do you have any other ideas what might be going on?

Hmm, 4 Mb eh? Would be nice to have one. If only I could solder SMD devices. smiley-sad

Is it possible to solder the 8SOIC package onto an adapter board if you are careful? Or is it throwing money away?

Anyway, getting 0xFF back tends to imply getting no response, since the line is probably pulled high anyway. Without having one in my hands, I would suggest to you to try the "Read Manufacturer and Device ID" (page 27 of spec) to check your comms with the chip are going OK. Then we can move onto programming it.

With the logic level converter, are you using the Tx pins? Or let me put it another way, how exactly did you wire it up?
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 9
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It is fairly easy with these http://www.sparkfun.com/products/494.

I've actually used two of the level converters with all 4 Tx pins. Not using any of the Rx rails.

MISO, MOSI, SCK, CS are all going from the nano to the level converters 4 Tx pins, then to the EEPROM's SO, SI, SCK, CS. WP and HOLD are both pulled high on the EEPROM. The high voltage side of the level converters are powered by the 5V pin on the nano, and the low voltage side is powered by the 3.3V pin on the nano, as is the EEPROM.

I didn't even think to try to read the manufacturer and ID...I will update when I've tried that.
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 495
Posts: 19032
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, I know I'm showing my ignorance here, but I'm hoping one of the electronics experts will chime in and explain ...

That Sparkfun level-converter you are using - I looked at that and looked at the circuit. On the Sparkfun page the claim is made that the Tx circuits are bi-directional. Now  they are implemented with BSS138 FET transistors. I didn't know transistors could switch "both ways". If someone can explain or give a reference that would be great.

The text on the page says:

Quote
A digital one going into the TXI pin on the 3.3V side will show up on the TXO pin on the 5V side as 5V.

And one of the photographs states (with arrows) that the Tx is one-way. So either I am totally wrong (not the first time), or you should not be using the Tx pins for all 4 signals. The way I read their design you should use the voltage-divider resistors for high-to-low, and the BSS138 transistors for low-to-high. Can someone else comment?
Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 495
Posts: 19032
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

This document may explain the design behind the bi-directional level-shifter:

http://www.nxp.com/acrobat_download/applicationnotes/AN10441_1.pdf
Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 495
Posts: 19032
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

After re-reading the document I posted above (about the level shifter) I think I understand it better. But as I read it, it is for I2C and not SPI. In particular this sentence:

Quote
The devices of each section have I/Os with supply voltage related logic input levels and an open-drain output configuration.

But SPI is not open-drain (whereas I2C is). This may not matter. But the technical note specifically mentions I2C (and even mentions SDA and SCL), so there may be some subtle "gotcha" when trying to use it for SPI. For example, a timing issue.
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 9
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It seems the Tx lines are indeed working both ways. Using this code:

Code:

//opcodes
#define WREN  6  //write enabled
#define WRDI  4  //write disabled
#define RDSR  5  //read status register
#define WRSR  1
#define READ  11
#define WRITE 2
#define RMDID 159 //Read Manufacturer Device ID

int DATAOUT =  11;//MOSI
int DATAIN =  12;//MISO
int SPICLOCK =  13;//sck
int SLAVESELECT =  10;//ss

byte eeprom_output_data;
byte clr;

void setup()
{
 
  Serial.begin(9600);
 
  initPins();  //initialize data pins
 
  digitalWrite(SLAVESELECT,HIGH); //disable device
 
  DDRB = (1<<DDB5)|(1<<DDB3)|(1<<DDB2);  // set DDRB register
   
  delay(100);      // delay
 
  // CPOL = 0 base value of clock is zero (SPI Mode0)
  // CPHA = 0 data captured on rising edge(SPI Mode0)
  // Arduino Master Mode
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
  clr=SPSR;
  clr=SPDR;
 
  delay(1000);    // delay
 
  Serial.print('\n',BYTE);
  Serial.print("Setup Complete");
  Serial.print('\n',BYTE);//debug
}

void loop()
{
  digitalWrite(SLAVESELECT,LOW);
  spi_transfer(RMDID);                             //transmit read device and manf. ID
  eeprom_output_data = spi_transfer(RMDID);
  digitalWrite(SLAVESELECT,HIGH);
 
  delay(500);
 
  Serial.print("Data: ");Serial.print(eeprom_output_data,DEC);
  Serial.print('\n',BYTE);
  delay(500);
 
}

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

void initPins()
{
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
}

I successfully get this in return:

Code:
Data: 31

Which is the correct manufacturers ID.

So now I just need to figure out what is going on in my code for writing to it.   smiley-confuse
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey all,

I've been using a new memory chip since the last time (still working on same problem). I tried what sark did, as far as reading the manufacturer ID using this code:

Code:
#include <SPI.h>

const int SlaveSelect = 10;
const int MOSI = 11;
const int MISO = 12;
const int CLK = 13;
byte myByte;

void setup()
{
  Serial.begin(9600);
   
  pinMode(MISO, INPUT);
 
  SPI.begin();
 
  delay(10);
 
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV128);
 
  delay(10);
 
 
  digitalWrite(SlaveSelect,LOW);
  delay(10);
  SPI.transfer(0x06);
  delay(10);
  digitalWrite(SlaveSelect,HIGH);
 
  delay(10);
 
   digitalWrite(SlaveSelect,LOW);
  delay(10);
  SPI.transfer(0x02);
  SPI.transfer(0x00);
  SPI.transfer(0x00);
  SPI.transfer(0x05);
  myByte = SPI.transfer(0x255);
   digitalWrite(SlaveSelect,HIGH);
 
 
  Serial.println("done");
 
 
}


void loop()
{
 
  digitalWrite(SlaveSelect,LOW);
  delay(10);
  SPI.transfer(0x90);
  SPI.transfer(0x00);
  SPI.transfer(0x00);
  SPI.transfer(0x00);
  myByte = SPI.transfer(0x01);
  digitalWrite(SlaveSelect,HIGH);
 
 
  Serial.println(myByte,DEC);
  delay(500);

 
}

with the following output:
Code:
done
0
239
0
0
0
239
0
0
0
239
0
0
0
239

239 is the correct ID, but does anyone have an idea why its not continuous and there are 0s in between?


Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 495
Posts: 19032
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Why the extra transfers?

You didn't say which chip you are using this time, but based on the AT25DF641 data sheet, I would expect you to do something like this:

 
 
Code:
  digitalWrite(SlaveSelect,LOW);
  SPI.transfer(0x90);   // send "read manufacturer ID" command (or is it 0x9F?)
  delay(1);
  byte man_id = SPI.transfer(0);
  delay(1);
  byte device_id1 = SPI.transfer(0);
  delay(1);
  byte device_id2 = SPI.transfer(0);
  delay(1);
  byte extended_info = SPI.transfer(0);
  digitalWrite(SlaveSelect,HIGH);

The spec says the manufacturer's ID comes first (and it would be surprising if different chips put the manufacturer's ID in different places, that would kind-of defeat the point).

And are you sure that 0x90 is the correct command? The previous post mentioned 159 (0x9F) which is what the AT25DF641 uses. Again, it would be surprising if different manufacturers used a different "read manufacturer ID" command, because you need to know in advance then, which command to send (ie. need to know the manufacturer), in order to find out who the manufacturer is. In which case, again not much point.
Logged


Pages: [1] 2   Go Up
Jump to: