Show Control board

From pauljmac.com Projects
Jump to: navigation, search

Abstract

This is a show control board designed to replace a stage lighting system to generate realistic flicker of incandescent bulbs. For Halloween this year I built and operated an electric chair for a neighbor’s haunted maze. For the chair I wanted to have a trigger which would start a “show” when people walked past it. The show would consist of sound, in this case electrical arcing, and a simple lighting effect, flickering. The first channel of this board is run by the show. The remaining 3 channels are for ambiance lighting in other parts of the house.

Operation

Diming/flickering is make possible by use of TRIACs driven by an Atmega88 via opto isolators. A 120hz AC wave in phase with the AC load wave is taped from the power supply to trigger interrupts on the AVR. When an interrupt is triggered the main loop of the code starts cycling though delay routines according to the dimness value set in code. When the appropriate amounts of delays are run the AVR will trigger the TRIAC and thus turn on the light. The higher the dimness value is, the more delays the AVR runs, and the longer the AVR waits to turn on the TRIAC, and the dimmer the light is. If you vary the dimness from time to time you make the light flicker.

Settings are set at compile time before being burned to the AVR and are as follows;

#define ChanX_Dimness
Sets the overall dimness of the channel as a percentage. 100% is completely dim (off), 0% is completely on.
#define ChanX_Flicker
Sets weather the channel should flicker or not. 1 is yes, 0 is no.
#define ChanX_Flicker_Multiplier
Sets the severity of the flicker. The numbers in flicker[] are multiplied by this number before being applied.
#define Flicker_Update
How many times a second a flickering channel is updated. Because of the nature of incandescent lights 10 seems to be a good number.
#define TIMEOFSOUND
Sets the length of time in seconds for the event of the first chan (ch0 in code). 

The first Chanel of the board is special in that it is used for the small show. There is a simple button input on the AVR used to trigger the start of the show. When pressed it will send a play to the ipod to play the sound and change the first TRIAC from solid state to flicker with the sound. When TIMEOFSOUND has expired the program will send a pause to the IPOD to stop the sound and set the first channel back to solid dimness.

Parts

Schematic

ShowControl -800x600-.png

Code

main.c

/***********************************************************/
// Paul Mac
// Halloween show control board
// v1.0 Build Oct 27, 2008
// NOTES: EAD   
//
/***********************************************************/

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#include "Ipod.h"

/***********************************************************/
//Defines

#define bit_get(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define bit_flip(p,m) ((p) ^= (m))
#define bit_write(c,p,m) (c ? bit_set(p,m) : bit_clear(p,m))
#define BIT(x) (0x01 << (x))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))
#define NOP asm("nop")

#define LIGHT0_ON bit_clear(PORTB, BIT(0))
#define LIGHT0_OFF bit_set(PORTB, BIT(0))

#define LIGHT1_ON bit_clear(PORTB, BIT(1))
#define LIGHT1_OFF bit_set(PORTB, BIT(1))

#define LIGHT2_ON bit_clear(PORTB, BIT(2))
#define LIGHT2_OFF bit_set(PORTB, BIT(2))

#define LIGHT3_ON bit_clear(PORTC, BIT(0))
#define LIGHT3_OFF bit_set(PORTC, BIT(0))

#define HEART_ON bit_clear(PORTD, BIT(0))
#define HEART_OFF bit_set(PORTD, BIT(0))

#define BMID_ON bit_clear(PORTD, BIT(7))
#define BMID_OFF   bit_set(PORTD, BIT(7))

#define BTOP_ON bit_clear(PORTC, BIT(3))
#define BTOP_OFF   bit_set(PORTC, BIT(3))

/***********************************************************/
//USER DEFINES

// Set the max dimness of each chan in %
#define Chan0_Dimness 75   //chair 75
#define Chan1_Dimness 62   //saw  70
#define Chan2_Dimness 50  //broken :-( 50
#define Chan3_Dimness 65  //bloody marry 65

// Set weather the chan should flicker or not
#define Chan0_Flicker 0
#define Chan1_Flicker 1
#define Chan2_Flicker 1
#define Chan3_Flicker 1   

//Define the savarity of the flicker for the chan
#define Chan0_Flicker_Multiplier 3 
#define Chan1_Flicker_Multiplier 2   
#define Chan2_Flicker_Multiplier 2
#define Chan3_Flicker_Multiplier 2

// the time between flicker value updates
#define Flicker_Update 10

#define TIMEOFSOUND 6 //how long the sound is in seconds
/***********************************************************/
//Global Vars

volatile char unsigned Triac_refresh=0,timer=0, ADC_rand, CH0_Dim, CH1_Dim, CH2_Dim, CH3_Dim, CH0_flicker, CH1_flicker, CH2_flicker, 
CH3_flicker, TNCNT2_save, CH0_static, CH1_static, CH2_static, CH3_static, Time_to_flicker, flicker_counter=0, i, button_timer=0, 
debounce=3, button=0, send_stop=0, stop_debounce=0;
char flicker[] = {5, 17, 12, 7, 3, 10, 20, 0, 14, 18, 14, 13, 8, 13, 6, 9};

/***********************************************************/
// ISRs

ISR(INT0_vect) //120hz mains int
{
   Triac_refresh=1;
}

ISR(TIMER2_OVF_vect) //RTC int
{
   timer++; //inc RTC
   if(debounce>0){
   debounce--;
   }
   PORTD ^= (1<<PORTD4); // toggle heart beat LED
}
/***********************************************************/
// Functions

