Trouble with SPI interfaced SRAM chip

I'm trying to get my Uno communicating with an SRAM chip (Microchip 23LC512) via SPI.
As a simple verification, I write several bytes of data then go back and read them, and print it on the serial monitor. But, when I run my code I get this:

Memory status:	
20	1010101
21	1010101
22	1010101
23	1010100
400	10101010
401	1010101
402	1010101
403	1010101

Memory addresses are 16 bit, shown in hex; values are in binary. In this case I wrote B10101010 to each memory address.
I can't find any consistent pattern--sometimes several bytes in a row are correct, others none. Here it looks like it's wrapping values from the back to the front?
Any help is very much appreciated!

The code:

// Read and write to SRAM via SPI

#include <SPI.h>

//Pin definitions:
#define CS 8

//SRAM SPI commands
#define READ 0x03    //READ and WRITE to be followed by 16 bit address
#define WRITE 0x02
#define RDMR 0x05    //read mode register
#define WRMR 0x01    //write mode register
#define RSTIO 0xFF   //reset input mode
//Mode commands
#define BYTE 0x00
#define PAGE 0x80
#define SEQUENTIAL 0x40

void configureRAM(void);    //declare function

//Incoming and outgoing start addresses (16 bit)
unsigned int cw_addr=0x0020;
unsigned int ccw_addr=0x0400;

byte incoming=0xFF;      //incoming SPI data
byte cw_status[4];
byte ccw_status[4];

int i=0;

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  configureRAM();
  
  
  //read CW memory status byte
  digitalWrite(CS,LOW);
  SPI.transfer(READ);
  SPI.transfer(highByte(cw_addr));
  SPI.transfer(lowByte(cw_addr));
  for(i=0;i<4;i++)
  {
    cw_status[i]=SPI.transfer(0x00);
  }
  digitalWrite(CS,HIGH);
  
  //read CCW memory status byte
  digitalWrite(CS,LOW);
  SPI.transfer(READ);
  SPI.transfer(highByte(ccw_addr));
  SPI.transfer(lowByte(ccw_addr));
  for(i=0;i<4;i++)
  {
    ccw_status[i]=SPI.transfer(0x00);
  }
  digitalWrite(CS,HIGH);
  
  //print results
  Serial.println("Memory status:\t");
  
  for(i=0;i<4;i++){
    Serial.print(cw_addr+i,HEX);
    Serial.write('\t');
    Serial.println(cw_status[i],BIN);
  }
  for(i=0;i<4;i++){
    Serial.print(ccw_addr+i,HEX);
    Serial.write('\t');
    Serial.println(ccw_status[i],BIN);
  }
}

void loop()
{
  
}


//Configure RAM settings and clear memory status bytes.
//Initialize SPI before calling configureRAM()
void configureRAM(void)
{
  SPI.setClockDivider(SPI_CLOCK_DIV2);    //SPI clock at 8MHz
  SPI.setDataMode(SPI_MODE0);            //CPOL=0, CPHA=0
  SPI.setBitOrder(MSBFIRST);              //MSB first
  
  pinMode(CS,OUTPUT);
  
  digitalWrite(CS,LOW);    //set to single SPI mode
  SPI.transfer(WRMR);
  SPI.transfer(RSTIO);
  digitalWrite(CS,HIGH);
  
  digitalWrite(CS,LOW);
  SPI.transfer(WRMR);        //Set data mode to sequential
  SPI.transfer(SEQUENTIAL);
  digitalWrite(CS,HIGH);
  
  digitalWrite(CS,LOW);
  SPI.transfer(WRITE);        //clear outgoing memory status byte
  SPI.transfer(highByte(cw_addr));  //16 bit address transferred one byte at a time
  SPI.transfer(lowByte(cw_addr));
  for(i=0;i<4;i++){
  SPI.transfer(0xAA);
  }
  digitalWrite(CS,HIGH);      //pulse CS
  
  digitalWrite(CS,LOW);
  SPI.transfer(WRITE);        //clear incoming memory status byte
  SPI.transfer(highByte(ccw_addr));
  SPI.transfer(lowByte(ccw_addr));
  for(i=0;i<4;i++){
  SPI.transfer(0xAA);
  }
  digitalWrite(CS,HIGH);
  
}

180 views.. who has the answers?
I have checked all the SPI modes, and the chip is rated for 20MHz.
The 23LC512 is functionally equivalent to the more popular 23LC256.. can anyone replicate the problem?
Thanks all

