I have a simple circuit where an MSP430G2152 is used to control a transistor. The intent is to briefly cut the power to a device once per day (the reason is a long story so I won't get into it). I'm trying to get as close to once per day as possible using a minimum of parts to keep the cost low. So, no external crystal, RTC, etc.).
I built three replicates of the same circuit, and programmed them with the same program in order to look at the variation between the three.
My test code is as follows:
#include <msp430.h> //***************************************************************************** // GLOBAL VARIABLES * //***************************************************************************** #define RED_BIT BIT7 #define GREEN_BIT BIT6 #define ENABLE_OUTPUT_BIT BIT0 #define THRESHOLD_0M 6646 // Estimated total counts for a 24 hour period #define RESET_DURATION 95541 // ~2 sec off time long CYC_COUNTER = 0; //***************************************************************************** // Set up device for specific clock rate, power configuration, and outputs * // before enabling the watchdog timer. * //***************************************************************************** int main(void) { // Stop the watchdog timer WDTCTL = WDTPW | WDTHOLD; // Setup the auxiliary clock (ACLK) // - DIVA_2 = /4 (~10.9s) BCSCTL1 = DIVA_2 | BCSCTL1; // Setup the master clock (MCLK): // - Run from DCOCLK (SELM_0) // - SMCLK from VLOCLK (SELS) BCSCTL2 = BCSCTL2 | SELM_0 | SELS; // - Set to use 12kHz VLOCLK since XTS = 0 (LFXT1S_2) BCSCTL3 = LFXT1S_2; // Setup the IO functions for the LED and output enable line P2SEL = P2SEL & (~RED_BIT) & (~GREEN_BIT); // Select IO function for P2.6 and P2.7 (LEDs) P1SEL = P1SEL & (~ENABLE_OUTPUT_BIT); // Select IO function for P1.0 (Power control) P2OUT = P2OUT | GREEN_BIT; // Pre-configure the output to turn on the GREEN LED P2DIR = P2DIR | RED_BIT | GREEN_BIT; // Set Port 2 direction to output. P1OUT = P1OUT | ENABLE_OUTPUT_BIT; // Turn on output enable P1DIR = P1DIR | ENABLE_OUTPUT_BIT; // Set Port 1 direction to output. // Turn on the GREEN LED and turn off the RED LED P2OUT = P2OUT & (~RED_BIT) | GREEN_BIT; // Configure watchdog/interval timer (WDT): // - From ACLK (WDTSSEL) // - Interval timer (WDTTMSEL) // - ACLK/32768 (WDTIS0 and WDTIS1 UNSET) // - Counter cleared (WDTCNTCL) WDTCTL = WDTPW + WDTSSEL + WDTTMSEL + WDTCNTCL; // Set the interrupt configuration // - Enable WDTIFG interrupt for interval timer mode (WDTIE) IE1 = IE1 | WDTIE; // Configure power mode // - LPM3 = CPU, MCLK, SMCLK, DCO are disabled. DC generator disabled. ACLK is active. _BIS_SR(LPM3_bits + GIE); // Original: Enter LPM3 with interrupts return 0; } //***************************************************************************** // The watchdog timer controls the counter and the visual display indicating * // to the user the amount of time remaining in a power cycle. This routine * // is entered based on the WDT interrupt, disabled, the count checked and the * // indication shown to the user and/or the power cycled, and then the watchdog* // is re-enabled for another loop. This continues until power is removed. The * // counter is volatile and resets upon power-up. * //***************************************************************************** #pragma vector=WDT_VECTOR __interrupt void WatchdogTimer(void) { volatile long i = 0; volatile long j = 0; volatile int blink = 0; CYC_COUNTER++; // Reset the watchdog timer WDTCTL = WDTHOLD + WDTPW; // Enable the LEDs P2DIR = P2DIR | GREEN_BIT | RED_BIT; if (CYC_COUNTER >= THRESHOLD_0M) { // 0M (RESET EVENT) P2OUT = P2OUT & (~GREEN_BIT) | RED_BIT ; // Turn on the RED LED and turn off the GREEN LED P1OUT = P1OUT & (~ENABLE_OUTPUT_BIT); // Turn off the output. for (i = RESET_DURATION; i >= 0; i--) {} // Delay P1OUT = P1OUT | ENABLE_OUTPUT_BIT; // Turn on the output. P2OUT = P2OUT | (~GREEN_BIT) | (~RED_BIT); // Turn off all LEDs CYC_COUNTER = 0; // Reset the cycle counter }
// Turn off the LEDs P2DIR = P2DIR & (~GREEN_BIT) & (~RED_BIT); // Configure WDT to run as interval timer again, restarting the whole process WDTCTL = WDTPW + WDTSSEL + WDTCNTCL + WDTTMSEL; return; }
The results are dramatically different looking at the actual time between power-on and the output cut event (intended to be close to 24:00:00):
Board 1 - 22:33:44
Board 2 - 24:01:22 (this was the board used to determine the value of THRESHOLD_0M
Board 3 - 25:28:32
The variation seems to be directly tied to the watchdog interval timer.
My questions:
1. Is this much variation expected from controller to controller within the watchdog when used as an interval timer?
2. What can I do to limit this variation? I do not want to add more parts, so software solutions are ideal. I also cannot leave the part active all the time since power consumption is a consideration for my application, although I'm not sure that would address the underlying cause anyway.
As a test, I also ran the same code using LPM2, LPM1, and LPM0 and the results are very similar.
Thanks for reading. Any help would be appreciated.