//****************************************************************************** // F2013 periodically measures voltage produced by the LED, then drives // the LED with a PWM signal proportional in some way to the light level // // MSP430F2013 // ----------------- // /|\| XIN|- // | | | // --|RST XOUT|- // | | // | P1.0|-->RESISTOR-->LED-->GROUND // //****************************************************************************** #include #define PIN_0 (0x01 << 0) #define PIN_1 (0x01 << 1) #define OUTPUT_PIN (PIN_0) #define A0_PIN PIN_0 // Turn reference on; note that this takes substantial power void SD16ReferenceOn () { int i; SD16CTL = SD16REFON+SD16SSEL0; // 1.2V ref, SMCLK,(don't)enable output of reference for (i = 0; i < 0x3600; i++); // Delay for 1.2V ref startup } void SD16ReferenceOff() { SD16CTL = 0; // Reference off } // // Measure the LED voltage; this is proportionate to the ambient // light level. Units are arbitrary. // int doSD16Conversion (int nAvgCycles, int refAlwaysOn) { int i; unsigned long results = 0; // Make the LED pin an input, connect to ADC P1DIR &= ~(A0_PIN); P1SEL |= (A0_PIN); P1REN &= ~(A0_PIN); // Configure the ADC SD16CTL = SD16REFON+SD16SSEL0; // 1.2V ref, SMCLK,(don't)enable output of reference SD16CCTL0 |= SD16SNGL+SD16UNI; // Single conv, Unipolar SD16INCTL0 = SD16INCH_0 + SD16GAIN_1; // Change gain here if desired if (refAlwaysOn == 0) { for (i = 0; i < 0x3600; i++); // Delay for 1.2V ref startup } for (i = 0; i < nAvgCycles; i++) { SD16CCTL0 |= SD16SC; // Set bit to start conversion while ((SD16CCTL0 & SD16IFG)==0); // Poll interrupt flag for CH2 results += SD16MEM0; // Save CH2 results (clears IFG) } results /= nAvgCycles; if (refAlwaysOn == 0) { SD16CTL = 0; // Reference off } SD16AE &= ~0x01; // disable input A0. IMPORTANT! if not cleared, can't enable output on this pin. return (unsigned int)results; } // // Set hardware to so that the LED can be driven high // by changing only the port pin direction; this // prepares the hardware for the software-generated PWM // void ledInit () { P1DIR &= ~(PIN_0); P1SEL &= ~(PIN_0); P1OUT |= (PIN_0); P1REN = 0; } // If ON_WHEN_DARK is defined, the LED will get brighter as // the light level decreases. Comment the definition out // for the opposite behavior. This function returns 0 if // the LED is off in order to enable power-saving in the // main loop. #define ON_WHEN_DARK int doPWM (unsigned int nLightLevel) { #define PWM_LEVELS 64 #define DARK_OFFSET 2700 // this may need adjustment for different systems unsigned int i, j; unsigned int level; static unsigned int nMinLevel = DARK_OFFSET; static unsigned int nMaxLevel = 0; // Keep track of the minimum light level if (nLightLevel < nMinLevel) { nMinLevel = nLightLevel; } // Also keep track of the maximum level if (nLightLevel > nMaxLevel) { nMaxLevel = nLightLevel; } int nMaxLevel2 = (nMaxLevel - nMinLevel)/PWM_LEVELS; if (nMaxLevel2 <= 0) { nMaxLevel2 = 1; } if (nLightLevel < nMinLevel) { level = 0; } else { level = (nLightLevel - nMinLevel)/nMaxLevel2; } ledInit(); // Do PWM of light level for (j = 0; j < 200; j++) { for (i = 0; i < PWM_LEVELS; i++) { #ifdef ON_WHEN_DARK if (i < level) { P1DIR &= ~(PIN_0); } else { P1DIR |= PIN_0; } #else if (i < level) { P1DIR |= PIN_0; } else { P1DIR &= ~PIN_0; } #endif } } return (level < PWM_LEVELS); } void main(void) { unsigned int nLightLevel; // // Initialize th processor to use the internal // low-frequency oscillator, and set a timer for 250ms // period for the processor to go into low-power state // to save power. // BCSCTL3 |= LFXT1S_2; // Enable VLO for LFXT1 WDTCTL = WDT_ADLY_250; IE1 |= WDTIE; while (1) { // Measure the light level using the LED nLightLevel = doSD16Conversion (1, 4); // Do PWM for a few hundred ms using the latest light level if (doPWM (nLightLevel) == 0) { // If light is off, sleep to save power // Turn off reference - this will otherwise draw several hundred uA SD16ReferenceOff(); _BIS_SR(LPM3_bits + GIE); // Low power for 250ms SD16ReferenceOn(); } } } #pragma vector=WDT_VECTOR __interrupt void watchdog_timer (void) { _BIC_SR_IRQ (LPM3_bits); }