Sorry, don't have either of those chips. Haven't had time to browse the datasheet yet.

This doesn't look right:

  digitalWrite(CS,LOW);    //set to single SPI mode
  SPI.transfer(WRMR);
  SPI.transfer(RSTIO);
  digitalWrite(CS,HIGH);

RSTIO is an instruction like WRITE or WRMR. This writes 0xFF to the mode register and that isn't valid.

Pete

el_supremo:
RSTIO is an instruction like WRITE or WRMR. This writes 0xFF to the mode register and that isn't valid.

Good catch; the problem persists, however.

I have the feeling that this is a timing problem. Have you tried a lower clock speed or perhaps try Mode 1 (or even 2 or 3)?

Pete

That's what I thought; I tried a higher clock divider, but the result was the exact same.
As for SPI modes, I have tried them all--modes 0 and 3 give similar results, and 1 or 2 don't work at all.

This is what I get using your code after changing SPI_MODE0 to SPI_MODE3:

Memory status:
20 10101010
21 10101010
22 10101010
23 10101010
400 10101010
401 10101010
402 10101010
403 10101010

Now that it works, how do you plan to keep track of variables as you store them? Storing bytes is not very usable unless you convert them to ints or longs... ...unless for text or images

I don't get it - I worked on something else and then went back to it (had to rewire it) and now it doesn't work....

I rewire again with shorter wires (noise issue), use the default SPI speed (/ by 4) and changed the 0x00 to 0xFF on reads

results:
Memory status:
0 0x0 0b10101010
1 0x1 0b10101010
2 0x2 0b10101010
3 0x3 0b10101010
4 0x4 0b10101010
5 0x5 0b10101010
6 0x6 0b10101010
7 0x7 0b10101010
8 0x8 0b10101010
9 0x9 0b10101010
10 0xA 0b10101010
11 0xB 0b10101010
12 0xC 0b10101010
13 0xD 0b10101010
14 0xE 0b10101010
15 0xF 0b10101010
16 0x10 0b10101010
17 0x11 0b10101010
18 0x12 0b10101010
19 0x13 0b10101010
20 0x14 0b10101010
21 0x15 0b10101010
22 0x16 0b10101010
23 0x17 0b10101010
24 0x18 0b10101010
25 0x19 0b10101010
26 0x1A 0b10101010
27 0x1B 0b10101010
28 0x1C 0b10101010
29 0x1D 0b10101010
30 0x1E 0b10101010
31 0x1F 0b10101010
freeRam(): 1767

...but it still has problems - it works very well 9 out of 10 times when reading/writing 1024 bytes - I'll have to read the specs again and again... let me know what you find out - thanks

How do you have the unused pin configured? Mine is tied to ground, though I get different results with it tied to Vcc. The datasheet doesn't specify which is best, though it advises not to leave it floating.

I'll try rewiring mine with shorter jumpers.. maybe it's noise or there's too much capacitance on the line? I'm going to try observing the SCK signal on my o-scope later, some devices are sensitive to long rise-times. On a preliminary search, I couldn't find any free samples for Schmitt triggers..

As for tracking data, I'm vaguely thinking of using it as a type of data buffer between MCUs, so that one isn't stuck waiting for the other to free up. Effectively it'd be structured as a stack, with a header byte saying how many bytes and of what type it should be expecting.

PolkaDot:
How do you have the unused pin configured? Mine is tied to ground, though I get different results with it tied to Vcc. The datasheet doesn't specify which is best, though it advises not to leave it floating.

23LC512 pin 1 CS to UNO pin 10 (CS)

23LC512 pin 2 SO to UNO pin 12 (MISO) or(pin 1 of ICSP bus)
23LC512 pin 3 to GND
23LC512 pin 4 to GND
23LC512 pin 5 SI to UNO pin 11 (M0S1) or (pin 4 of ICSP bus)
23LC512 pin 6 SCK to UNO pin 13 (SCK) or (pin 3 of ICSP bus)
23LC512 pin 7 to Vcc
23LC512 pin 8 to Vcc



I'll try rewiring mine with shorter jumpers.. maybe it's noise or there's too much capacitance on the line? I'm going to try observing the SCK signal on my o-scope later, some devices are sensitive to long rise-times. On a preliminary search, I couldn't find any free samples for Schmitt triggers..


I also got better results when I slowed down my serial port - I went from 115200 down to 38600 and didn't seem to find a fail



As for tracking data, I'm vaguely thinking of using it as a type of data buffer between MCUs, so that one isn't stuck waiting for the other to free up. Effectively it'd be structured as a stack, with a header byte saying how many bytes and of what type it should be expecting.

