Go Down

Topic: Trouble with SPI read/write to 23K256 SRAM chip (SOLVED) (Read 2437 times) previous topic - next topic

chrisharrington

Sep 20, 2013, 10:36 am Last Edit: Sep 28, 2013, 03:50 pm by chrisharrington Reason: 1
Hi,

EDIT: This may simply have been a defect with the breakout board.

EDIT: It was a bug with the board after all. Was able to get this to function using just dip chips on a breadboard with jumper wire. However note that the level shifter I had available (the 4050) is too slow to run the attached source as is. Changing the line
Code: [Select]
 SPI.setClockDivider(SPI_CLOCK_DIV2);
to
Code: [Select]
 SPI.setClockDivider(SPI_CLOCK_DIV32);
and it ran fine (t/y SurferTim).
----------------------------------------------------
original post
----------------------------------------------------
I'm having issues reading/writing to a 23K256 SRAM chip via SPI. It appears that bytes read are not the ones written.

Environment:
Arduino Uno R3
Arduino IDE 1.05

The 23K256 chip is on a small breakout board by Switch Science (Japan) that includes a level shifter on board to allow direct connection to the Arduino.

Sketch (includes pin connection chart in comments):
Code: [Select]
#include <SPI.h>
#include <SPISRAM.h>

/*
SRAM   Arduino
1 CS   10(CS)
2 SO   12(MISO)
3 -    -
4 Vss  GND
5 SI   11(MOSI)
6 SCK  13(SCK)
7 HOLD <-- 100k ohm -- 3.3V
8 Vcc  3.3V
*/

SPISRAM myRAM(10); // CS pin
char buffer[128];

void setup()
{
 pinMode(5, OUTPUT);
 Serial.begin(9600);

 SPI.begin();
 SPI.setBitOrder(MSBFIRST);
 SPI.setClockDivider(SPI_CLOCK_DIV2);
 SPI.setDataMode(SPI_MODE0);

 Serial.println("Byte write...");
 myRAM.write(0,'H');
 myRAM.write(1,'e');
 myRAM.write(2,'l');
 myRAM.write(3,'l');
 myRAM.write(4,'o');
 myRAM.write(5,'-');

 Serial.println("Byte read...");
 Serial.println(myRAM.read(0));
 Serial.println(myRAM.read(1));
 Serial.println(myRAM.read(2));
 Serial.println(myRAM.read(3));
 Serial.println(myRAM.read(4));
 Serial.println(myRAM.read(5));

 Serial.println("\nByte write...");
 myRAM.write(0x7FFC,'W');
 myRAM.write(0x7FFD,'o');
 myRAM.write(0x7FFE,'r');
 myRAM.write(0x7FFF,'l');
 myRAM.write(0x8000,'d');
 myRAM.write(0x8001,'!');
 myRAM.write(0x8002,'!');

 Serial.println("Byte read...");
 Serial.println(myRAM.read(0x7FFC));
 Serial.println(myRAM.read(0x7FFD));
 Serial.println(myRAM.read(0x7FFE));
 Serial.println(myRAM.read(0x7FFF));
 Serial.println(myRAM.read(0x8000));
 Serial.println(myRAM.read(0x8001));
 Serial.println(myRAM.read(0x8002));

 Serial.println("\nseq write...");
 int addr = 0x7F00;
 myRAM.write(addr, (byte*)"Hello world!!", sizeof("Hello world!!"));

 Serial.println("seq read...");
 myRAM.read(addr, (byte*)buffer, sizeof(buffer));
 Serial.println( buffer );

 Serial.println("\nByte read at operator[]...");
 myRAM[100] = 'H';
 myRAM[101] = 'e';
 myRAM[102] = 'l';
 myRAM[103] = 'l';
 myRAM[104] = 'o';
 Serial.println( myRAM[100] );
 Serial.println( myRAM[101] );
 Serial.println( myRAM[102] );
 Serial.println( myRAM[103] );
 Serial.println( myRAM[104] );

 Serial.println("\nRandom read/write...");
 for(int i=0;i<100;i++){
   int addr = random() & 0x7fff;
   byte val = i;
   Serial.print( addr, HEX );
   Serial.print( " " );
   Serial.print( val, HEX );
   myRAM[addr] = val;
   Serial.print( " " );
   Serial.println( myRAM[addr], HEX );
 }
}

