23K256 with levelshifter

External Serial RAM (23K256, 23LC1024)


A usual Arduino (e.g. Uno, Nano) only has 2048 Bytes of SRAM. Especially for bigger projects, this might not be enough.


The solution often is using a bigger microcontroller which brings along more RAM, they are more expensive, maybe more complicated and bigger. In the Arduino world, the next would be a mega 2560 which costs about 4 times the price of a Nano.

If you want to keep using your devices and just need a memory upgrade, a separate RAM module is the best choice. There are cheap DRAMs out there, whereas SRAM is more expensive. There is even NVSRAM being even more expensive. There are lots of differences and each device is useful under its preferred circumstances. Simply said, your choice is a tradeoff between:

  • DRAM is very cheap, but needs refreshes. This is not very complicated at all (just keep a timer running which updates all rows periodically). However, you need to preempt your usual program. Timing may become a horrible culprit.
  • SRAM is not that cheap, but easier to use – there is no need for refreshes.
  • NVSRAM is the most expensive variant, it may however hold its contents by using a battery backup which you already have anyway with an RTC clock.

Furthermore, we do not have too much digital PINs on our smaller Arduinos. Using non-serial devices introduces the need for more hardware like multiplexers or shift registers.

There fortunately already are RAM devices with a serial interface. This usually excludes DRAM devices for our choice.

Due to the higher costs for NVRAM, I will just consider two models of serial SRAM, the 23K256 and 23LC1024.

What this is about

With this post, I will discuss how to use the microchip 23K256 (datasheet) and 23LC1024 (datasheet) devices. A short introduction from the datasheets follows.


Identical specifications

  • Max. 20MHz operation (this is quite fast, more than possible with a Nano/Uno/Mega)
  • SPI interface
  • Unlimited Reads/Writes
  • Bytewise, pagewise or streamed access
  • 32 Byte pages
  • Temperature blah blah…

Nevertheless, there are important differences

  • The 23K256 has a total amount of 2^15 x 8 Bit (=32kB).
  • The 23LC1024 has a total amount of 2^17 x 8 Bit (=128kB)
  • The 23K256 operates at 2.7-3.6V and it NOT 5V input tolerant!
  • The 23LC1024 operates at 2.5-5.5V


Apparently, the bigger one is more expensive, but provides more RAM. However, this additional RAM comes at computational costs: Due to the addressing scheme and size, actual addressing via the serial interfaces requires 3 Bytes [17 bits address msbf, 7bits dont care], whereas the smaller device only needs 2 Bytes [15 bit address msbf, 1 dont care]. However, this additional byte for addressing may be negligible especially as one usually does not access single bytes randomly. Running SPI with a divider of e.g. 2 is very fast anyway.

Voltage Levels

A serious limitation for our use case is the voltage of the 23K256. There are lots of people using the device at 5V. Due to the datasheet this exceeds the absolute maximum ratings and will most likely kill the chip one day.

Infeasable simple solution #1

Another solution may be adapting the microcontroller voltage to this chip – this is not that easy with the Arduinos and probably not desired at the speed would be decreased.

Simple solution #2

Another solution will be using voltage dividers. Due to SPI being unidirectional on all lines, one may use voltage dividers on all lines from the microcontroller to the device and just connect the other direction directly.

The biggest problem will be the line from the device towards the microcontroller. The devices may send a signal at VCC at maximum. The maximum rating due to the datasheet is 3.6V, whereas the Arduino only provides 3.3V.

So without using any kind of step-up converter or different power source, we end up running the device at 3.3V.

This ultimately means that the return signal may have a level of 3.3V at maximum – which may or may not be recognised as a HIGH signal by your microcontroller: E.g. the atmega328p datasheet is a CMOS mc. The CMOS voltage levels are defined as [see ‘Logic Level’ on wikipedia]:

Technology Low voltage Low@5V High voltage High@5V
CMOS 0V to 1/3 VCC 0V to 1.7V 2/3 VCC to VCC 3.33V to 5V

So this may do the work, but is very tight in terms of the specifications and therefore imho not the best way to do it.

Recommended solution #3

The best way connecting 3.3V devices to a 5V system is imho using bidirectional level shifters. They consist of some MOSFET transistors which convert the logic levels bidirectional. For our purpose, I would prefer 4-channel converters. Such devices are quite cheap – I just purchased packs of 5pcs for 4 USD.

You can provide two reference voltages and just use 4 distinct channels, pretty easy.

The design is as follows (further technical details are available here):

4-Channel logical Level Shifter

4-Channel logical Level Shifter: The signals are converted bidirectional via MOSFET transistors. Both, the High and Low voltage sides define a reference.


Luckily, there is already a small sketch on the Arduino page (SpiRAM) available which allows using the 23K256 chip.

Due to the different address length, I have made small adjustments to the library; I replaced all address transfers with a new method. Furthermore, I have added a new membervar defining the address length as follows:

This code snippet may be optimized at compile time when using only one type at a time (the uint32_t introduces overhead for the 23K256 devices calls).

23K256 with levelshifter

The 23K256 chip with levelshifter attached to a mega board. Blue: GND, RED: 5V, ORANGE: 3.3V, BLACK: MOSI, WHITE: MISO, YELLOW: SS, GREEN: SCK.


Actual RAM access becomes quite simple with this solution. After having created an instance of the SpiRAM class, one may simply call the read or write methods in either byte, page or stream mode.

Here is a very basic example writing and reading a byte:

Level shifting problems

Sounds nice. Works. With the 23LC1024. However, the 23K256 was tricky to get it running. It seemed that the chip was VERY picky with the supply voltage – at least when also using the level shifter in my setup.

Dividing the internal clock by 8 (fuse setting) and using an SPI clock divider of 128 let me test the chip at rather different voltages. It was also possible to run the chip without the level shifter (do not do this for a longer time!).

However, this is not desired and one wants to use the full frequency. So turning back to usual operation with a clock divider of 2, the chips just did not work at all (not at 3.3V, nor 5V, nor 5V without the level shifter).

So I used a potentiometer to adjust the voltages, tried several caps (especially the 3.3 line of the mega shows lots of ripple) – nothing worked out. I ended up by setting the voltage to a certain value on order to not having any errors (not even occasional bad reads). This is not really reproducable at all smells like there must be something different very wrong.

Solution: I had used 2x20cm jump-wires for connecting the level-shifter with the mega board and an experiment breadboard. This just does not seem to work out as the wires are too long (too much capacity?). Anyway, using shorter wires solves the problem. But, a cap (e.g. 1u) for the rippled 3.3V from the mega is definitely necessary.

And finally this looks like follows:

SPI RAM @2V, 15.6kHz

SPI RAM @2V, 15.6kHz; CH8: SS, CH1: RECT, CH2: MOSI, CH3: SCK, CH4: MISO

SPI RAM @5V, 15.6kHz

SPI RAM @5V, 15.6kHz; CH8: SS, CH1: RECT, CH2: MOSI, CH3: SCK, CH4: MISO

SPI RAM @3.3V, 8MHz; CH8: SS, CH2: MOSI, CH3: CLK, CH4: MISO

SPI RAM @3.3V, 8MHz; CH8: SS, CH2: MOSI, CH3: SCK, CH4: MISO

Note however, in diverse setups, 8MHz operation failed. Try using lower frequencies, e.g. SPI_CLOCK_DIV4.


I have conducted these first tests on an Arduino mega2560 board. Results may not be the same on my Nanos…


Leave a Reply