Адресная светодиодная лента представляет собой ленту на которой размещены адресные светодиоды, каждый светодиод состоит из RGB светодиода и контроллера. Адресная лента имеет как правило имеет три входных контакта: +5V, GND и DIN. Каждый отдельный светодиод ленты (пиксель) имеет выход DOUT, для передачи управляющего сигнала к следующему светодиоду.
Наиболее популярные адресные ленты работают на чипах WS2812b и WS2811. Чип WS2812b размещен внутри RGB светодиода, питание 5 В, а в адресных лентах использующих чип WS2811 установлен отдельно от светодиода, напряжение питания 12 В, так же чип WS2811 управляет сразу тремя RGB светодиодами, которые представляют собой один пиксель.
Те же адресные ленты имеют разное кол-во светодиодов на 1 метр и соответственно разную мощность потребления и цену.
Если просто подать питание на адресную ленту, то она работать не будет, чтобы она заработала необходимо подать управляющий цифровой сигнал на вход DIN. Управляющий цифровой сигнал состоит 24 бит, по 8 бит на каждый цвет, причем в начале каждого байта первый бит старший.
При этом один бит передается за 1,25 мкс, все 24 бита передаются за 30 мкс. Длительность импульса при передачи логического нуля равна 0,4 мкс ±0,125 мкс, а скважность 0,85 мкс ±0,125 мкс, длительность импульса для логической единицы равна 0,85 мкс ±0,125 мкс, а скважность 0,4 мкс ±0,125 мкс.
После передачи всех 24 бит в первый RGB светодиод следует пауза не более 50 мкс, далее снова передаются 24 бита, но первый светодиод не реагирует на них, он просто передается эту информацию следующему светодиоду и так далее по цепочке до последнего светодиода. После окончания передачи следует пауза больше 50 мкс, после чего лента переходит в исходное состояние и готова принимать цифровой сигнал начиная с первого светодиода.
Для поддержки адресной светодиодной ленты на базе Arduino существуют несколько библиотек, но мне было интересно запустить адресную ленту без использования библиотек, так как в планы входит использовать адресную ленту совместно с микроконтроллером ATtmega2313, которая имеет очень маленький объем памяти и применение стандартных библиотек не целесообразно.
Первоначально был написан следующий скетч для Arduino Nano:
// D8 >> DIN #define L_BIT PORTB &= ~(1<<0) #define H_BIT PORTB |= (1<<0) byte times; int i=0; void setup(){ DDRB |= (1<<0); // выход D8 } void loop(){ led(100, 0, 0); led(0, 100, 0); led(0, 0, 100); led(100, 0, 0); res(); delay(500); } void bit_0(){H_BIT;asm("nop");asm("nop"); L_BIT;asm("nop");asm("nop");asm("nop"); asm("nop");} void bit_1(){H_BIT;asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); L_BIT;} void bit_w(bool x){if(x) bit_1(); else bit_0();} void res(){delayMicroseconds(100);} void led(long rrr, int ggg, byte bbb){ unsigned long data = ((rrr << 16) & 0xFF0000) + ((ggg << 8) & 0xFF00) + bbb; cli(); bit_w(data & 0b100000000000000000000000); bit_w(data & 0b010000000000000000000000); bit_w(data & 0b001000000000000000000000); bit_w(data & 0b000100000000000000000000); bit_w(data & 0b000010000000000000000000); bit_w(data & 0b000001000000000000000000); bit_w(data & 0b000000100000000000000000); bit_w(data & 0b000000010000000000000000); bit_w(data & 0b000000000000000010000000); bit_w(data & 0b000000000000000001000000); bit_w(data & 0b000000000000000000100000); bit_w(data & 0b000000000000000000010000); bit_w(data & 0b000000000000000000001000); bit_w(data & 0b000000000000000000000100); bit_w(data & 0b000000000000000000000010); bit_w(data & 0b000000000000000000000001); bit_w(data & 0b000000001000000000000000); bit_w(data & 0b000000000100000000000000); bit_w(data & 0b000000000010000000000000); bit_w(data & 0b000000000001000000000000); bit_w(data & 0b000000000000100000000000); bit_w(data & 0b000000000000010000000000); bit_w(data & 0b000000000000001000000000); bit_w(data & 0b000000000000000100000000); sei(); delayMicroseconds(10); }
Скетч использует 972 байт (3%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
После загрузки скетча адресная лента заработала, скетч занимает всего 972 байта и 9 байт динамической памяти.
WS2811
Выход D8 Arduino через резистор 300 Ом подключен ко входу адресной ленты.
За создание нуля или единицы для отправки на вход DIN отвечают две функции:
void bit_0(){H_BIT;asm("nop");asm("nop"); L_BIT;asm("nop");asm("nop");asm("nop"); asm("nop");} void bit_1(){H_BIT;asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); L_BIT;}
Эти функции задают правильные временные импульсы:
Бит нуля (развертка 1 мкс.дел)
Бит единицы (развертка 1 мкс.дел)
Для отправки бита нуля или единицы отвечает функция bit_w():
void bit_w(bool x){if(x) bit_1(); else bit_0();}
Функция работает очень просто, если в функцию поместить ноль, то произойдет отправка бита нуля, если единицу, то будет отправлен бит единицы.
Далее следует функция led(), в которую заносятся три байта яркости для каждого цвета RGB (0…255), осуществляется преобразование 3-х байт в один 24-х битный байт и отправка 24 раза бита со сдвигом бита. В итоге адресная лента получает 24 битный байт, после каждого байта следует пауза в 10 мкс.
void led(long rrr, int ggg, byte bbb){ unsigned long data = ((rrr << 16) & 0xFF0000) + ((ggg << 8) & 0xFF00) + bbb; cli(); bit_w(data & 0b100000000000000000000000); bit_w(data & 0b010000000000000000000000); bit_w(data & 0b001000000000000000000000); bit_w(data & 0b000100000000000000000000); bit_w(data & 0b000010000000000000000000); bit_w(data & 0b000001000000000000000000); bit_w(data & 0b000000100000000000000000); bit_w(data & 0b000000010000000000000000); bit_w(data & 0b000000000000000010000000); bit_w(data & 0b000000000000000001000000); bit_w(data & 0b000000000000000000100000); bit_w(data & 0b000000000000000000010000); bit_w(data & 0b000000000000000000001000); bit_w(data & 0b000000000000000000000100); bit_w(data & 0b000000000000000000000010); bit_w(data & 0b000000000000000000000001); bit_w(data & 0b000000001000000000000000); bit_w(data & 0b000000000100000000000000); bit_w(data & 0b000000000010000000000000); bit_w(data & 0b000000000001000000000000); bit_w(data & 0b000000000000100000000000); bit_w(data & 0b000000000000010000000000); bit_w(data & 0b000000000000001000000000); bit_w(data & 0b000000000000000100000000); sei(); delayMicroseconds(10); }
Функция res(), просто содержит паузу в 100 мкс для перезапуска ленты:
void res(){delayMicroseconds(100);}
Функция отправки данных в один пиксель ленты:
led(10, 0, 0);
Отправка функции led() в этом скетче требуется для каждого пикселя адресной ленты, что не совсем удобно, но зато скетч занимает очень мало места и при использовании функции led() динамическая память не заполняется, а используется только память для программ (флеш память). Для простых проектов, особенно для контроллеров с малым объемом памяти можно использовать этот скетч.
Немного доработав скетч, функция led() стала иметь адрес пикселя, что заметно упростило создание различных световых эффектов. Например если указать:
led(23, 255, 255, 255);
то загорится только светодиод под номером 23 (считать с нуля).
Ниже показан скетч, повторяющий предыдущий, но с адресами для пикселей:
#define L_BIT PORTB &= ~(1<<0) #define H_BIT PORTB |= (1<<0) #define LED_MAX 4 unsigned long dat[LED_MAX]; byte aa,bb,cc; void setup(){ DDRB |= (1<<0); } void loop(){ data_led(0, 10,0,0); data_led(1, 0,10,0); data_led(2, 0,0,10); data_led(3, 10,0,0); led_rgb(); res(); } void bit_0(){H_BIT;asm("nop");asm("nop"); L_BIT;asm("nop");asm("nop");asm("nop"); asm("nop");} void bit_1(){H_BIT;asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); L_BIT;} void bit_w(bool x){if(x) bit_1(); else bit_0();} void res(){delayMicroseconds(100);} void data_led(int num, unsigned long rrr, int ggg, byte bbb){ dat[num] = ((rrr << 16) & 0xFF0000) + ((ggg << 8) & 0xFF00) + bbb; } void led_rgb(){ for(unsigned char num_i=0;num_i<LED_MAX;num_i++){unsigned long data=dat[num_i]; cli(); bit_w(data & 0x80); bit_w(data & 0x40); bit_w(data & 0x20); bit_w(data & 0x10); bit_w(data & 0x08); bit_w(data & 0x04); bit_w(data & 0x02); bit_w(data & 0x01); bit_w(data & 0x800000); bit_w(data & 0x400000); bit_w(data & 0x200000); bit_w(data & 0x100000); bit_w(data & 0x080000); bit_w(data & 0x040000); bit_w(data & 0x020000); bit_w(data & 0x010000); bit_w(data & 0x8000); bit_w(data & 0x4000); bit_w(data & 0x2000); bit_w(data & 0x1000); bit_w(data & 0x0800); bit_w(data & 0x0400); bit_w(data & 0x0200); bit_w(data & 0x0100); sei(); delayMicroseconds(10); }}
Скетч использует 962 байт (2%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 25 байт (1%) динамической памяти, оставляя 2023 байт для локальных переменных. Максимум: 2048 байт.
В этом скетче функция led(), не отправляет команду адресной ленте, а создает массив из данных функции led(). Отправка данных осуществляется функцией led_rgb(). При этом чем больше светодиодов используется тем больше расходуется динамическая память.
Следующий скетч демонстрирует простой световой эффект:
#define L_BIT PORTB &= ~(1<<0) #define H_BIT PORTB |= (1<<0) #define LED_MAX 4 unsigned long dat[LED_MAX]; byte aa,bb,cc; void setup(){ DDRB |= (1<<0); } void loop(){ ///////////////// for(int i1=LED_MAX;i1>=0;i1--){ for(int i=0;i<100;i++){ switch(i1){ case 0: aa=i;bb=0;cc=0;break; case 1: aa=0;bb=i;cc=0;break; case 2: aa=0;bb=0;cc=i;break; case 3: aa=i;bb=i;cc=i;break; } data_led(i1, aa,bb,cc); led_rgb();res(); delay(2); }} for(int i1=0;i1<LED_MAX;i1++){ for(int i=100;i>=0;i--){ switch(i1){ case 0: aa=i;bb=0;cc=0;break; case 1: aa=0;bb=i;cc=0;break; case 2: aa=0;bb=0;cc=i;break; case 3: aa=i;bb=i;cc=i;break; } data_led(i1, aa,bb,cc); led_rgb();res(); delay(2); }} delay(50); /////////////////// for(int i1=0;i1<LED_MAX;i1++){ for(int i=0;i<100;i++){ switch(i1){ case 0: aa=i;bb=0;cc=0;break; case 1: aa=0;bb=i;cc=0;break; case 2: aa=0;bb=0;cc=i;break; case 3: aa=i;bb=i;cc=i;break; } data_led(i1, aa,bb,cc); led_rgb();res(); delay(2); }} for(int i1=LED_MAX;i1>=0;i1--){ for(int i=100;i>=0;i--){ switch(i1){ case 0: aa=i;bb=0;cc=0;break; case 1: aa=0;bb=i;cc=0;break; case 2: aa=0;bb=0;cc=i;break; case 3: aa=i;bb=i;cc=i;break; } data_led(i1, aa,bb,cc); led_rgb();res(); delay(2); }} delay(50); //////////////////////// } void bit_0(){H_BIT;asm("nop");asm("nop"); L_BIT;asm("nop");asm("nop");asm("nop"); asm("nop");} void bit_1(){H_BIT;asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); L_BIT;} void bit_w(bool x){if(x) bit_1(); else bit_0();} void res(){delayMicroseconds(100);} void data_led(int num, unsigned long rrr, int ggg, byte bbb){ dat[num] = ((rrr << 16) & 0xFF0000) + ((ggg << 8) & 0xFF00) + bbb; } void led_rgb(){ for(unsigned char num_i=0;num_i<LED_MAX;num_i++){unsigned long data=dat[num_i]; cli(); bit_w(data & 0x80); bit_w(data & 0x40); bit_w(data & 0x20); bit_w(data & 0x10); bit_w(data & 0x08); bit_w(data & 0x04); bit_w(data & 0x02); bit_w(data & 0x01); bit_w(data & 0x800000); bit_w(data & 0x400000); bit_w(data & 0x200000); bit_w(data & 0x100000); bit_w(data & 0x080000); bit_w(data & 0x040000); bit_w(data & 0x020000); bit_w(data & 0x010000); bit_w(data & 0x8000); bit_w(data & 0x4000); bit_w(data & 0x2000); bit_w(data & 0x1000); bit_w(data & 0x0800); bit_w(data & 0x0400); bit_w(data & 0x0200); bit_w(data & 0x0100); sei(); delayMicroseconds(10); }}
Скетч использует 1858 байт (5%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 28 байт (1%) динамической памяти, оставляя 2020 байт для локальных переменных. Максимум: 2048 байт.