void loop()
{
}


The SPISRAM library used is as follows.

Code: [Select]
/*
 Copyright (c) 2010 by arms22 (email redacted)
 Microchip 23x256 SPI SRAM library for Arduino
 
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#ifndef SPISRAM_H
#define SPISRAM_H

#include <SPI.h>

class SPISRAM
{
public:
struct MemCell {
SPISRAM &device;
unsigned int address;
MemCell &operator=(byte data){
device.write(address,data);
return *this;
}
operator byte() {
return device.read(address);
}
MemCell(SPISRAM &d,unsigned int a) : device(d), address(a) {};
~MemCell(){};
};
MemCell operator[](unsigned int address){
MemCell memcell(*this,address);
return memcell;
}
SPISRAM(byte ncsPin);
byte read(unsigned int address);
void read(unsigned int address, byte *buffer, unsigned int size);
void write(unsigned int address, byte data);
void write(unsigned int address, byte *buffer, unsigned int size);
private:
byte _ncsPin;
byte _buf[4];
void select(void);
void deselect(void);
byte transfer(byte length);
};

#endif


Code: [Select]
/*
 Copyright (c) 2010 by arms22 (email redacted)
 Microchip 23x256 SPI SRAM library for Arduino
 
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include "SPISRAM.h"

// INSTRUCTION SET
#define READ 0x03 // Read data from memory
#define WRITE 0x02 // Write data to memory
#define RDSR 0x05 // Read Status register
#define WRSR 0x01 // Write Status register

// STATUS REGISTER
#define BYTE_MODE 0x00
#define PAGE_MODE 0x80
#define SEQ_MODE 0x40

SPISRAM::SPISRAM(byte ncsPin)
: _ncsPin(ncsPin)
{
}

byte SPISRAM::read(unsigned int address)
{
_buf[0] = READ;
_buf[1] = address >> 8;
_buf[2] = address & 0xff;
_buf[3] = 0x00;
return transfer(4);
}

void SPISRAM::read(unsigned int address, byte *buffer, unsigned int size)
{
_buf[0] = WRSR;
_buf[1] = SEQ_MODE;
transfer(2);
select();
SPI.transfer(READ);
SPI.transfer(address >> 8);
SPI.transfer(address & 0xff);
for(unsigned int i=0; i<size; i++){
*buffer++ = SPI.transfer(0);
}
deselect();
}

void SPISRAM::write(unsigned int address, byte data)
{
_buf[0] = WRITE;
_buf[1] = address >> 8;
_buf[2] = address & 0xff;
_buf[3] = data;
transfer(4);
}

void SPISRAM::write(unsigned int address, byte *buffer, unsigned int size)
{
_buf[0] = WRSR;
_buf[1] = SEQ_MODE;
transfer(2);
select();
SPI.transfer(WRITE);
SPI.transfer(address >> 8);
SPI.transfer(address & 0xff);
for(unsigned int i=0; i<size; i++){
SPI.transfer(*buffer++);
}
deselect();
}

void SPISRAM::select(void)
{
digitalWrite(_ncsPin, LOW);
}

void SPISRAM::deselect(void)
{
digitalWrite(_ncsPin, HIGH);
}

byte SPISRAM::transfer(byte length)
{
byte ret,i;
select();
for(i=0; i<length; i++){
ret = SPI.transfer(_buf[i]);
}
deselect();
return ret;
}


And the serial output looks like this. I would expect the serial output to start with the same characters that were written, particularly with the sequential read/write operation returning the character string "Hello world!!". However, I get seemingly random characters: (Ø×aD?

Code: [Select]
Byte write...
Byte read...
1
66
81
81
92
16

Byte write...
Byte read...
26
93
97
81
64
0
1

seq write...
seq read...

(Ø×aD?

Byte read at operator[]...
1
66
81
81
94

Random read/write...
41A7 0 0
3AF1 1 0
2CD9 2 0
C2A 3 2
3782 4 1
5AC8 5 1
ED8 6 4
9FE 7 6
2F43 8 0
44D 9 0
1898 A 1
3C55 B 5
128C C 8
5BE2 D 8
54B3 E C
3747 F F
3917 10 0
4111 11 1
4398 12 3
4954 13 7
2E2F 14 0
2B11 15 0
162D 16 B
B05 17 C
3258 18 10
66F5 19 10
16B 1A 10
1DD6 1B 17
7788 1C 18
1D07 1D 19
6499 1E 1C
3492 1F 1E
F48 20 0
5133 21 1
6E62 22 0
1441 23 6
4CF3 24 0
F0D 25 3
5023 26 8
6AE5 27 C
785F 28 0
3530 29 2
22D1 2A 0
76C8 2B B
75ED 2C 13
4561 2D 13
680C 2E 18
154B 2F 1E
2902 30 20
5435 31 20
39 32 22
5081 33 26
6484 34 20
25B8 35 22
6514 36 28
1BA2 37 2C
339C 38 31
66B4 39 30
6E5A 3A 32
1267 3B 34
2C2A 3C 38
34CA 3D 38
5AE5 3E 3D
1248 3F 3E
49E9 40 0
20C5 41 0
5F1 42 3
22B0 43 4
56C4 44 0
1C15 45 2
2B8A 46 9
11E5 47 E
6A9B 48 0
784D 49 3
4339 4A 2
75F6 4B F
7F7 4C 12
26E8 4D 11
4CA1 4E 18
1005 4F 1E
6AD3 50 0
78FE 51 3
73ED 52 0
4DA5 53 8
38D5 54 7
60F3 55 0
1AD9 56 17
4CE4 57 1C
3C5B 58 23
3BFA 59 23
546C 5A 20
DC3 5B 28
1151 5C 30
73E2 5D 32
1C20 5E 39
1FAE 5F 3F
100C 60 40
1EE1 61 41
7D06 62 43
2698 63 44


Please let me know if any of you have an idea what I'm doing wrong. Thanks!

Edit: By the way, following manufacturer spec (Switch Science) I have a 100K resistor between VCC and pin 7, so that isn't the cause from what I can see (apparently that is a common error).

A pic of the breakout board and pin diagram can be found here: http://docid81hrs3j1.cloudfront.net/contents/cms/10121702_5077ce9567a88.jpg

SurferTim

#1
Sep 20, 2013, 11:54 am Last Edit: Sep 20, 2013, 12:01 pm by SurferTim Reason: 1
Try slowing down the SPI bus just as a test. Maybe it is a bit too high for "breadboard" type wiring.
Code: [Select]
 SPI.setClockDivider(SPI_CLOCK_DIV4);


edit: Maybe the 100K resistor is not enough current to pin 7. Try a smaller value, like 10K.


chrisharrington

Thanks, unfortunately neither change caused any change in the serial output.

SurferTim

Have you tried just a basic write and read? That random thing at the end of your code may be just that.

Try this code:
Code: [Select]
#include <SPI.h>
#include <SPISRAM.h>

/*
SRAM   Arduino
1 CS   10(CS)
2 SO   12(MISO)
3 -    -
4 Vss  GND
5 SI   11(MOSI)
6 SCK  13(SCK)
7 HOLD <-- 100k ohm -- 3.3V
8 Vcc  3.3V
*/

