gordon 229 Posted June 19, 2011 Share Posted June 19, 2011 Can't wait for the code appear. The hardware looks cool . Oh, did I mentioned that my MIDI track is from Star Wars? This was shocking news. Surely, noone expected the Imperial March ever to turn up in a geek toy! :twisted: :twisted: Quote Link to post Share on other sites
RobG 1,892 Posted June 21, 2011 Author Share Posted June 21, 2011 Here's the code. There is still room for improvement, like making zero crossing detector immune to noise, better handling of small loads like LED lights, and programmable MIDI channel and octave. #include "msp430g2553.h" #define MIDI_IN_PIN BIT1 // MIDI in pin #define ZERO_X_PIN BIT0 // Zero Crossing Detector in pin #define MIDI_RCVD_LED_PIN BIT2 // MIDI message received LED out pin #define P1_OUT_PINS BIT4 + BIT5 + BIT6 + BIT7 // Outputs on P1 void setUp(); char charIndex = 0; // general use index char rxData = 0; // received data char rxByte = 1; // data byte, first or second char channel = 0; // MIDI channel to listen to char status = 0; // Status byte converted to something easy to use char runningStatus = 0; // running status flag char light = 255; // light index char lights[40]; // array of light brightness values (we are only using 10, but 40 or more are possible) char counter = 1; // PWM counter char lightCounter = 0; // used to track lights when setting/sending light on/off int bitCounter = 0; // used for setting parallel lights on/off void main(void) { setUp(); __bis_SR_register(GIE); // enable global while(1) { // main loop lightCounter = 0; // clear light counter bitCounter = 1; // first light on port 2 (P2.0-P2.5) while(lightCounter < 6) { // loop through parallel lights 0-5 if((counter > 8) && (lights[lightCounter] == counter)) {// when value equals PWM counter... (unreliable when below 8) P2OUT |= bitCounter; // turn light on } lightCounter++; // next light P2OUT &= ~bitCounter; // clear output bitCounter <<= 1; // next bit on port 2 } bitCounter = 0x10; // first light on port 1 (P1.4-P1.7) while(lightCounter < 10) { // loop through parallel lights 6-9 if((counter > 8) && (lights[lightCounter] == counter)) {// when value equals PWM counter... (unreliable when below 8) P1OUT |= bitCounter; // turn light on } lightCounter++; // next light P1OUT &= ~bitCounter; // clear output bitCounter <<= 1; // next bit on port 1 } __bis_SR_register(LPM0_bits); // go to sleep and wait for timer } } // increment count up to 64 #pragma vector = TIMER0_A0_VECTOR __interrupt void Timer_A0 (void) { if(counter > 1) { counter--; // decrement PWM counter } __bic_SR_register_on_exit(LPM0_bits); // wake up and update lights } // Port 1 interrupt service routine #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { counter = 64; // reset counter P1OUT &= ~(P1_OUT_PINS); // clear outputs just in case P2OUT = 0; P1OUT |= MIDI_RCVD_LED_PIN; // MIDI LED off P1IFG = 0; } void setUp() { WDTCTL = WDTPW + WDTHOLD; // disable WDT BCSCTL1 = CALBC1_8MHZ; // 8MHz clock DCOCTL = CALDCO_8MHZ; P1OUT |= MIDI_RCVD_LED_PIN; P1DIR |= MIDI_RCVD_LED_PIN; P1OUT &= ~(P1_OUT_PINS); // port 1, 4 outs P1DIR |= (P1_OUT_PINS); P1SEL = MIDI_IN_PIN; // setup MIDI in pin P1SEL2 = MIDI_IN_PIN; P2SEL &= ~(BIT6|BIT7); // port 2 all out P2OUT = 0; P2DIR = 0xFF; P1DIR &= ~ZERO_X_PIN; // port P1.0 zero crossing detector input P1IES |= ZERO_X_PIN; // high/lo edge P1IFG &= ~ZERO_X_PIN; P1IE |= ZERO_X_PIN; CCTL0 = CCIE; // setup timer CCR0 = 1042; // 130.2us TACTL = TASSEL_2 + MC_1; // SMCLK, upmode UCA0CTL1 |= UCSSEL_2; // setup UART UCA0BR0 = 0x00; UCA0BR1 = 0x01; // 31.25 Kbaud UCA0CTL1 &= ~UCSWRST; IE2 |= UCA0RXIE; charIndex = 0; // clear lights while(charIndex < 40) { lights[charIndex] = 0; charIndex++; } } // this is where the MIDI magic happens #pragma vector=USCIAB0RX_VECTOR __interrupt void USCI0RX_ISR(void) { rxData = UCA0RXBUF; // copy RX buffer to var if(rxData & BIT7) { // is it status? if((rxData & 0xF8) != 0xF8) { // ignore RealTime Category if((rxData & 0x0F) == channel) { // are we listening on this channel status = (rxData >> 4) - 8; // yep, let's set status runningStatus = 1; // expect running status messages for our channel } else { // nope, not our channel runningStatus = 0; // do not process data for other channels } } } else { // it's data byte if(runningStatus) { // process data only for our channel including running status messages if(rxByte == 1) { // is it first data byte? rxByte = 2; // second byte will follow unless... if(status == 4 || status == 5) {// it's Prog Change or Channel Pressure rxByte = 1; // only one data byte expected } else if (status == 0 || status == 1) {// it's Note Off or Note On, select light if(rxData > 59 && rxData < 100) { // check range: 60-99, middle C to ... light = rxData - 60; // offset to start at 0 P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on } } else if (status == 3) { // it's Controller if(rxData > 0 && rxData < 17) {// check range: 1-16, General Purpose Slider light = rxData - 1; // offset to start at 0 P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on } else if(rxData == 120 || rxData == 123) {// if it's All Sound Off or All Notes Off... charIndex = 0; while(charIndex < 40) { // clear all lights lights[charIndex] = 0; charIndex++; } } } } else if(rxByte == 2) { // it's second data byte rxByte = 1; // next byte will be first byte if(light == 255 ) // light not in range... return; // and return if(status == 1) { // it's Note On lights[light] = rxData >> 1;// velocity is brightness (divided by 2, 0-64 range) } else if(status == 0) { // it's Note Off lights[light] = 0; // we ignore velocity and just clear the light } else if(status == 3) { // it's Controller if(light < 16) { // are those sliders? lights[light] = rxData >> 1; // yes, we use sliders to control lights 0-15 (divided by 2, 0-64 range) } } light = 255; // set light to not in range } } } } gordon 1 Quote Link to post Share on other sites
SouLSLayeR 5 Posted June 21, 2011 Share Posted June 21, 2011 W00t.. so many comments.. I also saw your new video, shiny stuff! :mrgreen: Thanks for sharing Rob :3 Quote Link to post Share on other sites
RobG 1,892 Posted June 23, 2011 Author Share Posted June 23, 2011 Here's my first light show. Sorry to disappoint you guys, but a) I do not own any Star Wars media, I am so busy at work and with the kids I hardly have any time for my hobbies. I know "It's No Good" but it is what it is bluehash and NatureTM 2 Quote Link to post Share on other sites
bluehash 1,581 Posted June 23, 2011 Share Posted June 23, 2011 Sweet! This is more than enough. We know how you are going to decorate this Christmas. Quote Link to post Share on other sites
RobG 1,892 Posted June 27, 2011 Author Share Posted June 27, 2011 New code, this one has more robust and cleaner MIDI. It is much easier to customize it now. For example handling notes and controllers on different channels is a breeze. In the next version, I am thinking of adding some servo outputs so I can control direction of the light with controllers. #include "msp430g2553.h" #define MIDI_IN_PIN BIT1 // MIDI in pin #define ZERO_X_PIN BIT0 // Zero Crossing Detector in pin #define MIDI_RCVD_LED_PIN BIT2 // MIDI message received LED out pin #define P1_OUT_PINS BIT4 + BIT5 + BIT6 + BIT7 // Outputs on P1 void setUp(); void handleMIDI(unsigned char rxByte); void handleNoteOn(unsigned char channel, unsigned char noteNumber, unsigned char velocity); void handleNoteOff(unsigned char channel, unsigned char noteNumber, unsigned char velocity); void handleControlChange(unsigned char channel, unsigned char controllerNumber, unsigned char value); char charIndex = 0; // general use index unsigned char status = 0; // MIDI status unsigned char channel = 0; // MIDI channel of the current MIDI status unsigned char byteNumber = 1; // received byte number unsigned char byteOne = 0; // first byte received when status requires two bytes unsigned char myChannel = 1; // channel we are listening on char light = 255; // light index char lights[40]; // array of light brightness values char counter = 1; // PWM counter char lightCounter = 0; // used to track lights when setting/sending light on/off int bitCounter = 0; // used for setting parallel lights on/off void main(void) { setUp(); __bis_SR_register(GIE); // enable global while(1) { // main loop lightCounter = 0; // clear light counter bitCounter = 1; // first light on port 2 (P2.0-P2.5) while(lightCounter < 6) { // loop through parallel lights 0-5 if((counter > 8) && (lights[lightCounter] == counter)) {// when value equals PWM counter... P2OUT |= bitCounter; // turn light on } lightCounter++; // next light P2OUT &= ~bitCounter; // clear output bitCounter <<= 1; // next bit on port 2 } bitCounter = 0x10; // first light on port 1 (P1.4-P1.7) while(lightCounter < 10) { // loop through parallel lights 6-9 if((counter > 8) && (lights[lightCounter] == counter)) {// when value equals PWM counter... P1OUT |= bitCounter; // turn light on } lightCounter++; // next light P1OUT &= ~bitCounter; // clear output bitCounter <<= 1; // next bit on port 1 } __bis_SR_register(LPM0_bits); // go to sleep and wait for timer } } // increment count up to 64 #pragma vector = TIMER0_A0_VECTOR __interrupt void Timer_A0 (void) { if(counter > 1) { counter--; // decrement PWM counter } __bic_SR_register_on_exit(LPM0_bits); // wake up and update lights } // Port 1 interrupt service routine #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { counter = 64; // reset counter P1OUT &= ~(P1_OUT_PINS); // clear outputs just in case P2OUT = 0; P1OUT |= MIDI_RCVD_LED_PIN; // MIDI LED off P1IFG = 0; } void setUp() { WDTCTL = WDTPW + WDTHOLD; // disable WDT BCSCTL1 = CALBC1_8MHZ; // 8MHz clock DCOCTL = CALDCO_8MHZ; P1OUT |= MIDI_RCVD_LED_PIN; // MIDI received (on our channel and status that we will be processing) P1DIR |= MIDI_RCVD_LED_PIN; P1OUT &= ~(P1_OUT_PINS); // port 1, 4 outs P1DIR |= (P1_OUT_PINS); P1SEL = MIDI_IN_PIN; // setup MIDI in pin P1SEL2 = MIDI_IN_PIN; P2SEL &= ~(BIT6|BIT7); // port 2 all out P2OUT = 0; P2DIR = 0xFF; P1DIR &= ~ZERO_X_PIN; // port P1.0 zero crossing detector input P1IES |= ZERO_X_PIN; // high/lo edge P1IFG &= ~ZERO_X_PIN; P1IE |= ZERO_X_PIN; CCTL0 = CCIE; // setup timer CCR0 = 1042; // 130.2us TACTL = TASSEL_2 + MC_1; // SMCLK, upmode UCA0CTL1 |= UCSSEL_2; // setup UART UCA0BR0 = 0x00; UCA0BR1 = 0x01; // 31.25 Kbaud UCA0CTL1 &= ~UCSWRST; IE2 |= UCA0RXIE; charIndex = 0; // clear lights while(charIndex < 40) { lights[charIndex] = 0; charIndex++; } } // this is where the MIDI magic happens #pragma vector=USCIAB0RX_VECTOR __interrupt void USCI0RX_ISR(void) { handleMIDI(UCA0RXBUF); // handle received byte } void handleNoteOn(unsigned char channel, unsigned char noteNumber, unsigned char velocity) { if(channel == myChannel) { // my channel? if(noteNumber > 59 && noteNumber < 100) { // check range: 60-99 lights[noteNumber - 60] = velocity >> 1; // offset by 60, shift velocity to get 64 values only P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on } } } void handleNoteOff(unsigned char channel, unsigned char noteNumber, unsigned char velocity) { if(channel == myChannel) { // my channel? if(noteNumber > 59 && noteNumber < 100) { // check range: 60-99 lights[noteNumber - 60] = 0; // offset by 60, turn light off P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on } } } void handleControlChange(unsigned char channel, unsigned char controllerNumber, unsigned char value) { switch(controllerNumber) { case 123: case 120: // if it's All Sound Off or All Notes Off... P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on charIndex = 0; while(charIndex < 40) { // clear all lights lights[charIndex] = 0; charIndex++; } break; default: if(channel == myChannel) { // my channel? if(controllerNumber > 0 && controllerNumber < 17) { // check range: 1-16 P1OUT &= ~MIDI_RCVD_LED_PIN; // MIDI LED on lights[light = controllerNumber - 1] = value >> 1; // offset by 1, shift to get 64 values only } } } } void handleMIDI(unsigned char rxByte) { if(rxByte & BIT7) { // is it status? if((rxByte & 0xF8) == 0xF8) { // System RealTime category? // skip, do nothing } else if ((rxByte & 0xF0) == 0xF0) { // System Common category? status = rxByte; // get status switch(status) { case 0xF0: // skip, do nothing case 0xF7: // skip, do nothing case 0xF6: // skip, do nothing break; } byteNumber = 1; } else { // Voice category channel = rxByte & 0x0F; // get channel status = rxByte & 0xF0; // get status byteNumber = 1; } } else { // it's data byte if(byteNumber == 1) { // is it first data byte? switch(status) { case 0xC0: // skip, do nothing case 0xD0: // skip, do nothing case 0xF0: // skip, do nothing case 0xF1: // skip, do nothing case 0xF3: // skip, do nothing break; default: byteNumber = 2; // second byte will follow byteOne = rxByte; // save first byte } } else { byteNumber = 1; // next byte will be first byte switch(status) { case 0x90: // note on if(byteOne) { // handle only when >0, 0 is used as note off handleNoteOn(channel, byteOne, rxByte); break; } case 0x80: // note off handleNoteOff(channel, byteOne, rxByte); break; case 0xB0: // controller handleControlChange(channel, byteOne, rxByte); break; } } } } zborgerd 1 Quote Link to post Share on other sites
RobG 1,892 Posted October 6, 2011 Author Share Posted October 6, 2011 After redesigning my board over and over, I have finally settled on the final version. It has 16 channels, uses 28pin 2553, has zero-crossing detector, MIDI, audio spectrum analyzer chip, and a configuration header. This is the controller board only, the power board is next. zborgerd and zeke 2 Quote Link to post Share on other sites
RobG 1,892 Posted October 12, 2011 Author Share Posted October 12, 2011 I am finally done with the power board design (unless I come up with new ideas yet again.) 8 channels, can be expanded to 16 or more ( 8 or 16 with a single Control Board or 8+ using serial in/out.) Can be used with inductive loads. The board is 4" x 4" (created using free version of Eagle ) zborgerd and zeke 2 Quote Link to post Share on other sites
timotet 44 Posted October 20, 2011 Share Posted October 20, 2011 Hey Rob did you see this: http://www.buildlounge.com/2011/10/07/b ... er-cutter/ I think your project qualifies Quote Link to post Share on other sites
RobG 1,892 Posted October 20, 2011 Author Share Posted October 20, 2011 Cool! I am in. It would help me cut those panelized boards Thanks for the info. Quote Link to post Share on other sites
RobG 1,892 Posted December 10, 2011 Author Share Posted December 10, 2011 Boards are finally here, passed the smoke test. Will do some serial mode tests before mounting the control board. Now I need to find a nice box for it, probably some junction box. Optional 595 mounted on the bottom, used for serial mode. And here it is with the control board dacoffey and zeke 2 Quote Link to post Share on other sites
zeke 693 Posted December 11, 2011 Share Posted December 11, 2011 Who made your pcb's? Seeed? What did they charge you fo your 4" board? Quote Link to post Share on other sites
RobG 1,892 Posted December 11, 2011 Author Share Posted December 11, 2011 Yes, those were made by Seeed. $24.90 for 10 boards 10cm x 10cm, 1.6mm, registered Air Parcel $8.10, total: $33.00 Shipping prices went up, shipping for 5cm x 5cm boards is now $4.10. Also, they do not make 12 anymore, my last 3 orders all had 10 boards BTW, do you know any fabs that can make custom flex strips? Passed serial tests zeke, cubeberg and bluehash 3 Quote Link to post Share on other sites
bluehash 1,581 Posted October 10, 2012 Share Posted October 10, 2012 Hey Rob, I was just looking around and 4 sets of this project( 4 control + 4 Power boards). Can you put the schematics here for me to upload. Thanks! Quote Link to post Share on other sites
RobG 1,892 Posted October 10, 2012 Author Share Posted October 10, 2012 Wow, it's that time of the year again? Looks like I have to finish my LED light dimmer, it's been almost 10 months. Important, heat sinks have to be at least 2mm above the board! Here they are. 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.