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.

MSP430FR5969 ADC 12 Can't Exit ISR

Other Parts Discussed in Thread: MSP-EXP430FR5969

Hi there,

I am attempting to read input from two sensors on my MSP-EXP430FR5969 Launchpad but I am having some issues with the interrupt routine. I am able to successfully read the sensor values and store them in ADC12MEM0 and ADC12MEM1, but my problem is the program never leaves the ISR and I am unable to process the data as I would like to.

Here is my code:

#include <msp430.h>

#define CONVERSION_VALUE_1_2 0.29296875 // Vref 1200mV / 4095 (12 bit ADC resolution)

unsigned int ADC_value=0;
float temp=0, light=0;

float calculate_Temp(unsigned int ADC_value);
float calculate_Light(unsigned int ADC_value);

int main(void)
{

  WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT

  // GPIO Setup
  P1OUT &= ~BIT0;                           // Clear LED to start
  P1DIR |= BIT0;                            // P1.0 output
  P1SEL1 |= BIT3 | BIT4;                    // Configure P1.3 and P1.4 for ADC
  P1SEL0 |= BIT3 | BIT4;

  // Disable the GPIO power-on default high-impedance mode to activate
  // previously configured port settings
  PM5CTL0 &= ~LOCKLPM5;

  // By default, REFMSTR=1 => REFCTL is used to configure the internal reference
  while(REFCTL0 & REFGENBUSY);              // If ref generator busy, WAIT
  REFCTL0 |= REFVSEL_0 | REFON;             // Select internal ref = 1.2V
                                            // Internal Reference ON

  // Configure ADC12
  ADC12CTL0 = ADC12SHT0_2 | ADC12ON | ADC12MSC;
  ADC12CTL1 |= ADC12SHP | ADC12CONSEQ_1;    // ADCCLK = MODOSC; sampling timer
  ADC12CTL2 |= ADC12RES_2;                  // 12-bit conversion results
  ADC12IER0 |= ADC12IE1;         			// Enable ADC conv complete interrupt
  ADC12MCTL0 |= ADC12INCH_3 | ADC12VRSEL_1; // A3 ADC input select; Vref=1.2V
  ADC12MCTL1 |= ADC12INCH_4 | ADC12VRSEL_1; // A4 ADC input select; Vref=1.2V

  while(!(REFCTL0 & REFGENRDY));            // Wait for reference generator
                                            // to settle

  while(1)
  {
    __delay_cycles(5000);                    // Delay between conversions
    ADC12CTL0 |= ADC12ENC | ADC12SC;         // Sampling and conversion start
    __bis_SR_register(LPM0_bits + GIE);      // LPM0, ADC10_ISR will force exit
    ADC_value = ADC12MEM0;
    temp = calculate_Temp(ADC_value);
    ADC_value = ADC12MEM1;
    light = calculate_Light(ADC_value);
    __no_operation();                        // For debug only
  }
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12_ISR (void)
#else
#error Compiler not supported!
#endif
{
  switch (__even_in_range(ADC12IV, ADC12IV_ADC12RDYIFG))
  {
    case ADC12IV_ADC12IFG0:                 // Vector 12:  ADC12MEM0 Interrupt
      __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
    case ADC12IV_ADC12IFG1:   		        // Vector 14:  ADC12MEM1 Interrupt
      __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
      break;                                // Clear CPUOFF bit from 0(SR)
    default: break;
  }
}

float calculate_Temp(unsigned int ADC_value){

	// Retrieve voltage measurement, subtract 500mv (thermistor base @ 0C)
	// Divide by 10 since thermistor reports 10mV per degree C
	return ((ADC_value*CONVERSION_VALUE_1_2-500)/10);

}

float calculate_Light(unsigned int ADC_value){

	// Retrieve voltage measurement for Light sensor
	return (ADC_value*CONVERSION_VALUE_1_2);

}

Am I missing something inside the ISR in order to exit it? Any help would be greatly appreciated.

Thanks,

Mike

  • I am now able to leave the ISR after making some changes, but I'm having a new problem now. The program reads the sensors twice and then gets stuck at the line

    __bis_SR_register(LPM0_bits + GIE); 

    indefinitely. I'm not sure where to go from here. Here is the updated code:

    /* --COPYRIGHT--,BSD_EX
     * Copyright (c) 2012, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     *******************************************************************************
     *
     *                       MSP430 CODE EXAMPLE DISCLAIMER
     *
     * MSP430 code examples are self-contained low-level programs that typically
     * demonstrate a single peripheral function or device feature in a highly
     * concise manner. For this the code may rely on the device's power-on default
     * register values and settings such as the clock configuration and care must
     * be taken when combining code from several examples to avoid potential side
     * effects. Also see www.ti.com/grace for a GUI- and www.ti.com/msp430ware
     * for an API functional library-approach to peripheral configuration.
     *
     * --/COPYRIGHT--*/
    //******************************************************************************
    //  MSP430FR59xx Demo - ADC12, Sample A1, 1.2V Shared Ref, Set P1.0 if A1 > 0.5V
    //
    //  Description: A single sample is made on A1 with reference to internal
    //  1.2V Vref. Software sets ADC12SC to start sample and conversion - ADC10SC
    //  automatically cleared at EOC. ADC12 internal oscillator times sample (16x)
    //  and conversion. In Mainloop MSP430 waits in LPM0 to save power until ADC12
    //  conversion complete, ADC12_ISR will force exit from LPM0 in Mainloop on
    //  reti. If A1 > 0.5V, P1.0 set, else reset.
    //
    //                MSP430FR5969
    //             -----------------
    //         /|\|              XIN|-
    //          | |                 |
    //          --|RST          XOUT|-
    //            |                 |
    //        >---|P1.3/A1      P1.0|-->LED
    //
    //   William Goh
    //   Texas Instruments Inc.
    //   February 2014
    //   Built with IAR Embedded Workbench V5.60 & Code Composer Studio V5.5
    //******************************************************************************
    #include <msp430.h>
    
    #define CONVERSION_VALUE_1_2 0.29296875 // Vref 1200mV / 4095 (12 bit ADC resolution)
    
    unsigned int ADC_value=0, ADC_value2=0;
    float temp=0, light=0;
    
    float calculate_Temp(unsigned int ADC_value);
    float calculate_Light(unsigned int ADC_value);
    
    int main(void)
    {
    
      WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT
    
      // GPIO Setup
      P1OUT &= ~BIT0;                           // Clear LED to start
      P1DIR |= BIT0;                            // P1.0 output
      P1SEL1 |= BIT3 | BIT4;                    // Configure P1.3 and P1.4 for ADC
      P1SEL0 |= BIT3 | BIT4;
    
      // Disable the GPIO power-on default high-impedance mode to activate
      // previously configured port settings
      PM5CTL0 &= ~LOCKLPM5;
    
      // By default, REFMSTR=1 => REFCTL is used to configure the internal reference
      while(REFCTL0 & REFGENBUSY);              // If ref generator busy, WAIT
      REFCTL0 |= REFVSEL_0 | REFON;             // Select internal ref = 1.2V
                                                // Internal Reference ON
    
      // Configure ADC12
      ADC12CTL0 = ADC12SHT0_2 | ADC12ON | ADC12MSC;
      ADC12CTL1 |= ADC12SHP | ADC12CONSEQ_1;    // ADCCLK = MODOSC; sampling timer
      ADC12CTL2 |= ADC12RES_2;                  // 12-bit conversion results
      ADC12IER0 |= ADC12IE1;         			// Enable ADC conv complete interrupt
      ADC12MCTL0 |= ADC12INCH_3 | ADC12VRSEL_1; // A3 ADC input select; Vref=1.2V
      ADC12MCTL1 |= ADC12INCH_4 | ADC12VRSEL_1 | ADC12EOS; // A4 ADC input select; Vref=1.2V
    
      while(!(REFCTL0 & REFGENRDY));            // Wait for reference generator
                                                // to settle
    
      while(1)
      {
    	__delay_cycles(5000);
    	ADC12CTL0 |= ADC12ENC | ADC12SC;         // Sampling and conversion start
        __bis_SR_register(LPM0_bits + GIE);      // LPM0, ADC12_ISR will force exit
    	light = calculate_Light(ADC_value2);
    	temp = calculate_Temp(ADC_value);
        __no_operation();                        // For debug only
      }
    }
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = ADC12_VECTOR
    __interrupt void ADC12_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch (__even_in_range(ADC12IV, ADC12IV_ADC12RDYIFG))
      {
        case ADC12IV_NONE:        break;        // Vector  0:  No interrupt
        case ADC12IV_ADC12OVIFG:  break;        // Vector  2:  ADC12MEMx Overflow
        case ADC12IV_ADC12TOVIFG: break;        // Vector  4:  Conversion time overflow
        case ADC12IV_ADC12HIIFG:  break;        // Vector  6:  ADC12BHI
        case ADC12IV_ADC12LOIFG:  break;        // Vector  8:  ADC12BLO
        case ADC12IV_ADC12INIFG:  break;        // Vector 10:  ADC12BIN
        case ADC12IV_ADC12IFG0:   break;        // Vector 12:  ADC12MEM0 Interrupt
        case ADC12IV_ADC12IFG1:   		        // Vector 14:  ADC12MEM1 Interrupt
        	ADC_value = ADC12MEM0;
        	ADC_value2 = ADC12MEM1;
          __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
          break;
        case ADC12IV_ADC12IFG2:   break;        // Vector 16:  ADC12MEM2
        case ADC12IV_ADC12IFG3:   break;        // Vector 18:  ADC12MEM3
        case ADC12IV_ADC12IFG4:   break;        // Vector 20:  ADC12MEM4
        case ADC12IV_ADC12IFG5:   break;        // Vector 22:  ADC12MEM5
        case ADC12IV_ADC12IFG6:   break;        // Vector 24:  ADC12MEM6
        case ADC12IV_ADC12IFG7:   break;        // Vector 26:  ADC12MEM7
        case ADC12IV_ADC12IFG8:   break;        // Vector 28:  ADC12MEM8
        case ADC12IV_ADC12IFG9:   break;        // Vector 30:  ADC12MEM9
        case ADC12IV_ADC12IFG10:  break;        // Vector 32:  ADC12MEM10
        case ADC12IV_ADC12IFG11:  break;        // Vector 34:  ADC12MEM11
        case ADC12IV_ADC12IFG12:  break;        // Vector 36:  ADC12MEM12
        case ADC12IV_ADC12IFG13:  break;        // Vector 38:  ADC12MEM13
        case ADC12IV_ADC12IFG14:  break;        // Vector 40:  ADC12MEM14
        case ADC12IV_ADC12IFG15:  break;        // Vector 42:  ADC12MEM15
        case ADC12IV_ADC12IFG16:  break;        // Vector 44:  ADC12MEM16
        case ADC12IV_ADC12IFG17:  break;        // Vector 46:  ADC12MEM17
        case ADC12IV_ADC12IFG18:  break;        // Vector 48:  ADC12MEM18
        case ADC12IV_ADC12IFG19:  break;        // Vector 50:  ADC12MEM19
        case ADC12IV_ADC12IFG20:  break;        // Vector 52:  ADC12MEM20
        case ADC12IV_ADC12IFG21:  break;        // Vector 54:  ADC12MEM21
        case ADC12IV_ADC12IFG22:  break;        // Vector 56:  ADC12MEM22
        case ADC12IV_ADC12IFG23:  break;        // Vector 58:  ADC12MEM23
        case ADC12IV_ADC12IFG24:  break;        // Vector 60:  ADC12MEM24
        case ADC12IV_ADC12IFG25:  break;        // Vector 62:  ADC12MEM25
        case ADC12IV_ADC12IFG26:  break;        // Vector 64:  ADC12MEM26
        case ADC12IV_ADC12IFG27:  break;        // Vector 66:  ADC12MEM27
        case ADC12IV_ADC12IFG28:  break;        // Vector 68:  ADC12MEM28
        case ADC12IV_ADC12IFG29:  break;        // Vector 70:  ADC12MEM29
        case ADC12IV_ADC12IFG30:  break;        // Vector 72:  ADC12MEM30
        case ADC12IV_ADC12IFG31:  break;        // Vector 74:  ADC12MEM31
        case ADC12IV_ADC12RDYIFG: break;        // Vector 76:  ADC12RDY
        default: break;
      }
    }
    
    float calculate_Temp(unsigned int ADC_value){
    
    	// Retrieve voltage measurement, subtract 500mv (thermistor base @ 0C)
    	// Divide by 10 since thermistor reports 10mV per degree C
    	return ((ADC_value*CONVERSION_VALUE_1_2-500)/10);
    
    }
    
    float calculate_Light(unsigned int ADC_value){
    
    	// Retrieve voltage measurement for Light sensor
    	return (ADC_value*CONVERSION_VALUE_1_2);
    
    }
    

    Again, any help would be greatly appreciated. Thank you!

  • >__bic_SR_register_on_exit
    >break
     
    When C is compiled it figures out if it needs to use registers and pushes and pulls on to the stack during a IRQ, 
    Does it calculate correctly where it stored the SR on the stack if you use bic_SR but not at an exit? 

    e.g If it had to do 2 push_to_stack, and encounters __bic_SR_register_on_exit,  it will compile to use  bic.w #LPM0,4(SP)
    And only use bic.w #LPM0,0(SP) if it never used the stack.
    scratch-registers are always restored last after any bic_sr I could guess.

     

  • Try clearing GIE as well as LPM0_bits when you exit the interrupt.

    The second time you trigger ADC12 sampling GIE is already set. If the expected ADC12 interrupt occurs before the CPU enters LPM0 then there will be nothing to wake the CPU from sleep. This can be avoided by ensuring that GIE isn't already set before the CPU sleeps.
  • Sorry if this is an obvious question, but how would I clear them? I am very new to MSP430 development and I have just been working off examples thus far.

  • This chip has one ADC with multiple input channels. It can only convert one of the input channels at a time.

    The original example code tries to convert the input channel at P1.3 pin only. And it works.

    You attempt to add code to convert the input channel at P1.4 pin as well as that at P1.3 pin. You can do so one at a time, but you cannot convert both at the same time.
  • So then would it be possible to alternate between the two input channels, letting only one have access to the ADC at one time, or is it not possible to read from two sensors on the FR5969?
  • You could connect up to 16 analog signals to input pins individually at the same time and convert any one of them to a 12-bit digital number -- one input at a time, alternately, sequentially, or any order you like.
  • Michael Pappas said:

    Sorry if this is an obvious question, but how would I clear them? I am very new to MSP430 development and I have just been working off examples thus far.

    Change this line in the ISR:

    __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU

    to:

    __bic_SR_register_on_exit(LPM0_bits | GIE); // Exit active CPU and disable interrupts

    old_cow_yellow said:
    This chip has one ADC with multiple input channels. It can only convert one of the input channels at a time.

    The original example code tries to convert the input channel at P1.3 pin only. And it works.

    You attempt to add code to convert the input channel at P1.4 pin as well as that at P1.3 pin. You can do so one at a time, but you cannot convert both at the same time.

    Huh? The code in the second post is already set up to use sequence-of-channels mode with two channels converting one after the other. ADC12MSC and ADC12SHP are both set, so a single trigger pulse starts of conversion of both. The interrupt fires on when the second channel has converted, at which point the ISR reads out both conversion results. Seems fine to me...

  • Hi Robert,

    That appears to have been the solution. My code (from my second post) is now performing both conversions and continuing past the ISR.

    Thank you to you and everyone else who responded!
  • Robert Cowsill said:
    Huh? The code in the second post is already set up to use sequence-of-channels mode

    You are right. I goofed.

  • "If it had to do 2 push_to_stack, and encounters __bic_SR_register_on_exit, it will compile to use bic.w #LPM0,4(SP)"

    Yes, that's exactly why the _on_exit intrinsics exist. Only the compiler knows how many registers (and maybe local variables) it has pushed onto the stack at the moment you call the intrinsic. This amount may even change depending on the exact position of the call. But the compiler keeps track of it.
    This is also the reason why this intrinsic can only be used inside the original interrupt function and not in a function that is called from within an ISR. Only while compiling the ISR itself, this information is available.
    But if you use inline assembly inside the ISR, you may void the compiler's knowledge abut the current stack position.
    BTW: The _on_exit intrinsic does not exit the ISR, and multiple occurrences stack, in the order of execution, until the ISR regularly returns.

    To answer the original question
    " I'm not sure where to go from here."

    Setting the LPM bis in the status register stops the CPU clock until an interrupt occurs. When the interrupt comes, the lock is reactivated, the status register is saved to the stack and the ISR is executed. At the end of the ISR, the original status register is restored, which instantly halts the CPU clock again (so the main code doesn't continue). Unless the ISR has altered the saved status register content (cleared the LPM bits) with the _on_exit intrinsics. In this case, the CPU clock remains on when the ISR exits and the main code continues execution.
    So where do you go from there? Straight away. It is just a "wait here until the ISR says to continue". Similar to a while loop waiting for a global flag being set by the ISR (but consuming much less power as the CPU is sopped rather than constantly checking the flag)
    However, when you enter LPM, a breakpoint placed on the instruction right after the LPM entry, this breakpoint is triggered instantly, as the next instruction is already fetched the very moment the clock stops. The instruction is not executed (until the ISR has exited LPM), but the breakpoint is triggered on instruction fetch (which happens due to pipelining while the previous is being executed) and not on instruction execution. Many have been fallen into this pit.

**Attention** This is a public forum