Часы на адресной светодиодной ленте WS2812B (Arduino)

Адресная светодиодная лента представляет собой ленту на которой размещены адресные светодиоды, каждый светодиод состоит из 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,1);}}// очистка памяти при первом включении 
  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


Форум — http://forum.rcl-radio.ru/viewtopic.php?id=432

Добавить комментарий

Войти с помощью: