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.