Code:
/***********************************************************
* DMX-512 Reception based on code Developed by Max Pierson
* GE CE G35 code based on code by Robert Quattlebaum <darco@deepdarc.com>
* and Scott Harris <scottrharris@gmail.com>
* Version 1.0 January 12, 2012
* Author: John Abate
************************************************************/
// This program controls two equal length GE Color Effects G35 RGB LED light strings from
// dmx input
/******************************* Addressing variable declarations *****************************/
#include <EEPROM.h>
#define SWITCH_PIN_0 11 //the pin number of our "0" switch
#define SWITCH_PIN_1 12 //the pin number of our "1" switch
#define xmas_color_t uint16_t // typedefs can cause trouble in the Arduino environment
// These are the defines needed for the RGB Lights
#define XMAS_LIGHT_COUNT (36) //I only have a 36 light strand. Should be 50 or 36
#define XMAS_CHANNEL_MAX (0xF)
#define XMAS_DEFAULT_INTENSITY (0xCC)
#define XMAS_MAX_INTENSITY (0xCC)
#define XMAS_HUE_MAX ((XMAS_CHANNEL_MAX+1)*6-1)
#define XMAS_COLOR(r,g,b) ((r)+((g)<<4)+((b)<<8))
#define XMAS_COLOR_WHITE XMAS_COLOR(XMAS_CHANNEL_MAX,XMAS_CHANNEL_MAX,XMAS_CHANNEL_MAX)
#define XMAS_COLOR_BLACK XMAS_COLOR(0,0,0)
#define DELAYLONG 17 //DigitalWrite takes ~ 3.4us
#define DELAYSHORT 6
#define ZERO(pin) digitalWrite(pin,LOW); delayMicroseconds(DELAYSHORT); digitalWrite(pin,HIGH); delayMicroseconds(DELAYLONG);
#define ONE(pin) digitalWrite(pin,LOW); delayMicroseconds(DELAYLONG); digitalWrite(pin,HIGH); delayMicroseconds(DELAYSHORT);
#define XMASPIN 4 // I drive the LED strand from pin #4
#define XMASPIN2 7 // I drive the LED strand 2 from pin #5
#define NUMBER_OF_CHANNELS XMAS_LIGHT_COUNT * 3 * 2
int bulb = 0;
int j = 2;
unsigned int dmxaddress = 1;
/* The dmx address we will be listening to. The value of this will be set in the Addressing()
* function, if triggered, and read from EEPROM addresses 510 and 511.
/******************************* MAX485 variable declarations *****************************/
#define RECEIVER_OUTPUT_ENABLE 2
/* receiver output enable (pin2) on the max485.
* will be left low to set the max485 to receive data. */
#define DRIVER_OUTPUT_ENABLE 3
/* driver output enable (pin3) on the max485.
* will left low to disable driver output. */
#define RX_PIN 0 // serial receive pin, which takes the incoming data from the MAX485.
#define TX_PIN 1 // serial transmission pin
/******************************* DMX variable declarations ********************************/
volatile byte i = 0; //dummy variable for dmxvalue[]
volatile byte dmxreceived = 0; //the latest received value
volatile unsigned int dmxcurrent = 0; //counter variable that is incremented every time we receive a value.
volatile byte dmxvalue[NUMBER_OF_CHANNELS+2]; //stores the DMX values we're interested in using.
volatile boolean dmxnewvalue = false; //set to 1 when new dmx values are received.
/******************************* Timer2 variable declarations *****************************/
volatile byte zerocounter = 0;
/* a counter to hold the number of zeros received in sequence on the serial receive pin.
* When we've received a minimum of 11 zeros in a row, we must be in a break. As written,
* the timer2 ISR actually checks for 22 zeros in a row, for the full 88uS break. */
void Addressing(byte switch0, byte switch1)
{
byte bitnumber = 0; //the number of bits recorded so far
boolean blink = 0; //used to blink the pin 13 LED once the address is recorded.
if (switch0 == 0 && switch1 == 0)
{
EEPROM.write(510, lowByte(dmxaddress));
EEPROM.write(511, highByte(dmxaddress));
//if BOTH addressing switches are held down, the starting address is reset to 1.
}
else if (switch0 == 0 ^ switch1 == 0)
{
/* if EITHER switch0 or switch1 is held down (but not both), the addressing subroutine is
* run. Since it writes the new address into EEPROM, there's no need to pass anything
* back to the main function. */
while (bitnumber < 9) //runs 9 times, recording a bit in dmxaddress each time.
{
digitalWrite(13, HIGH); //turn on pin 13 to indicate addressing mode.
if (switch0 != digitalRead(SWITCH_PIN_0) || switch1 != digitalRead(SWITCH_PIN_1))
//execute loop if either switch state has changed
{
if (digitalRead(SWITCH_PIN_0) == HIGH && digitalRead(SWITCH_PIN_1) == LOW)
//execute if 1 pin is pressed
{
switch0 = digitalRead(SWITCH_PIN_0);
switch1 = digitalRead(SWITCH_PIN_1); //update switch state variables with the new state
bitSet(dmxaddress, bitnumber); //write a 0 to the appropriate bit of dmxaddress
bitnumber++;
digitalWrite(13, LOW); //flash off pin 13 to indicate bit accepted
delay(500);
}
if (digitalRead(SWITCH_PIN_0) == LOW && digitalRead(SWITCH_PIN_1) == HIGH)
//execute if 0 pin is pressed
{
switch0 = digitalRead(SWITCH_PIN_0);
switch1 = digitalRead(SWITCH_PIN_1); //update switch state variables with the new state
bitClear(dmxaddress, bitnumber); //write a 0 to the appropriate bit of dmxaddress
bitnumber++;
digitalWrite(13, LOW); //turn off pin 13 to indicate bit accepted
delay(500);
}
} //end switch state change if statement
} //end while loop
}//end else if addressing subroutine
for (byte i = 0; i < 15; i++) //blink LED 4 times when new address is received.
{
digitalWrite(13, blink);
delay(100);
blink = ~blink;
}
EEPROM.write(510, lowByte(dmxaddress));//writes the first byte of dmxaddress to EEPROM address 510
EEPROM.write(511, highByte(dmxaddress));//writes the second byte of dmxaddress to EEPROM address 511
return;
} //end Addressing()
void setup() {
/******************************* Max485 configuration ***********************************/
pinMode(XMASPIN, OUTPUT);
pinMode(XMASPIN2,OUTPUT);
int i = 0;
delay(500);
// The way the GE CE lights get their addressing is from the first set of data
// send to them after power up. This code cycles through each bulb setting it to RED
// then GREEN and then BLUE followed by setting them all to BLACK
// If you do not want the RGB seqence on power up, then remove all but the last loop
// which will accomplish the node addressing without lighting up the nodes.
for(i = 0;i<XMAS_LIGHT_COUNT;i++)
{
SETBULB(XMASPIN,i,0xCC,255,0,0);
SETBULB(XMASPIN2,i,0xCC,255,0,0);
}
delay(100);
for(i = 0;i<XMAS_LIGHT_COUNT;i++)
{
SETBULB(XMASPIN,i,0xCC,0,255,0);
SETBULB(XMASPIN2,i,0xCC,0,255,0);
}
delay(100);
for(i = 0;i<XMAS_LIGHT_COUNT;i++)
{
SETBULB(XMASPIN,i,0xCC,0,0,255);
SETBULB(XMASPIN2,i,0xCC,0,0,255);
}
delay(100);
for(int i = XMAS_LIGHT_COUNT-1;i>=0;i--)
{
SETBULB(XMASPIN,i,0xCC,0,0,0);
SETBULB(XMASPIN2,i,0xCC,0,0,0);
}
pinMode(RECEIVER_OUTPUT_ENABLE, OUTPUT);
pinMode(DRIVER_OUTPUT_ENABLE, OUTPUT);
digitalWrite(RECEIVER_OUTPUT_ENABLE, LOW);
digitalWrite(DRIVER_OUTPUT_ENABLE, LOW); //sets pins 3 and 4 to low to enable reciever mode on the MAX485.
pinMode(RX_PIN, INPUT); //sets serial pin to receive data
/******************************* Addressing subroutine *********************************/
pinMode(SWITCH_PIN_0, INPUT); //sets pin for '0' switch to input
digitalWrite(SWITCH_PIN_0, HIGH); //turns on the internal pull-up resistor for '0' switch pin
pinMode(SWITCH_PIN_1, INPUT); //sets pin for '1' switch to input
digitalWrite(SWITCH_PIN_1, HIGH); //turns on the internal pull-up resistor for '1' switch pin
byte switch1 = 0;
byte switch0 = 0; //the switch states for the 1 and 0 pin
switch0 = digitalRead(SWITCH_PIN_0);
switch1 = digitalRead(SWITCH_PIN_1);
if (switch0 == 0 || switch1 == 0)
{
Addressing(switch0, switch1);
//call the addressing subroutine if either switch is pressed.
}
//read the previously stored value from EEPROM addresses 510 and 511.
dmxaddress = EEPROM.read(511); //read the high byte into dmxaddress
dmxaddress = dmxaddress << 8;
//bitshift the high byte left 8 bits to make room for the low byte
dmxaddress = dmxaddress | EEPROM.read(510); //read the low byte into dmxaddress
dmxaddress = dmxaddress + 3;
/* this will allow the USART receive interrupt to fire an additional 3 times for every dmx frame.
* Here's why:
* Once to account for the fact that DMX addresses run from 0-511, whereas channel numbers
* start numbering at 1.
* Once for the Mark After Break (MAB), which will be detected by the USART as a valid character
* (a zero, eight more zeros, followed by a one)
* Once for the START code that precedes the 512 DMX values (used for RDM). */
/******************************* USART configuration ************************************/
Serial.begin(250000);
//Serial.begin(115200);
//Serial.begin(57600);
/* Each bit is 4uS long, hence 250Kbps baud rate */
cli(); //disable interrupts while we're setting bits in registers
bitClear(UCSR0B, RXCIE0); //disable USART reception interrupt
/******************************* Timer2 configuration ***********************************/
//NOTE: this will disable PWM on pins 3 and 11.
bitClear(TCCR2A, COM2A1);
bitClear(TCCR2A, COM2A0); //disable compare match output A mode
bitClear(TCCR2A, COM2B1);
bitClear(TCCR2A, COM2B0); //disable compare match output B mode
bitSet(TCCR2A, WGM21);
bitClear(TCCR2A, WGM20); //set mode 2, CTC. TOP will be set by OCRA.
bitClear(TCCR2B, FOC2A);
bitClear(TCCR2B, FOC2B); //disable Force Output Compare A and B.
bitClear(TCCR2B, WGM22); //set mode 2, CTC. TOP will be set by OCRA.
bitClear(TCCR2B, CS22);
bitClear(TCCR2B, CS21);
bitSet(TCCR2B, CS20); // no prescaler means the clock will increment every 62.5ns (assuming 16Mhz clock speed).
OCR2A = 64;
/* Set output compare register to 64, so that the Output Compare Interrupt will fire
* every 4uS. */
bitClear(TIMSK2, OCIE2B); //Disable Timer/Counter2 Output Compare Match B Interrupt
bitSet(TIMSK2, OCIE2A); //Enable Timer/Counter2 Output Compare Match A Interrupt
bitClear(TIMSK2, TOIE2); //Disable Timer/Counter2 Overflow Interrupt Enable
sei(); //reenable interrupts now that timer2 has been configured.
} //end setup()
void loop() {
// the processor gets parked here while the ISRs are doing their thing.
if (dmxnewvalue == true) { //when a new set of values are received, jump to action loop...
action();
dmxnewvalue = 0;
dmxcurrent = 0;
zerocounter = 0; //and then when finished reset variables and enable timer2 interrupt
i = 0;
bitSet(TIMSK2, OCIE2A); //Enable Timer/Counter2 Output Compare Match A Interrupt
}
} //end loop()
//Timer2 compare match interrupt vector handler
ISR(TIMER2_COMPA_vect) {
if (bitRead(PIND, PIND0)) { // if a one is detected, we're not in a break, reset zerocounter.
zerocounter = 0;
}
else {
zerocounter++; // increment zerocounter if a zero is received.
if (zerocounter == 22) // if 22 0's are received in a row (88uS break)
{
bitClear(TIMSK2, OCIE2A); //disable this interrupt and enable reception interrupt now that we're in a break.
bitSet(UCSR0B, RXCIE0);
}
}
} //end Timer2 ISR
ISR(USART_RX_vect){
dmxreceived = UDR0;
/* The receive buffer (UDR0) must be read during the reception ISR, or the ISR will just
* execute again immediately upon exiting. */
dmxcurrent++; //increment address counter
if(dmxcurrent > dmxaddress) { //check if the current address is the one we want.
dmxvalue[i] = dmxreceived;
i++;
if(i == NUMBER_OF_CHANNELS+2) {
bitClear(UCSR0B, RXCIE0);
dmxnewvalue = true; //set newvalue, so that the main code can be executed.
}
}
} // end ISR
void xmas_set_color(uint8_t led,uint8_t intensity,xmas_color_t color)
{
uint8_t R, G, B;
R = color & 0x0F;
G = (color >> 4) & 0x0F;
B = (color >> 8) & 0x0F;
//xmas_set_color(begin++,intensity,color);
SETBULB(XMASPIN,led,intensity, R,G,B);
}
xmas_color_t
xmas_color(uint8_t r,uint8_t g,uint8_t b)
{
return XMAS_COLOR(r,g,b);
}
void SETBULB( byte IOPin, byte Bulb, byte Brightness, byte R, byte G, byte B)
{
R = R>>4;
G = G>>4;
B = B>>4;
digitalWrite(IOPin,HIGH);
delayMicroseconds(DELAYSHORT);
//Bulb
if (Bulb & 0x20) { ONE(IOPin) } else { ZERO(IOPin) };
if (Bulb & 0x10) { ONE(IOPin) } else { ZERO(IOPin) };
if (Bulb & 0x08) { ONE(IOPin) } else { ZERO(IOPin) };
if (Bulb & 0x04) { ONE(IOPin) } else { ZERO(IOPin) };
if (Bulb & 0x02) { ONE(IOPin) } else { ZERO(IOPin) };
if (Bulb & 0x01) { ONE(IOPin) } else { ZERO(IOPin) };
//Brightness
if (Brightness & 0x80) { ONE(IOPin) } else { ZERO(IOPin) };
if (Brightness & 0x40) { ONE(IOPin) } else { ZERO(IOPin) };
if (Brightness & 0x20) { ONE(IOPin) } else { ZERO(IOPin) };
if (Brightness & 0x10) { ONE(IOPin) } else { ZERO(IOPin) };
if (Brightness & 0x08) { ONE(IOPin) } else { ZERO(IOPin) };
if (Brightness & 0x04) { ONE(IOPin) } else { ZERO(IOPin) };
if (Brightness & 0x02) { ONE(IOPin) } else { ZERO(IOPin) };
if (Brightness & 0x01) { ONE(IOPin) } else { ZERO(IOPin) };
//Blue
if (B & 0x8) { ONE(IOPin) } else { ZERO(IOPin) };
if (B & 0x4) { ONE(IOPin) } else { ZERO(IOPin) };
if (B & 0x2) { ONE(IOPin) } else { ZERO(IOPin) };
if (B & 0x1) { ONE(IOPin) } else { ZERO(IOPin) };
//Green
if (G & 0x8) { ONE(IOPin) } else { ZERO(IOPin) };
if (G & 0x4) { ONE(IOPin) } else { ZERO(IOPin) };
if (G & 0x2) { ONE(IOPin) } else { ZERO(IOPin) };
if (G & 0x1) { ONE(IOPin) } else { ZERO(IOPin) };
//Red
if (R & 0x8) { ONE(IOPin) } else { ZERO(IOPin) };
if (R & 0x4) { ONE(IOPin) } else { ZERO(IOPin) };
if (R & 0x2) { ONE(IOPin) } else { ZERO(IOPin) };
if (R & 0x1) { ONE(IOPin) } else { ZERO(IOPin) };
digitalWrite(IOPin,LOW);
delayMicroseconds(40);
}
void action() {
j = 2;
for(bulb = 0; bulb < XMAS_LIGHT_COUNT; bulb++)
{
SETBULB(XMASPIN,bulb,0xCC,dmxvalue[j++],dmxvalue[j++],dmxvalue[j++]);
}
// strand 2
for(bulb = 0; bulb < XMAS_LIGHT_COUNT; bulb++)
{
SETBULB(XMASPIN2,bulb,0xCC,dmxvalue[j++],dmxvalue[j++],dmxvalue[j++]);
}
return; //go back to loop()
} //end action() loop
Bookmarks