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.

Design guideline

Other Parts Discussed in Thread: MSP430F2013

Hi

I am developing a very simple application that should give an buzzer indication and LED flash when weight goes below certain level set in software.

I have attached a my block diagram.

  1. I intend to use OPA 333 to ampilfy my weightstone bridge signals
  2. I intend to use MSP430F2013 as my controller since it has a build in 16 bit ADC.

My aim is to measure mimimum weight of 250 gms. I wanted to know if the circuit will suffice the requirements. The rating of load cell is 1.6 mv/v.

Thanks in advance

kind Regards

suchitra

 

  • SUCHITRA BHIDE said:
    I have attached a my block diagram

    Unfortunately you did not.

    Guessing from the application description there shouldn't be a problem.
    You need to amplify your weight signal so it is
    1) limited to VCC (you may not apply an analog signal greater than VCC to the MSP) and
    2) the desired threshold voltage after aplification is well in the range 0..2.5V or 0..1.5V (depending on the internal reference you're using).

    The rest depends on tolerance and calibration of the SD16 module and the other parts.

  • Hi Suchitra,

    What is the max weight that you want to measure using the R-bridge signals? Knowing the min and max weights you want to measure, would help determine what is the min voltage variation from the R-bridge that needs to be measured. And since F2013 has the SD16_A module, you could use the internal PGA (upto 32x) as the FSR is only 1.6m x 3V = 4.8mV. Also, you can use the over sampling ratio of upto 1024 to get some extra resolution.

    There is also an app note "MSP430F42x Single Chip Weigh Scale" (SLAA220) which has implemented an MSP430 based weigh scale using the SD16 module.

     

     

    Regards,

     

    Bhargavi

  • Bhargavi said:
    you can use the over sampling ratio of upto 1024 to get some extra resolution


    This is a common mistake.
    Oversampling a static signal (such as a weight) does not add a single bit to the resolution, since the average of, say, 16 times the same value is the very same value again.
    Oversampling only virtually increases resolution on changing signals (faster than required sampling frequency) to determine when in the row of (over)samples the signal has crossed the border to the next discrete value.

  • A good explanation of this can be found here: http://www.atmel.com/dyn/resources/prod_documents/doc8003.pdf

  •  Not to forget, the ADC in the F2013 is a sigma delta converter. The number of bits output by the digital filter in the SD16 is dependent on the over sampling ratio. Refer to the F2013 d/s, where you see considerably higher SINAD at PGAx=32 with OSR=256 and OSR=1024

    Regards,

    Bhargavi

     

  • eltury said:
    A good explanation of this can be found here: http://www.atmel.com/dyn/resources/prod_documents/doc8003.pdf   

     
    Indeed, this is a good article. It does, however, cover only a fraction of the whole topic.

    To sum it up for the current application (a quasi static value):
    To use oversampling you'll need to add a symmetric 'noise' signal that has at least the magnitude of 1 LSB and a frequency that equals your desired sampling frequency. This way, you can sample with a higher frequency than desired and interpolate the 'real' value by averaging the values. If you don't add your own noise, this method can be used to either reduce noise in your signal that has a higher frequency than your desired sampling frequency, or increase the number of effective bits (which might have been decreased below the theoretical limit of your ADC if the noise is too high).

    The dynamic range of the SD16 is 96dB (16 bit * 6dB). Looking at the table above, even with an oversampling of 1024 (5 bit increase), this range isn't met by the signal-to noise ratio (SNR). It just increases the number of 'usable' bits in the result to 13-14 (84dB SNR and therefore usable dynamic range). For a frequency of 100Hz (which we don't have here as the weight won't change with 100Hz).

    Anyway, this drifts a bit far from the original question.

  • eltury said:
    A good explanation of this can be found here: http://www.atmel.com/dyn/resources/prod_documents/doc8003.pdf   

     

    Just to clarify: The oversampling that the document talks about is the traditional approach where you get 1 bit of resolution for oversampling 4-times. The sigma delta converter is a 1-bit ADC and if the same traditional approach is used, then to get 12bits of resolution, the signal should be oversampled 4^11 ~ 4.2M times. The sigma delta ADCs use a technique called noise shaping which enables a gain of >6dB (each 6dB increase is equivalent to gaining one bit) for each factor of 4x oversampling (depending on the order of the sigma delta modulator). So the oversampling in the case of sigma delta converters is different from what the above document covers. Oversampling in sigma delta converters does not change the total noise power but moves it to higher frequencies which is easily filtered out by digital filter (Sinc3 filter that follows the sigma delta modulator).

    Oversampling is an inherent feature of the sigma delta converters. The min OSR (over sampling ratio) for the MSP430 SD16 converter is 32. And on the SD_16A modules, the max OSR=1024. If Fs is the sampling frequency (output data rate - output of the decimation filter) and Fm is the modulator freq (the SD16 input clock freq), then Fs = Fm/OSR. So, higher the OSR, lower is the output data rate. The OSR has to be selected such that Fs > 2x input signal bandwidth. The freq of 100Hz in the SINAD table (in the prev post) is the input signal bandwidth and only a test condition and does not imply that the values don’t apply if the signal bandwidth is closer to DC.

    The SINAD vs. OSR figure below is taken from the F2013 d/s and we can see that with increasing OSR, the SINAD/effective resolution of the SD16 is higher.

    In the case of weigh scales, we need to use sigma delta converters as we need to measure signals with FSR of the order of few mV. And making use of the internal PGA and the extended OSR settings in the SD16, we can eliminate the need for an external amplifier.

    Regards,

    Bhargavi

     

  • That's a good explanation about the differences between ADC12 and SD16.
    And it explains how you meant your initial 'gain resolution by oversampling' statement:

    Looking at your diagram, the SD16 delivers only 10 usable bits for an OSR of 32. Not very impressive for 32kHz sampling rate.

    When using OSR=1024, this increases to ~14.5 bits, at the cost of a reduced sampling frequency of 1kHz max. Which is still enough for frequencies from DC up to 500Hz. It will not, however, increase resolution beyond the 16 bits the SD16 returns. Quite the contrary, if not using oversampling, the usable bits are only a fraction of the delivered result. Much of the sample result is noise then.

    With the internal 1.2V reference (+-5% tolerance), the maximum usable resolution is ~0.05mV. Without amplifying these are less than 100 safely distinguishable steps for the original application. With a maximum gain of 32 in the PGA, this extends to ~3000 steps, quite a sufficient amount I'd say. But then, the SINAD shrinks by 24dB for a gain of 32 and we're back to 10.5 bits of usable resolution, which gives (with the gain of 32) a resolution of 0.026mV. 184 Steps. Not much better (I'm surprised, but that is what the datasheet tells). Maybe it is enough.
    If not, A carefully selected, good and fast external OpAmp will allow a gain of 100 (for maximum Vin of 0.5V) without dropping SINAD this much, allowing a far better resolution than with the internal PGA.

  • Hi All

    I tried the following code on msp430f2013. But I am not getting any stable readings from the ADC. After taking approximation of even 4096 samples  the value is not stable.

    Am I missing something.

    I guess the signal output from the strain gauge is tooo small to be used without amplificaiton. As per the strain gauge datasheet the signal would be around 100 uV for 1 kg.

    The sampled values are

    0x7ef9

    ox8123

    0x7488

    0x7f09

    0x7f8b

     

    Has any one really tried using strain gauge directly as in input to the ADC without amplification and normal RC low pass filtering.

    I am using a 0.1 uf 1 K filter at the input.

    thanks in Advance

    Kind Regards

    Suchitra

    //******************************************************************************
    //******************************************************************************
    #include  <msp430x20x3.h>
    unsigned int SampleValue,SampleNum;
    long int difference;
    static long int ADCValue, ADCOffset, LastADCValue;
    void main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
      P1DIR |= 0x01;                            // Set P1.0 to output direction
      SD16CTL = SD16REFON + SD16SSEL_1;         // 1.2V ref, SMCLK
      SD16INCTL0 = SD16INCH_1 + SD16GAIN_4 ;                  // A1+/-
      SD16CCTL0 =  SD16IE + SD16OSR_1024 + SD16DF + SD16LSBACC; // unipolar, interrupt enable
      SD16AE = SD16AE2 + SD16AE3;              // P1.1 A1+, A1-
      SD16CCTL0 |= SD16SC;                      // Set bit to start conversion
      SampleValue = 0;
      ADCOffset = 32768;
      SampleNum = 1024;
      LastADCValue = 0;
      _BIS_SR(LPM0_bits + GIE);
    }

    #pragma vector = SD16_VECTOR
    __interrupt void SD16ISR(void)
    {
     
      ADCValue = SD16MEM0 + ADCValue;
      SampleValue++;
      if (SampleValue >= SampleNum) {
     
      ADCValue = ADCValue / SampleNum;
      SampleValue = 0;
      ADCValue = ADCValue - ADCOffset;
      difference = ADCValue - LastADCValue;
      LastADCValue = ADCValue;
     
      ADCValue = 0;
     
      _NOP();
     
    }
     
    }
    //******************************************************************************

  • Hi Suchitra,

    By default SD16LSBACC bit is cleared => SD16MEMx contains the most significant 16-bits of the conversion result. In your code above, you have set the SD16LSBACC to high and you are reading the least significant 16-bits of the conversion result and with this you are definitely not going to get consistent results.

    Find attached example firmware that was developed to measure weigh scale load cell (0-3000gms being able to measure 1gm). In this case, PGA gain of 32 and additional bits from the digital filter are used. And 256 conversion results are accumulated every time. Also, reference is provided externally by GPIO pins via a R-divider and the internal reference is not used.

    // Get a 18-bit wide result from SD16 channel 0 (17 bit + sign bit)
    #define GET_RESULT_18(var)  SD16CCTL0 &= ~SD16LSBACC; \
                                var = (long)(int)SD16MEM0 << 2; \
                                SD16CCTL0 |= SD16LSBACC; \
                                var |= ((int)SD16MEM0 & 0x3C00) >> 12

    void main (void) { ....

      // SD16_A Configuration for load cell measurement
      SD16CTL = SD16SSEL_1;                  // SMCLK and External Ref
      SD16INCTL0 = SD16GAIN_32 + SD16INCH_0; // 32x gain, channel pair A0
      SD16CCTL0 = SD16DF + SD16IE + SD16OSR_1024; // OSR=1024

    }

    // SD16A ISR
    #pragma vector=SD16_VECTOR
    __interrupt void SD16ISR(void)
    {
      long int CurrentResult;
      
      switch  (__even_in_range(SD16IV, 4))
      {
      case 2:                              // SD16MEM Overflow
        break;                             // Not used
      case 4:                              // SD16MEM0 IFG
        GET_RESULT_18(CurrentResult);      // 18bits...OSR=1024; PGA=32..
          
        long_result += (long int)CurrentResult;
         if (++j > 256)
        {
            j=0;   
            P1OUT &= ~BIT0;                     
            P6OUT &= ~BRIDGE_SUPPLY;        // Power-down bridge sensor
            SD16CCTL0 &= ~SD16SC;           // Disable conversions
            Average = long_result >> 8;
            Disp_Value(2,Average);
        }   
        break;
      }
    }

    Regards,

    Bhargavi

    // Texas Instruments MSP430
    // Check two load cells - resistive full-bridge
    
    #include  <msp430x42x0.h>
    #define BRIDGE_SUPPLY       (0xC0)              // IO pins P6.6/P6.7 for
                                                    // pos. bridge rail
    
    // Get a 17-bit wide result from SD16 channel 0 (16 bit + sign bit)
    #define GET_RESULT_17(var)  SD16CCTL0 &= ~SD16LSBACC; \
                                var = (long)(int)SD16MEM0 << 1; \
                                SD16CCTL0 |= SD16LSBACC; \
                                var |= ((int)SD16MEM0 & 0x2000) >> 13
    
    // Get a 18-bit wide result from SD16 channel 0 (17 bit + sign bit)
    #define GET_RESULT_18(var)  SD16CCTL0 &= ~SD16LSBACC; \
                                var = (long)(int)SD16MEM0 << 2; \
                                SD16CCTL0 |= SD16LSBACC; \
                                var |= ((int)SD16MEM0 & 0x3C00) >> 12
    
    // Get a 19-bit wide result from SD16 channel 0 (18 bit + sign bit)
    #define GET_RESULT_19(var)  SD16CCTL0 &= ~SD16LSBACC; \
                                var = (long)(int)SD16MEM0 << 3; \
                                SD16CCTL0 |= SD16LSBACC; \
                                var |= ((int)SD16MEM0 & 0x3800) >> 11
    
    int result[64], offset;
    long int Average;
    long int long_result;
    unsigned int i,j;
    
    // Function Definitions
    extern void Disp_Value(unsigned int ShiftLeft, int Value);
    void StartNextConversion(void);
    
    void main(void)
    {
      char *pLCD = (char *)&LCDM1;
    
      //  WDTCTL = WDTPW + WDTHOLD;                     // Stop WDT
    
      WDTCTL = WDT_ADLY_1000;              // WDT 1sec, ACLK, interval timer
      IE1 |= WDTIE;                        // Enable WDT interrupt
    
      FLL_CTL0 |= XCAP18PF;                // Configure load caps
      while (FLL_CTL0 & LFOF);             // Stablize 32kHz crystal
    
      for (i = 0; i < 20; i++)             // Clear LCD memory
        *pLCD++ = 0;
    
      LCDACTL = LCDFREQ_96 + LCD4MUX + LCDON;// 4mux LCD, set LCD freq
      LCDAPCTL0 = 0x07;                    // Segs S0-S11 = outputs
      BTCTL = BTDIV + BT_fCLK2_DIV128;
    
      P1OUT = 0x00;                        // P1OUTs = 0
      P1DIR = 0xff;                        // Unused pins as outputs  
      P2OUT = 0x00;                        // P1OUTs = 0
      P2DIR = 0xff;                        // Unused pins as outputs  
      P5SEL = 0xff;                        // Set Rxx and COM pins for LCD
      
      P6OUT = 0x00;                        // P6OUTs = 0
      P6DIR = 0xff;                        // Unused pins as outputs
      P6SEL = BIT0+BIT1;                   // Select analog function P6.0/A1+ and P6.1/A1-
    
      // Power up external Vref and let settle
      P6OUT |= BRIDGE_SUPPLY;              // Power-up bridge sensor, external ref
      _BIS_SR(LPM0_bits+GIE);              // Delay for Vref settling
    
      // Add SD16_A Configuration Here
      //SD16CTL = 0;   // This is the default, not needed
      SD16CCTL0 |= SD16BUF_2+SD16OSR_1024+SD16DF+SD16SNGL;// 1024x, cont conv, enable int
    
      SD16INCTL0 = SD16GAIN_32+SD16INCH_7; // Input = A7+/-
      SD16CCTL0 |= SD16SC;                 // Get offset from ADC
      while(!(SD16CCTL0 & SD16IFG));
      offset = SD16MEM0;     
      
      SD16CCTL0 &= ~SD16SC;
      P6OUT &= ~BRIDGE_SUPPLY; 
      
      // SD16_A Configuration for load cell measurement
      SD16CTL = SD16SSEL_1;                   // SMCLK and External Ref
      SD16INCTL0 = SD16GAIN_32 + SD16INCH_0;  // 32x gain, channel pair A0
      SD16CCTL0 = SD16DF + SD16IE + SD16OSR_1024; // OSR=1024
    
      j=0;
      P1OUT = 0x0;
      while (1)
      {
        _BIS_SR(LPM0_bits+GIE);            // Enter LPM0, ints enabled 
        __no_operation();  
        // On exit
        long_result = 0x0;
        StartNextConversion();
      }
    }
    
    // SD16A ISR
    #pragma vector=SD16_VECTOR
    __interrupt void SD16ISR(void)
    {
      long int CurrentResult;
    
      //switch (SD16IV)
      switch  (__even_in_range(SD16IV, 4))
      {
      case 2:                              // SD16MEM Overflow
        break;                             // Not used
      case 4:                              // SD16MEM0 IFG
        //GET_RESULT_17(CurrentResult);      // ...OSR=1024; PGA=32......gives ~2 counts per gram.....variation in counts=4
        GET_RESULT_18(CurrentResult);      // ...OSR=1024; PGA=32......gives ~4 counts per gram.....variation in counts=6to7
        //GET_RESULT_19(CurrentResult);      // ...OSR=1024; PGA=16......gives ~4.5 counts per gram.....variation in counts=4to7
                                           // ...OSR=1024; PGA=32......gives ~8 counts per gram.....variation in counts=10to11
        //result[j] = CurrentResult-offset;       
        //CurrentResult = (int)SD16MEM0;
        //CurrentResult = CurrentResult-offset;
        //result[j] = CurrentResult;
        long_result += (long int)CurrentResult;
        //CurrentResult++;
        if (++j > 256)
        {
            j=0;    
            P1OUT &= ~BIT0;                      
            P6OUT &= ~BRIDGE_SUPPLY;        // Power-down bridge sensor
            SD16CCTL0 &= ~SD16SC;           // Disable conversions
            Average = long_result >> 8;
            Disp_Value(2,Average);
        }    
        break;
      }
    }
    
    void StartNextConversion(void)
    {
      P1OUT |= BIT0;                      
      P6OUT |= BRIDGE_SUPPLY;               // Power-up bridge sensor
      __delay_cycles(17000);                // ~12ms delay to allow voltages to settle - later use timer to put to sleep              
      SD16CCTL0 |= SD16SC;                  // Start conversion
    }
    
    // Watchdog Timer interrupt service routine
    #pragma vector=WDT_VECTOR
    __interrupt void watchdog_timer(void)
    {
      __bic_SR_register_on_exit(LPM0_bits); // Active on exit
    }

  • Hi Bhagravi

    Please consider the following code.

    Here My intension is to have sd16A work on higher frequency of MCLK. MCLK = DCO and the timer uses SMCLK = VLOCLK. I have programmed the BCSCTL registers accordingly.

    But I guess VLOCLK is not getting connected to SMCLK due to which timer interrupt doesn't occur. What is it that i am missing?

    Thanks in advance

    kind regards

    Suchitra

    //******************************************************************************
    //******************************************************************************
    #include  <msp430x20x3.h>
    unsigned int SampleValue,SampleNum;
    long int difference;
    unsigned long int ADCValue, ADCOffset, LastADCValue;
    void configureTimer(void);
    void main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
      P1DIR |= 0x01;                            // Set P1.0 to output direction
      BCSCTL1 |= RSEL3;                         // select maximum freq.
      BCSCTL2 |= SELM0 + SELS + DIVS_3;         // MCLK = DCO, SMCLK = VLOCLOCK , SMCLK div = 8
      BCSCTL3 |= LFXT1S_2;                      // VLOCLK select
      SD16CTL = SD16REFON + SD16SSEL_0;         // 1.2V ref, MCLK
      SD16INCTL0 = SD16INCH_1;                  // A1+/-
      SD16CCTL0 =  SD16UNI + SD16IE + SD16OSR_1024;            // 256OSR, unipolar, interrupt enable
      SD16AE = SD16AE2;                         // P1.1 A1+, A1- = VSS
     
      SampleValue = 0;
      ADCOffset = 74000;
      SampleNum = 512;
      LastADCValue = 0;
      configureTimer();
      _BIS_SR(LPM0_bits + GIE);
       __bis_SR_register(LPM0_bits + GIE);  
    }

    #pragma vector = SD16_VECTOR
    __interrupt void SD16ISR(void)
    {
     
      ADCValue = SD16MEM0 + ADCValue;
      SampleValue++;
      if (SampleValue >= SampleNum) {
     
      ADCValue = ADCValue / SampleNum;
      SampleValue = 0;
      ADCValue = ADCValue - ADCOffset;
      difference = ADCValue - LastADCValue;
      LastADCValue = ADCValue;
     
      ADCValue = 0;
     
      _NOP();
       __bis_SR_register_on_exit(LPM0_bits);
     
    }
     
    }


    // Timer A0 interrupt service routine
    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_A0 (void)
    {
      P1OUT ^= 0x1;       
      SD16CCTL0 |= SD16SC;                      // Add Offset to CCR0
       __bis_SR_register_on_exit(LPM0_bits);
    }

    void configureTimer(void) {
      CCTL0 = CCIE;
      TACTL = TASSEL_2 +  MC_3 + TAIE + ID_3;          // SMCLK, Contmode, int enabled
      TACCR0 = 0xFFFF;
    }

  • The VLO runs with 4 to 20 kHz frequency (12kHz typical). This means your SMCLK (with /8 divider) runs at roughly 1.5kHz. Then you have another /8 as input divider for the timer itself. It takes about 6 minutes before the timer hits 0xffff. (65535*8*8/12000 = 350 seconds)
    Have you been patient enough when you waited for the interrupt?

    Then you have enabled TAIE and CCIE, but you have only one ISR. TAIE shares one interrupt vector with TACCR1 and TACCR2. TACCR0 has its own interrupt vector with higher priority. Also, since TA is configured to up/&down mode (contmode would be MC_2), it will never overflow and therefore never issue an overflow interrupt.

    Also, always put a NOP() behind the instruction entering the LPM (and never set a breakpoint on it). Due to the structure of the MSP, the next instruction is already fetched before the LPM entering takes effect. Which causes the breakpoint to be triggered before the MSP enteres LPM.

    In the ISRs, mustn't it be '__bic_SR_register_on_exit'? since you want to clear, not set the LPM bits.

    ssb said:
      SD16CCTL0 |= SD16SC;                      // Add Offset to CCR0


    This line makes no sense to me. Looks like you want to start the conversion, but the comment tells something completely different.

  • Hi

    Let me explain what I wish to do. I wish to take 512 samples from ADC and get one CurrentValue using a faster clk (MCLK in this case). Then I go in Low power mode. Then the timer(VLO clk)  interrupt wakes the device once in say 10 sec and enables second ADC conversion.

    I have selected up - down mode to get maximum time period of the timer. The timer interrupt description in the datasheet is not clear enough. Which ISR should I use for timer in up - down mode. Secondly Do I need to set the LPM bits again in the ISR or the device would go in LPM mode automatically after exiting the interrupt. Do I need to reload the CCR0 in ISR  I guess not. What I am observing right now is the pc goes back to main every time it exists ISR which is not desirable.

    Secondly there are 2 ways to get low frequency clock to timer_a. One is set timer src clock as SMCLK and assign VLO clk to SMCLK. Second method is to assign ACLK to VLO and use ACLK as source clk for timer_A. Which one is adviceable.

    Hope I am able to explain my requirements :)

    Kind regards

     

  • Hi Suchitra,

    It seems like you need to use the timer as an interval generator and you could use the TAxCCR0 interrupt. And the user's guide clearly shows when this flag will be set/interrupt will be triggered for different timer modes. There are code examples for all the MSP430 devices and their modules and you could refer to the timer code examples to better understand using interrupts.

    On setting the LPM bits in main/active mode, the device enters low power mode . On entering an ISR, the device wakes up from LPM to service the interrupt and if there is no instruction in the ISR to clear LPM bits on ISR exit (__bic_SR_register_on_exit), it goes back into low power mode. If the ISR contains the instruction to clear LPM bits on exiting ISR, then the device returns to main/active mode.

    In order to get a longer time interval (say every 10sec), you could set up the timer to interrupt every 1sec and have a counter that counts upto10 and when 10 counts/sec has elapsed, then exit the ISR into active mode and trigger the ADC conversion.

    Hope this helps.

    Regards,

    Bhargavi

  • I already suspected that this is what you want. And I wrote what you did. With your code, you should see the timer interrupt coming after about 6 minutes, not just 10 seconds.

    SUCHITRA BHIDE said:
    I have selected up - down mode to get maximum time period of the timer.

    Good idea, but won't lead anywhere. In UP/Down mode, the timer itself will never generate an interrupt. The interrupt generated by the timer register is when the register rolls over from 0xffff to 0. In UP/DOWN mode this never happens, nor (obviously) in stopped mode. In UP mode, the timer overflow interrupt happens when the timer jumps back to 0 after it has hit CCR0 value. This happens one timer tick after the CCR0 interrupt happened.
    While the continuous mode is self-contained, The UP and UP/DOWN modes are for use with the capture/compare registers (in compare mode). The timer either counts up to the content of CCR0 and then resets to 0 or it counts up to CCR0 and then counts down again to 0.
    When on its way up AND DOWN it hits a value in one of the CCRx registers, the associated interrupt is flagged and (if enabled) generated.

    CCR0, as the limiting factor for the up/down mode got its own, separate interrupt vector, all other events (overflow CCR1 etc.) share a second one.

    SUCHITRA BHIDE said:
    Secondly Do I need to set the LPM bits again in the ISR or the device would go in LPM mode automatically after exiting the interrupt.


    The LPM bits are part of the status register. It is stored on stack when entering an ISR and restored (including the LPM bits) when leaving the ISR. If you want to exit LPM, you'll need to clear the LPM bits from the value stored on the stack. This does the __bic_SR_register_on_exit() intrinsic, which may only be used inside an ISR.These intrinsics (compiler-specific pseudo functions) are required since teh C language does not know of such things as a stack or a status register adn therefore has no way to handle it with normal C statements.

    SUCHITRA BHIDE said:
    Do I need to reload the CCR0 in ISR  I guess not.


    If you're operating in UP or UP/DOWN mode, no. The content does not change and the timer cycle will repeat and repeat...
    But another usage of CCR0 is in continuous mode. There the timer just counts up and begins at 0. If you set CCR0 to e.g. 1000, the interrupt will come after 1000 timer ticks., In the CCR0 ISR, you'll then increment CCR0 by another 1000 and the next interrupt will happen exactly 1000 timer ticks after the last.
    Since this works for CCR0, 1 or 2 independently, you can have three (plus the timer overflow) different intervals which happen equidistant.
    I guess the 'add offset to CCR0'-comment comes from a similar use of the timer.

    SUCHITRA BHIDE said:
    What I am observing right now is the pc goes back to main every time it exists ISR which is not desirable.


    That's what you tell the processor to do :) You execute the 'clear the LPM bits' intrinsic on every call of the ISR, so it will always wake up the main loop. If you don't want it, you may not execute it. maybe with a counter that does it only after several samples have been taken, and not at all in your timer ISR.

    SUCHITRA BHIDE said:
    Which one is adviceable.

    It depends on the MSP you use. On older MSPs, there are differences between SMCLK and ACLK. SMCLK cannot be sourced by LFXT1 and ACLK not by DCO and such. There it depends on what you'll need later in your project.
    On newer MSPs, ACLK and SMCLK can be exchanged in all uses, so it is just a matter of preference. They could have been renamed to CLK0 and CLK1 or such.
    In both cases, SMCLK and ACLK can be output to different port pins. These pins might have a second use, so it might be that you need one of them for something else and therefore have to go for the other clock. Also, in capture mode, the capture of the current tiemr register alue can be initiated by ACLK but not SMCLK on some MSPs, since the ACLK output is on the same pin as the CCR trigger. This too is specific to the MSP in use.

     

  • Hi All,

    I am facing a strange problem.

    I am pasting a small sd16 interrupt handler code.

    The problem - After about 4272 interrupts the device gets reset. I am not able to trace the reason why. WDT is disabled.  I am using EZ430F2013. and CCS ver 4.1.2.00027

    Steps to recreate the problem.

    1. Load the design and open the Debug Perspective.
    2. Set a break point on line 102.
    3. Put variable int_no_adc in the watch window.
    4. Run 6 times till the int_no_adc = 6.
    5. When you run the 7th time the code never reaches the break point

    Some Observations

    From step 4 when you do a single stepping, we find that the variables like ADCValue, VoltageSettleCtr etc take some unknown values.

     

    Please help me in solving this mistry.

    //=============================================================================================

    code is given below apologies if the code looses its formatting due to cut paste

    //=============================================================================================

    //=========================================================================
    //=========================================================================
    //  INCLUDES
    //=========================================================================
    #include  <msp430x20x3.h>

    // Used to store current ADCValue
    unsigned long int CurrentADCValue=0, ADCValue=0;

    // Used to store the ADCOffset
    static unsigned int ADCOffset=00;

    // Used to store previous ADC value
    static unsigned int tempVal;
    static unsigned int VoltageSettleCtr;
    static unsigned int SampleValue = 0;
    static unsigned int SampleNum = 512;
    static unsigned int int_no_adc = 0, int_no_timer = 0;
    //=========================================================================
    // function prototypes
    //=========================================================================
    void InitializeDevice(void) ;
    void ConfigureTimer(void);
    void ConfigureADC(void);
    void EnableADC(void);
    void DisableADC(void);
    //=========================================================================
    //                M A I N  P R O G R A M
    //=========================================================================
    void main(void) {
    //disable the WDT
      WDTCTL = WDTPW + WDTHOLD;                

    // Select VLO as clock source for ACLK
      BCSCTL3 |= LFXT1S_2;
      ADCValue = 0;
      // Setup The ADC
      ConfigureADC();

      // Set up the TIMER
      ConfigureTimer();
      _NOP();
     
      // Added enable ADC so that first value of timer are not 0
      EnableADC();
      // GO to low power mode
      _BIS_SR(LPM0_bits + GIE);
     }

    //=========================================================================
    // Timer A0 interrupt service routine
    //=========================================================================
    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_A0 (void)
    {
       EnableADC();
       int_no_timer++;
      _BIS_SR(LPM0_bits + GIE);

    }
    //===========================================================================

    //=========================================================================
    // ADC ISR
    //=========================================================================
    #pragma vector = SD16_VECTOR
    __interrupt void SD16ISR(void)
    {
     
      // Read the MSB ADC value
     
      tempVal = SD16MEM0;

     
      // calculate the right value of vth settling.
      if (VoltageSettleCtr) {
        VoltageSettleCtr--;
     
        return;
      }
     
      ADCValue = SD16MEM0 + ADCValue;
      tempVal = SD16MEM0;
      // Increment sample number
      SampleValue++;

      // IF current sample number is equal to max sample number approximate
      if (SampleValue == 512) {
     
        ADCValue = ADCValue >> 9;
     
        //ADCValue = ADCValue >> 9;

        // Reset sampleValue
        SampleValue = 0;

        // Substract the offset from the actual value
        ADCValue = ADCValue - ADCOffset;

        CurrentADCValue = ADCValue;
       int_no_adc++;
        ADCValue = 0;
     
        _NOP();
        DisableADC();
       EnableADC();
        _BIS_SR(LPM0_bits + GIE);
      }
    }


    //===========================================================================
    // Timer is configured in up/ down mode. TACCRO is loaded with a value such that
    // total timer value approximately equals 10 sec
    // Timer src clk is ACLK which is VLO clk
    // Capture/Compare interrupt is enabled
    //
    // The timer produces interrupt every 10 sec.
    //===========================================================================
    void ConfigureTimer(void) {

      // Enable capture/ compare interrupt   
     // CCTL0 = CCIE;

      // ACLK + UP/DN mode + DIV_BY_8
      TACTL = TASSEL_1 +  MC_3 + ID_3;

      // Approx 10 sec interrupt interval
      TACCR0 = 8192;
    }

    //===========================================================================
    // ADC configuration
    //===========================================================================
    void ConfigureADC(void) {

      // select internal reference + SMCLK (SMCLK == MCLK)   
      SD16CTL = SD16SSEL_1;        

      // Select channel A0
      SD16INCTL0 = SD16INCH_0;

      //UNIPOLAR_MODE + INTERRUPT_ENABLE + 1024 OSR
      SD16CCTL0 =  SD16UNI + SD16IE + SD16OSR_1024;
      //SD16CCTL0 =  SD16UNI + SD16OSR_1024;

      // P1.0 A0+ A0- = GND
      SD16AE = SD16AE0;   
    }

    //===========================================================================
    // Enables the ADC and the load cell for next conversion
    //===========================================================================
    void EnableADC(void) {

      // assert the exication voltage to load cells
      // load cells Enable is active LOW.
      VoltageSettleCtr = 200;   
        ADCValue = 0;
      SD16CTL |= SD16REFON;        
      // Start ADC
      SD16CCTL0 |= SD16SC;
    }

    //===========================================================================
    //===========================================================================
    void DisableADC(void) {

      // de-assert the exication voltage to load cells
      // load cells. Enable is active LOW   
      // Stop ADC
      SD16CCTL0 &= ~SD16SC; 
      // Disable the reference
      SD16CTL &= ~SD16REFON;        

    }


     

  • Hi All

    I figured out what is the problem :)

    well the problem is use of    _BIS_SR(LPM0_bits + GIE); This some how doesn't put reti instruction and every time an interrupt is called data is pushed onto the stack. Now the stack over flows after 7 iterations and then things dont work as expected. 

    I compiled and debugged the same code in IAR and during execution I found that there was a warning for stack overflow which helped me to nail down the problem.

    Unfortunately I didn't get any warning from CCS. Is there any verbosity setting where I can get all the warnings?

    Kind regards

    Suchitra

  • _BIS_SR just does that: BIt Set on StausRegister.

    It has nothing to do with calling an ISR or returning from one. It just sets bits in the status register (BIC = BIt Clear) as C has no means to manipulate the status register (C can only manipulate memory locations).

    The opposite intrinsic (fake-function) you'll use in your ISR will also just clear the bits, not immediately but on the stack because on exit of the ISR, the status register is restored from stack and with it the LPM and GIE bits.

    An ISR does not necessarily need to clear the LPM bits. If an ISR is called, the current status register (containing the bits) is pushed on stack, all bits are cleared and the ISR is executed. On exit, the previous state is restored from stack. If you use the _BIC_SR_ON_EXIT() (or so) intrinsic inside the program flow (if it is in an IF block, it will or will not be executed depending on the IF condition), this saved state is altered on the stack and the SR is restored without these bits thus your main is awaking again.

    It has nothing to do with entering and exiting an ISR at all. Also, the BIC_SR_ON_EXIT may only be used inside an ISR as it will else alter a value on the stack which is NOT a saved SR content, leading to a corrupted return address or other problems.

    CCS cannot warn you. It's not the compilers fault if the programmer writes bad code :)
    The IAR warning is jus tbased on the 'stack size' setting which does NOT define a stack size at all. i trather defines a threshold value that has NO effect on the code. It just forces the linker to give an error if there's less ram available than 'defined' for the stack, and gives a debugger warning, if the stack pointer moves below this threshold during a breakpoint.
    It will not limit the stack usage in any way. And it may well be that you use more stack than you defined between two breakpoints. The debugger will not notice nor warn. So it's a nice gadget but you cannot rely on it.

**Attention** This is a public forum