Я храню некоторые пользовательские символы для ЖК-контроллера Hitachi HD44780 в массиве моего микроконтроллера (Arduino Mega). Символ представляет собой растровое изображение с глубиной цвета в один бит, шириной 5 пикселей и высотой 8 пикселей.
Чтобы сохранить как можно больше драгоценной памяти, я решил хранить данные повернутыми. В противном случае я бы потратил три бита на строку. Так, например, É будет храниться так:
---##### 0x1F
-#-#-#-# 0x55
#--#-#-# 0x95
---#-#-# 0x15
---#---# 0x11
Вывод должен выглядеть так:
-----#-- 0x04
----#--- 0x08
-------- 0x00
---##### 0x1F
---#---- 0x10
---####- 0x1E
---#---- 0x10
---##### 0x1F
Мой вопрос заключается в том, как наиболее эффективно превратить это обратно в É. Мы говорим о 16 МГц процессоре с только 8 КБ ОЗУ, поэтому ключевым моментом является поддержание его как можно более быстрым и крошечным. Язык программирования — C (++).
Моя личная идея состояла в том, чтобы создать требуемый массив из 8 байтов и сканировать строки слева направо, устанавливая биты с помощью некоторых битовых масок. Вот почему я также отразил буквы, так что я могу легко перенести свою битовую маску вправо и использовать ее для обоих массивов.
В основном сканируйте первый входной байт, соответственно установите 3-й бит выходного массива, отсканируйте вторую строку, установите 4-й бит выходного массива и так далее.
Но есть ли лучший способ добиться этого?
Прежде всего, спасибо. Это самый интересный вопрос, который я видел за последние месяцы.
Так что пространство — это ограничение. На мой взгляд, основная проблема заключается в том, что есть затраты на извлечение и вставку битов, которые довольно быстро израсходуют память кода. Цикл, извлекающий один бит из массива из 5 байтов и вставляющий в массив из 8 байтов, может потребовать для этого хорошего куска исполняемого кода.
Я предлагаю другой способ представления данных для поддержки своего рода кодирование длин серий Выходной символ можно рассматривать как однобитовую строку, состоящую из потока 0/1 с, за которым следуют другие потоки 0/1 с, пока не будут заполнены 64 бита.
По сути, вы кодируете изменения состояния, а не фактическую битовую комбинацию.
Данные будут иметь ширину 1 бит для обозначения 0 или 1, шириной 3 бита для представления длины 1 … 8.
следовательно с
-----#-- 0x04
----#--- 0x08
-------- 0x00
---##### 0x1F
Кодировка будет
Это 3,5 байта вместо 4 байтов для кодирования.
Разновидность темы — не кодировать первые 3 бита на каждый байт. Когда исполняемый код приходит к этому, он автоматически помещает три 0 в него. Это снизило бы стоимость кодирования маленького примера выше до примерно 2,5 байтов за счет некоторого дополнительного исполняемого кода.
Я думаю, что выгода в том, что вы каждый раз вытягиваете биты из одного куска байта и разбрасываете их на биты одного байта. IMNSHO, который собирается получить самый большой размер за доллар.
Есть ли лучший способ добиться этого?
Вместо того, чтобы сохранять растровое изображение символа в повернутом виде, сохраните его упакованным в 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 может профилировать различные коды.