// ***************************************************************************** // Fred's LegoLight software // The majority of this code is from: // Code for application report SLAA283 - "Ultra-low Power Motion Detection using the MSP430F2013" // PWM info from http://www.msp430launchpad.com/2010/07/timers-and-clocks-and-pwm-oh-my.html // ****************************************************************************** #include #define PIR_THRESHOLD 50 // Threshold for motion #define LIGHT_ON_TIME 60 // x about .5s = 30s #define FADE_STEP 50 // P1 #define LED_OUT BIT0 // Bit location for LED #define LIGHT_OUT BIT6 // P2 #define SENSOR_PWR BIT7 // Bit location for power to sensor static unsigned int result_old = 0; // Storage for last PIR conversion static int lightTimer = 0; static volatile int dutyCycle = 0; void main(void) { // Use WD to wait about 10s WDTCTL = WDTPW+WDTTMSEL+WDTCNTCL+WDTSSEL; // ACLK/32768, int timer: ~10s BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz DCOCTL = CALDCO_1MHZ; BCSCTL1 |= DIVA_2; // ACLK = VLO/4 BCSCTL3 |= LFXT1S_2; P1DIR = LED_OUT | LIGHT_OUT; P1OUT = LED_OUT; P1SEL = LIGHT_OUT; // TA1 P2OUT = SENSOR_PWR; P2DIR = SENSOR_PWR; P2SEL = 0x00; // Let PIR settle for 10s while(!(IFG1 & WDTIFG)); P1OUT &= ~LED_OUT; // Set up PWM using Timer A CCR0 = 1000; // PWM Period CCTL1 = OUTMOD_7; // CCR1 reset/set CCR1 = 0; // Initial CCR1 PWM duty cycle TACTL = TASSEL_2 + MC_1; // SMCLK, up mode CCR1 = dutyCycle; // Reconfig WDT+ for normal operation: interval of ~341msec WDTCTL = WDTPW+WDTTMSEL+WDTCNTCL+WDTSSEL+WDTIS1;// ACLK/512, int timer: 341msec BCSCTL1 |= DIVA_3; // ACLK = VLO/8 IE1 |= WDTIE; // Enable WDT interrupt SD16CTL = SD16VMIDON + SD16REFON + SD16SSEL_1; // 1.2V ref, SMCLK SD16AE = SD16AE1 | SD16AE2; // P1.1 & P1.2: A4+/- SD16_A inputs SD16CTL &= ~SD16VMIDON; // VMID off: used to settle ref cap _BIS_SR(LPM0_bits + GIE); } void adcStartReadPIR(void) { SD16INCTL0 = SD16GAIN_4 + SD16INCH_4; // PGA = 4x, Diff inputs A4- & A4+ SD16CCTL0 = SD16SNGL + SD16IE; // Single conversion, 256OSR, Int enable SD16CTL |= SD16REFON; // If no, turn on SD16_A ref SD16CCTL0 |= SD16SC; // Set bit to start new conversion } #pragma vector = SD16_VECTOR __interrupt void SD16ISR(void) { //checkPIR(); // See if we've had movement volatile unsigned int result_new; volatile unsigned int change; SD16CTL &= ~SD16REFON; // Turn off SD16_A ref result_new = SD16MEM0; // Save result (clears IFG) if (result_new > result_old) // Get difference between samples change = result_new - result_old; else change = result_old - result_new; result_old = SD16MEM0; // Save last conversion // If motion detected... if (change > PIR_THRESHOLD) { // LED indicator P1OUT |= LED_OUT; // Reset counter lightTimer = LIGHT_ON_TIME; } else { P1OUT &= ~LED_OUT; } __bis_SR_register_on_exit(LPM0_bits); } // Watchdog Timer interrupt service routine #pragma vector=WDT_VECTOR __interrupt void watchdog_timer(void) { // Light on? if (lightTimer > 0) { // Fade up Light if (dutyCycle < 1000) { dutyCycle += FADE_STEP; CCR1 = dutyCycle; } // Tick, tick, tick lightTimer--; // We need to keep SMCLK on as we're using PWM __bic_SR_register_on_exit(LPM0_bits); } else { // Fade down light is off if (dutyCycle > 0) { dutyCycle -= FADE_STEP; CCR1 = dutyCycle; } // We can save some battery using low power mode 3 __bic_SR_register_on_exit(LPM3_bits); } // Has motion already been detected? if (P1OUT & LED_OUT) P1OUT &= ~LED_OUT; // If yes, turn off LED, measure on next loop else adcStartReadPIR(); } // Port 1 interrupt service routine #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { }