28 September, 2015

Arduino: store 64-bit integers in PROGMEM

Nowadays developers don't think about bits, bytes or kilobytes.

How much memory is allocated for the object? Who the hell cares?!

Honestly speaking, modern software does fairly simple things, but it requires gigabytes of memory; much more than they actually needed.

But with Arduino each byte is under control. And I like it. Simple programs works in a simple way and requires reasonable amount of resources.

Modern Arduino (based on ATmega328P) has:

  • 2 KB of RAM
  • 32 KB of Flash
  • 1KB of EEPROM

Much enough memory for good program )

It is possible to store the data in the Flash area via the PROGMEM modifier in the code. If you going to use the data from the Flash (PROGMEM) then you have to read it back into RAM.

I found it useful to keep 8x8 animation (created using the LED Matrix Editor) for LED dot matrix in the Flash instead of RAM. In my program each image is a 64-bit unsigned integer (uint64_t); an animation - is a series of these images.

How to store an array of uint64_t in PROGMEM:

const uint64_t IMAGES[] PROGMEM = {
  0x0000000000000000,
  0x7c92aa82aa827c00,
  0x7ceed6fed6fe7c00,
...
};

How to read single(i) uint64_t from the array from PROGMEM:

  uint64_t image;
  memcpy_P(&image, &IMAGES[i], 8);

  displayImage(image);

Just copy 8 (single uint64_t) bytes from the Flash to RAM.

Two sketches below show the small differences in the code, but big difference in memory usage.

Store images in the RAM:

#include <LedControl.h>

const int DIN_PIN = 7;
const int CS_PIN = 6;
const int CLK_PIN = 5;

const uint64_t IMAGES[] = {
  0x0000000000000000,
  0x7c92aa82aa827c00,
  0x7ceed6fed6fe7c00,
  0x10387cfefeee4400,
  0x10387cfe7c381000,
  0x381054fe54381000,
  0x38107cfe7c381000,
  0x00387c7c7c380000,
  0xffc7838383c7ffff,
  0x0038444444380000,
  0xffc7bbbbbbc7ffff,
  0x0c12129ca0c0f000,
  0x38444438107c1000,
  0x060e0c0808281800,
  0x066eecc88898f000,
  0x105438ee38541000,
  0x061e7efe7e1e0600,
  0xc0f0fcfefcf0c000,
  0x1038541054381000,
  0x6666006666666600,
  0xa0a0a0bca6a6fc00,
  0x0c324824124c3000,
  0xfefefe0000000000,
  0x7c38541054381000,
  0x383838fe7c381000,
  0x10387cfe38383800,
  0x10307efe7e301000,
  0x1018fcfefc181000,
  0xfefe0e0e0e000000,
  0x002844fe44280000,
  0xfefe7c7c38381000,
  0x1038387c7cfefe00,
  0x0000000000000000,
  0x180018183c3c1800,
  0x00000000286c6c00,
  0x6c6cfe6cfe6c6c00,
  0x103c403804781000,
  0x60660c1830660600,
  0xfc66a6143c663c00,
  0x0000000c18181800,
  0x060c1818180c0600,
  0x6030181818306000,
  0x006c38fe386c0000,
  0x0010107c10100000,
  0x060c0c0c00000000,
  0x0000003c00000000,
  0x0606000000000000,
  0x00060c1830600000,
  0x3c66666e76663c00,
  0x7e1818181c181800,
  0x7e060c3060663c00,
  0x3c66603860663c00,
  0x30307e3234383000,
  0x3c6660603e067e00,
  0x3c66663e06663c00,
  0x1818183030667e00,
  0x3c66663c66663c00,
  0x3c66607c66663c00,
  0x0018180018180000,
  0x0c18180018180000,
  0x6030180c18306000,
  0x00003c003c000000,
  0x060c1830180c0600,
  0x1800183860663c00,
  0x003c421a3a221c00,
  0x6666667e66663c00,
  0x3e66663e66663e00,
  0x3c66060606663c00,
  0x3e66666666663e00,
  0x7e06063e06067e00,
  0x0606063e06067e00,
  0x3c66760606663c00,
  0x6666667e66666600,
  0x3c18181818183c00,
  0x1c36363030307800,
  0x66361e0e1e366600,
  0x7e06060606060600,
  0xc6c6c6d6feeec600,
  0xc6c6e6f6decec600,
  0x3c66666666663c00,
  0x06063e6666663e00,
  0x603c766666663c00,
  0x66361e3e66663e00,
  0x3c66603c06663c00,
  0x18181818185a7e00,
  0x7c66666666666600,
  0x183c666666666600,
  0xc6eefed6c6c6c600,
  0xc6c66c386cc6c600,
  0x1818183c66666600,
  0x7e060c1830607e00,
  0x7818181818187800,
  0x006030180c060000,
  0x1e18181818181e00,
  0x0000008244281000,
  0xfe00000000000000,
  0x0000000060303000,
  0x7c667c603c000000,
  0x3e66663e06060600,
  0x3c6606663c000000,
  0x7c66667c60606000,
  0x3c067e663c000000,
  0x0c0c3e0c0c6c3800,
  0x3c607c66667c0000,
  0x6666663e06060600,
  0x3c18181800180000,
  0x1c36363030003000,
  0x66361e3666060600,
  0x1818181818181800,
  0xd6d6feeec6000000,
  0x6666667e3e000000,
  0x3c6666663c000000,
  0x06063e66663e0000,
  0xf0b03c36363c0000,
  0x060666663e000000,
  0x3e403c027c000000,
  0x1818187e18180000,
  0x7c66666666000000,
  0x183c666600000000,
  0x7cd6d6d6c6000000,
  0x663c183c66000000,
  0x3c607c6666000000,
  0x3c0c18303c000000,
  0x7018180c18187000,
  0x1818180018181800,
  0x0e18183018180e00,
  0x000000365c000000,
  0xfe8282c66c381000
};
const int IMAGES_LEN = sizeof(IMAGES)/8;


