RobG 1,892 Posted February 16, 2013 Share Posted February 16, 2013 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. roadrunner84, Philipp, rca and 1 other 4 Quote Link to post Share on other sites
RobG 1,892 Posted February 16, 2013 Author Share Posted February 16, 2013 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; } zborgerd 1 Quote Link to post Share on other sites
zborgerd 62 Posted February 16, 2013 Share Posted February 16, 2013 Very nice. I've done DMX control in the past from automation controllers. Thanks for posting this. RobG won't rest until he's written an MSP430 implementation of every protocol known to man. Quote Link to post Share on other sites
roadrunner84 466 Posted February 16, 2013 Share Posted February 16, 2013 Why do you use 8 bit spi for this? 10 bit is possible to and would be ideal. Quote Link to post Share on other sites
RobG 1,892 Posted February 16, 2013 Author Share Posted February 16, 2013 DMX uses 2 stop bits, so the byte is 11 bits. 11 bit is possible using USI, but not USCI. roadrunner84 and adroit_91 2 Quote Link to post Share on other sites
RobG 1,892 Posted February 17, 2013 Author Share Posted February 17, 2013 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; } } } jsolarski and roadrunner84 2 Quote Link to post Share on other sites
roadrunner84 466 Posted February 18, 2013 Share Posted February 18, 2013 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. Quote Link to post Share on other sites
RobG 1,892 Posted February 20, 2013 Author Share Posted February 20, 2013 My DMX receiver/pixel driver is almost finished. When done, it will be able to drive 100+ RGB pixels. DMX can be received during pixel updates, so there are no frame rate or any other limitations. Pixels can be WS2801 or WS2811, no configuration changes are necessary. roadrunner84 1 Quote Link to post Share on other sites
roadrunner84 466 Posted February 20, 2013 Share Posted February 20, 2013 Can you do some video with an animating effect, so we can see the real refresh rate? My top of the class math says you could drive 512/3 = 170+2/3 RGB LEDs. Quote Link to post Share on other sites
RobG 1,892 Posted February 20, 2013 Author Share Posted February 20, 2013 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. Quote Link to post Share on other sites
roadrunner84 466 Posted February 20, 2013 Share Posted February 20, 2013 I have never seen universes being used in real life, if 512 channels aren't enough, most techicians just split the DMX chain and drive two chains separately in universe 0. Quote Link to post Share on other sites
RobG 1,892 Posted February 20, 2013 Author Share Posted February 20, 2013 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. roadrunner84 1 Quote Link to post Share on other sites
RobG 1,892 Posted February 21, 2013 Author Share Posted February 21, 2013 Can you do some video with an animating effect, so we can see the real refresh rate? Here's a simple animation example. LEDs are switched one per fame, 360 channels, 120 RGB LEDs, 50 fps. And another one (pattern was created with my Universal Pixel Editor.) roadrunner84 1 Quote Link to post Share on other sites
RobG 1,892 Posted February 28, 2013 Author Share Posted February 28, 2013 Code for the 15x8 matrix scrolling text message.h dmxTx-matrix-scroll.c dmxRx-matrix-scroll.c bluehash 1 Quote Link to post Share on other sites
dhien 0 Posted October 9, 2013 Share Posted October 9, 2013 help me! hello! you give me the principle diagram Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.