23LC1024 SRAM Library

This is my first arduino/C++ library so may not be perfect. I have a couple of 23LC1024 SRAM chips laying about and thought they may come in handy on an arduino project at some point so wrote this library. I also wrote a keywords.txt (though this does not seem to work for some reason) and an examples program to test/check the library and chips.

EDIT: The library has been updated. For changes see the history in the SpiRAM.cpp file.
EDIT: 20-Dec-2013 - Updated library to allow for non hardware SS pins.
EDIT 27-Dec-2013 - Fixed data type in test sketch.
EDIT 28-Dec-2013 - Few more tweaks.

SpiRAM_23LC1024-v0.1.4.zip (3.32 KB)

Thank you very much for this!

Thanks for sharing!

Some remarks (based upon e.g. Arduino Playground - LibraryForI2CEEPROM )

missing header

//    FILE: SpiRAM.h
//  AUTHOR: 
// VERSION: 0.1.00
// PURPOSE: Arduino library for the 23A1024/23LC1024 SRAM memory chip 
//     URL: 
//
// HISTORY:
// see SpiRAM.cpp file

#ifndef SpiRAM_h
#define SpiRAM_h

// support for old IDE's
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif

#include <SPI.h>

#define SPIRAM_LIB_VERSION "0.1.00"

// SRAM Instructions
#define RDMR  5
#define WRMR  1
#define WRITE 2
#define READ  3

// SRAM modes
#define BYTE_MODE       0x00
#define PAGE_MODE       0x80
#define SEQUENTIAL_MODE 0x40

class SpiRAM {
  public:
    // Initialize and specify the SS pin
    SpiRAM          (byte ss_Pin);
    
    // Read a single byte from address
    byte readByte   (long address);
    
    // Write a single byte to address
    void writeByte  (long address, byte data_byte);
    
    // Read several bytes starting at address and of length into a char buffer
    void readBytes  (long address, char *buffer, long length);
    
    // Write several bytes starting at address and of length from a char buffer
    void writeBytes (long address, char *buffer, long length);
    
    // Write several bytes of value, starting at address and of length
    void fillBytes  (long address, byte value, long length);
    
  private:
    byte _ss_Pin;
};

#endif
// END OF FILE

you probably wnat to make addresses and lengths unsigned long as these cannot be negative.
probably a good idea to have the functions return an int to indicate number of bytes written/read

.cpp follows soon

The length you probably wont often exceed 65000 bytes as buffer length so an unsigned int will do.
probably an byte for length (max 255 bytes is very workable!)

Some proposals for the .cpp file

  • added header
  • mapped readbyte and writebyte upon multibyte calls
    reduces footprint
  • setAddressMode(uint32_t address, byte mode) -> must be private in .h file
    reduces footprint
  • made address unsigned long (uint32_t) and length unsigned int (uint16_t) - see post above
    for all function class.
//
//    FILE: SpiRAM.cpp
//  AUTHOR:  
// VERSION: 0.1.01
// PURPOSE: Arduino library for the 23A1024/23LC1024 SRAM memory chip
//
// HISTORY:
// 0.1.00 - 2013-12-15 initial version
// 0.1.01 -2013-12-15 refactored 
//
// Released to the public domain
//

#include <SpiRAM.h>

SpiRAM::SpiRAM(byte ss_Pin){
  _ss_Pin = ss_Pin;                     // Store Slave Select pin
  SPI.begin();                          // Start SPI
  // digitalWrite(_ss_Pin, LOW);         // Select RAM
  // SPI.transfer(RDMR);                 // Write to Mode Register
  // SPI.transfer(SEQUENTIAL_MODE);      // Sequencial Mode (It's default mode anyway)
  // digitalWrite(_ss_Pin, HIGH);        // Deselect RAM
}

byte SpiRAM::readByte(uint32_t address) {
  byte data_byte;
  readBytes(address, &data_byte, 1);
  return data_byte;
}

void SpiRAM::writeByte(uint32_t address, byte data_byte) {
  writeBytes(address, &data_byte, 1);
}

void SpiRAM::readBytes(uint32_t address, char *buffer, uint16_t length) {
  digitalWrite(_ss_Pin, LOW);
  setAddress(address, READ);
  for (uint16_t i = 0; i < length; i++) buffer[i] = SPI.transfer(0x00);   
  digitalWrite(_ss_Pin, HIGH);
}

