Jump to content
43oh

DMX code examples


Recommended Posts

I am starting this topic for those interested in DMX lighting.

My first example is a simple DMX controller, which can be used to test DMX devices.

You will need a RS-485 transmitter to make it work (I will start another topic about that.)

 

 

#include "msp430g2553.h"

typedef unsigned char u_char;
typedef unsigned int u_int;

#define DMX_OUT_PIN BIT2

void configMSP();
void sendDMX();

#define DATA_LENGTH 12
u_char dmxData[DATA_LENGTH] = { 0x55, 0x55, 0x55, 0x00, 0xFF, 0x00, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF}; // 4 RGB sets

u_int dataStart = 0;
u_int numberOfChannels = 3;
u_int dataEnd = 0;
u_int counter;

void main(void) {

	WDTCTL = WDTPW + WDTHOLD;
	configMSP();

	while (1) {
		_delay_cycles(1600000); // send DMX every 100ms
		counter++;
		if(counter == 20) { // switch data every 2s
			counter = 0;
			dataStart += numberOfChannels; // next data set
			if(dataStart == DATA_LENGTH) dataStart = 0;
			dataEnd = dataStart + numberOfChannels;
		}
		sendDMX();
	}
}

void sendDMX() {
	u_int c = dataStart;

	UCA0CTL1 |= UCTXBRK;
	UCA0TXBUF = 0;

	while (c < dataEnd) {
		while (!(IFG2 & UCA0TXIFG))
			;
		UCA0TXBUF = dmxData[c];
		c++;
	}
}

void configMSP() {

	// DCO
	BCSCTL1 = CALBC1_16MHZ;
	DCOCTL = CALDCO_16MHZ;

	P1SEL |= DMX_OUT_PIN;
	P1SEL2 |= DMX_OUT_PIN;

	UCA0CTL0 |= UCSPB + UCMODE_3; // 2 stop bits
	UCA0CTL1 |= UCSSEL_2;
	UCA0ABCTL |= UCDELIM0 + UCDELIM1,
	UCA0BR0 = 0x40; // 16MHz/64 = 250k
	UCA0BR1 = 0;
	UCA0MCTL = 0;
	UCA0CTL1 &= ~UCSWRST;
}

In the code above, BREAK is only 52us, so if that causes problems, use the code below, which generates 100us BREAK.

 

#include "msp430g2553.h"

typedef unsigned char u_char;
typedef unsigned int u_int;

#define DMX_OUT_PIN BIT2

void configMSP();
void sendDMX();

#define DATA_LENGTH 12
u_char dmxData[DATA_LENGTH] = { 0x55, 0x55, 0x55, 0x00, 0xFF, 0x00, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF}; // 4 RGB sets

u_int dataStart = 0;
u_int numberOfChannels = 3;
u_int dataEnd = 0;
u_int counter;

void main(void) {

	WDTCTL = WDTPW + WDTHOLD;
	configMSP();

	while (1) {
		_delay_cycles(1600000); // send DMX every 100ms
		counter++;
		if(counter == 20) { // switch data every 2s
			counter = 0;
			dataStart += numberOfChannels; // next data set
			if(dataStart == DATA_LENGTH) dataStart = 0;
			dataEnd = dataStart + numberOfChannels;
		}
		sendDMX();
	}
}

void sendDMX() {

	P1SEL &= ~DMX_OUT_PIN;
	P1SEL2 &= ~DMX_OUT_PIN;
	_delay_cycles(1600); // 100us
	P1SEL |= DMX_OUT_PIN;
	P1SEL2 |= DMX_OUT_PIN;
	_delay_cycles(160); // 10us

	u_int c = dataStart;

	UCA0TXBUF = 0;

	while (c < dataEnd) {
		while (!(IFG2 & UCA0TXIFG))
			;
		UCA0TXBUF = dmxData[c];
		c++;
	}
}

void configMSP() {

	// DCO
	BCSCTL1 = CALBC1_16MHZ;
	DCOCTL = CALDCO_16MHZ;

	P1OUT &= ~DMX_OUT_PIN;
	P1DIR |= DMX_OUT_PIN;
	P1SEL |= DMX_OUT_PIN;
	P1SEL2 |= DMX_OUT_PIN;

	UCA0CTL0 |= UCSPB; // 2 stop bits
	UCA0CTL1 |= UCSSEL_2;
	UCA0BR0 = 0x40; // 16MHz/64 = 250k
	UCA0BR1 = 0;
	UCA0MCTL = 0;
	UCA0CTL1 &= ~UCSWRST;
}

 

In the next post, I will show how to use SPI instead of UART, so that UART could be used to communicate with PC or another device.

Link to post
Share on other sites

DMX controller code which uses SPI (UCB) instead of UART (UCA.)

#include "msp430g2553.h"

typedef unsigned char u_char;
typedef unsigned int u_int;

#define DMX_OUT_PIN BIT7

void configMSP();
void sendDMX();
void sendByte(u_char byte);

const u_char START_SEQ[4] = { 0, 0, 0, 0xF0 };

#define DATA_LENGTH 12
u_char dmxData[DATA_LENGTH] = { 0x55, 0x55, 0x55, 0x00, 0xFF, 0x00, 0x0F, 0x0F,
		0x0F, 0xFF, 0xFF, 0xFF }; // 4 RGB sets

u_int dataStart = 0;
u_int numberOfChannels = 3;
u_int dataEnd = 0;
u_int counter;

void main(void) {

	WDTCTL = WDTPW + WDTHOLD;
	configMSP();

	while (1) {
		_delay_cycles(1600000); // send DMX every 100ms
		counter++;
		if (counter == 20) { // switch data every 2s
			counter = 0;
			dataStart += numberOfChannels; // next data set
			if (dataStart == DATA_LENGTH)
				dataStart = 0;
			dataEnd = dataStart + numberOfChannels;
		}
		sendDMX();
	}
}

