In the last post, I added an additional 32KB of SRAM to my Bluetooth LE enabled Arduino Project to overcome it crashing / locking up when storing large data payloads. I tested the proof of concept for external SRAM functionality with it’s corresponding smart phone app. The test of writing and reading bytes was very successful on it’s own.

However, implementing this new functionality into my firmware (includes LED pixel, security IC support, and serial debugging stuff) unveiled the biggest trade off to externally expanding the SRAM, time management.

My smartphone app has three slider. One each for Red, Green, and Blue values. When they are adjusted the new values are sent to the Arduino via BLE and the data is pushed to the LED pixels in real-time.

Making the changes to write the BLE data externally made the lag between making an adjustment and seeing the result painfully long. In fact, the whole damn thing was inoperable. So what gives? I made it this far, and now it’s back to the drawing board, for such a stupid bottle neck I didn’t have the foresight to see.

I’m not giving up on this. Wires aren’t getting pulled. Square one is not getting revisited. Never say never, if you’ve never read the data sheet.

The 23A256’s (the SRAM IC) documentation is well written and clearly explains that it has three distinct operating modes and how exactly they work. They are:

Byte Mode – This is the default behavior out of the box. When operating in byte mode, only one byte of data can be read / written per transaction. The 23A256 internal counter is disabled because of the “one time shot” nature of the transaction. When the next byte needs to be accessed, the Micro-Controller has to clock out the instruction command and address all over again.

Page Mode- More than one byte can be accessed per transaction provided that the SPI bus is clocking the SRAM Chip, and it’s chip select is active. It will keep sending bytes out over the MISO line (or receive on the MOSI) until it reaches the end of the page of the starting byte (1024 pages of 32 bytes each). At that point the 23A256’s internal counter will go back to the start of the page. I’m still a bit baffled as to why this is needed, ever, but it’s there and this is the last we will speak of it. Comment if you have insight.

Sequential Mode- Much like Page Mode, this mode also allows multiple bytes to be read/written to/from the 23A256, provided it’s being clocked while it’s chip select is active. Data will flow in/out of it without hesitation. The HUGE exception for sequential mode is that the internal counter will reset the address being accessed to 0x0000 after the last address (0x7FFF) has been reached. This looks really promising for what I’m doing.

Alright, why does all this matter again? Clock cycles. Precious, clock cycles.

The more clock cycles that a transaction takes, the longer the wait will be for the next transaction to occur. This became blatently obvious looking at the 23A256’s Timing Diagrams.

In order to begin any SPI transactions (provided we are to spec with clock rate and MODE ), right after pulling the CS low, the micro has to send out the 1 byte (8 bits) instruction command, followed by the 2 byte (16 bits). That’s a total of 3 bytes (24 bits). It requires one clock cycle to transmit a single bit. So to just initiate a transaction with the SRAM, the micro has to clock out a total of 24 bits = 24 SPI clock cycles. The data is then handled in/out of the 23A256 until the micro pulls the CS high to end end the transaction. This is the same for all three operating modes.

So imagine, if you had to send a full table setting to a friend. And every package (regardless how small or big) always cost $24 to send. It’s going to be stupidly cheaper if you just get it all sent in one-go.

In the byte read mode, when the 24 requisite bits have been received by the 23A256, it will send out/write in 1 byte (8 bits). It won’t do anything else until the chip select has been pulled high, then low and then the process repeats. The benefit is that you know exactly at which address you’ve stored that value or read it from. But, for every 24 bits you clock into the 23A256 in byte mode, you’ll only be able to access 1 byte in return.

Sequential read introduces a huge time saver (at the cost of accuracy (I guess )). When the 24 requisite instruction and address bits are clocked in, the 23A256 will just keep sending data out or writing it in, provided that you keep clocking the 23A256 while in sequential mode and CS is pulled low.

This is fantastic because, after the first byte has been accessed it only requires 8 cycles to access any other bytes (provided that they follow one after the next) until the CS line is pulled high, ending the transaction. You can perform an transaction for however long you want on a single 24 bit preamble.

In theory, I could see a four-fold increase in transaction time if I change to operating the 23A256 (If your having a TL; DR moment, the 23A256 is the SRAM chip we’re talking about here) in sequential mode. For me this works, because my application is really just using the SRAM chip as a giant buffer. I don’t plan on needing to access specific addresses for specific reasons.

Before implementing all this optimization hocus-pocus, I need to prove it. I’ve learned my lesson about wasting time implementing something that’s never been tested. I wrote a sketch that tested the difference in speeds between the modes by recording millis() before writing values to all addresses on the 23A256. Then it subtracts millis() at the end of the function from millis() at the start of the transaction. It does this for byte and sequential modes.

Alright, so fire the engines, and let it ROLL!

And hot damn. It worked! Profound speed increase in read and write times. Okay, I believe, I believe in the good word of OEM Documentation!

Now it’s time to fork my current firmware, and tweak it to operate the external SRAM in sequential mode.

The snag here was, that before the functions to read/write byte to/from the external SRAM would only return one byte per call. But now, the issue is that the external SRAM has the ability to send/receive an endless amount of bytes in a transaction. It’s going to require that the read/write functions return an array.

Fantastic! But, Arduino is built upon Wiring.

Wiring is built upon C/C++.

Returning an array isn’t allowed in C/C++.

In fact, you can’t even pass an array into a function. You can how ever pass in a pointer to an array that existed before the function was called. So, ehhhhhh. Let’s run with that. The greatest discovery was that in reality, only a few dozen lines in my source needed to be changed.

For example this line which stored the value for red in the internal SRAM ( InstructionBuffer[200] ) :

redChars[i] = InstructionBuffer[bytesRead];

Got changed to:

extSRAM_sequentialRead(bytesRead, redChars , delayLength);

And the function to read sequentially is



uint8_t extSRAM_sequentialRead(uint16_t address, char dataTarget[] ,uint16_t length) {

SPI.beginTransaction(SRAMsettings);

digitalWrite(SS_PIN, LOW);

SPI.transfer(READ_BYTE);

SPI.transfer((char)(address >> 8));

SPI.transfer((char)(address));



// Serial.println("dataTarget[r] extSRAM_sequentialRead :");





for(uint16_t r = 0; r < length; r++){

//This is where data gets written to dataTarget

dataTarget[r] = SPI.transfer(0);

// Serial.println(dataTarget[r]);

}

digitalWrite(SS_PIN, HIGH);

SPI.endTransaction();



return 0;

}



In my mind I had seen it as this great daunting task. Nope, not at all. At run time with the new firmware optimized to sequential access the external SRAM and my existing app, well, yeah, you guessed it. I victory danced. I victory danced hard.

For now, everything is just peachy. 32KB of external SRAM gets handled without dropping a beat. Half of the Arduino’s internal SRAM and flash memory are still unused. I may be against the celling but thankfully I’m near a sky light.