Sunday, August 12, 2012

Lovebirds 112

Two friends of mine are getting married. So, I decided a few months ago, I'd make them a personal wedding present which would light up their living room.

I knew I wanted to make an artwork with LEDs, but I had no idea what I would depict. The inspiration came when I received the wedding invitation. It contained two nice lovebirds.


So, that was it. Those two lovebirds were going to be LEDified. The first step was to make the design. There were some limitations to the design, I had already ordered 60 red LEDs and 50 white ones. I was going to use a 12V power supply and I decided to hook up the LEDs in goups of 4 in series. The number of LEDs used for each "component" would have to be a multiple of 4. The design shown below was not the final design, it used too many white LEDs, but it was the first concept I was really happy about.


After the design, I breadboarded the control circuit of the fading LEDs. I've done this using an AVRTiny2313 chip. This may be a bit overkill, but I had one lying around. In the picture you can see the AVRISP mkII to program the AVRTiny.


To drill the holes in the mdf board, I've edited the initial design and printed it for easy drilling.


112 holes and 1 spraypaint later...



After yet another spraypaint and a lot of work mounting and wiring the LEDs, this is what it looked like:
 To finish it all, I made the PCB and mounted it all in an ikea frame.




112 LEDs, 112 LED mounts, 32 resistors, 10 bolts and screws, 3 transistors, 2 capacitors, 1 voltage regulator, 1 transformer, 1 diode, 1 mdf board, 1 ikea frame, 1 AVRTiny2313 and lots of wire gives you this:



The source code:
#ifndef F_CPU
   #define F_CPU 8000000UL     
#endif 
#include <avr/io.h>
#include <util/delay.h>

//Functions
uint16_t readPWMValue(uint8_t reg);
void writePWMValue(uint8_t reg, uint16_t value);
void pulse(uint8_t port, int8_t speed, int8_t* direction);
void fadeIn(uint8_t port);

int main(void)
{
 // Set Port B pins as all outputs
 DDRB = 0xff;
 // Set Port D pins as all outputs
 DDRD = 0xff;

 // OC1A, OC1B outputs
 DDRB |= (1<<PB4)|(1<<PB3);

 // Timer 0 setup (8 bit timer)
 // /8 prescaler
 TCCR0A = (1 << COM0A1) | (1 << COM0B1) | (1 << WGM01) | (1 << WGM00); 
 TCCR0B = (1<<CS01);

 // Timer 1 setup (16 bit timer)
 // TOP, set to 255 to behave similarly to the 8-bit timer
 ICR1 = 255;
 // Timer 1 fast PWM mode 14
 // Clear on compare, set at TOP
 // /8 prescaler
 TCCR1A = (1<<COM1A1)|(1<<COM1B1)|(1<<WGM11);
 TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11);

 // Initialize compare registers
 OCR0A = 10;
 OCR0B = 10;
    OCR1A = 10;
    OCR1B = 10;
  
   //Initialize variables
  int8_t direction[4];
 direction[0] = direction[1] = direction[2] = direction[3] = 1;

 //Startup sequence
 writePWMValue(0, 0);
 writePWMValue(1, 0);
 writePWMValue(2, 0);

 _delay_ms(500);

 for(int i = 0; i < 3; i++)
 {
  fadeIn(i);
  _delay_ms(1000);
 }

 _delay_ms(500);

 for(int j = 0; j < 10; j++)
 {
  for(int i = 0; i < 3; i++)
  {
   writePWMValue(0, 0);
   writePWMValue(1, 0);
   writePWMValue(2, 0);
   writePWMValue(i, 255);
   _delay_ms(100);
  }
 }

 //Loop
 while(1)
 {
  pulse(0, 3, &direction[0]);
  pulse(1, 3, &direction[1]);
  pulse(2, 1, &direction[2]);
  pulse(3, 50, &direction[3]);
  _delay_ms(40);


  // Set all Port B pins as HIGH
  /*PORTB = 0xff;

  _delay_ms(2000);  

  PORTB = 0;

  _delay_ms(2000);*/
 }

  return 1;
}
void fadeIn(uint8_t port)
{
 writePWMValue(port, 0);

 for(int i = 0; i < 50; i++)
 {
  writePWMValue(port, i);
  _delay_ms(20);
 }

 for(int i = 50; i < 256; i++)
 {
  writePWMValue(port, i);
  _delay_ms(5);
 }
}
void pulse(uint8_t port, int8_t speed, int8_t* direction)
{
  int16_t registerValue = readPWMValue(port);

  if(registerValue < 60)
  {
   registerValue += (*direction)*speed;
  }
  else
  {
   registerValue += (*direction)*speed*3;
  }

  if(registerValue > 255)
  {
   *direction = -1;
   registerValue = 255;
  }
  else if(registerValue < 0)
  {
   *direction = 1;
   registerValue = 0;
  }

  writePWMValue(port, registerValue);
}

uint16_t readPWMValue(uint8_t reg)
{
 switch(reg)
 {
  case 0:
   return OCR1A;
  case 1:
   return OCR1B;
  case 2:
   return OCR0A;
  case 3:
   return OCR0B;
 }
 return 0;
}

void writePWMValue(uint8_t reg, uint16_t value)
{
 switch(reg)
 {
  case 0:
   OCR1A = value;
   break;
  case 1:
   OCR1B = value;
   break;
  case 2:
   OCR0A = value;
   break;
  case 3:
   OCR0B = value;
   break;
 }
}