Search code examples
arduinobitmapadafruitprogmem

How can I retrieve the address of bitmaps in PROGMEM to show it on OLED display without using a switch..case construct?


I have a rather complex application which controls the number of cups of coffee that are drawn. After different numbers the water tank has to be filled or the coffee grounds has to be discarded.

This application performs well for several years and now I want to add a fan to dry the coffee grounds to prevent mold, which now forms after several days.

The available space in flash is almost eaten up (around 100 bytes left after several loops of optimization).

Randomly bitmaps (smileys) are displayed, the bitmaps are 392 byte in size each, I have 7 of them.

They are stored as follows:

//const unsigned char Sm1Dimension[] PROGMEM = {56, 56};
const unsigned char Smiley1[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00,
    0x03, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0xff, 0x00,
... (cut out several lines of bytes)
    0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xc0, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00
};
const unsigned char Smiley2[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00,
    0x3f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00,
... (cut out several lines of bytes)
    0x0f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
... and so on until 
const unsigned char Smiley7[] PROGMEM = {...};

In the first approach I selected the to be displayed bitmap by means of a switch..case construct which works perfectly. The Adafruit GFX library accepts the pointers and displays the bitmaps as expected.

Below the part of working code:

        switch(cSmileySelect)       // select Smiley
    {
        case 1: 
            pSmileyAdresse = Smiley1;
            break;
        case 2:
            pSmileyAdresse = Smiley2;
            break;
        case 3:
            pSmileyAdresse = Smiley3;
            break;
        case 4:
            pSmileyAdresse = Smiley4;
            break;
        case 5:
            pSmileyAdresse = Smiley5;
            break;
        case 6:
            pSmileyAdresse = Smiley6;
            break;
        case 7:
            pSmileyAdresse = Smiley7;
            break;
        default:    
            pSmileyAdresse = Smiley1;
            break;
    }
// void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t width, int16_t height, uint16_t color);
    display.drawBitmap((128 - cSmileyBreite) / 2, 0 + (64 - cSmileyHoehe) / 2, pSmileyAdresse,
            (unsigned int)cSmileyBreite, (unsigned int)cSmileyHoehe, WHITE);

After again experiencing memory shortage when adding the logic to drive a fan, I found, that this switch..case construct uses up plenty of flash.

I tried to replace the switch..case by means of mathematics to derive the pointer to the bitmap in flash, but I get no result.

My test are as so:

pSmileyAdresse = (uint8_t*)(uint16_t)((Smiley7) + (2352 - (((cSmileySelect - 1) % 7) * (sizeof(Smiley7))  )));

The magic number 2352 is the size of the complete array of 7 smileys, the bitmaps are stored in descending order (smiley7 at the lowest address).

My expectation was, adding the size of each Smiley bitmap should derive the address of the next smiley, but to no avail.

Next approach was, to build up an array of pointers to the smileys...

const unsigned char*  Smileys[] = {
&Smiley1[0], &Smiley2[0], &Smiley3[0], &Smiley4[0], &Smiley5[0], &Smiley6[0], &Smiley7[0], &Smiley13_bw[0]
};

...and get the address of the selected smiley by this:

pSmileyAdresse = (const uint8_t*)(Smileys[cSmileySelect - 1][0]);

This might have worked, but this small addition to my code swells the code in flash by 3166 byte, busting the maximum by ~2000 byte.

Why this?

And lastly:
Is there a way of deriving the PROGMEM addresses of the smileys by simple mathematics, not busting the flash limit?

Thanks!


Solution

  • The initial question arose when I was on search for code optimization to free flash memory for further enhancing the program by additional functions. I back then found, that commenting out the switch...case construct, I used to get the adress of the to be displayed bitmap, frees a considerable amount of flash space. But I missed the fact, that the now no longer referenced bitmaps are optimized away by the linker. So I was mislead into thinking, the switch...case construct uses up so much space and has to be replaced by something more efficient. In the end, supported by @Maximilian and @hcheung, I found, that every approach leads to the same memory shortage as the initial switch...case construct. Finally: No solution but gain in knowledge :)