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 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
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.
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
...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?
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.
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.
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);
}