如何在不使用 switch..case 构造的情况下检索 PROGMEM 中位图的地址以将其显示在 OLED 显示屏上?

问题描述 投票:0回答:1

我有一个相当复杂的应用程序,它控制绘制的咖啡杯数。不同数量后,必须将水箱注满,否则必须丢弃咖啡渣。

该应用程序在几年内表现良好,现在我想添加一个风扇来干燥咖啡渣,以防止霉菌,现在几天后就会形成霉菌。

flash 的可用空间几乎被吃光了(经过多次优化后,只剩下 100 字节左右)。

随机显示位图(笑脸),每个位图大小为 392 字节,我有 7 个。

它们的存储方式如下:

//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 = {...};

在第一种方法中,我通过 switch..case 结构选择要显示的位图,该结构工作得很好。 Adafruit GFX 库接受指针并按预期显示位图。

工作代码部分下方:

        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);

在添加驱动风扇的逻辑时再次遇到内存不足后,我发现这个开关..case 结构消耗了大量的闪存。

我试图通过数学的方式替换switch..case来推导Flash中位图的指针,但没有得到结果。

我的测试是这样的:

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

幻数2352是7个笑脸的完整数组的大小,位图按降序存储(笑脸7位于最低地址)。

我的期望是,添加每个笑脸位图的大小应该得出下一个笑脸的地址,但无济于事。

下一个方法是,建立一个指向笑脸的指针数组......

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

...并通过以下方式获取所选笑脸的地址:

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

这可能有效,但是我的代码中的这个小添加使闪存中的代码增加了 3166 字节,将最大值增加了约 2000 字节。

为什么会这样?

最后:
有没有一种方法可以通过简单的数学推导笑脸的 PROGMEM 地址,而不突破闪存限制?

谢谢!

arduino bitmap adafruit progmem
1个回答
0
投票

void drawBitmap(int16_t x, int16_t y, uint8_t *位图, int16_t 宽度, int16_t 高度, uint16_t 颜色);

AdafruitGFX 为

drawBitmap()
提供了两种重载方法。这个特殊的方法允许您传递一个指向位于 RAM 中的
bitmap
的指针,正如您从 源代码 中看到的那样,它不会从 PROGMEM 中读取数据,而只是获取一个字节
b = bitmap[j * byteWidth + i / 8];
。因此,如果您使用函数签名调用该方法,则需要首先将数据复制到 RAM 缓冲区中,然后使用指向 RAM 缓冲区的指针调用该函数,显然这将占用 Smileyx 大小的 RAM 内存。

正确调用的API是第二个重载方法:

void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color)

注意到细微的差别是

const uint8_t bitmap[]
源代码 表明它直接从 PROGMEM 中读取一个字节:

b = pgm_read_byte(&bitmap[j * byteWidth + i / 8]);

这只需要 1 个字节的 RAM 来存储

b
,而不是 Smiley1 的大小。要使用此方法,只需直接传入
Smiley1
即可:

display.drawBitmap(x, y, Smiley1, w, h, color);
© www.soinside.com 2019 - 2024. All rights reserved.