Адресная светодиодная лента представляет собой ленту на которой размещены адресные светодиоды, каждый светодиод состоит из 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 мкс, после чего лента переходит в исходное состояние и готова принимать цифровой сигнал начиная с первого светодиода.
На базе адресной светодиодной ленты WS2812B можно собрать простые часы. Часы собраны на адресной ленте плотностью 96 пикселей на 1 метр. Дополнительно в часах используются часы (модуль) реального времени DS3231 и четыре тактовые кнопки.
Схема часов
Кнопки:
- MODE — позволяет менять цвет свечения адресной ленты
- UP — в режиме часов кнопка позволяет увеличивать яркость свечения адресной ленты, в режиме коррекции времени изменяет время часов (НН)
- DOWN — в режиме часов кнопка позволяет уменьшать яркость свечения адресной ленты, в режиме коррекции времени изменяет время минут (MM)
- SET — активация режима коррекции времени
Сборка часов
Материал на который наклеена адресная лента для создания часов может быть различный, адресную ленту необходимо разрезать на 28 отрезков по три пикселя для сегментов индикаторов , и 2 отрезка по 2 пикселя для разделительных точек.
Порядок наклеивания отрезков адресной ленты на основание показан на рисунках:
При установке большой яркости свечения адресной ленты, необходимо использовать отдельный от Arduino источник питания 5 В, так ток потребления адресной ленты может превысить 2 А.
// Atmega328, Atmega8 16MHz #include <avr/io.h> #include <util/delay.h> #define ADDR_DS3231 0b1101000 #define CPU_F 16000000 // Clock Speed #define SCL_F 100000 // // I2C Speed #define L_BIT PORTB &= ~(1<<PB0) #define H_BIT PORTB |= (1<<PB0) #define LED_MAX 88 byte rr[LED_MAX],gg[LED_MAX],bb[LED_MAX],r,g,b; int i,up,down,brig=1,hh,mm; byte sec,min,hour,datat,mont,year,day; byte r_led=0,g_led=1,b_led=0; byte mode; bool sett,w,w1; unsigned long times,times0; int main(void){ cli(); TWBR = (((CPU_F)/(SCL_F)-16 )/2) ; TWSR = 0; DDRD &= ~(1<<PD2);// INPUT SQW DS3231 DDRB |= (1<<PB0); // INPUT WS2812B (D8 - ARDUINO) DDRD &= ~(1<<PD3);// INPUT BUTTON MODE DDRD &= ~(1<<PD4);// INPUT BUTTON UP DDRD &= ~(1<<PD5);// INPUT BUTTON DOWN DDRD &= ~(1<<PD6);// INPUT BUTTON SET PORTD |= (1<<PD3);PORTD |= (1<<PD4);PORTD |= (1<<PD5);PORTD |= (1<<PD6); DDRB |= (1<<PB0); // INPUT WS2812B (D8 - ARDUINO) DDRD &= ~(1<<PD2);// BUTTON (D2 - ARDUINO) PORTD |=(1<<PD2); // INPUT_PULLUP /// TIMER 0 = 1ms ADCSRA = 0b10000100; TCCR0A = 0;TCCR0B = 0;TCNT0 = 0; OCR0A = 249; TCCR0A |= (1 << WGM01); TCCR0B |= (1 << CS01) | (1 << CS00); TIMSK0 |= (1 << OCIE0A); sei(); _delay_ms(100); i2c_write(ADDR_DS3231,0x0E,0b00000000); //set_time(21,1,12,26,12,12,0);// год 00-99, ДН 1-7 (1=ВС), месяц 1-12, дата 1-31, час 0-23, минуты 0-59, секунды 0-59 if(EEPROM_read(100)!=0){for(int i1=0;i1<101;i1++){EEPROM_write(i1,0);}}// очистка памяти при первом включении mode = EEPROM_read(0);brig = EEPROM_read(1); while(1){ /////// BUTTON /////////////////////////////////////////////// if(((PIND >> PD3) & 1) == 0){mode++;if(mode>7){mode=0;}_delay_ms(300);w=1;times0=times;} switch(mode){ case 0: r_led=1,g_led=1,b_led=1; break; case 1: r_led=1,g_led=0,b_led=0; break; case 2: r_led=0,g_led=1,b_led=0; break; case 3: r_led=0,g_led=0,b_led=1; break; case 4: r_led=1,g_led=1,b_led=0; break; case 5: r_led=1,g_led=0,b_led=1; break; case 6: r_led=0,g_led=1,b_led=1; break; case 7: r_led=0,g_led=0,b_led=0; break; } if(((PIND >> PD4) & 1) == 0 && sett==0){brig++;if(brig>255){brig=255;}_delay_ms(1);w=1;times0=times;} if(((PIND >> PD5) & 1) == 0 && sett==0){brig--;if(brig<1){brig=1;}_delay_ms(1);w=1;times0=times;} if(((PIND >> PD6) & 1) == 0 && sett==0){sett=1;_delay_ms(300);} if(((PIND >> PD6) & 1) == 0 && sett==1){sett=0;_delay_ms(300);} /////// READ TIME //////////////////////////////////////////// sec = (i2c_read_1bit(ADDR_DS3231,0) & 0x0F) + (((i2c_read_1bit(ADDR_DS3231,0) & 0x70) >> 4) * 10); min = (i2c_read_1bit(ADDR_DS3231,1) & 0x0F) + (((i2c_read_1bit(ADDR_DS3231,1) & 0x70) >> 4) * 10); hour = ((i2c_read_1bit(ADDR_DS3231,2) & 0x0F) + ((i2c_read_1bit(ADDR_DS3231,2) & 0x70) >> 4) * 10); i = hour*100+min; //////////// SET TIME ////////////////////////////////////////////////////////////////////// if(sett==1){ hh=hour; mm=min; if(((PIND >> PD4) & 1) == 0){hh++;if(hh>23){hh=0;}_delay_ms(100);w1=1;} if(((PIND >> PD5) & 1) == 0){mm++;if(mm>59){mm=1;}_delay_ms(100);w1=1;} if(w1==1){w1=0;set_time(21,1,12,26,hh,mm,0);} i = hh*100+mm; } ////////// RGB //////////////////////////////////////////////// switch(i/1000){ case 0: ws(0,1);ws(3,1);ws(6,1);ws(9,0);ws(12,1);ws(15,1);ws(18,1);break; case 1: ws(0,0);ws(3,0);ws(6,1);ws(9,0);ws(12,0);ws(15,0);ws(18,1);break; case 2: ws(0,0);ws(3,1);ws(6,1);ws(9,1);ws(12,1);ws(15,1);ws(18,0);break; case 3: ws(0,0);ws(3,1);ws(6,1);ws(9,1);ws(12,0);ws(15,1);ws(18,1);break; case 4: ws(0,1);ws(3,0);ws(6,1);ws(9,1);ws(12,0);ws(15,0);ws(18,1);break; case 5: ws(0,1);ws(3,1);ws(6,0);ws(9,1);ws(12,0);ws(15,1);ws(18,1);break; case 6: ws(0,1);ws(3,1);ws(6,0);ws(9,1);ws(12,1);ws(15,1);ws(18,1);break; case 7: ws(0,0);ws(3,1);ws(6,1);ws(9,0);ws(12,0);ws(15,0);ws(18,1);break; case 8: ws(0,1);ws(3,1);ws(6,1);ws(9,1);ws(12,1);ws(15,1);ws(18,1);break; case 9: ws(0,1);ws(3,1);ws(6,1);ws(9,1);ws(12,0);ws(15,1);ws(18,1);break; } switch(i/100%10){ case 0: ws(21,1);ws(24,1);ws(27,1);ws(30,0);ws(33,1);ws(36,1);ws(39,1);break; case 1: ws(21,0);ws(24,0);ws(27,1);ws(30,0);ws(33,0);ws(36,0);ws(39,1);break; case 2: ws(21,0);ws(24,1);ws(27,1);ws(30,1);ws(33,1);ws(36,1);ws(39,0);break; case 3: ws(21,0);ws(24,1);ws(27,1);ws(30,1);ws(33,0);ws(36,1);ws(39,1);break; case 4: ws(21,1);ws(24,0);ws(27,1);ws(30,1);ws(33,0);ws(36,0);ws(39,1);break; case 5: ws(21,1);ws(24,1);ws(27,0);ws(30,1);ws(33,0);ws(36,1);ws(39,1);break; case 6: ws(21,1);ws(24,1);ws(27,0);ws(30,1);ws(33,1);ws(36,1);ws(39,1);break; case 7: ws(21,0);ws(24,1);ws(27,1);ws(30,0);ws(33,0);ws(36,0);ws(39,1);break; case 8: ws(21,1);ws(24,1);ws(27,1);ws(30,1);ws(33,1);ws(36,1);ws(39,1);break; case 9: ws(21,1);ws(24,1);ws(27,1);ws(30,1);ws(33,0);ws(37,1);ws(39,1);break; } if(((PIND >> PD2) & 1) == 0 && sett==0){data_led(42, r_led*brig, g_led*brig, b_led*brig);data_led(43, r_led*brig, g_led*brig, b_led*brig); data_led(44, r_led*brig, g_led*brig, b_led*brig);data_led(45, r_led*brig, g_led*brig, b_led*brig);} else{data_led(42, 0, 0, 0);data_led(43, 0, 0, 0);data_led(44, 0, 0, 0);data_led(45, 0, 0, 0);} if(sett==1){data_led(42, 0, brig, 0);data_led(43, brig, 0, 0);data_led(44, 0, brig, 0);data_led(45, brig, 0, 0);} switch(i/10%10){ case 0: ws(46,1);ws(49,1);ws(52,1);ws(55,0);ws(58,1);ws(61,1);ws(64,1);break; case 1: ws(46,0);ws(49,0);ws(52,1);ws(55,0);ws(58,0);ws(61,0);ws(64,1);break; case 2: ws(46,0);ws(49,1);ws(52,1);ws(55,1);ws(58,1);ws(61,1);ws(64,0);break; case 3: ws(46,0);ws(49,1);ws(52,1);ws(55,1);ws(58,0);ws(61,1);ws(64,1);break; case 4: ws(46,1);ws(49,0);ws(52,1);ws(55,1);ws(58,0);ws(61,0);ws(64,1);break; case 5: ws(46,1);ws(49,1);ws(52,0);ws(55,1);ws(58,0);ws(61,1);ws(64,1);break; case 6: ws(46,1);ws(49,1);ws(52,0);ws(55,1);ws(58,1);ws(61,1);ws(64,1);break; case 7: ws(46,0);ws(49,1);ws(52,1);ws(55,0);ws(58,0);ws(61,0);ws(64,1);break; case 8: ws(46,1);ws(49,1);ws(52,1);ws(55,1);ws(58,1);ws(61,1);ws(64,1);break; case 9: ws(46,1);ws(49,1);ws(52,1);ws(55,1);ws(58,0);ws(61,1);ws(64,1);break; } switch(i%10){ case 0: ws(67,1);ws(70,1);ws(73,1);ws(76,0);ws(79,1);ws(82,1);ws(85,1);break; case 1: ws(67,0);ws(70,0);ws(73,1);ws(76,0);ws(79,0);ws(82,0);ws(85,1);break; case 2: ws(67,0);ws(70,1);ws(73,1);ws(76,1);ws(79,1);ws(82,1);ws(85,0);break; case 3: ws(67,0);ws(70,1);ws(73,1);ws(76,1);ws(79,0);ws(82,1);ws(85,1);break; case 4: ws(67,1);ws(70,0);ws(73,1);ws(76,1);ws(79,0);ws(82,0);ws(85,1);break; case 5: ws(67,1);ws(70,1);ws(73,0);ws(76,1);ws(79,0);ws(82,1);ws(85,1);break; case 6: ws(67,1);ws(70,1);ws(73,0);ws(76,1);ws(79,1);ws(82,1);ws(85,1);break; case 7: ws(67,0);ws(70,1);ws(73,1);ws(76,0);ws(79,0);ws(82,0);ws(85,1);break; case 8: ws(67,1);ws(70,1);ws(73,1);ws(76,1);ws(79,1);ws(82,1);ws(85,1);break; case 9: ws(67,1);ws(70,1);ws(73,1);ws(76,1);ws(79,0);ws(82,1);ws(85,1);break; } led_rgb();res(); _delay_ms(100); ////// EEPROM /////////////////// if(times-times0>10000&&w==1){EEPROM_write(0,mode);EEPROM_write(1,brig);w=0;} }} // end loop void ws(int ind, bool datt){ if(datt==1){data_led(ind, r_led*brig, g_led*brig, b_led*brig);data_led(ind+1, r_led*brig, g_led*brig, b_led*brig);data_led(ind+2, r_led*brig, g_led*brig, b_led*brig);} if(datt==0){data_led(ind, 0, 0, 0);data_led(ind+1, 0, 0, 0);data_led(ind+2, 0, 0, 0);}} 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(){_delay_us(100);} void data_led(int num, byte rrr, byte ggg, byte bbb){bb[num]=bbb;rr[num]=rrr;gg[num]=ggg;} void led_rgb(){ for(int num_i=0;num_i<LED_MAX;num_i++){ cli(); bit_w((bb[num_i] & 0x80)>>7); bit_w((bb[num_i] & 0x40)>>6); bit_w((bb[num_i] & 0x20)>>5); bit_w((bb[num_i] & 0x10)>>4); bit_w((bb[num_i] & 0x08)>>3); bit_w((bb[num_i] & 0x04)>>2); bit_w((bb[num_i] & 0x02)>>1); bit_w((bb[num_i] & 0x01)>>0); bit_w((rr[num_i] & 0x80)>>7); bit_w((rr[num_i] & 0x40)>>6); bit_w((rr[num_i] & 0x20)>>5); bit_w((rr[num_i] & 0x10)>>4); bit_w((rr[num_i] & 0x08)>>3); bit_w((rr[num_i] & 0x04)>>2); bit_w((rr[num_i] & 0x02)>>1); bit_w((rr[num_i] & 0x01)>>0); bit_w((gg[num_i] & 0x80)>>7); bit_w((gg[num_i] & 0x40)>>6); bit_w((gg[num_i] & 0x20)>>5); bit_w((gg[num_i] & 0x10)>>4); bit_w((gg[num_i] & 0x08)>>3); bit_w((gg[num_i] & 0x04)>>2); bit_w((gg[num_i] & 0x02)>>1); bit_w((gg[num_i] & 0x01)>>0); sei(); }} void set_time(byte years, byte days, byte monts, byte datas, byte hours ,byte minute, byte second){ if(second < 255){i2c_write(ADDR_DS3231,0x00,(second/10<<4)+second%10);} if(minute < 255){i2c_write(ADDR_DS3231,0x01,(minute/10<<4)+minute%10);} if(hours < 255){i2c_write(ADDR_DS3231,0x02,(hours/10<<4)+hours%10);} if(days < 255){i2c_write(ADDR_DS3231,0x03,days);} if(datas < 255){i2c_write(ADDR_DS3231,0x04,(datas/10<<4)+datas%10);} if(monts < 255){i2c_write(ADDR_DS3231,0x05,(monts/10<<4)+monts%10);} if(years < 255){i2c_write(ADDR_DS3231,0x06,(years/10<<4)+years%10);} } int i2c_read_1bit(byte i2c_addr, byte i2c_reg){ TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); // START while (!(TWCR & (1<<TWINT))); TWDR = i2c_addr << 1; TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR & (1<<TWINT))); TWDR = i2c_reg; TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR & (1<<TWINT))); TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); // START while (!(TWCR & (1<<TWINT))); TWDR = (i2c_addr << 1) | 1; TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR & (1<<TWINT))); TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); while(~TWCR&(1<<TWINT)); byte i2c_data = TWDR; TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR & (1<<TWINT))); TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO); // СТОП return i2c_data; } void i2c_write(byte i2c_addr, byte i2c_reg, byte i2c_dat){ TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); // START while (!(TWCR & (1<<TWINT))); TWDR = i2c_addr << 1; TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR & (1<<TWINT))); TWDR = i2c_reg; TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR & (1<<TWINT))); TWDR = i2c_dat; TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR & (1<<TWINT))); TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO); // СТОП } ISR(TIMER0_COMPA_vect) {times++;}// millis = 1 ms unsigned char EEPROM_read(unsigned int uiAddress){ while(EECR & (1<<EEPE)); // проверка готовности EEPROM EEARH = ((uiAddress & 0xF0) << 2); // регистр адреса H EEARL = uiAddress & 0x0F; // регистр адреса L EECR |= (1<<EERE);// чтение EEPROM return EEDR; // вывод значения } void EEPROM_write(unsigned int uiAddress, unsigned char ucData){ while(EECR & (1<<EEPE)); // проверка готовности EEPROM EEARH = ((uiAddress & 0xF0) << 2); // регистр адреса H EEARL = uiAddress & 0x0F; // регистр адреса L EEDR = ucData; // регистр данных EECR |= (1<<EEMPE);// Разрешение записи в EEPROM EECR |= (1<<EEPE); // Запись в EEPROM }
Скетч использует 3992 байт (12%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 289 байт (14%) динамической памяти, оставляя 1759 байт для локальных переменных. Максимум: 2048 байт.
Часы на адресной светодиодной ленте WS2812B (ATtiny2313)
Скетч — http://forum.rcl-radio.ru/viewtopic.php?pid=5185#p5185