Jump to content
43oh

Christmas PCB - 2014 Edition


Recommended Posts

So last year I made a Christmas Ornament - figured I'd do something again this year.  It's an MSP430 powered Christmas tree!  It uses 2 PCBs that slide together.  There is a button and a motion sensor (cheap spring-based motion sensor).  It uses 12 leds - broken into two sets of 6 that are charlieplexed (so 6 pins total used).  

 

I managed to score some cheap MSP430G2152's off of Newark for $0.50 each (although the $5 shipping cut into it a bit), but the 10 trees cost well over $100 in total (including $25 shipping to get them here before Christmas).  

 

For those interested, gerbers and eagle files are attached.  I'll post some code as well - it's just very basic right now.

 

post-1690-0-04670500-1417397097_thumb.pngpost-1690-0-68731500-1417397102_thumb.pngpost-1690-0-16969200-1417397236_thumb.png

 

 

Quick video (might put up a nicer one later)

 

 

It turned out so great - I actually got my wife to solder one!

tree_v1_part2.zip

tree_batt_v1.zip

christmas pcb eagle.zip

Link to post
Share on other sites

And my code so far:
 

#include <msp430.h> 

/*
 * main.c
 */
  void setLED(char led);

int main(void) {
    WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer

	//reset all pin config
	P1DIR = 0x00;
	P1OUT = 0x00;
	P1IE = 0x00;
	P2DIR = 0x00;
	P2SEL2 = 0x00;
	P2SEL = 0x00;

    //Clock setup
    BCSCTL2 = SELM_0 | DIVM_0 | DIVS_0;

    if (CALBC1_1MHZ != 0xFF) {
        /* Follow recommended flow. First, clear all DCOx and MODx bits. Then
         * apply new RSELx values. Finally, apply new DCOx and MODx bit values.
         */
        DCOCTL = 0x00;
        BCSCTL1 = CALBC1_1MHZ;      /* Set DCO to 1MHz */
        DCOCTL = CALDCO_1MHZ;
    }

    BCSCTL1 |= XT2OFF | DIVA_0;

    BCSCTL3 = XT2S_0 | LFXT1S_2 | XCAP_1;

    //set up timer @ 250ms interval
    /*
     * TA0CCTL0, Capture/Compare Control Register 0
     *
     * CM_0 -- No Capture
     * CCIS_0 -- CCIxA
     * ~SCS -- Asynchronous Capture
     * ~SCCI -- Latched capture signal (read)
     * ~CAP -- Compare mode
     * OUTMOD_0 -- PWM output mode: 0 - OUT bit value
     *
     * Note: ~<BIT> indicates that <BIT> has value zero
     */
    TA0CCTL0 = CM_0 | CCIS_0 | OUTMOD_0 | CCIE;

    /* TA0CCR0, Timer_A Capture/Compare Register 0 */
    TA0CCR0 = 2999;

    /*
     * TA0CTL, Timer_A3 Control Register
     *
     * TASSEL_1 -- ACLK
     * ID_0 -- Divider - /1
     * MC_1 -- Up Mode
     */
    TA0CTL = TASSEL_1 | ID_0 | MC_1;

    //configure pin for motion interrupt
    P1IE |= BIT7;
    P1IES |= BIT7; //high to low
    P1REN |= BIT7;
    P1OUT |= BIT7; //pull-up
    P1IFG = 0x00;;


    //enable interrupts
    __bis_SR_register(GIE);

	while(1)
	{
	    LPM3; //ACLK remains active - only one we're using
	}
	
}


void setLED(char led)
{
    //set led pins all in high-z (input)
    P1DIR &= ~(0x7F);
    //set all as low so that we only have to set high pins
    P1OUT &= ~(0x7F);
    switch(led)
    {
        case 0:
            //1.0 in, 1.1 out
            P1OUT |= BIT1;
            P1DIR |= BIT0|BIT1;
            break;
        case 1:
            P1OUT |= BIT2;
            P1DIR |= BIT1|BIT2;
            break;
        case 2:
            P1OUT |= BIT0;
            P1DIR |= BIT0 | BIT1;
            break;
        case 3:
            P1OUT |= BIT1;
            P1DIR |= BIT1|BIT2;
            break;
        case 4:
            P1OUT |= BIT2;
            P1DIR |= BIT2|BIT0;
            break;
        case 5:
            P1OUT |= BIT0;
            P1DIR |= BIT0|BIT2;
            break;
        case 6:
            P1OUT |= BIT4;
            P1DIR |= BIT3|BIT4;
            break;
        case 7:
            P1OUT |= BIT5;
            P1DIR |= BIT4|BIT5;
            break;
        case 8:
            P1OUT |= BIT3;
            P1DIR |= BIT3|BIT4;
            break;
        case 9:
            P1OUT |= BIT4;
            P1DIR |= BIT4|BIT5;
            break;
        case 10:
            P1OUT |= BIT5;
            P1DIR |= BIT3|BIT5;
            break;
        case 11:
            P1OUT |= BIT3;
            P1DIR |= BIT3|BIT5;
            break;
        case 12:
            P1OUT |= BIT6;
            P1DIR |= BIT6;
            break;

    }
}

volatile char i, rounds;

/*
 *  ======== Timer0_A3 Interrupt Service Routine ========
 */
#pragma vector=TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR_HOOK(void)
{
    //static char i, rounds;
	if(rounds <= 4)
	{
		setLED(i);
		i++;
	}


    if(i > 12)
    {
        i = 0;
        rounds++;
        if(rounds > 4)
        {
            //rounds = 0;
            setLED(13);//turns all off
            //LPM4; //drop into low power until something happens
        }
    }

    //once we implement fuller code - enter LPM4 to disable this timer
}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
    P1IFG = 0x00;
    rounds = 0;
    //LPM4_EXIT;  //wake CPU which will drop us into lpm3
}


Link to post
Share on other sites

@@roadrunner84 - It's my first time doing charlieplexing - I read that for LEDs with different voltage drops, it was necessary to separate them into different arrays - part of the reason why I broke them out.  However - I used the same value resistors all around, so I'm not sure if it would have made a difference or if it was necessary.  If I do these next year though - I may go with a smaller pin count because of chip price.  We'll see :)

Link to post
Share on other sites

Thanks @@roadrunner84 - I'll have to try that out next time.

 

BTW - I ran into an odd issue that I'm going to have to do some more testing on.  Shouldn't pin interrupts be available on LPM4?  I need to do some testing, but once I dropped into LPM4, my PORT1_VECTOR never seemed to fire...

Link to post
Share on other sites

Yes, LPM4 should allow for any interrupt that does not require an internal clock source (so in theory even timers could fire, given that they use a separate external clock).

Make sure to NOT enter any LPM when inside an ISR, your interrupt vector will go haywire, since the GIE is disabled when entering the ISR.

Link to post
Share on other sites

Yes, LPM4 should allow for any interrupt that does not require an internal clock source (so in theory even timers could fire, given that they use a separate external clock).

Make sure to NOT enter any LPM when inside an ISR, your interrupt vector will go haywire, since the GIE is disabled when entering the ISR.

Ah!  That's my problem.  I was trying to change power states on interrupt.  I believe there are some ON_EXIT options available if I remember correctly.  I'll have to take a look at that or wake to change states.

Link to post
Share on other sites

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...