This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/MSP430G2553: ADC with internal Vref=2.5V not working when using WDT as timer for waking up from LPM3

Part Number: MSP430G2553

Tool/software: Code Composer Studio

Hi everyone, 

I am coding a program where the MSP430 is on LPM3 most of the time, and it is waked up by the WDT every 4 secs using the WDT interruption. 

From the ISR for WDT goes to LMP0 and then it must read an Analog input and then go to LMP3 again. The program is working when I am using ADC with VCC as reference, with the following configuration:

ADC10CTL0 = ADC10SHT_2 + ADC10ON + ADC10IE; // ADC10ON, interrupt enabled
ADC10CTL1 = INCH_7; // input A7
ADC10AE0 |= 0x80; // PA.1.7 ADC option select

Then in the infinite while:

ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
__bis_SR_register(CPUOFF + GIE); // LPM0, ADC10_ISR will force exit
ADC_result=ADC10MEM;

My problem is that now I want to use the ADC but with the internal Vref=2.5V an it is not working (it seems that WDT is not working and it not waking up the system anymore). Is there any incompatibility between the use of the WDT as timer to wake the system and the use of the internal voltage generator for the ADC internal reference?

Bes

Below it is the code that I am trying:

#include <msp430.h>

#define CLOCK           4                  //wake up interval in seconds-1


 int wake_up_sec=CLOCK;                // initially wake_up with day clock.



 int main(void)
{
  BCSCTL1 |= DIVA_0;                        // ACLK/1
  BCSCTL3 |= XCAP_3;                       //12.5pF cap- setting for 32768Hz crystal
  WDTCTL = WDT_ADLY_1000;                   // WDT 1s/4 interval timer
  IE1 |= WDTIE;                             // Enable WDT interrupt
  ADC10CTL0 = SREF_1 + ADC10SHT_2 + REFON + REF2_5V+ ADC10ON + ADC10IE; // ADC10ON, Vref ON at 2.5V, interrupt enabled
   __enable_interrupt();                     // Enable interrupts.
    TACCR0 = 30;                              // Delay to allow Ref to settle
    TACCTL0 |= CCIE;                          // Compare-mode interrupt.
    TACTL = TASSEL_2 | MC_1;                  // TACLK = SMCLK, Up mode.
    LPM0;                                     // Wait for delay.
    TACCTL0 &= ~CCIE;                         // Disable timer Interrupt
    __disable_interrupt();
  ADC10CTL1 = INCH_7;                       // input A7
  ADC10AE0 |= 0x80;                         // PA.1.7 ADC option select
  P1DIR = 0xFB;                             // All P1.x outputs except P1.2 input
  P1OUT = 0;                                // All P1.x reset
  P1REN = 1;                                // All P1.x resistors pull U/D enable
  P2DIR = 0xF7;                             // All P2.x outputs except P2.3 (DAY SIGNAL) as input
  P2OUT = 0;                                // All P2.x reset
  P2REN = 1;                                // All P2.x resistors pull U/D enable
  P1IE=0;                                   // All P1 port interruptions disable

  while(1)
  {
    
    if (P2IN==0x08)                                  // if DAY SIGNAL is ON
        {
         //do nothing
        }
    else
        {
        //Read ADC
        ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit

if (ADC10MEM < 0x88) // 
P1OUT &= ~0x01; // Clear P1.0 LED off
else
P1OUT |= 0x01; // Set P1.0 LED on

        }
  __bis_SR_register(LPM3_bits + GIE);     // Enter LPM3

  }
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer (void)
{
    static int c=0;
    if (c<wake_up_sec)
    {
        c++;
    }
    else
    {
    __bic_SR_register_on_exit(LPM3_bits);   // Clear LPM3 bits from 0(SR)
    c=0;
    }
}

// ADC10 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
  __bic_SR_register_on_exit(CPUOFF);        // Clear CPUOFF bit from 0(SR)
}

 

  • Hi Bes,

    Are you able to set breakpoints in your code to see the furthest it executes before it no longer responds? 

    I'm still looking into what might be preventing your code from waking up from low power mode in the main while loop. It appears one of the issues is that after the ADC measurement, the code enters LPM3 which disables oscillators, including the one that the watch dog timer is using. Try going into LPM0 instead by using the following line of code after the ADC measurement:

      __bis_SR_register(LPM0_bits + GIE);     // Enter LPM0

    As well as updating the line of code that exits low power mode in the WDT interrupt:

        __bic_SR_register_on_exit(LPM0_bits);   // Clear LPM0 bits from 0(SR)

    Regards,

    Ryan

  • Hi Ryan,

    Thanks for your reply. I cannot use de debugger because the system is entering in lpm3 almost all the time, but I use pin outs as flags to check by hardware if the execution is stuck. Using the ADC with the internal voltage generator is totally stuck, no the first line of the while is executed and showing a consumption higher than I experienced in lpm3.

    If ADC is set with reference to vcc, the code works well going to lpm3 and waking up without problems, I am using the ackl with an external oscillator, and the wdt is working.

  • Hi everyone,

    I think I found the origin of the problem. Table 2.2. pag 39 of the "MSP430x2xx Family User Guide". In LPM3 the DC generator is off.
    This is because I am able to use the ADC with the internal DC generator. Going to LPM2 keeps the DC generator and it seems that it is working, although the consumption is 10 times more....(from 20uA to 200uA) at least in my application .
  • Hi Bes,

    Is this exactly the code you are running (except of Timer0_A0 ISR which must have been omitted, but I guess it looks exactly as the ADC10 ISR)? I'm asking because there are a few points in it that may lead to undefined behavior:

    1. WDT is clocked from ACLK before the 32K crystal sourcing ACLK actually starts up (this may take a few hundred milliseconds). See section 5.2.7.1 of the 2xx family guide (SLAU144).

    2. Pull-down resistors are enabled on pins P1.1 and P2.1 only (BIT0), contrary to the comments. PxREN = 0xFF would enable all.

    3. Port 3 is left floating. Even if your MSP has a 20-pin package, the port is still present on the silicon chip. Pull-down resistors should be enabled as pointed out under pinout description in the 2553 datasheet (SLAS735).

    The above issues may be worth fixing before investigating further. You can also try sourcing ACLK from VLO (BCSCTL3 |= LFXT1S_2) instead of the 32K crystal.

    If low power is important, you may consider not leaving the 2.5 V reference enabled (REFON bit) all the time (the buffer switches off automatically between conversions, but the reference remains on).

    And finally a note on Timer usage: you can stop it (MC_0) when the counting is done and save some power. Anyway, while timer + low-power mode is generally much better than a delay loop, using the timer to count as little as 30 cycles is an overkill -- all the timer setup and servicing the timer interrupt takes comparable time.

    Regards,

    Michal

  • Hi Michal, 

    Thanks for your reply. You are right the Timer0_A0 ISR was missed, but I have it in the code, just in the ADC10_02 example. 

    About 1. make a delay between before clocking WDT from ACLK. Shoud it be something like that?

     BCSCTL3 |= XCAP_3;                       //12.5pF cap- setting for 32768Hz crystal
      __enable_interrupt();                     // Enable interrupts.
        TACCR0 = 30;                              // Delay to allow ACLK to settle
        TACCTL0 |= CCIE;                          // Compare-mode interrupt.
        TACTL = TASSEL_2 | MC_1;                  // TACLK = SMCLK, Up mode.
        LPM0;                                     // Wait for delay.
        TACCTL0 &= ~CCIE;                         // Disable timer Interrupt
        __disable_interrupt();
        WDTCTL = WDT_ADLY_1000;                   // WDT 1s/4 interval timer

    Sorry in section 5.2.7.1 the example is done in Assembler which it is not straight forward to me. 

    About 2 and 3. Good advice, Done it, thanks:)

    I prefer to clock from 32k crystal, I need to optimize consumption not footprint space or cost. I have experience that LMP3 with a ACLK from 32k crystal is much more precise that VLO and consumption is very low. Since in LPM3 the internal DC generator is OFF, I managed in the application to not use the ADC when the signal to measure is below VCC, so I can use the VCC as reference for the ADC and save more power.  Certainly your suggestion about keeping REFON disable can work very well but from LPM2 to upper modes. 

    About your Timer usage advice, I do not really caught it Which timer do you refer with MC_0?. I am using the WDT as first timer, the while is done and then the system goes to LPM3, waked up by WDT, make while (read ADC, take actions) and go to sleep again.  Maybe for doing some actions that involve other interruptions, like make a PWM during certain time in a pin using a timer, I was thinking to Stop the WDT, make the PWM pulses, and when finished activate WDT again. Is this the right way to do that?

    Anyway thanks again for your advice.

    Best regards  

  • Hi Bes,

    Regarding oscillator startup, the C code might look as below:

    int StartupOsc(void)
    {
      int i;
    
      for (i = 100; i != 0; --i)
      {
        IFG1 &= ~OFIFG;           // clear oscillator fault flag
        __delay_cycles(10000);    // wait ~10 milliseconds (at ~1 MHz)
        if ((IFG1 & OFIFG) == 0)  // fault flag not set again?
          return 0;               // ok, oscillator is working
      }
      return -1;                  // give up after 100 times (error code)
    }
    
    

    WDT should be kept stopped (WDTCTL = WDTPW | WDTHOLD | WDTTMSEL | WDTCNTCL or similar) and not switched to ACLK until the above function returns 0.

    Green Power said:
    I prefer to clock from 32k crystal, I need to optimize consumption not footprint space or cost. I have experience that LMP3 with a ACLK from 32k crystal is much more precise that VLO and consumption is very low. Since in LPM3 the internal DC generator is OFF, I managed in the application to not use the ADC when the signal to measure is below VCC, so I can use the VCC as reference for the ADC and save more power.  Certainly your suggestion about keeping REFON disable can work very well but from LPM2 to upper modes.

    I suggested to try VLO only temporarily, just to see if this changes anything. You are observing an unusual behavior and have to identify the factor which is causing it.

    Green Power said:
    Since in LPM3 the internal DC generator is OFF, I managed in the application to not use the ADC when the signal to measure is below VCC, so I can use the VCC as reference for the ADC and save more power.

    I don't understand... Any signal at ADC input (and at any pin) must be below VCC (and above VSS) whether you measure it or not, otherwise you can permanently damage the chip.

    Anyway, the internal dc generator is related to DCO (digitally controlled oscillator) and it does not seem to have anything to do with ADC10's REF.

    Green Power said:
    About your Timer usage advice, I do not really caught it Which timer do you refer with MC_0?

    I meant stopping Timer A after it counted to 30 and caused the interrupt (TACTL = TASSEL_2 | MC_0 or equivalent code to switch from Up to Stop mode). Otherwise it is still counting (and consuming some minor power) when SMCLK is enabled.

    Green Power said:
    Maybe for doing some actions that involve other interruptions, like make a PWM during certain time in a pin using a timer, I was thinking to Stop the WDT, make the PWM pulses, and when finished activate WDT again. Is this the right way to do that?

    Yes, this sounds like a reasonable approach.

    Best regards,

    Michal

**Attention** This is a public forum