我在微控制器(Arduino Mega)的阵列中存储了Hitachi HD44780 LCD控制器的一些自定义字符。字符基本上是位图,颜色深度为1位,宽5 px,高8 px。
为了节省尽可能多的宝贵内存,我决定存储旋转的数据。否则我会浪费每行三位。例如,É会像这样存储:
---##### 0x1F
-#-#-#-# 0x55
#--#-#-# 0x95
---#-#-# 0x15
---#---# 0x11
输出应如下所示:
-----#-- 0x04
----#--- 0x08
-------- 0x00
---##### 0x1F
---#---- 0x10
---####- 0x1E
---#---- 0x10
---##### 0x1F
我的问题是,将这种情况转变为É的最有效方法是什么。我们在谈论一个只有8 KB RAM的16 MHz处理器,所以保持它尽可能快和微小是关键。编程语言是C(++)。
我个人的想法是创建所需的8字节数组并从左到右扫描行,用一些位掩码设置这些位。这就是为什么我也会对字母进行镜像,这样我就可以轻松地将我的位掩码移位并将其用于两个数组。
基本扫描第一个输入字节,相应地设置输出数组的第3位,扫描第二行,设置输出数组的第4位,依此类推。
但有没有更好的方法来实现这一目标?
答案 0 :(得分:0)
首先,谢谢。这是我几个月来看到的最有趣的问题。
所以空间是约束。我认为主要的问题是位提取和插入成本会很快耗尽代码内存。从5个字节的数组中提取单个位的循环,插入到8个字节的数组中可能需要大量可执行代码来执行此操作。
我建议用不同的方式来表示数据是支持一种run-length encoding.输出字符可以被认为是一个单位字符串,由0 / 1s流组成,后跟其他0流/ 1s直到64位已填满。
实际上,您正在编码状态更改,而不是实际的位模式。 数据宽度为1位,表示0或1,长度为3位宽,表示长度为1 .. 8.
因此
-----#-- 0x04
----#--- 0x08
-------- 0x00
---##### 0x1F
编码将是
这是3.5个字节而不是4个字节来编码。
主题的变体是不对每个字节的前3位进行编码。当可执行代码到达时,它会自动将三个0放入其中。这会将上面的小例子的编码成本降低到大约2.5个字节,代价是一些额外的可执行代码。
我认为这样做的好处是,每次从字节的一个半字节中精确地拉出位,然后将它们丢弃为单个字节的位。 IMNSHO即将获得最大规模的收益。
答案 1 :(得分:0)
有更好的方法来实现这一目标吗?
不是以旋转的方式保存字符位图,而是将其保存为5个字节。
在编译时打包:
创建32个常量(或eunm
)
#define L_____ 0
#define L____X 1
#define L___X_ 2
...
#define LXXXX_ 30
#define LXXXXX 31
制作宏
#define PACK8_5(a,b,c,d,e,f,g,h) \
((((((uint64_t)(a) << 5) + (b)) << 5) + (c)) << 5) + (d) ...
#define UNPACK40_5(a) \
((a) >> 32) & 31, ((a) >> 24) & 31, ((a) >> 16) & 31, ((a) >> 8) & 31, (a) & 31
#define CHBM(a,b,c,d,e,f,g,h) (UNPACK40_5(PACK8_5((a),(b),(c),(d),(e),(f),(g),(h))))
制作角色位图。有趣的是源代码看起来像字符位图。
unsigned char letter[5] = { CHBM( \
L__X__, \
L_X___, \
L_____, \
LXXXXX, \
LX____, \
LXXXX_, \
LX____, \
LXXXXX) };
要在运行时解压缩 - 可能有多种方法。以下是一个简单的想法。
void unpack5to8(unsigned char dest[8], const unsigned char src[5]) {
uint64_t pack = src[0];
for (unsigned i=1; i<5; i++) {
pack <<= 8;
pack += src[i];
}
for (unsigned i=8; i>0; ) {
i--;
dest[i] = pack & 31;
pack >>= 5;
}
另一个想法是使用更多代码,但使用32位变量。这是OP可以分析不同代码的地方。