如何无需switch-case获取PROGMEM中位图地址以在OLED显示并节省Flash?
问题解答
一、指针数组占用大量Flash的原因
- 代码逻辑错误:你在赋值指针时错误地写了
pSmileyAdresse = (const uint8_t*)(Smileys[cSmileySelect - 1][0]);,这是取指针指向的第一个字节的值,再强制转换为指针地址,完全偏离了原本取指针的意图。这个错误会导致编译器误将PROGMEM中的位图数据复制到RAM,同时Flash中保留原数据,直接造成Flash占用暴增。 - 未正确使用PROGMEM修饰指针数组:在AVR这类8位单片机中,未加
PROGMEM的const数组会同时占用Flash和RAM(RAM用于初始化复制)。正确的指针数组定义应该将其放入PROGMEM:
读取时需要用const uint8_t* const Smileys[] PROGMEM = {&Smiley1[0], &Smiley2[0], ..., &Smiley7[0]};pgm_read_word获取指针:
即使正确定义,8个指针(每个2字节)仅占用16字节Flash,远不会出现3166字节的异常占用,核心问题还是你的代码写法错误。pSmileyAdresse = (uint8_t*)pgm_read_word(&Smileys[cSmileySelect - 1]);
二、无额外Flash占用的地址计算方法
由于你的7个位图按降序存储(Smiley7在最低Flash地址),且每个位图固定为392字节,可以直接通过基准地址加偏移量计算目标位图地址,完全不需要额外Flash空间:
推导逻辑
- 以Smiley7的起始地址为基准:
- Smiley7的地址 = 基准地址 + 0*392
- Smiley6的地址 = 基准地址 + 1*392
- ...
- Smiley1的地址 = 基准地址 + 6*392
- 对应
cSmileySelect(取值1-7),偏移量为(7 - cSmileySelect)*392
实现代码
// 确保所有Smiley数组都用PROGMEM定义 const uint8_t Smiley1[] PROGMEM = { ... }; const uint8_t Smiley2[] PROGMEM = { ... }; // ... const uint8_t Smiley7[] PROGMEM = { ... }; uint8_t cSmileySelect = ...; // 取值范围1-7 const uint8_t* pSmileyAdresse = (const uint8_t*)((uint16_t)Smiley7 + (7 - cSmileySelect) * 392);
验证
- 当
cSmileySelect=7时,偏移量为0,指向Smiley7的起始地址 - 当
cSmileySelect=1时,偏移量为6392=1960,正好指向Smiley1的起始地址(总大小7392=2352,2352-392=1960) - 该方法仅占用少量代码空间,完全符合你剩余100字节Flash的限制。
内容的提问来源于stack exchange,提问作者Harry S
相关产品推荐
相关产品推荐