SPISRAM myRAM(10); // CS pin
char buffer[128];

void setup()
{
  pinMode(5, OUTPUT);
  Serial.begin(9600);

  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV4);
  SPI.setDataMode(SPI_MODE0);

  Serial.println("Byte write...");
  myRAM.write(0,'H');
  myRAM.write(1,'e');
  myRAM.write(2,'l');
  myRAM.write(3,'l');
  myRAM.write(4,'o');
  myRAM.write(5,' ');

  Serial.println("Byte read...");
  Serial.println(myRAM.read(0));
  Serial.println(myRAM.read(1));
  Serial.println(myRAM.read(2));
  Serial.println(myRAM.read(3));
  Serial.println(myRAM.read(4));
  Serial.println(myRAM.read(5));
}

void loop()
{
}


chrisharrington

#4
Sep 21, 2013, 07:48 am Last Edit: Sep 21, 2013, 07:51 am by chrisharrington Reason: 1
Thanks again, but not this has no effect. I still get garbled output:

Code: [Select]
Byte write...
Byte read...
1
66
81
81
92
0


So here, the first and last byte are incorrect, while the "ello" is at least coming back as the correct ascii codes, which happens every other time as well (though I don't understand why it is output as integers instead of char).

So the issue is that some of the bytes are being written and read, but that somehow the SRAM chip is weirding out at the start of the write operation (and the end?).

SurferTim

Try connecting the HOLD pin to 3.3v directly. If you are uncomfortable with that, use a very small value resistor, like 1K or smaller. Here is a quote from another user trying to get the gibberish problem solved.

Quote
However, I couldn't get the chip to function. My test program was returning values, but they were gibberish. Something was clearly wrong.

I experimented over the course of several days with substituting an Adafruit TXB0108 Bidirectional Level Shifter. I searched the web and discovered many people had similar problems getting these chips to operate. I even tried using a few resister pair/voltage dividers in place of the level shifter. I began to get very skeptical of anyone that claimed they made these chips work. I searched the web some more and re-read the datasheet (there's also a Microchip Application Note). I added a 10K ohm pull-up resister to the CS pin, but still gibberish.

Eventually I swallowed my pride and wrote a "help-wanted" post on the Arduino Forum. A few responses later I recieved a solution! I needed to tie the 23K256 HOLD pin high. While the datasheet doesn't specifically state that, after countless re-readings I'll admit it hints at it.


chrisharrington

#6
Sep 22, 2013, 09:14 am Last Edit: Sep 22, 2013, 09:22 am by chrisharrington Reason: 1
My assumption from reading that blog ( http://ucexperiment.wordpress.com/2013/02/23/interfacing-microchip-23k256-32kb-sram-spi-memory-to-the-arduino/ ) before I posted my question here was that he had a resistor between VCC and HOLD, but it does appear that he just hooked that pin directly to 3.3V. The documentation I had, as mentioned in the sketch source comments above, explicitly states that a 100k resister between VCC and HOLD is *required*. Interestingly, removing it entirely and connecting HOLD to 3.3V directly has no effect for my setup.

I've tested two of these 23k256 breakout boards from this manufacturer with identical results. I'm still waiting for more help from the manufacturer, and I've been promised a reply within the next couple of days.

I also have a few through hole versions of the chip w/o breakout boards, so I might try testing the circuit using level shifters I have on hand to see if there is anything different (might help isolate the issue).

Edit: I also take this blog post with a grain of salt seeing as he has the Arduino's 3.3V pin connected to *CS* on the SRAM chip, which makes me wonder if he knows what CS is for. Maybe he figures he has only this one component on the SPI bus so it doesn't matter, but I would still think this would be a technically incorrect setup.

SurferTim

Quote
I also take this blog post with a grain of salt seeing as he has the Arduino's 3.3V pin connected to *CS* on the SRAM chip, which makes me wonder if he knows what CS is for.

He stated why he did that. The CS pin is connected to 3.3v with the 10K resistor as a weak pullup. That should not be a problem either way.

Let us know what you find out from the manufacturer. That might be a handy IC for me someday.

chrisharrington

My understanding is that the point of the SPI interface is to allow communicating with multiple devices using a minimum of pins, so connecting CS to voltage out does not seem like good practice IMHO, even if it may work, because it means you cannot add other SPI devices without rewiring.

I'll post whatever reply I get from the manufacturer.

SurferTim

Quote
My understanding is that the point of the SPI interface is to allow communicating with multiple devices using a minimum of pins, so connecting CS to voltage out does not seem like good practice IMHO, even if it may work, because it means you cannot add other SPI devices without rewiring.

That is not correct. He connected CS to 3.3v with a 10K resistor. That is just a weak pullup to keep the device from booting with the SPI active. The SRAM CS pin is still connected directly to the Arduino pin, and should allow multiple devices on the SPI bus.

I would like to know if you get this working. Nice addition to the parts inventory for the Arduino. Data memory is the one thing that is in short supply.

chrisharrington

Ah, OK. TIL. I went ahead and added a 10K resistor on CS to my setup as well, but still no change. The first and last characters of "hello" are still getting garbled.

One thing I hadn't noticed, there is also a surface mount capacitor on the board between Vcc and Vss, as per the chip maker's data sheet, so it isn't just the SRAM chip and the level shifter.

I'll let you know what the board maker says.

SurferTim

Just a thought. Since it is just the first and last character garbled, try adding a microsecond delay to your library on the select and deselect calls. Like this. What could it hurt at this point, right?
Code: [Select]
void SPISRAM::select(void)
{
    digitalWrite(_ncsPin, LOW);

// add this
    delayMicroseconds(1);
}

void SPISRAM::deselect(void)
{
// add this
    delayMicroseconds(1);

    digitalWrite(_ncsPin, HIGH);
}


chrisharrington

That's an interesting angle, but unfortunately that also had no effect, so it probably isn't the timing. Which is actually reassuring, because the application I want to use this for is in an Arduino based Shoutcast client. The idea to use the SRAM comes from a guy in Japan who's managed to play back at least 128Kbps streams on a breadboard Uno equivalent (which he subsequently made a shield for which I'm not using because it has everything on one board, including a few components I would rather replace with others) by altering the Ethernet library and writing to SRAM from the ethernet chip's buffer, and from there to the MP3 player. Requiring a delay could mess with the timing and make the higher bit rate streams harder to read, I imagine, because he really struggled to get the throughput high enough to play those streams.

http://www.geocities.co.jp/orion_cosmo/irshield.html

chrisharrington

Just a heads up here, the manufacturer has contacted me stating that it may be a defect in the product (probably the board). I'll update again if that is in fact the case.

chrisharrington

The manufacturer sent me three new breakout boards, but they all had the same issue (assuming it wasn't something I'm doing all along).

So I've gone ahead and wired up the same thing on a breadboard using loose parts, using the 23K256 DIP package and a 4050 for level shifting.

I've got a different odd result, but at least now I am reading consistently and predictably. Basicly, the values I write are being *halved*, so I'm reading back half the ascii value for each char I write (even rounded down, such as 'e', 101, becoming 50). I'm assuming I'm doing something silly with the level shifter, so I'll muck about with the datasheet and see what I can come up with.

Code: [Select]
Byte write...
Byte read...
36
50
54
54
55
22

Byte write...
Byte read...
43
55
57
54
50
16
16

seq write...
seq read...
$2667;7962

Byte read at operator[]...
36
50
54
54
55

Random read/write...
41A7 0 0
3AF1 1 0
2CD9 2 1
C2A 3 1
3782 4 2
5AC8 5 2
ED8 6 3
9FE 7 3
2F43 8 4
44D 9 4
1898 A 5
3C55 B 5
128C C 6
5BE2 D 6
54B3 E 7
3747 F 7
3917 10 8
4111 11 8
4398 12 9
4954 13 9
2E2F 14 A
2B11 15 A
162D 16 B
B05 17 B
3258 18 C
66F5 19 C
16B 1A D
1DD6 1B D
7788 1C E
1D07 1D E
6499 1E F
3492 1F F
F48 20 10
5133 21 10
6E62 22 11
1441 23 11
4CF3 24 12
F0D 25 12
5023 26 13
6AE5 27 13
785F 28 14
3530 29 14
22D1 2A 15
76C8 2B 15
75ED 2C 16
4561 2D 16
680C 2E 17
154B 2F 17
2902 30 18
5435 31 18
39 32 19
5081 33 19
6484 34 1A
25B8 35 1A
6514 36 1B
1BA2 37 1B
339C 38 1C
66B4 39 1C
6E5A 3A 1D
1267 3B 1D
2C2A 3C 1E
34CA 3D 1E
5AE5 3E 1F
1248 3F 1F
49E9 40 20
20C5 41 20
5F1 42 21
22B0 43 21
56C4 44 22
1C15 45 22
2B8A 46 23
11E5 47 23
6A9B 48 24
784D 49 24
4339 4A 25
75F6 4B 25
7F7 4C 26
26E8 4D 26
4CA1 4E 27
1005 4F 27
6AD3 50 28
78FE 51 28
73ED 52 29
4DA5 53 29
38D5 54 2A
60F3 55 2A
1AD9 56 2B
4CE4 57 2B
3C5B 58 2C
3BFA 59 2C
546C 5A 2D
DC3 5B 2D
1151 5C 2E
73E2 5D 2E
1C20 5E 2F
1FAE 5F 2F
100C 60 30
1EE1 61 30
7D06 62 31
2698 63 31

Go Up