Close

Using external memory as heap storage

A project log for Arduino MEGA 2560 32Kb RAM shield

A simple Arduino mega SRAM expansion.

andrey-skvortsovAndrey Skvortsov 05/05/2019 at 15:370 Comments

According to avr-libc documentation the most flexible way to manage the  different memories of AVR mcu is linker script. But if Arduino IDE is being used, this way is not obvious to implement, because there is no Makefile.

Therefore, there are two simplest ways to use external memory on Arduino MEGA.

1. The low-level way of readings and writings, based on volatile pointer to external memory area. Test program from this page gives an example.

External memory occupies area of 0x8000 to 0xFFFF in Atmega2560 address space.

Suppose we need an array of 8192 float values to write values from some probes. Then simply declare:

volatile float* dataBuffer = reinterpret_cast<volatile float*>(0x8000);

 Then it's possible to use dataBuffer as pointer to array of 8192 float numbers (8192 * 4 = 32768, the size of external memory):

...
dataBuffer[i] = analogRead(N);
...
Serial.println(dataBuffer[j]);
... 

2. The second way is to place heap memory area to external memory:

void setup()
{
  Serial.begin(115200);
  
  XMCRA |= 1ul<<7; // Switch ext mem iface on
  XMCRB = 0;
  __malloc_heap_start = 0x8000;
  __malloc_heap_end = 0xFFFF;

  uint32_t* dataBuffer = new uint32_t[1024];
  Serial.print("Dynamic buffer of 4096 bytes created, address: ");
  Serial.println((uintptr_t)dataBuffer, HEX);

  for (size_t i=0; i<1024; ++i) {
    dataBuffer[i] = i*16;
  }

  for (size_t i=0; i<1024; i+=4) {
    Serial.println();
    Serial.print(i, DEC);
    Serial.print('\t');
    Serial.print(dataBuffer[i], DEC);
    Serial.print('\t');
    Serial.print(dataBuffer[i+1], DEC);
    Serial.print('\t');
    Serial.print(dataBuffer[i+2], DEC);
    Serial.print('\t');
    Serial.print(dataBuffer[i+3], DEC);
  }
  
  delete [] dataBuffer;
}

void loop()
{
  delay(1000);
}

At the begining we set 2 internal avr-libc variables to the external memory boundaries:

 __malloc_heap_start = 0x8000;
 __malloc_heap_end = 0xFFFF;

After that simple variables and objects, declared at the module level or inside functions will be placed at the 8192 bytes of AVR internal RAM, but all dynamic variables and objects will be placed at external memory.

You can declare multiple arrays or simple variables:

float tempBuffer = new float[512];
assert(tempBuffer != nullptr);
...
tempBuffer[i] = readData(source);
...
delete [] tempBuffer;
...

It's possible to use external memory for example with some libraries:

https://bitbucket.org/starling13/libps2

include "ps2_keyboardstream.hpp"

#define DATA_PIN 5
#define CLK_PIN 2

static PS2::Keyboard* kbd;
static PS2::KeyboardStream* kstream;

void
setup()
{
  XMCRA <<= 1<<7;
  XMCRB = 0;
  __malloc_heap_start = 0x8000;
  __malloc_heap_end = 0xFFFF;

  kbd = new PS2::Keyboard;
  kstream = new PS2::KeyboardStream(*kbd);

  Serial.begin(115200);
  kbd->begin(DATA_PIN, CLK_PIN);
  
}

void
loop()
{
  while (kstream->available() > 0)
    Serial.write(kstream->read());
}

Discussions