LedControl display = LedControl(DIN_PIN, CLK_PIN, CS_PIN);


void setup() {
  display.clearDisplay(0);
  display.shutdown(0, false);
  display.setIntensity(0, 10);
}

void displayImage(uint64_t image) {
  for (int i = 0; i < 8; i++) {
    byte row = (image >> i * 8) & 0xFF;
    for (int j = 0; j < 8; j++) {
      display.setLed(0, i, j, bitRead(row, j));
    }
  }
}

int i = 0;

void loop() {
  uint64_t image = IMAGES[i];

  displayImage(image);
  if (++i >= IMAGES_LEN ) {
    i = 0;
  }
  delay(1000);
}

Sketch uses 3,424 bytes (10%) of program storage space.
Maximum is 32,256 bytes.
Global variables use 1,123 bytes (54%) of dynamic memory,
leaving 925 bytes for local variables. Maximum is 2,048 bytes.

Store images on the Flash:

#include <LedControl.h>

const int DIN_PIN = 7;
const int CS_PIN = 6;
const int CLK_PIN = 5;

const uint64_t IMAGES[] PROGMEM = {
  0x0000000000000000,
  0x7c92aa82aa827c00,
  0x7ceed6fed6fe7c00,
  0x10387cfefeee4400,
  0x10387cfe7c381000,
  0x381054fe54381000,
  0x38107cfe7c381000,
  0x00387c7c7c380000,
  0xffc7838383c7ffff,
  0x0038444444380000,
  0xffc7bbbbbbc7ffff,
  0x0c12129ca0c0f000,
  0x38444438107c1000,
  0x060e0c0808281800,
  0x066eecc88898f000,
  0x105438ee38541000,
  0x061e7efe7e1e0600,
  0xc0f0fcfefcf0c000,
  0x1038541054381000,
  0x6666006666666600,
  0xa0a0a0bca6a6fc00,
  0x0c324824124c3000,
  0xfefefe0000000000,
  0x7c38541054381000,
  0x383838fe7c381000,
  0x10387cfe38383800,
  0x10307efe7e301000,
  0x1018fcfefc181000,
  0xfefe0e0e0e000000,
  0x002844fe44280000,
  0xfefe7c7c38381000,
  0x1038387c7cfefe00,
  0x0000000000000000,
  0x180018183c3c1800,
  0x00000000286c6c00,
  0x6c6cfe6cfe6c6c00,
  0x103c403804781000,
  0x60660c1830660600,
  0xfc66a6143c663c00,
  0x0000000c18181800,
  0x060c1818180c0600,
  0x6030181818306000,
  0x006c38fe386c0000,
  0x0010107c10100000,
  0x060c0c0c00000000,
  0x0000003c00000000,
  0x0606000000000000,
  0x00060c1830600000,
  0x3c66666e76663c00,
  0x7e1818181c181800,
  0x7e060c3060663c00,
  0x3c66603860663c00,
  0x30307e3234383000,
  0x3c6660603e067e00,
  0x3c66663e06663c00,
  0x1818183030667e00,
  0x3c66663c66663c00,
  0x3c66607c66663c00,
  0x0018180018180000,
  0x0c18180018180000,
  0x6030180c18306000,
  0x00003c003c000000,
  0x060c1830180c0600,
  0x1800183860663c00,
  0x003c421a3a221c00,
  0x6666667e66663c00,
  0x3e66663e66663e00,
  0x3c66060606663c00,
  0x3e66666666663e00,
  0x7e06063e06067e00,
  0x0606063e06067e00,
  0x3c66760606663c00,
  0x6666667e66666600,
  0x3c18181818183c00,
  0x1c36363030307800,
  0x66361e0e1e366600,
  0x7e06060606060600,
  0xc6c6c6d6feeec600,
  0xc6c6e6f6decec600,
  0x3c66666666663c00,
  0x06063e6666663e00,
  0x603c766666663c00,
  0x66361e3e66663e00,
  0x3c66603c06663c00,
  0x18181818185a7e00,
  0x7c66666666666600,
  0x183c666666666600,
  0xc6eefed6c6c6c600,
  0xc6c66c386cc6c600,
  0x1818183c66666600,
  0x7e060c1830607e00,
  0x7818181818187800,
  0x006030180c060000,
  0x1e18181818181e00,
  0x0000008244281000,
  0xfe00000000000000,
  0x0000000060303000,
  0x7c667c603c000000,
  0x3e66663e06060600,
  0x3c6606663c000000,
  0x7c66667c60606000,
  0x3c067e663c000000,
  0x0c0c3e0c0c6c3800,
  0x3c607c66667c0000,
  0x6666663e06060600,
  0x3c18181800180000,
  0x1c36363030003000,
  0x66361e3666060600,
  0x1818181818181800,
  0xd6d6feeec6000000,
  0x6666667e3e000000,
  0x3c6666663c000000,
  0x06063e66663e0000,
  0xf0b03c36363c0000,
  0x060666663e000000,
  0x3e403c027c000000,
  0x1818187e18180000,
  0x7c66666666000000,
  0x183c666600000000,
  0x7cd6d6d6c6000000,
  0x663c183c66000000,
  0x3c607c6666000000,
  0x3c0c18303c000000,
  0x7018180c18187000,
  0x1818180018181800,
  0x0e18183018180e00,
  0x000000365c000000,
  0xfe8282c66c381000
};
const int IMAGES_LEN = sizeof(IMAGES)/8;


LedControl display = LedControl(DIN_PIN, CLK_PIN, CS_PIN);


void setup() {
  display.clearDisplay(0);
  display.shutdown(0, false);
  display.setIntensity(0, 10);
}

void displayImage(uint64_t image) {
  for (int i = 0; i < 8; i++) {
    byte row = (image >> i * 8) & 0xFF;
    for (int j = 0; j < 8; j++) {
      display.setLed(0, i, j, bitRead(row, j));
    }
  }
}

int i = 0;

void loop() {
  uint64_t image;
  memcpy_P(&image, &IMAGES[i], 8);

  displayImage(image);
  if (++i >= IMAGES_LEN ) {
    i = 0;
  }
  delay(1000);
}

Sketch uses 3,470 bytes (10%) of program storage space.
Maximum is 32,256 bytes.
Global variables use 99 bytes (4%) of dynamic memory,
leaving 1,949 bytes for local variables. Maximum is 2,048 bytes.

As you can see - the second (PROGMEM) program uses more than 10 times less RAM memory than the first.