![image](https://habrastorage.org/files/e96/836/614/e96836614c634af18c287f90c46ea75e.jpg)
И вновь приветствую, Хабр!
Моя новая идея посвящена использованию цветовых пространств в микроконтроллерах.
То что моя новость кому-то таковой не покажется, я нисколько не удивлюсь.
Однако я предлагаю метод и его реализацию, подобных которому я не встречал.
Немного вступления, с чего все началось. Совсем недавно закончился год и в наследство от него у меня осталось около 300 светодиодов ws2811. Почему-то руки до них так и не доходили, но после празднования нового года у родственников я заметил, что у тётки стояла маленькая сувенирная ёлочка из стекла, которую она очень любит. Но она в этот раз совсем не сверкала и не искрилась по весьма банальной причине… сели батарейки. Вспомнив про свой набор светодиодов, пообещал сделать маленькую гирлянду для этой ёлочки, чтобы устранить эти недостатки раз и навсегда и сделать еще одного близкого мне человека хоть немного счастливее.
Разработка вертится в голове, постепенно ускоряясь, иногда превращаясь в вихрь мыслей, но до этих дней с места так и не сдвинулась. Останавливало то, что все те идеи, которые касались программы эффектов и их переключений, выглядели в голове очень громоздкими и страшно неудобными, при реализации их в цветовом пространстве RGB, к тому же я никак не мог из-за этого определиться, какой контроллер использовать. Но благодаря моему небольшому опыту работы дизайнером, я имею знания и о других пространствах, что натолкнуло меня на мысли о HSL и HSV. Изобретать новое смысла не было, реализаций полно нужно, лишь поискать… но я так и не нашел. Нет, я конечно нашел много интересных решений, и многие из них были Вашими, представленными прямо здесь, на Хабре, и за что я всем Вам бесконечно благодарен, Вы дали пищу для ума.
В итоге было решено остановиться на HSL, исключив из него одну компоненту S (Saturation), оставив ее константной (так как в реализации ЦДУ, этот параметр лишний), а контроллер решил использовать ATMega8U.
Набросав код и испытав прошивку, я понял что чего-то не хватает. И не хватило мне маленького несоответствия реалу.
Каждый охотник желает знать… И вновь я возвращаюсь к этому вопросу, и вновь осознаю, что нет его…
Нет оранжевого цвета в природе, как ни крути, он всего лишь оттенок.
Это и стало корнем и стержнем моей идеи, благодаря чему я полностью переписал код и придумал метод использования компактного описания цветового пространства, причем это уже совсем не HSL, HSV и не RGB, потому что я расширил диапазон оранжевого и вынес его в отдельный пространственный сектор.
Теперь только по-сути
Пространство можно представить как поверхность цилиндра в случае если оно замкнуто, с расположенными в углах, смещенными на 60 градусов базовыми цветами, между которыми заполнено градиентными переходами от одного базового цвета к другому:
![image](https://habrastorage.org/files/126/5c0/335/1265c03356354703b4b067063a0fa5f5.jpg)
Где высота цилиндра — это яркость, которая так же является составляющей градиента оттенков.
То же самое пространство представляет прямоугольник со сторонами «A x B», где «А» это угол определяющий базовый цвет и его оттенок, а «B» — яркость, в случае если пространство не замкнуто или развернуто из цилиндра.
Для поставленной задачи я решил использовать квадрат со стороной 256 х 256, тем самым уложившись в тип байт, где для угла, значения изменяются в пределах 0..255 (байт), а яркость: -127..127 (знаковый байт), благодаря этому получил возможность использования 8-ми базовых цветов и 32 градаций оттенков для каждого.
Пространство описывается как массив с RGB — компонентами базовых цветов, а градиенты оттенков рассчитываются на лету.
Пример описания:
{0,128,0}, {64,128,0}, {128,128,0}, {128,0,0}, {128,0,128}, {0,0,128}, {0,128,128}, {0,128,0}
здесь описана последовательность базовых цветов, назовем ее просто — радуга, где порядок компонент изменен согласно даташиту ws2811 (GRB), а последний — красный цвет, служит для замыкания пространства, выглядит это так:
![image](https://habrastorage.org/files/25f/dbf/07b/25fdbf07b92f4f748afb6d488d19b9ba.jpg)
Теперь, если взять некоторый индекс цвета, например 183, то можно сделать его отображение в пространство RGB таким образом:
Индекс базового цвета = 183 / 32 = 5 (синий, или {0,0,128})
Смещение оттенка = 183 % 32 = 23
Теперь вычисляем разницу между компонентами полученного базового цвета ({0,0,128}) и следующего за ним {0,128,128}, чтобы вычислить приращение (назовем его дельтой):
dG = 0 - 0 = 0, dR = 128 - 0 = 128, dB = 128 - 128 = 0;
Так как между цветами 32 градации, необходимо выполнить разбиение разности компонент:
dG= 0 / 32 = 0, dR= 128 / 32 = 4, dB= 0 / 32 = 0;
Теперь необходимо полученную дельту домножить на смещение оттенка и прибавить к компонентам текущего базового цвета ({0,0,128}):
G = 0 + 0 * 23 = 0, R = 0 + 4 * 23 = 92, B = 128 + 0 * 23 = 128;
Получили {0,92,128}, к которому теперь можно прибавить яркость, например 50: {50,142,178} — искомый цвет.
Как видно из примера, ничего сложного нет. В случае если дельта принимает отрицательное значение, то смещение оттенка при умножении даёт отрицательное дополнение, которое в сумме даст разность с конечной компонентой, это произойдет в случае, когда компонента градиента идет на спад.
И да, яркость применяется не пропорционально, а линейно, что дает некоторую погрешность, но в рамках описанной задачи это не страшно. Так же необходимо следить за нижним порогом чтобы не вызвать переполнение при отрицательной яркости.
Таким образом полученное пространство дает возможность использования 256 * 256 = 65536 оттенков.
Весь описанный метод был оптимизирован, устранены все умножения и деления, и приведен под упрощенную быструю математику, позволяет легко переписать его на ассемблер (лично меня скорость абсолютно устраивает: 200 ns между расчетами компонент для 7-и светодиодов), если нужна оптимизация для уменьшения размера кода.
В результате, первоначальное решение об использовании ATMega8u было отменено в пользу тиньки, так как полученная прошивка заняла меньше половины килобайта.
Нет, я не жлоб, я просто ленивый.
Итак, код ниже, выполняет инициализации, и содержит функцию вывода данных на линию ws2811:
![image](https://habrastorage.org/files/2f2/8f2/f8d/2f28f2f8da394430828d2fb9f71f2727.png)
А это код основного цикла и функция преобразования индекса пространства в цветовые составляющие с примером использования:
![image](https://habrastorage.org/files/4f8/4b6/b49/4f84b6b499cf4cc78173fb99582e3371.png)
А вот видео с демонстрацией работы (правда цвета далеки от реальности):
Описывать подробней комментариев в тексте смысла нет, сам метод уже описан выше. Если есть вопросы, задавайте.
Ну вот, собственно, и все, что я хотел рассказать.
… шутка — не все!
![image](https://habrastorage.org/files/988/66f/81f/98866f81f5d74e41856229607c42b96e.jpg)
Давайте теперь на секунду представим, как можно использовать данный метод. Как вам, например, такое пространство, которое показано на этой картинке:
Правильно! Можно описывать разные цвета и градиенты между ними. И кто вначале описания решил что метод не позволяет использовать оттенки серого — не прав.
В заключении — самое вкусное
Массив, описывающий в памяти пространство, занимает 32 байта (из-за выравнивания), который хранится в программной памяти. Если создать несколько таких описаний пространств, и переключаться между ними на ходу, переключая указатель на массив, то можно выбирать текущее пространство для каждого нового светодиода в очереди, а так же позволяет расширить число градаций между оттенками. И если слегка модифицировать код, и увеличить число ступеней в описании компонент до 8-ми, то 32*8 даст 256 (в моем коде 3 ступени), то использование семи массивов (0..255 красный, желтый, зеленый, голубой, синий, фиолетовый, белый) дают комбинацию в 16m оттенков «одновременно на экране»! При использовании 224 байт прошивки для хранения. А если еще немного по-потеть и чуть переписать код, то можно уложиться и в 96 байт с тем же результатом.
Плюс, благодаря оптимизации и полученной скорости обработки, можно использовать так называемый метод дизеринга ((англ. dither от старо-английского didderen — дрожать) С помощью чего, можно добиться еще большего числа оттенков (только задумайтесь: 512*512*512=134.217.728). Так же это позволяет создавать замыкания пространств друг на друга, чтобы устранить или сделать невидимыми эффекты перехода. В общем, все предложенное может быть модифицировано как Вашей душе угодно, на Ваш вкус и цвет, а может использоваться как есть, и удовольствие я Вам гарантирую!
26.02.2015: Предлагаю для скачивания исходный код в безвозмездное пользование.
27.02.2015: Доступна обновленная версия кода v1.2
Изменения такие:
1. Базовый цвет кодируется битами одного байта: 0bxxGGRRBB, массив уменьшился до 8-и байт.
2. Изменена функция преобразования в соответствии п.1., код стал немного больше (20-30 байт)
В перспективе планирую еще изменения, кому интересно — не пропустите!
28.02.2015: Последнее обновление кода до версии v2.0
Изменения:
1. Добавлена работа с подпространствами, введено пространство 8x8 (раздельный выбор 65536 оттенков для подпространств с переключением)
![image](https://habrastorage.org/files/1a8/40e/d24/1a840ed24311434686a1e3aeb8c134b0.jpg)
2. Добавлен пример выбора цветового подпространства (в демонстрационном примере идет переключение на следующее подпространство, при достижении нижнего порога яркости)
![image](https://habrastorage.org/files/473/5f1/1d8/4735f11d8f3e441fa7f5977bcd2c59f7.jpg)
3. Размер кода еще вырос до 626 байт (64 байт — само пространство + 16 байт на указатели, итого — 80 байт, остальное — код примера: 188 байт). Таким образом доступно более 500 байт дополнительного кода для организации программы эффектов.
4. Немного оптимизирован код.
5. Профилирование не выполнялось.
ИТОГ: 5 свободных ног тиньки, позволяют применить несколько раздельных каналов для независимого отображения на линиях ws2811 цветовых эффектов, и реализовать управление ими через аппаратный ввод, либо в автоматическом режиме. Реализованный буфер (14 светодиодов) позволяет использовать линию светодиодов кратную этому числу, и передавать данные в следующую секцию, повтором передачи буфера.
Больше обновлений в этом направлении не будет, будем считать алгоритм законченным, хотя идеи кажутся неисчерпаемыми. Предлагаю Вам самостоятельно развивать разработки в этом направлении.
Например: можно реализовать плавную смену цветовых подпространств (телепортация), изменить алгоритм управления яркостью на корректный, ввести параметр насыщенности цвета, и многое другое, чего здесь не хватает.
Удачных всем изысков и свершений, с наступающей весной!
С нетерпением жду ваших комментариев, критики, вопросов и советов.
Спасибо за внимание, до новых встреч!
Использование в коммерческих проектах, перепродажа исходного кода, использование с целью наживы и любых корыстных целях, запрещено. Исходные тексты распространяются бесплатно как есть, в случае использования на других сайтах, либо в других источниках, указание автора и уведомление о размещении — обязательно.