Lab_interaccio/2011/dmx_readout/dmx_readout.pde

270 lines
11 KiB
Plaintext
Raw Normal View History

2025-02-25 21:29:42 +01:00
/************************************************************
* DMX-512 Read out on 2-line LCD Display *
* Displays a selectable 8 values from the stream on a *
* LCD display. Also displays the frames/second metric. *
* Developed by Dan Julio (www.danjuliodesigns.com) *
* Rev 2.0 Jan 1, 2010 *
* Released under the WTFPL license with no warranties (e.g. *
* the program could have bugs and I take no responsbility*) *
* *
* Based on DMX-512 Reception, Developed by Max Pierson *
* Version Rev15 9 Oct 2010 *
* Released under the WTFPL license, although I would *
* appreciate Attribution and Share-Alike *
* (blog.wingedvictorydesign.com) *
* *
* Modified to decouple processing of data with ISR handling *
* of DMX frames. The ISRs now run by themselves to detect *
* each frame. The ActionLoop routine displays whatever data *
* is available when it runs. Done to allow code to measure *
* frames per second. *
* *
* This version compiles under Arduino 0021. *
* *
* Arduino Port Connections: *
* 0: RX - TTL RX input from MAX485 driver *
* 1: TX - TTL TX output to MAX485 driver (unused) *
* 2: nRE - Active low Receiver Enable to MAX485 *
* 3: DE - Active high Driver Enabled to MAX485 *
* 4: RS - LCD RS control signal *
* 5: R/W - LCD R/W control signal *
* 6: E - LCD enable control signal *
* 7: D4 - LCD D[4] data signal (LCD in 4-bit mode) *
* 8: D5 - LCD D[5] data signal *
* 9: D6 - LCD D[6] data signal *
* 10: D7 - LCD D[7] data signal *
* 11: AddrUp - Active low AddrUp button input *
* 12: AddrDown - Active low AddrDown button input *
* 13: AddrFast - Active low AddrFast button input *
* *
* Note: Arduino configured to use internal pull-ups on the *
* button inputs. *
* *
* * Program issues: *
* - Program timing may fail when looking at DMX values at *
* the end of the frame (e.g. DMX Addr 504 and above in *
* a 512 entry frame) *
*************************************************************/
#include <LiquidCrystal.h>
/******************************* Addressing variable declarations *****************************/
#include <EEPROM.h>
#define NUMBER_OF_CHANNELS 8
//the number of channels we want to receive (8 by default).
/*
* Control Operation
* Press Up Button - address increases by 1
* Press Down Button - starting address decreases by 1
* Press Up & Down simultaneously - starting address reset to 1
* Press Fast Button with either Up or Down - Starting address increments rapidly
*/
#define ADDR_UP_BUTTON 11 //the pin number of the address increment button
#define ADDR_DOWN_BUTTON 12 //the pin number of the address decrement button
#define ADDR_FAST_BUTTON 13 //the pin of the fast change modifier button
unsigned int dmxaddress = 1;
/* The starting dmx address we will be listening to.
/******************************* 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]; //stores the DMX values we're interested in using.
volatile boolean dmxnewvalue = 0; //set to 1 when new dmx values are received.
unsigned long curTime;
unsigned long prevTime;
unsigned long deltaT;
unsigned int fpsArray[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // Array of frames/second for averaged display
/******************************* 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. */
/******************************* Button variable declarations *****************************/
volatile byte debounceCount = 0;
boolean prevAddrUp = 0; // Value from last sample
boolean prevAddrDown = 0;
boolean prevAddrFast = 0;
boolean AddrUpPressed = 0; // Flag indicating button just pressed
boolean AddrDownPressed = 0;
boolean AddrUpDown = 0; // Flag indicating button held pressed
boolean AddrDownDown = 0;
boolean AddrFastDown = 0;
/************************** LCD Display connection declarations ***************************/
LiquidCrystal lcd(4, 5, 6, 7, 8, 9, 10);
unsigned int lcdCol[8] = {5, 8, 11, 14, 5, 8, 11, 14}; // Display starting column positions
unsigned int lcdRow[8] = {0, 0, 0, 0, 1, 1, 1, 1}; // Display row positions
void setup() {
/******************************* Max485 configuration ***********************************/
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(ADDR_UP_BUTTON, INPUT);
digitalWrite(ADDR_UP_BUTTON, HIGH); //turns on the internal pull-up resistor
pinMode(ADDR_DOWN_BUTTON, INPUT);
digitalWrite(ADDR_DOWN_BUTTON, HIGH);
pinMode(ADDR_FAST_BUTTON, INPUT);
digitalWrite(ADDR_FAST_BUTTON, HIGH);
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). */
/******************************* LCD Initialization ************************************/
lcd.begin(16,2);
delay(1);
lcd.clear();
delay(1);
lcd.setCursor(0,0);
lcd.print(" DMX Readout");
delay(500); //let them see this for a split second!
lcd.clear();
/******************************* USART configuration ************************************/
Serial.begin(250000);
/* 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
curTime = millis(); //get the first timestamp
} //end setup()
void loop() {
// the processor gets parked here while the ISRs are doing their thing.
delay(1); // Delay only a short time in each loop so we are responsive to new DMX data
// Debounce and process the address buttons (sample every ~25 mSec)
if (++debounceCount >= 25) {
processAddressButtons();
debounceCount = 0;
}
if (dmxnewvalue == 1) { //when a new set of values are received, jump to action loop...
action();
dmxnewvalue = 0;
}
} //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 == 20) // if 20 0's are received in a row (80uS 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. */
// Note time of first character to measure frames/second
if (dmxcurrent++ == 0) {
prevTime = curTime;
curTime = millis();
deltaT = curTime - prevTime; // in mSec, atomically computed
}
// Look for our data window
if(dmxcurrent > dmxaddress) { //check if the current address is the one we want.
dmxvalue[i++] = dmxreceived;
if(i == NUMBER_OF_CHANNELS) {
dmxnewvalue = 1; //set newvalue, so that the action loop can be executed.
dmxcurrent = 0; //reset for the next frame
zerocounter = 0;
i = 0;
bitClear(UCSR0B, RXCIE0); //Disable receiving serial data
bitSet(TIMSK2, OCIE2A); //Enable Timer/Counter2 Output Compare Match A Interrupt for next break
}
}
} // end ISR