Actually, I think it might be better to write 64K of 0xAA and then go back an read all 64K to find a fail - can't trust the serial output

You're using the hardware CS on the Uno? Won't that go high in the middle of a command, since we call spiTransfer multiple times in what the SRAM considers one command?

CS is under software control. You can set i it low, send as many bytes as you want, then bring it high again.

PolkaDot:
You're using the hardware CS on the Uno? Won't that go high in the middle of a command, since we call spiTransfer multiple times in what the SRAM considers one command?

Honestly, I'm a newbie, I got my first UNO this Feb (2013) and all I did was to copy and paste your code and figured since you were having a problem I'd see what the internet had to say. Well, this is what I came up with. Something is still wrong because I don't understand this comment (what did you do to correct it?):

Quote from: el_supremo on April 30, 2013, 09:47:35 PM
RSTIO is an instruction like WRITE or WRMR. This writes 0xFF to the mode register and that isn't valid.

Good catch; the problem persists, however.

):

using pin 8:
Configuing RAM
RAM configured
Memory status:	
0	0x0	0b11010101
1	0x1	0b1010101
2	0x2	0b1010101
3	0x3	0b1010101
freeRam(): 1743 bytes, using: 305 bytes

using pin 10:
Configuing RAM
RAM configured
Memory status:	
0	0x0	0b10101010
1	0x1	0b10101010
2	0x2	0b10101010
3	0x3	0b10101010
freeRam(): 1743 bytes, using: 305 bytes

However, I still don't understand it, both ways consume more memory as you increase byteCnt. Why would that be if it was truly read/writing to the SRAM?

Have you tried tying the HOLD pin to VCC? A similar issue was raised in a thread for the Microchip 23k256 sram chips. I also noticed some errors due to my IC socket not being 100% seated on my breadboard (it can get loose). I used the wiring diagram from the referenced thread on a Due with a clock divider of 0 and received the right output.

23k256 thread for reference: Arduino Forum

with:
CS pin 8
SPI_CLOCK_DIV2
SPI_MODE0
and no delayMicroseconds()
and no pinMode(10, OUTPUT);

