Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to "verify" your details, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Electronics ➜ Microprocessors ➜ Connecting SRAM to your microprocessor using SPI

Connecting SRAM to your microprocessor using SPI

Postings by administrators only.

Refresh page


Posted by Nick Gammon   Australia  (23,070 posts)  Bio   Forum Administrator
Date Sat 05 Mar 2011 12:54 AM (UTC)

Amended on Sun 27 Nov 2011 10:32 PM (UTC) by Nick Gammon

Message
I've been thinking of doing some data collection (eg. make a logic analyzer) and for cases like that the inbuilt SRAM of the Arduino (Atmega328 chip) of 2Kb isn't really enough.

So I looked around for expansion RAM. And found this chip: 23A256. This has 256 K bits (ie. 32 K bytes) of SRAM. You could have multiple extra SRAM chips connected by using a different slave-select line for each one.

[EDIT] As suggested below, the 23K256 would be more appropriate, and you might need some logic-level conversion for the data lines. See reply further down.

Now the difference between SRAM and EEPROM is:


  • It is faster because it doesn't have to be "burnt" into the chip.

  • It can be used many times without "wearing out".

  • It does not retain the data once powered off - so it can't be used for long-term storage.


So, it is useful for "scratch" RAM, something you need during this session.

The chip costs around $US 1.70 in quantities of 10+.

This particular chip interfaces using SPI, which is a fast interface protocol. For more details about SPI see:

http://www.gammon.com.au/forum/?id=10892

Wiring up


Wiring is really simple, check out the pinouts:



Basically for the Arduino Uno (and similar boards) you connect:


  • CS (pin 1) to Arduino D10 (SPI slave select)
  • SO (pin 2) to Arduino D12 (SPI MISO)
  • Pin 3 is not connected
  • Vss (pin 4) to ground
  • SI (pin 5) to Arduino D11 (SPI MOSI)
  • SCK (pin 6) to Arduino D13 (SPI SCK)
  • HOLD (pin 7) to +5V (disables "hold" feature)
  • Vcc (pin 8) to +5V (power)


Like this:



Now you don't have to use D10 as slave select. If you have other SPI devices using D10 just choose a spare pin as slave select, configure it for output, and set it to high initially. In the code below I have a "define" for the slave select pin (SRAM_DEVICE), so you can easily change it.

Example code



#include <SPI.h>

#define SRAM_DEVICE 10  // SRAM on pin 10

// 23A256 commands

#define SRAM_READ  0b011  // Read data from memory array beginning at selected address
#define SRAM_WRITE 0b010  // Write data to memory array beginning at selected address
#define SRAM_RDSR  0b101  // Read STATUS register
#define SRAM_WRSR  0b001  // Write STATUS register

// operation modes (status register)

#define SRAM_BYTE_MODE 0b00000000  // Byte mode (default operation) 
#define SRAM_PAGE_MODE 0b10000000  // Page mode
#define SRAM_SEQN_MODE 0b01000000  // Sequential mode
#define SRAM_RSVD_MODE 0b11000000  // Reserved
#define SRAM_HOLD_MODE 0b00000001  // Set this bit to DISABLE hold mode

// setup SRAM device by turning into sequential mode
void sram_setup (const byte device)
{
  digitalWrite (device, LOW);   // select device 
  SPI.transfer (SRAM_WRSR); 
  SPI.transfer (SRAM_SEQN_MODE); 
  digitalWrite (device, HIGH);  // deselect device
}  // end of sram_setup

// write 'length' bytes from 'data' to device at address 'address'
void sram_write (const byte device, unsigned int address, byte * data, unsigned int length)
{
  if (length == 0)  // don't bother if nothing to do
     return;
     
  digitalWrite (device, LOW);  
  SPI.transfer (SRAM_WRITE);     // write mode
  SPI.transfer (address >> 8);   // high-order address byte
  SPI.transfer (address & 0xFF);   // low-order address byte
  for ( ; length ; --length)
    SPI.transfer (*data++);     // data byte
  digitalWrite (device, HIGH);  // deselect device
}  // end of sram_write

// read 'length' bytes into 'data' to device from address 'address'
void sram_read (const byte device, unsigned int address, byte * data, unsigned int length)
{
  if (length == 0)  // don't bother if nothing to do
     return;

  digitalWrite (device, LOW);     // select device
  SPI.transfer (SRAM_READ);       // read mode
  SPI.transfer (address >> 8);    // high-order address byte
  SPI.transfer (address & 0xFF);  // low-order address byte
  for ( ; length ; --length)
    *data++ = SPI.transfer (0);     // data byte
  digitalWrite (device, HIGH);    // deselect device
}  // end of sram_write

void setup (void)
{
  Serial.begin (9600);
  SPI.begin ();    
  sram_setup (SRAM_DEVICE);
}

char test [] = "hello, world";

void loop (void)
{
 sram_write (SRAM_DEVICE, 100, (byte *) test, sizeof test);
 
 char buf [100];
 
 sram_read (SRAM_DEVICE, 100, (byte *) buf, sizeof buf);

 Serial.println (buf);
 delay (1000);
 
}