void SpiRAM::writeBytes(uint32_t address, char *buffer, uint16_t length) {
  digitalWrite(_ss_Pin, LOW);
  setAddressMode(address, WRITE);
  for (uint16_t i = 0; i < length; i++) SPI.transfer(buffer[i]);
  digitalWrite(_ss_Pin, HIGH);
}

void SpiRAM::fillBytes(uint32_t address, byte value, uint16_t length) {
  digitalWrite(_ss_Pin, LOW);
  setAddressMode(address, WRITE);
  for (uint16_t i = 0; i < length; i++) SPI.transfer(value);
  digitalWrite(_ss_Pin, HIGH);
}

void SPiRAM::setAddressMode(uint32_t address, byte mode) {
  SPI.transfer(mode);
  SPI.transfer((byte)(address >> 16));
  SPI.transfer((byte)(address >> 8));
  SPI.transfer((byte)address);
}

// END OF FILE

give it a try (might need some small fixes to get it to work

Thanks for all the suggestions Rob, as said in the original post this was my first attempt and I have only been tinkering with Arduino and C++ for 2 years so made several schoolboy errors.

you probably wnat to make addresses and lengths unsigned long as these cannot be negative.

First head slap moment, I figured because I only use 24 bit addresses the sign bit did not matter but now realize that something like a -1 would cock the whole thing up.

probably a good idea to have the functions return an int to indicate number of bytes written/read

I don't understand why you would need to do this unless the block read/write routines do bounds checking for end of memory as I don't use page mode.

The length you probably wont often exceed 65000 bytes as buffer length so an unsigned int will do.

Good point, I doubt many/any Arduinos have more than 64K of RAM.

  • mapped readbyte and writebyte upon multibyte calls reduces footprint

Another head slap moment. So tied up in writing the functions forgot to rationalize them.

I will try and find a RAM chip to test with and redo the library unless you want to adopt it as your own and add it to your playground article.

Hi Rob,

I have tried to implement the changes you suggested but came unravelled with type casting byte to char in the SpiRAM::readByte & SpiRAM::writeByte routines. I appear to have sussed the readByte but have no idea how to fix writeByte. Any ideas?
I have managed to get both the readByte & writeByte sorted and have renamed the readBytes/writeBytes to readBuffer/writeBuffer. All the tests seem to work okay apart from readBuffer that causes some strange corruption. It's probably something simple I'm missing as the memory seems to be written correctly but reading it back goes wrong. I had to attach a screen grab as cut & paste does not show the corruption.

Tests Begin.

Fill Memory with 0xFF.

0:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
A:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
14:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
1E:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
28:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
32:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
3C:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
46:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
50:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
5A:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  

FF96:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
FFA0:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
FFAA:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
FFB4:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
FFBE:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
FFC8:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
FFD2:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
FFDC:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
FFE6:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  
FFF0:	FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  

Fill Memory with 0xAA.

0:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
A:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
14:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
1E:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
28:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
32:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
3C:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
46:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
50:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
5A:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  

FF96:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
FFA0:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
FFAA:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
FFB4:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
FFBE:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
FFC8:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
FFD2:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
FFDC:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
FFE6:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  
FFF0:	AA  AA  AA  AA  AA  AA  AA  AA  AA  AA  

Fill Memory with Buffer.

0:	41  42  43  44  45  46  47  48  49  4A  
A:	41  42  43  44  45  46  47  48  49  4A  
14:	41  42  43  44  45  46  47  48  49  4A  
1E:	41  42  43  44  45  46  47  48  49  4A  
28:	41  42  43  44  45  46  47  48  49  4A  
32:	41  42  43  44  45  46  47  48  49  4A  
3C:	41  42  43  44  45  46  47  48  49  4A  
46:	41  42  43  44  45  46  47  48  49  4A  
50:	41  42  43  44  45  46  47  48  49  4A  
5A:	41  42  43  44  45  46  47  48  49  4A  

FF96:	41  42  43  44  45  46  47  48  49  4A  
FFA0:	41  42  43  44  45  46  47  48  49  4A  
FFAA:	41  42  43  44  45  46  47  48  49  4A  
FFB4:	41  42  43  44  45  46  47  48  49  4A  
FFBE:	41  42  43  44  45  46  47  48  49  4A  
FFC8:	41  42  43  44  45  46  47  48  49  4A  
FFD2:	41  42  43  44  45  46  47  48  49  4A  
FFDC:	41  42  43  44  45  46  47  48  49  4A  
FFE6:	41  42  43  44  45  46  47  48  49  4A  
FFF0:	41  42  43  44  45  46  47  48  49  4A  

Read Buffer.
ABCDEFG??
FGHIJABC?

Write byte.

0:	0  0  0  0  0  0  0  0  0  0  
A:	1  1  1  1  1  1  1  1  1  1  
14:	2  2  2  2  2  2  2  2  2  2  
1E:	3  3  3  3  3  3  3  3  3  3  
28:	4  4  4  4  4  4  4  4  4  4  
32:	5  5  5  5  5  5  5  5  5  5  
3C:	6  6  6  6  6  6  6  6  6  6  
46:	7  7  7  7  7  7  7  7  7  7  
50:	8  8  8  8  8  8  8  8  8  8  
5A:	9  9  9  9  9  9  9  9  9  9  

FF96:	F  F  F  F  F  F  F  F  F  F  
FFA0:	10  10  10  10  10  10  10  10  10  10  
FFAA:	11  11  11  11  11  11  11  11  11  11  
FFB4:	12  12  12  12  12  12  12  12  12  12  
FFBE:	13  13  13  13  13  13  13  13  13  13  
FFC8:	14  14  14  14  14  14  14  14  14  14  
FFD2:	15  15  15  15  15  15  15  15  15  15  
FFDC:	16  16  16  16  16  16  16  16  16  16  
FFE6:	17  17  17  17  17  17  17  17  17  17  
FFF0:	18  18  18  18  18  18  18  18  18  18  

Tests Finished.

SpiRAM_Test.ino (2.22 KB)

SpiRAM.h (1.37 KB)

SpiRAM.cpp (1.55 KB)

Clipboard-1.jpg

NO need to call this library my own, but you might add a line in the history to refer to my contribution :wink:

The return values can be useful in the future, when you use blocktransfers. As said it are just tips that might help to improve the lib.

Negative values for addresses are used sometimes to define an address from the end backwards. [e.g. Python ]
So writeBUffer(-10, "hello", 5) would write to 0xFFF6 and further.
Negative values for length, maybe in a hyperdrive for a starship :wink:

More serious, strange error, As i do not have the chip I cannot test it, but I will scrutinize the test code if I see something that can explain the 'bug'

I'll be back.

ReadBuffer() code does CORRECTLY not add a terminating zero after the char array to end the 'string'.
Serial.println(buffer2) will print until it finds a \0 in memory.

you can confirm this by printing the strlen(buffer2) to see if the length == length read.

robtillaart:
ReadBuffer() code does CORRECTLY not add a terminating zero after the char array to end the 'string'.
Serial.println(buffer2) will print until it finds a \0 in memory.

you can confirm this by printing the strlen(buffer2) to see if the length == length read.

I trimmed down the test sketch a bit.

#include <SPI.h>
#include <SpiRAM.h>

const uint16_t ramSize = 0x1FFFF;           // 128K x 8 bit
const byte LED = 13;

char buffer[] = {"ABCDEFGHIJ"};
char buffer2[sizeof(buffer)];

SpiRAM sRam(SS);                            // Initialize the RAM with SS

void setup(){
  pinMode(LED,OUTPUT);
  digitalWrite(LED,LOW);                    // Turn off LED (Mega LED stays on else)
  Serial.begin(115200);
  Serial.println("Tests Begin.");
  
  Serial.println("\r\nFill Memory with Buffer.");
  for (uint32_t x = 0; x < ramSize - sizeof(buffer); x += sizeof(buffer) - 1){
    sRam.writeBuffer(x, buffer, sizeof(buffer) - 1);
  }
  dumpSRAM(0UL,100);
  
  Serial.println("\r\nRead Buffer. ");
  
  Serial.print("sizeof(buffer2) = ");
  Serial.print(sizeof(buffer2));
  Serial.print("\tstrlen(buffer2) = ");
  Serial.println(strlen(buffer2));
  
  sRam.readBuffer(0UL,buffer2,sizeof(buffer2) - 1);
  Serial.print("Serial.println(buffer2); = ");
  Serial.println(buffer2);
  
  Serial.print("sizeof(buffer2) = ");
  Serial.print(sizeof(buffer2));
  Serial.print("\tstrlen(buffer2) = ");
  Serial.println(strlen(buffer2));
  
  Serial.print("One at a time = ");
  for(byte y = 0; y < sizeof(buffer2); y++){
    char z = buffer2[y];
    Serial.print(z);
  }
  Serial.println();
  
  Serial.print("(byte)buffer[10] = ");
  Serial.println((byte)buffer[10]);
  
  Serial.println("\r\nTests Finished.");
}

void loop(){
  
}

void dumpSRAM(uint32_t addr, uint16_t length)
{
  // block to 10
  addr = addr / 10 * 10;
  length = (length + 9)/10 * 10;
  
  byte b = sRam.readByte(addr);
  for (int i = 0; i < length; i++)
  {
    if (addr % 10 == 0)
    {
      Serial.println();
      Serial.print(addr, HEX);
      Serial.print(":\t");
    }
    Serial.print(b, HEX);
    b = sRam.readByte(++addr);
    Serial.print("  ");
  }
  Serial.println();
}

void dumpSRAM2(uint32_t addr, uint16_t length)
{
  // block to 10
  addr = addr / 10 * 10;
  length = (length + 9)/10 * 10;
  
  char b = sRam.readByte(addr);
  for (int i = 0; i < length; i++)
  {
    if (addr % 10 == 0)
    {
      Serial.println();
      Serial.print(addr);
      Serial.print(":\t");
    }
    Serial.print(b);
    b = sRam.readByte(++addr);
    Serial.print("  ");
  }
  Serial.println();
}

but despite strlen being correct the result is still wrong. (See attached Clipboard1 image)
Some extra debug code in the readBuffer routine show the reading is corrupt.

void SpiRAM::readBuffer(uint32_t address, char *buffer, uint16_t length) {
  digitalWrite(_ss_Pin, LOW);
  setAddressMode(address, READ);
  Serial.print("Address = ");
  Serial.println(address);
  for (uint16_t i = 0; i < length; i++){
    buffer[i] = SPI.transfer(0x00);
    char r = buffer[i];
    Serial.print("\r\n>");
    Serial.print(i);
    Serial.print(" = ");
    Serial.print(r);
    Serial.println("<");
  }
  digitalWrite(_ss_Pin, HIGH);
}

And the result (Clipboard2)
I think it's a pointer type thing but have no idea how to correct it

Clipboard-1.jpg

Finally after spending so much time on this the problem seemed to be due to SPI speed. Adjusted the clock down to SPI_CLOCK_DIV8 and it works fine now.
I will repackage the zip file and replace one in first post with it.

Serial.print("(byte)buffer[10] = ");
Serial.println((byte)buffer[10]);

if buffer has 10 elements their indices go from 0..9 !

Riva:
Finally after spending so much time on this the problem seemed to be due to SPI speed. Adjusted the clock down to SPI_CLOCK_DIV8 and it works fine now.
I will repackage the zip file and replace one in first post with it.

Should be in the datasheet .
What is the write latency of the EEPROM? (the time after a write the eeprom cannot accept commands)

robtillaart:
Serial.print("(byte)buffer[10] = ");
Serial.println((byte)buffer[10]);

if buffer has 10 elements their indices go from 0..9 !
But I wanted to confirm the string terminator had not been overwritten.

Riva:
Finally after spending so much time on this the problem seemed to be due to SPI speed. Adjusted the clock down to SPI_CLOCK_DIV8 and it works fine now.

Should be in the datasheet .
What is the write latency of the EEPROM? (the time after a write the eeprom cannot accept commands)
SRAM not EEPROM, the timings are all in the sub 100ns range and the chip should be able to handle 20MHz. I suspect it's an arduino thing and not a SRAM thing as the original library was written and tested on an UNO but now I'm using a MEGA to test and it fails with original library code. I arrived at the SPI speed because I had tried inserting delays in readBuffer and that increased the errors.

hey Riva,
I'm afread your lib with test script doesn't work on my Mega 2560. I've been trying to debug it but I get nowhere... All that I get as return is FF. at first I thought the problem was the buffer but when I removed it, the problem persisted.

This script works for me:

byte SRAM_READ(uint32_t address, int SS_PIN) {
  byte read_byte;
 
  digitalWrite(SS_PIN, LOW);
  setAddressMode(address, READ);
  read_byte = SPI.transfer(0x00);
  digitalWrite(SS_PIN, HIGH);
  
  return read_byte;
}
  
void SRAM_WRITE(uint32_t address, byte data_byte, int SS_PIN) {
  enable(SS_PIN);        //set SPI_SS low
  setAddressMode(address,WRITE);
  SPI.transfer(data_byte);
  disable(SS_PIN);         //set SPI_SS high
}
void enable(int SS_PIN){
  digitalWrite(SS_PIN, LOW);
}
void disable(int SS_PIN){
  digitalWrite(SS_PIN, HIGH);
}
void setAddressMode(uint32_t address, byte mode) {
  SPI.transfer(mode);
  SPI.transfer((byte)(address >> 16));
  SPI.transfer((byte)(address >> 8));
  SPI.transfer((byte)address);
}

MathiasVDA:
hey Riva,
I'm afread your lib with test script doesn't work on my Mega 2560. I've been trying to debug it but I get nowhere... All that I get as return is FF. at first I thought the problem was the buffer but when I removed it, the problem persisted.

What version of the Arduino IDE are you using? I tested it with my Mega2560 okay on 1.0.5
From the changes you made to get it to work it looks like the SS pin is not being defined properly. What value for SS get printed if you run the below sketch?

// Test connections are...
// Chip UNO MEGA  NAME
//  1   10  53    SS
//  2   12  50    MISO
//  3   NC  NC
//  4   GND GND   Vss
//  5   11  51    MOSI
//  6   13  52    SCK
//  7   +5V +5V   ~HOLD
//  8   +5V +5V   Vcc

#include <SPI.h>

void setup(){
  Serial.begin(115200);
  Serial.print("SS Pin Number = ");
  Serial.println(SS);
}

void loop(){
}

If the value for SS on your Mega2560 is not 53 then try altering the SpiRAM_Test.ini so the line below says 53 instead of SS.

SpiRAM sRam(SS);                            // Initialize the RAM with SS

becomes

SpiRAM sRam(53);                            // Initialize the RAM with SS

I've got 2 of thoose RAM chips. One with SS on 30 and one on 32. I've adjusted the script accordingly with no success. I'll debug some more tonight.

Just for your information: Ive been writing that script in my previous post myself before I knew of this lib. Some problems I've had with them before:

  • Setting the SS pin to high in setup()
  • Putting 'byte' before SRAM_READ and iunt_8 before the return variable

MathiasVDA:
I've got 2 of thoose RAM chips. One with SS on 30 and one on 32. I've adjusted the script accordingly with no success. I'll debug some more tonight.

Just for your information: Ive been writing that script in my previous post myself before I knew of this lib. Some problems I've had with them before:

  • Setting the SS pin to high in setup()
  • Putting 'byte' before SRAM_READ and iunt_8 before the return variable

Right, I think I see what the problem was. I had not allowed for non standard (hardware) SS pins. I have altered the library and the test sketch (zip file attached to first post in this thread) and testing it with my Mega using pin 9 as SS works fine (have also tested it using pin 30 but UNO does not have a pin 30 so altered test sketch to use 9 instead)

Great! Thanks for all your work on this!

I will test it out tonight (in approx 10 hrs ;-))

It's working perfectly now!

Just a reminder for beginners: if you have other IC's connected to the SPI bus, then you need to deactivate them by setting the pin HIGH...

Thanks Riva!

Hey Riva,

Small comment about your example. Shouldn't this:

const uint16_t ramSize = 0x1FFFF;
const uint32_t ramSize = 0x1FFFF;

?

(about the other topic with multiple SPI IC's, I'll get back to that when I've stabilized my sketches a little bit ;-))