void port_setup(void){

   DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2);   //B0:2 output

   DDRC |= (1<<DDC0) | (1<<DDC3);

   DDRD |= (0<<DDD3) | (1<<DDD4) | (0<<DDD6) | (1<<DDD7);
   PORTD |= (1<<PORTD3); //enable the pullup on PD3, an input

   EICRA |= (1<<ISC01) | (1<<ISC00);    // Setup INT0 control reg
   EIMSK |= (1<<INT0); //enable external int0
}

void timer_setup(void){
   ASSR|=(1<<AS2);
   TCCR2B|= (1<<CS22)|(0<<CS21)|(1<<CS20); //prescaler of 0
   TIMSK2|=(1<<TOIE2); //enable the timer 2 overflow int.
}

void initialize (void) {
   CLKPR=(1<<CLKPCE); // enable divisor change to turn of clk/8
   CLKPR=0; // set divisor to 1 for a clock of 8mhz

   port_setup();
   timer_setup();

   sei(); //enable global ints

   LIGHT0_OFF;
   LIGHT1_OFF;
   LIGHT2_OFF;
   LIGHT3_OFF;
   
   //converting dimness % to 8 bit numbers
   CH0_Dim=Chan0_Dimness*2.55;
   CH1_Dim=Chan1_Dimness*2.55;
   CH2_Dim=Chan2_Dimness*2.55;
   CH3_Dim=Chan3_Dimness*2.55;

   CH0_static=CH0_Dim;
   CH1_static=CH1_Dim;
   CH2_static=CH2_Dim;
   CH3_static=CH3_Dim;

   // converting the chan flicker defines to vars
   CH0_flicker=Chan0_Flicker;
   CH1_flicker=Chan1_Flicker;
   CH2_flicker=Chan2_Flicker;
   CH3_flicker=Chan3_Flicker;

   TNCNT2_save=timer;

}

/***********************************************************/
// Main Function

int main (void){
   initialize();
   USART_setup();
   _delay_ms(20);
   for(;;)
   {
      if(button==1)
      {
         button_timer=timer+TIMEOFSOUND;   //set the length of time for the flicker
         send_stop=timer+TIMEOFSOUND;
         stop_debounce=1;
         CH0_flicker=1;         //start the flicker
         //send play/pause
         USART_write(0xFF);   //header
         USART_write(0x55);
         USART_write(0x03);   //length
         USART_write(0x02);   //mode
         USART_write(0x00);   //play
         USART_write(0x01);
         USART_write(0xFA);   //crc

         //end button press
         USART_write(0xFF);
         USART_write(0x55);
         USART_write(0x03);
         USART_write(0x02);
         USART_write(0x00);
         USART_write(0x00);
         USART_write(0xFB);
         button=0;
      }
      if((bit_get(PIND, BIT(3))==0) && debounce==0)
      {   
         button=1;
         debounce=TIMEOFSOUND;
      }
      if((send_stop==timer) && (stop_debounce==1))
      {
         stop_debounce=0;
                  //send play/pause
         USART_write(0xFF);   //header
         USART_write(0x55);
         USART_write(0x03);   //length
         USART_write(0x02);   //mode
         USART_write(0x00);   //play
         USART_write(0x01);
         USART_write(0xFA);   //crc

         //end button press
         USART_write(0xFF);
         USART_write(0x55);
         USART_write(0x03);
         USART_write(0x02);
         USART_write(0x00);
         USART_write(0x00);
         USART_write(0xFB);
      }
      if (button_timer==timer)
      {
         CH0_flicker=0;         //end the flicker
         CH0_Dim=Chan0_Dimness*2.55;
      }
      if(TCNT2>TNCNT2_save)   // see if its time to change a chan dimness for flicker
      {
         Time_to_flicker=1;
         TNCNT2_save=TCNT2+Flicker_Update;
      }
      if(Time_to_flicker==1)
      {   
         if(CH0_flicker==1)   //testing the chan flicker settings to see if they are set and if we should flicker them
         {
            CH0_Dim=CH0_static+(flicker[flicker_counter]*Chan0_Flicker_Multiplier);   //subtrat the rand num from the dimness of the chan
         }
         if(CH1_flicker==1)
         {
            CH1_Dim=CH1_static+(flicker[flicker_counter]*Chan1_Flicker_Multiplier);
         }
         if(CH2_flicker==1)
         {
            CH2_Dim=CH2_static+(flicker[flicker_counter]*Chan2_Flicker_Multiplier);
         }
         if(CH3_flicker==1)
         {
            CH3_Dim=CH3_static+(flicker[flicker_counter]*Chan3_Flicker_Multiplier);
         }
         flicker_counter++;
         Time_to_flicker=0;
         if(flicker_counter==15)
         {
            flicker_counter=0;
         }
      }
      if(Triac_refresh==1)
      {
         for(i=0; i<255 ; i++)
         {
            if(CH0_Dim<i)
            LIGHT0_ON;
            if(CH1_Dim<i)
            LIGHT1_ON;
            if(CH2_Dim<i)
            LIGHT2_ON;
            if(CH3_Dim<i)
            LIGHT3_ON;
            _delay_us(20);
            NOP;
         }
         LIGHT0_OFF;
         LIGHT1_OFF;
         LIGHT2_OFF;
         LIGHT3_OFF;
         Triac_refresh=0;
      }
   }
} 

ipod.c

#include <avr/io.h>

//for communication with an ipod.


#define USART_BAUDRATE 19200
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

void USART_setup (void){
   UCSR0B |= (1 << RXEN0) | (1 << TXEN0);   // Turn on the transmission and reception circuitry
   UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00);

   UBRR0L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register
   UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
}

void USART_write (char Send_char){
   while ((UCSR0A & (1 << UDRE0)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
   UDR0 = Send_char; // Echo back the received byte back to the computer
} 

Pictures