The example code writes "hello, world" to address 100, and then reads it back into another buffer and displays it to confirm it got it back OK.

I have set the device to sequential mode, which means you can write as many bytes as you like - the address will eventually wrap around and go back to zero if you overflow its size (that is, address beyond 0x7FFF).


Timing



  • Time taken to write "hello, world" : 52.2 microseconds

  • Time taken to read 100-byte buffer: 338 microseconds


Basically the SPI protocol transfers a byte every 3 microseconds. To read or write from the chip it needs to send an addition 3 bytes at the start:


  • The command (1 byte) - that is, read, write, get/set status
  • The address (2 bytes)


So it is more efficient to do batches of bytes than one, because the overhead of the command/address is spread over more bytes. Basically the time to read or write n bytes is going to be (n + 3) * 3 microseconds ... plus probably a bit more for the overhead of the loop.

To put that into perspective, I measured the time taken to read back 1000 bytes, which was 3.22 ms, so it is probably fair to say that you can read or write n bytes at the rate of (n + 3) * 3.2 microseconds.

So is this an acceptable speed? I think it is. For example this code:


 digitalWrite (8, HIGH);    
 int val = analogRead(A0);    
 digitalWrite (8, LOW);    


I'm using D8 as a timing pin. By measuring how long it is high I can see how long the analogRead took. And the answer is 115 microseconds (pretty much what the reference predicts, which is around 100 microseconds).

Now adding in saving that data into SRAM, like this:


 digitalWrite (8, HIGH);    
 int val = analogRead(A0);    
 sram_write (SRAM_DEVICE, 100, (byte *) &val, sizeof val);
 digitalWrite (8, LOW);    


The time is now 143 microseconds, an additional 28 microseconds. We would predict from the formula above an extra 16 microseconds, so the extra 12 on top of that must have been the time taken to execute the write function and return.

So that shows that storing the value in memory took under half the time taken to read the value in the first place. That isn't too bad.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Hallidaymj   (1 post)  Bio
Date Reply #1 on Sun 27 Nov 2011 08:04 AM (UTC)
Message
Firstly, I find your site well organised and the detail in topics of the highest quality.

I came across your site as I was looking for 23k256 / Arduino SPI information. I am surprised to see that you have specified the 23A chip which is 1v8 operation and not the 23k which is 3v3. Even so I am more amazed that you advocate powering the lot from 5v without any level shifting or protection.

How long will these chips survive above their manufacturer's Absolute Ratings of 4v5?

Do you think the circuit would operate correctly with 10k resistors on the bus lines, with a 23k powered from 3v3?

Mike
Top

Posted by Nick Gammon   Australia  (23,070 posts)  Bio   Forum Administrator
Date Reply #2 on Sun 27 Nov 2011 10:30 PM (UTC)

Amended on Sun 27 Nov 2011 11:20 PM (UTC) by Nick Gammon

Message
Looks like I didn't read the spec closely. :)

You are quite right, some level shifting would be required, and the 23K256 would be more appropriate.

For the logic lines something like this could be used for level conversion:


http://www.dsscircuits.com/i2c-level-converter.html


[EDIT] Ooops, it's SPI not I2C. Then in that case:


http://www.sparkfun.com/products/8745


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by R3cgm   (1 post)  Bio
Date Reply #3 on Fri 11 May 2012 08:33 AM (UTC)
Message
Hi there, just wanted to chime in with my experiences.

First off, thanks Nick for posting such clear and well written documentation. I've bookmarked the "SPI Protocol" tutorial page for further study at: http://www.gammon.com.au/forum/?id=10892

Second, despite best efforts I was unsuccessful getting my 23K256 to work with the code and images provided here. I took note of the maximum voltage requirements for the 23k256, being careful to run the MOSI, CS, and CLOCK pins through a 74HC4050 voltage buffer/regulator to make sure they went down to 3V3, and tried leaving the MISO pin with a direct connection back to the Arduino.

From the data sheet, it looked like the pinouts for the 23k256 vs. the 23a256 should have been the same. Maybe they are not?

I originally bought the 23k256 chips because it looked like the Arduino playground had some good docs/code at: http://arduino.cc/playground/Main/SpiRAM

But like many others before me, discovered that the SPIRam2.zip at that location was corrupted, which prompted me to hunt for alternate test source code, which brought me here.

In any case, I was finally able to get the 23k256 chip to work with an Uno, without doing any 5V > 3V3 conversion, by following the code and video from 'idogendel' at http://www.youtube.com/watch?v=th_koS8d5LA

Thanks for posting the images and code. It gave me the confidence to believe these 23k256 memory chips *could* work. And, now they do!
Top

Posted by Nick Gammon   Australia  (23,070 posts)  Bio   Forum Administrator
Date Reply #4 on Sat 12 May 2012 03:03 AM (UTC)
Message
R3cgm said:

Second, despite best efforts I was unsuccessful getting my 23K256 to work with the code and images provided here.


Strange.

R3cgm said:

It gave me the confidence to believe these 23k256 memory chips *could* work. And, now they do!


Glad to hear that.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


40,988 views.

Postings by administrators only.

Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.