Go Down

Topic: 23LC1024 SRAM Library (Read 28232 times) previous topic - next topic

Riva

Aug 16, 2013, 01:29 pm Last Edit: Dec 28, 2013, 10:15 am by Riva Reason: 1
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.
Don't PM me for help as I will ignore it.

MathiasVDA

Thank you very much for this!

robtillaart

Thanks for sharing!

Some remarks (based upon e.g. http://playground.arduino.cc/Main/LibraryForI2CEEPROM )

missing header
Code: (proposal spiram.h) [Select]

//    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
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

robtillaart

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!)

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

robtillaart


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.

Code: (not tested) [Select]

//
//    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
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Riva

#5
Dec 16, 2013, 11:26 am Last Edit: Dec 17, 2013, 01:10 pm by Riva Reason: 1
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.

Quote
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.
Quote
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.
Quote
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.
Quote
+ 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.
Don't PM me for help as I will ignore it.

Riva

#6
Dec 16, 2013, 02:09 pm Last Edit: Dec 16, 2013, 04:03 pm by Riva Reason: 1
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.
Code: [Select]
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.


Don't PM me for help as I will ignore it.

robtillaart

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

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 ;)

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.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

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.

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Riva



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.
Code: [Select]
#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.
Code: [Select]
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
Don't PM me for help as I will ignore it.

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.
Don't PM me for help as I will ignore it.

robtillaart

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

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


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)
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Riva


  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.


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.
Don't PM me for help as I will ignore it.

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.

This script works for me:
Code: [Select]

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

Riva


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?
Code: [Select]
// 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.
Code: [Select]
SpiRAM sRam(SS);                            // Initialize the RAM with SS

becomes
Code: [Select]
SpiRAM sRam(53);                            // Initialize the RAM with SS


Don't PM me for help as I will ignore it.

Go Up