void sendDMX() {

	u_int c = 0;

	while (c < 4) {
		while (!(IFG2 & UCB0TXIFG))
			;
		UCB0TXBUF = START_SEQ[c];
		c++;
	}

	sendByte(0);

	c = dataStart;

	while (c < dataEnd) {
		sendByte(dmxData[c]);
		c++;
	}
}

void sendByte(u_char byte) {
	u_char first = byte << 1;
	u_char second = byte >> 7;
	second |= 0xFE;
	while (!(IFG2 & UCB0TXIFG))
		;
	_bic_SR_register(GIE);
	UCB0TXBUF = first;
	while (!(IFG2 & UCB0TXIFG))
		;
	UCB0TXBUF = second;
	_bis_SR_register(GIE);
}

void configMSP() {

	// DCO
	BCSCTL1 = CALBC1_16MHZ;
	DCOCTL = CALDCO_16MHZ;

	P1SEL |= DMX_OUT_PIN;
	P1SEL2 |= DMX_OUT_PIN;

	UCB0CTL0 |= UCCKPH + UCMST + UCSYNC;
	UCB0CTL1 |= UCSSEL_2;
	UCB0BR0 |= 0x40; // 16MHz/64 = 250k
	UCB0BR1 = 0;
	UCB0CTL1 &= ~UCSWRST;
}
Link to post
Share on other sites

Time for the receiver.

 

Again, very simple code, receives 3 consecutive channels and stores the values in the array. 

Received values can then be used as PWM values to drive RGB LED.

 

#include "msp430g2553.h"

typedef unsigned char u_char;
typedef unsigned int u_int;

#define DMX_IN_PIN BIT1

void configMSP();

u_char data[3] = { 0, }; //
u_char dataCounter = 0;

u_char ucaStatus = 0;
u_char rxData = 0;

#define DMX_IDLE 0
#define DMX_BREAK 1
#define DMX_START 2
#define DMX_READY 3
#define DMX_RECEIVE 4

u_char dmxStatus = 0;
u_char dmxDataReady = 0;
u_int dmxChannel = 1;
u_int dmxLength = 3;
u_int dmxCounter = 0;

void main(void) {

    WDTCTL = WDTPW + WDTHOLD;
    configMSP();

    while (1) {
    }
}

void configMSP() {

    // DCO
    BCSCTL1 = CALBC1_16MHZ;
    DCOCTL = CALDCO_16MHZ;

    P1SEL |= DMX_IN_PIN;
    P1SEL2 |= DMX_IN_PIN;

    UCA0CTL0 |= UCSPB; // 2 stop bits
    UCA0CTL1 |= UCSSEL_2 + UCRXEIE + UCBRKIE;
    UCA0BR0 = 0x40; // 16MHz/64 = 250k
    UCA0BR1 = 0;
    UCA0MCTL = 0;
    UCA0CTL1 &= ~UCSWRST;
    IE2 |= UCA0RXIE;

    _bis_SR_register(GIE);
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void) {

    ucaStatus = UCA0STAT;
    rxData = UCA0RXBUF;

    if (ucaStatus & UCBRK) {
        dmxStatus = DMX_BREAK;
        ucaStatus = 0;
        dmxCounter = 0;
        dataCounter = 0;
    } else {
        switch (dmxStatus) {
        case DMX_IDLE:
            break;
        case DMX_BREAK:
            if (rxData == 0) {
                dmxStatus = DMX_START;
            } else {
                dmxStatus = DMX_IDLE;
            }
            dmxCounter++;
            break;
        case DMX_START:
            if (dmxCounter == dmxChannel) {
                dmxStatus = DMX_RECEIVE;
            } else {
                dmxCounter++;
                break;
            }
        case DMX_RECEIVE:
            data[dataCounter] = rxData;
            dataCounter++;
            if (dataCounter == dmxLength) {
                dmxStatus = DMX_IDLE;
                dmxDataReady = 1;
                break;
            }
            dmxCounter++;
            break;
        }
    }
}
Link to post
Share on other sites

A lot of the "professional" RGB par spots use 4 channels; red, green, blue, intensity. This enables you to change the intensity of the light without changing the colour. Ofcourse you'd need to do some more processing in your MSP430 to mix the two components

  • Drive PWM with R*I, G*I, B*I. So every time you receive data you multiply the values.
  • Drive PWM with R, G, B and drive a second PWM with I; this is usefult when driving a matrix display, as you actively drive cathode and anode. Be sure the I is a multiple of the R,G,B driver (so every period of the R,G,B you increment the I counter by 1. Or for every period of I you increment the R,G,B driver by 1).

There's a lot of other options too ofcourse; drive in CMY, HSL, HSV, CIELAB or any other colour scheme. HSL and HSV tend to be useful as you can easily change between a colour and a grayscale component, or use chases to get rainbow effects.

Link to post
Share on other sites

Since I am using G2553 here, the limit is little over 400 DMX channels (due to RAM size.)

I have three other boards, one with F5131, one with STM8S, and one with Stellaris.

Those will be able to do 512 channels and up to 512 RGB LEDs (8 bit color using LUT.)

In addition, Stellaris board will be capable of 8 virtual universes (4096 channels,) F5131 possibly 2 virtual universes.

Link to post
Share on other sites

Two separate DMX chains are basically two universes, even if they are attached to two different controllers. You can have controller 1 with universe 1 & 2, or controller 1 with universe 1 and controller 2 with universe 2. When using DMX to drive "smart" pixels, you will run out of DMX channels pretty quickly. It would be impractical to use multiple controllers with a single universe.

Link to post
Share on other sites
  • 7 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...