every thing works fine with even shorter wires (don't use jumper wires with SRAM, with EEPROM OK, at least for me)
64K bytes written in 1.2 sec (in BYTE mode)
64K writes and 64K reads 100 times with different data each time and no fails
64K reads and writes in SEQUENTIAL mode:
write time= 128 mSec
read time= 128 mSec
no fails - I claim done

sparkfuntoday:
(I) figured since you were having a problem I'd see what the internet had to say.

I certainly appreciate the help! Its been very helpful that you were able to replicate the problem.

sparkfuntoday:
Something is still wrong because I don't understand this comment (what did you do to correct it?):

el_supremo:
RSTIO is an instruction like WRITE or WRMR. This writes 0xFF to the mode register and that isn't valid.

You can think of sending
spiTransfer(WRMR);
spiTransfer(RSTIO);
as calling WriteModeRegister(RSTIO); . Since RSTIO is a command, not a parameter, we want to call what would be RSTIO(); .

Abstraction aside, you fix it by removing the WRMR line, or:

  digitalWrite(CS,LOW);    //set to single SPI mode
  SPI.transfer(RSTIO);
  digitalWrite(CS,HIGH);

kemonine:
Have you tried tying the HOLD pin to VCC?

This is how I have it configured. /HOLD to Vcc and unused pin 3 to GND.

sparkfuntoday:
everything works fine with even shorter wires

I'm having success now, even with longer jumpers; the fix seemed to come from sending 0xFF instead of 0x00 after READ commands. I really don't know why that should affect performance.. data sheet claims it's a don't-care.
I'm away from my board right now, but I'm going to write a conclusive test case (ie write and read 0-255 and compare for errors) before wrapping up the topic and summarizing changes. But I hope it's done!

Okay I take back the bit about longer jumpers.. using 15cm jumpers I got an error every roughly 200 sequences (the whole sequence corrupted).

Making my own, I wrote to 1024 memory addresses 256 times with zero errors.
I have a picture of my setup attached, and my final test code below. Enjoy!

// Verify SRAM functionality
/*
  Microchip 23LC512--Arduino Uno
  1  /CS --- 8
  2  SO  --- 12
  3  NC  --- GND
  4  Vss --- GND
  5  SI  --- 11
  6  SCK --- 13
  7  HOLD--- Vcc
  8  Vcc --- Vcc
  
  Use short jumpers if breadboarding!!
*/

#include <SPI.h>

//Pin definitions:
#define CS 8

#define LENGTH 512  //length of each write sequence

//SRAM SPI commands
#define READ 0x03    //READ and WRITE to be followed by 16 bit address
#define WRITE 0x02
#define RDMR 0x05    //read mode register
#define WRMR 0x01    //write to mode register
#define RSTIO 0xFF   //reset input mode
//Mode arguments
#define BYTE 0x00
#define PAGE 0x80
#define SEQUENTIAL 0x40

void configureRAM(void);    //declare function

//Incoming and outgoing start addresses (16 bit)
unsigned int cw_addr=0x0000;
unsigned int ccw_addr=0x1000;

byte incoming=0xFF;      //incoming SPI data
byte cw_status[LENGTH];  //bytes read from first address
byte ccw_status[LENGTH];  //bytes read from second address
byte checkValue=0;        //value being written
int checkValueint=0;      //neccessary to check every value of byte checkValue

long i=0;
int errors=0;

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  configureRAM();
  
  //Write a sequence of bytes starting at two memory addresses
  for(checkValueint=0;checkValueint<256;checkValueint++)
  {  
    //write values to array starting at first address
    digitalWrite(CS,LOW);      //select SRAM
    SPI.transfer(WRITE);
    SPI.transfer(highByte(cw_addr));  //16 bit address transfered one byte at a time
    SPI.transfer(lowByte(cw_addr));
    for(i=0;i<LENGTH;i++)              //write the check value to each byte
      SPI.transfer(checkValue);
    digitalWrite(CS,HIGH);      //deselect
    
    //write values to array starting at second address
    digitalWrite(CS,LOW);
    SPI.transfer(WRITE);
    SPI.transfer(highByte(ccw_addr));
    SPI.transfer(lowByte(ccw_addr));
    for(i=0;i<LENGTH;i++)              //write the check value to each byte
      SPI.transfer(checkValue);
    digitalWrite(CS,HIGH); 
    
    //read first array
    digitalWrite(CS,LOW);
    SPI.transfer(READ);
    SPI.transfer(highByte(cw_addr));
    SPI.transfer(lowByte(cw_addr));
    for(i=0;i<LENGTH;i++)
      cw_status[i]=SPI.transfer(0xFF);  //errors occur when sending values other than 0xFF
    digitalWrite(CS,HIGH);
    
    //read second array
    digitalWrite(CS,LOW);
    SPI.transfer(READ);
    SPI.transfer(highByte(ccw_addr));
    SPI.transfer(lowByte(ccw_addr));
    for(i=0;i<LENGTH;i++)
      ccw_status[i]=SPI.transfer(0xFF);
    digitalWrite(CS,HIGH);
    
    //compared stored values against expected.
    for(i=0;i<LENGTH;i++)
    {
      //if an error, display expected and received values.
      if(!(cw_status[i]==checkValue&&ccw_status[i]==checkValue))
      {
        Serial.print("Checking ");
        Serial.println(checkValue);
        
        Serial.print(cw_status[i]);          //display received values
        Serial.write('\t');
        Serial.println(ccw_status[i]);
        errors++;                            //increment error count
      }
    }
    
    checkValue++;
  }
  
  Serial.print("Errors encountered: ");      //display total errors
  Serial.println(errors);
}

void loop()
{
  
}


//Configure RAM settings and clear memory status bytes.
//Must initialize SPI before calling configureRAM()
void configureRAM(void)
{
  SPI.setClockDivider(SPI_CLOCK_DIV2);    //SPI clock at 8MHz
  SPI.setDataMode(SPI_MODE3);             //CPOL=1, CPHA=1
  SPI.setBitOrder(MSBFIRST);              //MSB first
  
  pinMode(CS,OUTPUT);
  
  digitalWrite(CS,LOW);    //set to single SPI mode
  SPI.transfer(RSTIO);
  digitalWrite(CS,HIGH);
  
  digitalWrite(CS,LOW);
  SPI.transfer(WRMR);        //Set data mode to sequential
  SPI.transfer(SEQUENTIAL);
  digitalWrite(CS,HIGH);
  
  digitalWrite(CS,LOW);
  SPI.transfer(RDMR);        //Verify status register
  incoming=SPI.transfer(0xFF);
  digitalWrite(CS,HIGH);
  
  Serial.write('\n');
  Serial.println("Status Register:");
  Serial.println(incoming,HEX); 
}