АДРЕСНАЯ ЛЕНТА

Мы с вами уже обсуждали адресные светодиодные ленты, есть гайд по особенностям подключения и сравнение библиотек. Что объединяет все библиотеки для управления адресной светодиодной лентой? Правильно, проблемы с нехваткой памяти, потому что один светодиод требует 3 байта оперативной памяти, по байту на каждый базовый цвет: красный, зелёный и синий. Протокол передачи данных на ленту состоит из последовательных передач байта каждого цвета, примерно так: R1, G1, B1, R2, G2, B2… Rn, Gn, Bn. Именно поэтому микроконтроллер хранит в памяти байт каждого цвета каждого светодиода в ленте. Для одного светодиода это выглядит так: RRRRRRRR GGGGGGGG BBBBBBBB – по 8 бит на каждый цвет. А что если попробовать “сжать” цвет? Мы попробовали!

Сначала сжали цвет до 2-х байт (16 бит), цвет хранится вот таким образом: RRRRRGGG GGGBBBBB. “Запаковка” трёх байт r, g и b в два выглядит вот так:

((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | ((b & 0b11111000) >> 3)

Распаковка для отправки данных на ленту делается так:

loopData[0] = (data & 0b1111100000000000) >> 8;
loopData[1] = (data & 0b0000011111100000) >> 3;
loopData[2] = (data & 0b0000000000011111) << 3;

Далее сжали цвет до одного байта, хранится как RRGGGBBB. “Запаковка” трёх байт r, g и b в один выглядит вот так:

( (r & 0b11000000) | ((g & 0b11100000) >> 2) | (b & 0b11100000) >> 5)

Распаковка для отправки данных на ленту делается так:

loopData[0] = data & 0b11000000;
loopData[1] = (data & 0b00111000) << 2;
loopData[2] = (data & 0b00000111) << 5;

Вот и всё! Получилась очень сильная экономия памяти, которая позволяет, например, впихнуть в Arduino Nano почти 2 тысячи светодиодов при разрешении 8 бит, или 1 тысячу при разрешении 16 бит. Круто! =) Алгоритм вывода данных на ленту разработан Egor ‘Nich1con’ Zaharov и обгоняет все существующие библиотеки для работы с лентами, а я в свою очередь дописал лёгкую оболочку по работе с цветовыми пространствами и светодиодными матрицами. Сравнения по весу и скорости с другими библиотеками:

Частота обновления (Гц) от количества светодиодов (сравнение с другими библиотеками), включая минимальное время между отправками (40 мкс). microLED работает в режиме MAX_DATA_SPEED – разогнанном протоколе связи (не работает на WS2811!)

Кол-во диодов FastLED 24-bit NeoPixel 24-bit WS2812 24-bit microLED 24-bit microLED 16-bit microLED 8-bit
8 400 1818 3260 4782 4420 4460
16 400 1264 1751 2663 2437 2462
50 333 554 589 923 840 848
100 220 301 298 472 428 432
500 60 65 61 96 87 88

Занимаемая память (байт) от количества диодов, где LED – количество светодиодов (сравнение с другими библиотеками)

Память FastLED 24-bit NeoPixel 24-bit WS2812 24-bit microLED 24-bit microLED 16-bit microLED 8-bit
Flash 2786 1984 946 306 346 324
SRAM 90+3*LED 40+3*LED 31+3*LED 20+3*LED 20+2*LED 20+1*LED

Скачать саму библиотеку можно ниже, там ещё примеры и документация.

БИБЛИОТЕКА microLED

microLED – ультра-лёгкая библиотека для работы с адресной лентой/матрицей

  • Основная фишка: сжатие цвета, код занимает в разы меньше места в SRAM по сравнению с аналогами (FastLED, NeoPixel и др.)
    • Использование 8 битного цвета занимает в 3 раза меньше SRAM чем у других библиотек
    • Использование 16 битного цвета занимает в 1.5 раза меньше SRAM чем у других библиотек
  • Скорость обновления ленты в 1.5 раза выше, чем у остальных библиотек
  • Поддержка сжатия цвета: 8, 16 и 24 бита
  • Поддержка порядка цветов: RGB, GRB, BRG
  • Работа с цветом:
    • RGB
    • HSV
    • HEX цвета
    • “Цветовое колесо” (1500 самых ярких оттенков)
    • 16 встроенных цветов
  • Возможность чтения сжатого цвета в HEX 0xRRGGBB
  • Ограничение тока (автокоррекция макс. яркости)
  • Быстрейший протокол данных
  • Функция уменьшения яркости одного пикселя
  • Поддержка работы с адресными матрицами (см. пример)
  • Поддержка чипов: 2811/2812/2813
  • Частичная совместимость со скетчами для FastLED (смотри пример fastToMicro)

Поддерживаемые платформы: тестировалось только на ATmega328 (Nano, UNO, Mini) и ATtiny85. Должны поддерживаться и остальные МК этого поколения, частота желательно 16 МГц.

ДОКУМЕНТАЦИЯ


// ============ Методы класса microLED ============
// ЛЕНТА: имя буфера, количество ледов, пин
microLED(LEDdata *LEDarray, int LEDamount, byte pin);
// МАТРИЦА: имя буфера, пин, ширина матрицы, высота матрицы, тип матрицы, угол подключения, направление	(см. ПОДКЛЮЧЕНИЕ МАТРИЦЫ)
microLED(LEDdata *LEDarray, byte pin, byte width, byte height, M_type type, M_connection conn, M_dir dir);
// лента и матрица
void setRGB(int num, byte r, byte g, byte b);   // RGB
void setHSV(int num, byte h, byte s, byte v);   // HSV
void setHEX(int num, uint32_t color);           // HEX
void setColor(int num, COLORS color);           // стандартный цвет (см. "Стандартные цвета")
void colorWheel(int num, int color);            // цвет 0-1530	
void fill(LEDdata color);                       // заливка цветом (mRGB, mWHEEL, mHEX, mHSV)
void setLED(int n, LEDdata color);              // ставим цвет светодиода (mRGB, mWHEEL, mHEX, mHSV)	
uint32_t getColorHEX(int num);                  // получить HEX цвет диода (для сравнения и т.п.)
LEDdata getColor(int num);                      // получить цвет диода в LEDdata
void fade(int num, byte val);                   // уменьшить яркость на val
void setBrightness(byte newBright);             // яркость 0-255
void clear();                                   // очистка
void show();                                    // отправка
// матрица
void setPix(int x, int y, LEDdata color);       // ставим цвет пикселя x y в LEDdata (mRGB, mWHEEL, mHEX, mHSV)
uint32_t getColorHEX(int x, int y);             // получить цвет пикселя в HEX
LEDdata getColor(int x, int y);                 // получить цвет пикселя в LEDdata
void fadePix(int x, int y, byte val);           // уменьшить яркость пикселя на val
uint16_t getPixNumber(int x, int y);            // получить номер пикселя в ленте по координатам
void setVoltage(int mv);                        // установить напряжение питания в мв, по умолч. 5000 (для расчёта тока)
void setMaxCurrent(int ma);                     // установить максимальный ток в ма (автокоррекция яркости). 0 - выключено
// ============ Функции кодирования цвета ============
LEDdata mRGB(byte r, byte g, byte b);   // RGB 255, 255, 255
LEDdata mWHEEL(int color);              // цвета 0-1530
LEDdata mHEX(uint32_t color);           // HEX цвет
LEDdata mHSV(byte h, byte s, byte v);   // HSV 255, 255, 255
LEDdata mCOLOR(COLORS color);           // цвет
// ==================== Константы ====================
// Стандартные цвета
WHITE
SILVER
GRAY
BLACK
RED
MAROON
YELLOW
ORANGE
OLIVE
LIME
GREEN
AQUA
TEAL
BLUE
NAVY
PINK
PURPLE

ПРИМЕРЫ


// пример работы с лентой
#define LED_PIN 6       // пин ленты
#define NUMLEDS 64       // кол-во светодиодов
#define ORDER_GRB       // порядок цветов ORDER_GRB / ORDER_RGB / ORDER_BRG
#define COLOR_DEBTH 2   // цветовая глубина: 1, 2, 3 (в байтах)
// на меньшем цветовом разрешении скетч будет занимать в разы меньше места,
// но уменьшится и количество оттенков и уровней яркости!
// ВНИМАНИЕ! define настройки (ORDER_GRB и COLOR_DEBTH) делаются до подключения библиотеки!
#include "microLED.h"
LEDdata leds[NUMLEDS];  // буфер ленты типа LEDdata (размер зависит от COLOR_DEBTH)
microLED strip(leds, NUMLEDS, LED_PIN);  // объект лента
void setup() {
strip.setBrightness(30);    // яркость (0-255)
// яркость применяется при выводе .show() !
strip.clear();   // очищает буфер
// применяется при выводе .show() !
strip.show(); // выводим изменения на ленту
// Библиотека поддерживает два варианта работы с лентой:
// изменение цвета конкретного пикселя при помощи готовой функции
// или работа с массивом "вручную".
// Чтобы вывести обновлённый массив на ленту, используется .show()
// ------------------------------------------
// методы для заливки конкретного пикселя
// диод 0, цвет RGB 255 0 0 (красный)
strip.setRGB(0, 255, 0, 0);
// диод 1, цвет HSV 30 255 255 (цвет 30, яркость и насыщенность максимум)
strip.setHSV(1, 30, 255, 255);
// диод 2, цвет HEX 0x30B210
strip.setHEX(2, 0x30B210);
// диод 3, цвет AQUA
strip.setColor(3, AQUA);
// диод 4, цвет 1200 (диапазон 0-1530 вдоль радуги)
strip.colorWheel(4, 1200);
strip.show(); // выводим изменения на ленту
delay(2000);
// цвет любого диода можно получить в виде hex кода и сравнить с другим:
if (strip.getColorHEX(0) == strip.getColorHEX(1));
// ------------------------------------------
// данные функции возвращают конвертированный "цвет", который можно присвоить в массив
// mRGB(r, g, b);  // RGB 255, 255, 255
// mWHEEL(color);  // цвета 0-1530
// mHEX(color);    // HEX цвет
// mHSV(h, s, v);  // HSV 255, 255, 255
// mCOLOR(color);  // цвет
// Например покрасим половину ленты в один, половину в другой
for (byte i = 0; i < NUMLEDS / 2; i++) {
leds[i] = mHSV(0, 255, 255);  // красный
}
for (byte i = NUMLEDS / 2; i < NUMLEDS; i++) {
leds[i] = mHSV(80, 255, 255); // примерно зелёный
}
strip.show(); // выводим изменения на ленту
delay(2000);
// ------------------------------------------
// Также есть метод setLED, который красит диод цветом
strip.clear();   // очищает буфер
// диод 0, цвет RGB 255 0 0 (красный)  
strip.setLED(0, mRGB(255, 0, 0));
// диод 1, цвет HSV 30 255 255 (цвет 30, яркость и насыщенность максимум)
strip.setLED(1, mHSV(30, 255, 255));
// диод 2, цвет HEX 0x30B210
strip.setLED(2, mHEX(0x30B210));
// диод 3, цвет AQUA
strip.setLED(3, mCOLOR(AQUA));
// диод 4, цвет 1200 (диапазон 0-1530 вдоль радуги)
strip.setLED(4, mWHEEL(1200));
strip.show(); // выводим изменения на ленту
delay(2000);
// ------------------------------------------
// Есть готовая функция для заливки всей ленты цветом - .fill()
// принимает конвертированный цвет, например от функций выше
strip.fill(mCOLOR(YELLOW)); // заливаем жёлтым
strip.show(); // выводим изменения на ленту
delay(2000);
// ------------------------------------------
// Для ускорения ручных заливок можно создать переменную типа LEDdata
LEDdata value1, value2;
value1 = mHSV(60, 100, 255);
value2 = mHSV(190, 255, 190);
for (byte i = 0; i < NUMLEDS; i++) {
// заливаем по половине ленты
if (i < NUMLEDS / 2) leds[i] = value1;
else leds[i] = value2;
}
strip.show(); // выводим изменения на ленту
delay(2000);
strip.clear();
}
void loop() {
// радуга!
static byte counter = 0;
for (byte i = 0; i < NUMLEDS; i++) {
//strip.setHSV(i, counter + i * (255 / NUMLEDS), 255, 255);  // можно так
leds[i] = mHSV(counter + i * (255 / NUMLEDS), 255, 255); // или в стиле fastLED
}
counter += 1;
strip.show();
delay(20);
}
// пример работы с модулем матрицы 8x8
#define M_WIDTH 8             // ширина матрицы
#define M_HEIGHT 8            // высота матрицы
#define ORDER_GRB       // порядок цветов ORDER_GRB / ORDER_RGB / ORDER_BRG
#define COLOR_DEBTH 2   // цветовая глубина: 1, 2, 3 (в байтах)
// на меньшем цветовом разрешении скетч будет занимать в разы меньше места,
// но уменьшится и количество оттенков и уровней яркости!
// ВНИМАНИЕ! define настройки (ORDER_GRB и COLOR_DEBTH) делаются до подключения библиотеки!
#include "microLED.h"
#define LED_PIN 8       // пин ленты
#define NUM_LEDS M_WIDTH * M_HEIGHT
LEDdata leds[NUM_LEDS];  // буфер ленты типа LEDdata
microLED matrix(leds, LED_PIN, M_WIDTH, M_HEIGHT, PARALLEL, LEFT_TOP, DIR_RIGHT);  // объект матрица
// тип матрицы: ZIGZAG - зигзаг, PARALLEL - параллельная
// угол подключения: LEFT_BOTTOM - левый нижний, LEFT_TOP - левый верхний, RIGHT_TOP - правый верхний, RIGHT_BOTTOM - правый нижний
// направление ленты из угла подключения: DIR_RIGHT - вправо, DIR_UP - вверх, DIR_LEFT - влево, DIR_DOWN - вниз
// шпаргалка по настройке матрицы в папке docs в библиотеке
void setup() {
matrix.setBrightness(30);  // яркость (0-255)
// Проверка ориентации матрицы
// Система координат - декартовая первая четверть
// Левый нижний угол - жёлтый
// Левый верхний - пурпурный
// Правый нижний - голубой
// рисуем пиксель: setPix(x, y, цвет)
// цвет в формате LEDdata, то есть:
// mRGB(r, g, b);  // RGB 255, 255, 255
// mWHEEL(color);  // цвета 0-1530
// mHEX(color);    // HEX цвет
// mHSV(h, s, v);  // HSV 255, 255, 255
// mCOLOR(color);  // цвет
matrix.setPix(0, 0, mCOLOR(YELLOW));
matrix.setPix(0, 7, mCOLOR(PURPLE));
matrix.setPix(7, 0, mCOLOR(TEAL));
matrix.show();
delay(1000);
matrix.clear();
}
void loop() {
// примеры эффектов
//rainbow();    // горизонтальная радуга
//matrixNeo();  // матрица
//balls();      // шарики
confetti();
matrix.show();
delay(50);
}
void rainbow() {
static byte hue = 0;
hue++;
for (byte i = 0; i < M_WIDTH; i++) {
LEDdata thisColor = mHSV((byte)(hue + i * float(255 / M_WIDTH)), 255, 255);
for (byte j = 0; j < M_HEIGHT; j++)
matrix.setPix(i, j, thisColor);
}
}
void matrixNeo() {
for (byte x = 0; x < M_WIDTH; x++) {
// заполняем случайно верхнюю строку
uint32_t thisColor = matrix.getColorHEX(x, M_HEIGHT - 1);
if (thisColor == 0)
matrix.setPix(x, M_HEIGHT - 1, mHEX(0x00FF00 * (random(0, 10) == 0)));
else if (thisColor < 0x002000)
matrix.setPix(x, M_HEIGHT - 1, mHEX(0));
else
matrix.setPix(x, M_HEIGHT - 1, mHEX(thisColor - 0x002000));
}
// сдвигаем всё вниз
for (byte x = 0; x < M_WIDTH; x++) {
for (byte y = 0; y < M_HEIGHT - 1; y++) {
// красим пиксель цветом верхнего над ним
matrix.setPix(x, y, matrix.getColor(x, y + 1));
}
}
}
#define BALLS_AMOUNT 5
boolean loadingFlag = true;
int coord[BALLS_AMOUNT][2];
int8_t vector[BALLS_AMOUNT][2];
LEDdata ballColors[BALLS_AMOUNT];
void balls() {
if (loadingFlag) {
loadingFlag = false;
for (byte j = 0; j < BALLS_AMOUNT; j++) {
int sign;
// забиваем случайными данными
coord[j][0] = M_WIDTH / 2 * 10;
random(0, 2) ? sign = 1 : sign = -1;
vector[j][0] = random(4, 15) * sign;
coord[j][1] = M_HEIGHT / 2 * 10;
random(0, 2) ? sign = 1 : sign = -1;
vector[j][1] = random(4, 15) * sign;
ballColors[j] = mHSV(random(0, 9) * 28, 255, 255);
}
}
matrix.clear();  // очистить
// движение шариков
for (byte j = 0; j < BALLS_AMOUNT; j++) {
for (byte i = 0; i < 2; i++) {
coord[j][i] += vector[j][i];
if (coord[j][i] < 0) { coord[j][i] = 0; vector[j][i] = -vector[j][i]; } } if (coord[j][0] > (M_WIDTH - 1) * 10) {
coord[j][0] = (M_WIDTH - 1) * 10;
vector[j][0] = -vector[j][0];
}
if (coord[j][1] > (M_HEIGHT - 1) * 10) {
coord[j][1] = (M_HEIGHT - 1) * 10;
vector[j][1] = -vector[j][1];
}
matrix.setPix(coord[j][0] / 10, coord[j][1] / 10, ballColors[j]);
}
}
void confetti() {
for (int i = 0; i < NUM_LEDS; i++) {
if (matrix.getColor(i) == 0)
if (random(0, 30) == 0) matrix.setLED(i, mHSV(random(0, 255), 255, 255));
matrix.fade(i, 20);
}
}
// пример работы с лентой
#define LED_PIN 6       // пин ленты
#define NUMLEDS 64       // кол-во светодиодов
#define ORDER_GRB       // порядок цветов ORDER_GRB / ORDER_RGB / ORDER_BRG
#define MAX_DATA_SPEED  // разогнанный протокол связи (на 40% быстрее). Работает на ws2812/13. На 2811 не работает
#define COLOR_DEBTH 2   // цветовая глубина: 1, 2, 3 (в байтах)
// на меньшем цветовом разрешении скетч будет занимать в разы меньше места,
// но уменьшится и количество оттенков и уровней яркости!
// ВНИМАНИЕ! define настройки (ORDER_GRB и COLOR_DEBTH) делаются до подключения библиотеки!
#include 
LEDdata leds[NUMLEDS];  // буфер ленты типа LEDdata (размер зависит от COLOR_DEBTH)
microLED strip(leds, NUMLEDS, LED_PIN);  // объект лента
void setup() {
strip.setBrightness(30);    // яркость (0-255)
// яркость применяется при выводе .show() !
strip.setVoltage(4500);     // установить напряжение питания в мв, по умолч. 5000 (для расчёта тока)
strip.setMaxCurrent(700);   // установить максимальный ток (автокоррекция яркости). 0 - выключено
// при .show() яркость будет скорректирована, чтобы ток не превышал установленный
}
void loop() {
}
/*
Как перевести скетч с FastLED на microLED:
0) Задефайнить REPLACE_FASTLED
1) Задефайнить настройки microLED
2) Убрать подключение FastLED
3) Подключить библиотеку microLED  
4) Заменить CRGB на LEDdata при создании буфера
5) Создать объект ленты microLED
6) задефайнить FastLED на название своего объекта
7) Убрать FastLED.addLeds из setup()
*/
#define NUM_LEDS 8  // кол-во диодов
#define LED_PIN 8   // пин подключения
#define REPLACE_FASTLED // пункт 0
#define COLOR_DEBTH 3   // пункт 1
//#include "FastLED.h"  // пункт 2
#include "microLED.h"   // пункт 3
//CRGB leds[NUM_LEDS];
LEDdata leds[NUM_LEDS]; // пункт 4
microLED strip(leds, NUM_LEDS, LED_PIN);  // пункт 5
#define FastLED strip   // пункт 6
void setup() {
// пункт 7
//FastLED.addLeds<WS2811, LED_PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
FastLED.setBrightness(50);
pinMode(13, OUTPUT);
}
void loop() {
static byte counter = 0;
for (int i = 0; i < NUM_LEDS; i++ ) {
// заливка радугой
leds[i] = CHSV(counter + i * (255 / NUM_LEDS), 255, 255);
}
counter++;        // counter меняется от 0 до 255 (тип данных byte)
FastLED.show();
delay(5);         // скорость движения радуги
}