timotet 44 Posted June 7, 2014 Share Posted June 7, 2014 I built a 3d printer last year and needed a small light to illuminate the build area, this is what I came up with. I call it the ringLight. I thought about using an Adafruit Neopixel ring, but due to the finished part being for a specific size I decided to roll my own. I used a MSP430G2452 in the TSSOP 14 pin package for the micro. I have to thank every one on the forum for the wealth of info related to the WS2812 leds, and a special thanks to @@oPossum for the led driver code he posted. The code is pretty straight forward. Once powered on the msp430 loads what ever value from flash that was used from the last time power was on, then waits for a button press. If the button gets pressed it will scroll through a few pre programmed colors or animations, per press. Then that value is re-written into flash for the next time. Here is the code: // 5/10/14 // For controlling the Mandelbots ringLight WS2812 leds // Thanks to Kevin Timmerman for the ws2811_hs.asm led driver code // Written by Larry Fogg and Tim Toliver // #include "stdint.h" #include "stdlib.h" #include <msp430g2452.h> void write_ws2811_hs(uint8_t *data, unsigned length, uint8_t pinmask); //prototype for ws2811_hs.asm #define button BIT3 #define ledPin BIT7 #define numColors 1972 static const uint8_t red[12] = { 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00 }; static const uint8_t green[12] = { 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00 }; static const uint8_t blue[12] = { 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF }; static const uint8_t purple[12] = { 0, 128, 128, 0, 128, 128, 0, 128, 128, 0, 128, 128 }; static const uint8_t yellow[12] = { 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00 }; static const uint8_t orange[12] = { 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x00, 0x80, 0xFF, 0x00 }; static const uint8_t steelBlue[12] = { 130, 70, 180, 130, 70, 180, 130, 70, 180, 130, 70, 180 }; static const uint8_t pink[12] = { 20, 255, 147, 20, 255, 147, 20, 255, 147, 20, 255, 147 }; static const uint8_t aqua[12] = { 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255 }; static uint8_t z[12]; //GRB values for 4 LEDs uint8_t pressCount; //keep track of button presses uint8_t buttonPressed = 1; //start with true so stored pressCount is executed //for writing flash uint8_t * Flash_ptr = (unsigned char *) 0x1040; //location to write in flash info segment C void millisecDelay(int delay) { int i; for (i=0; i<delay; i++) _delay_cycles(10000); //roughly 1 msec. calibrate this } #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR(void) { _disable_interrupts(); pressCount++; //increment button presses buttonPressed = 1; //true millisecDelay(300); //for button debounce P1IFG &= ~button; //clear interrupt flag _enable_interrupts(); } void writeFlash(unsigned char data){ _disable_interrupts(); FCTL1 = FWKEY + ERASE; //Set Erase bit FCTL3 = FWKEY; //Clear Lock bit *Flash_ptr = 0; //Dummy write to erase Flash segment FCTL1 = FWKEY + WRT; //Set WRT bit for write operation *Flash_ptr = data; //Write value to flash FCTL1 = FWKEY; //Clear WRT bit FCTL3 = FWKEY + LOCK; //Set LOCK bit _enable_interrupts(); } void loadLED (int ledNum, uint8_t R, uint8_t G, uint8_t //load z[] for one LED { z[ledNum*3]=G; z[ledNum*3+1]=R; z[ledNum*3+2]=B; } void writeLEDs () { _disable_interrupts(); write_ws2811_hs(z, sizeof(z), ledPin); _enable_interrupts(); } void getRGB(int color, uint8_t *R, uint8_t *G, uint8_t * //this generates RGB values for 1972 rainbow colors from red to violet and back to red { float brightness = 0.3; //scale factor to dim LEDs. if they are too bright, color washes out if (color>=(numColors/2)) //adjust color for return from violet to red color=numColors-color; if (color < 199) { *R = 255; *G = 56 + color; *B = 57; } else if (color < 396) { *R = 254 - (color - 199); *G = 255; *B = 57; } else if (color < 592) { *R = 57; *G = 255; *B = 58 + (color - 396); } else if (color < 789) { *R = 57; *G = 254 - (color - 592); *B = 255; } else { *R = 58 + (color - 789); *G = 57; *B = 255; } *R*=brightness; //apply brightness modification *G*=brightness; *B*=brightness; } void allWhite(uint8_t intensity) //all LEDs stay white { int i; for (i=0; i<12;i++) z[i] = intensity; writeLEDs(); } void allBlack() //all LEDs off (black) { int i; for (i=0; i<12;i++) z[i] = 0x00; writeLEDs(); } void lightning () { uint8_t R, G, B; int color; //numeric index into color palette int flashDuration; while (!buttonPressed) { allBlack(); //clear LEDs color = rand()%numColors; if (color%7==0) //every so often, throw in a bright white flash { R=G=B=0xff; flashDuration = 10; //quicker for white flash } else { getRGB (color, &R, &G, &; //get random color flashDuration = 50; //slower for color flash. makes color more apparent } loadLED (rand()%4, R, G, ; //load color into random LED writeLEDs(); //flash the LED millisecDelay(flashDuration); allBlack(); //clear LEDs millisecDelay(rand()%500); //wait for next flash } } void drawSnake (int headLoc) //draw snake starting at headLoc { int shoulderLoc, torsoLoc, tailLoc; uint8_t R, G, B; //color of head getRGB (rand()%numColors, &R, &G, &; shoulderLoc = (headLoc+1)%4; //find correct array locations for body parts. keep within 0-3 range torsoLoc = (headLoc+2)%4; tailLoc = (headLoc+3)%4; loadLED (headLoc, R, G, ; //random color head loadLED (shoulderLoc, 0, 0, 0); loadLED (torsoLoc, 0, 0, 0); loadLED (tailLoc, 0, 0, 0); //black tail writeLEDs(); //draw the snake } void chaseSnakeTail () //white head, black tail. Snake goes in circles { int headLocation=0; //which LED is the head (white) int crawlSpeed; //delay between snake moves int slowSpeed=150; int fastSpeed= 10; int CWdirection = 1; //start with clockwise direction = true while (!buttonPressed) { for (crawlSpeed=slowSpeed; crawlSpeed>fastSpeed; crawlSpeed-=2) //speed up tail chasing { drawSnake (headLocation); millisecDelay(crawlSpeed); if (CWdirection) headLocation +=3; else headLocation++; headLocation%=4; //keep LED in range (0-3) if (buttonPressed) break; } CWdirection = !CWdirection; //reverse direction for next cycle for (crawlSpeed=fastSpeed; crawlSpeed<slowSpeed; crawlSpeed+=2) //slow down tail chasing { drawSnake (headLocation); millisecDelay(crawlSpeed); if (CWdirection) headLocation +=3; else headLocation++; headLocation%=4; //keep LED in range (0-3) if (buttonPressed) break; } } } void allColors() //display rainbow color progression, each LED out of phase with the others { uint8_t R, G, B; int color; int ledNum; //0-3 while (!buttonPressed) for (color=0; color<numColors; color++) //progress from red to violet and back to red { for (ledNum=0; ledNum<4; ledNum++) { getRGB ((color+ledNum*200)%numColors, &R, &G, &; //offset LED colors by 200 from neighbors loadLED (ledNum, R, G, ; } writeLEDs(); millisecDelay(4); if (buttonPressed) //allow exit from function break; } } void writeLeds(const uint8_t color[12]) { unsigned j; for (j = 0; j < 12; j++) { z[j] = color[j]; write_ws2811_hs(z, sizeof(z), ledPin); } } void main(void) { WDTCTL = WDTPW + WDTHOLD; // No watchdog reset DCOCTL = 0; BCSCTL1 = CALBC1_12MHZ; // Run at 12 MHz DCOCTL = CALDCO_12MHZ; //P1SEL &= ~ledPin | button; P1DIR |= ledPin; // ledPin is an output P1OUT = 0; // set port 1 low P1DIR &= ~button; // button is an input P1REN |= button; // pull down on button P1OUT &= ~button; // set pull down P1IES &= ~button; // Interrupt Edge Select - 0: trigger on rising edge, 1: trigger on falling edge P1IFG &= ~button; // interrupt flag for p1.3 is off P1IE |= button; // enable interrupt FCTL2 = FWKEY + FSSEL_1 + FN3; // MCLK/32 for Flash Timing Generator 12mhz/32 = 375hz // these both work has to be between 257-476 hz //FCTL2 = FWKEY + FSSEL_1 + 0x1A; // MCLK/27 for Flash Timing Generator 12mhz/27 = 444hz pressCount = *Flash_ptr; // load value written in flash _enable_interrupts(); while (1) { if (buttonPressed) { buttonPressed = 0; //reset switch (pressCount) { case 0: //bright white allWhite(0x80); break; case 1: //dim white allWhite(0x40); break; case 2: lightning(); break; case 3: allColors(); break; case 4: chaseSnakeTail(); break; case 5: writeLeds(red); break; case 6: writeLeds(blue); break; case 7: writeLeds(green); break; case 8: writeLeds(purple); break; case 9: writeLeds(yellow); break; case 10: writeLeds(orange); break; case 11: writeLeds(steelBlue); break; case 12: writeLeds(pink); break; case 13: writeLeds(aqua); break; default: pressCount = 0; //default to 0 buttonPressed = 1; //force execution of case 0 break; } writeFlash(pressCount); // write to flash for poweroff } } } I didn't use any kind of level shifting for the leds. It seems alot of folks on here had no trouble getting the leds to work with out it, and I didn't read about the level shifting until after I sent off for the boards. So I got lucky on that one. For powering the the board I wanted to use the existing 12volt supply for the printer, so I put a couple of LDO's on board for that. I of course used TI parts that I sampled from TI. (as a matter of fact I sampled the msp430 too thanks TI!!!) For the 3.6 volt side I used the TPS70936 and for the 5 volt side I used the LP2989. Both of the LDO's are able according to the data sheets to handle a 12 volt input.The 3.6 volt side worked out as expected but I must have done something wrong with the LP2989. The data sheet says the part will provide up to 500mA continuously, the WS2812 at full brightness pulls 60mA. I put 4 leds on the board so thats 240mA if they are all full blast. When I have all the leds on full the LDO gets HOT! As far as the layout goes I copied the shematic from the data sheet, and used the recommended parts. This is from the datasheet: This is from my schematic: To get around a full redesign I don't push them that hard, and it works. It does bug me though, so if anyone has any insight or suggestions let me know. I have one more pcb I can hack on and see if I can make it work better. Besides that I am happy with the design and it fits the machine perfectly. (Just like it supposed to!) It's nice to see what I am printing. Here are a couple more photos , and some video. jsolarski and bluehash 2 Quote Link to post Share on other sites
bluehash 1,581 Posted June 8, 2014 Share Posted June 8, 2014 I like it! @@timotet Quote Link to post Share on other sites
oPossum 1,083 Posted June 8, 2014 Share Posted June 8, 2014 For powering the the board I wanted to use the existing 12volt supply for the printer, so I put a couple of LDO's on board for that. I of course used TI parts that I sampled from TI. (as a matter of fact I sampled the msp430 too thanks TI!!!) For the 3.6 volt side I used the TPS70936 and for the 5 volt side I used the LP2989. Both of the LDO's are able according to the data sheets to handle a 12 volt input.The 3.6 volt side worked out as expected but I must have done something wrong with the LP2989. The data sheet says the part will provide up to 500mA continuously, the WS2812 at full brightness pulls 60mA. I put 4 leds on the board so thats 240mA if they are all full blast. When I have all the leds on full the LDO gets HOT! As far as the layout goes I copied the shematic from the data sheet, and used the recommended parts. 12V - 5V = 7V 7V * 0.5A = 3.5W So you need a heatsink that will get rid of that 3.5 watts with acceptable temperature rise. It's in a SOIC package, so that's not really possible. So... use a TO-220 package regulator with a heatsink or a switching regulator. timotet and bluehash 2 Quote Link to post Share on other sites
greeeg 460 Posted June 8, 2014 Share Posted June 8, 2014 12V - 5V = 7V 7V * 0.5A = 3.5W So you need a heatsink that will get rid of that 3.5 watts with acceptable temperature rise. It's in a SOIC package, so that's not really possible. Cool project, these LEDs are so versatile Another option, is to place a high wattage resistor in series before your LDO. This could be done now, the resistor could be mounted on a small heatsink somewhere in your printer, and the then a cable can run from that to the LEDs. R = V/I = 5/0.5 = 10R P = 2.5W Then the LDO's power dissipation, V_drop = 7 - 5 = 2. P = 2 * 0.5 = 1 W. That is still high, but now a 3x improvement. timotet and oPossum 2 Quote Link to post Share on other sites
oPossum 1,083 Posted June 8, 2014 Share Posted June 8, 2014 Ballast resistor is a good idea. An LM7806 (yes there is such a thing) would also work. The LP2989 would be happy with 6V in because it is a LDO. Quote Link to post Share on other sites
timotet 44 Posted June 8, 2014 Author Share Posted June 8, 2014 Ahhh! the old power equation kicking my butt!!! Thanks for the suggestions guys, just what I was looking for. 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.