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.

MSP430FR2533: ADC register ADCSC and ADCBUSY bits remain set after initial ADC interrupt

Part Number: MSP430FR2533

What we have found on about 0.5% of the MSP430FR2533IRHB chips is that after setting up the ADC and receiving the first interrupt, we no longer receive subsequent ADC interrupts. After setting a breakpoint I can see that the ADCSC bits (register ADCCTL0) and ADCBUSY bits (register ADCCTL1) are set and never cleared.

On the remaining 99.5% of chips, everything is working fine. We have validated that the input to the ADC matches the of a known good chip. In fact, we have swapped the IC with a known good IC, and the problem follows the IC.

Our factory has sent known failed samples to TI and they are claiming the ADC is functioning as expected, but we clearly see that it is not. 

Obviously firmware is the first place to point the finger but the fact that it follows the chip seems suspect to me. Not to mention the fact that erasing the FRAM and loading a known good firmware image (that we have loaded on thousands of other products) does not correct the issue.

What do you reccomend as path forward on this? 

  • Hey Jacob,

    This is very odd and I didn't see any errata related to this. Have you tried clearing the ADCSC bit in SW on a failed device to see if that fixes the issue? Can you give me a bit of background on your application? What other interrupts do you have enabled in your software?

    Thanks,

    Mitch

  • If you can post the code you're using to drive the ADC, maybe someone will see something. I would be looking for a code sequence that usually works, but is susceptible to a nudge, e.g. if the ADC clock is running at 4.5MHz rather than 5MHz. (There are differences between individual chips.) Even if you're stuck with "that's how it works" there may be a workaround.

    There are modes where ADCBUSY=1  all the time. I've seen (but didn't study) some cases where SC stays on e.g. if SC is set with SHS>0 (but not always).

    Do you know what exactly TI said about the chips they tested? The words they chose might be relevant.

  • Hi Bruce, thanks for the response.

    Our code is fairly straight forward. There are only 3 functions that apply to the ADC, one of which is the ISR:

    (Please be cautious, looks like some of the comments in the code snippets below are incorrect)

    void App_adcInit(void)
    {
        adc_val = 0;
        adc_interrupt = false;
        adcIntVal = 3;
    
        P2OUT |= GPIO_PIN7;
    
        // input channel 6 |
        ADCMCTL0 = ADCINCH_6; // select default reference (AVCC and AVSS); 0110b = A6 for P1.6 input channel
    
        ADCCTL0 = ADCSHT_15;
        // Signal sourced from sample timer | Clock source is ACLK | Divide by 3
        ADCCTL1 = ADCSHP | ADCSSEL0_L | ADCDIV0;
    
        ADCCTL2 = ADCSR | ADCPDIV0; // predivide by 1; 8-bit resolution; binary unsigned readback, 200 ksps samplling rate
    
        // enable interrupts
        ADCIE = ADCIE0;
    
        ADCCTL0 |= ADCON | ADCENC | ADCSC; // enable adc
    
        ADCLO = 0x00;
        ADCHI = 0x00;
        __delay_cycles(300000); // wait 30 ms for ADC to settle
    }
    
    void setAdcLowPower(bool set)
    {
        // Go to low power mode
        if(set) 
        {
            // Halt ADC conversions
            ADCCTL0 &= ~(ADCON | ADCENC);
    
            P2OUT &= ~(GPIO_PIN7);
        }
        else
        {
            App_adcInit();
        }
    }
    
    #pragma vector=ADC_VECTOR
    __interrupt void ADC_ISR(void)
    {
        // Get the reason that the ISR has fired
        unsigned int ifg = _even_in_range(ADCIFG, 0x0c);
    
        /* If the reason the ISR has fired is that it is out
         * of the current range, set the interrupt flag to
         * true and alert the QCC if it is awake.
         */
        // FIXME: This should be outside of an ISR...
        if ((ifg & ADCHIIFG) || (ifg & ADCLOIFG))
        {
            // Get the current value
            adc_val = ADCMEM0;
    
            // Reset the hi/lo values for interrupt generation
            ADCLO = (adc_val < adcIntVal) ? 0x00 : adc_val - adcIntVal;
            ADCHI = (adc_val > (255 - adcIntVal)) ? 255 : adc_val + adcIntVal;
            updateADCReg();
        }
    
        ADCIFG = 0;
    
        // Start next conversion...
        ADCCTL0 |= ADCSC;
    
    }
    

    The response from Ti was very brief:

    Test Part : MSP430FR2533IRHB

    Test Pin : P1.6

    Test Function : ADC ( 10bits )

    Power Supply : 3.3V

    Test voltage point : 0V 0.2V 1.2V 1.5V 1.8V 3.3V

    Test Result :

    Input Voltage(V)

    0

    0.2

    1.2

    1.5

    1.8

    3.3

    AD_Value

    0

    61

    369

    462

    549

    1022

  • Hi Mitch,

    You can see the code posted below in a reply to @Bruce McKenney47378. We setup the ADC, after our initial reading we set the LO and HIGH comparator such that an interrupt will fire on a reading outside some range. We also have a main processor which will power down and notify the MSP430 that it is powering down, at which point the MSP will disable the ADC to reduce power consumption. Also, the ADC readings are reported back to the main processor. Note that the MSP is a I2C slave so a GPIO pin is asserted to notify the main processor that the MSP has something to say, at which point the main processor will issue an I2C read of the current ADC value.

    We have the following interrupts enabled: 41, 44, 45, 54, 55, 56, 57

    I'll try clearing the ADCSC bit in SW and report back.

  • When you looked at the ADC registers, what was the value of ADCON? Also, how are you driving ACLK (crystal vs REFO)?

    I don't have my equipment here, but I have a few observations.

    The dynamics here are a bit unusual (which is Not the same as "wrong", but maybe opens different windows). The combination of SSEL0/PDIV0/DIV0 gives you a 32kHz/4/2=4kHz ADC clock, which is fairly slow. Combined with SHT=15 (1k ADC clocks) that's an output rate of about 4sps. Depending on how fast (MCLK) the CPU is running, the first conversion may or may not complete during that 300k-clock delay. I mention this since it may bear on the fails-after-first-completion observation.

    > ADCCTL0 &= ~(ADCON | ADCENC); 

    This is almost certainly happening in mid-conversion. The effects of setting ADCON=0 during a conversion are mentioned in UG (SLAU445I) Sec 21.2.7.1, where "the value of the interrupts flags [is] unpredictable", which suggests to me that the ISR may or may not be called. If the ISR is called, it will set SC with ADCON=0; I wouldn't be surprised if SC (and maybe BUSY) stays stuck-on in that case. I also wouldn't be surprised if "unpredictable" results vary from one chip to another. And a (relatively) slow ADC clock probably makes a bigger target out of any small races within the ADC unit. I suggest a "soft-stop" mechanism, e.g. using a "stop next time around" flag shared between the ISR and setAdcLowPower. (Alternatively: set IE=0, set ENC=0, spin for BUSY==0, then set ON=0.)

    I asked about ACLK since a low-speed crystal can take quite a long time to start up, and I wouldn't be surprised if this varies between individual chips.

  • Hi Jacob,

    One thing I wanted to point out is that I noticed you are turning on the ADC and starting a conversion in the same line:

    ADCCTL0 |= ADCON | ADCENC | ADCSC; // enable adc

    We recommend turning on the ADC for at least 100ns before starting a conversion to avoid conversion errors:

    Here's an example of how we would recommend configuring the ADC and starting a conversion:

        // Configure ADC10
        ADCCTL0 |= ADCSHT_2 | ADCON;                             // ADCON, S&H=16 ADC clks
        ADCCTL1 |= ADCSHP;                                       // ADCCLK = MODOSC; sampling timer
        ADCCTL2 |= ADCRES;                                       // 10-bit conversion results
        ADCMCTL0 |= ADCINCH_1;                                   // A1 ADC input select; Vref=AVCC
        ADCIE |= ADCIE0;                                         // Enable ADC conv complete interrupt
      
        while(1)
        {
            ADCCTL0 |= ADCENC | ADCSC;                           // Sampling and conversion start
            __bis_SR_register(LPM0_bits | GIE);                  // LPM0, ADC_ISR will force exit
            __no_operation();                                    // For debug only
            if (ADC_Result < 0x1FF)
                P1OUT &= ~BIT0;                                  // Clear P1.0 LED off
            else
                P1OUT |= BIT0;                                   // Set P1.0 LED on
            __delay_cycles(5000);
        }

    I would recommend setting the ADCON bit before starting a conversion with the ADCSC bit like the example above. This does not explain why the ADCBUSY and ADCSC bits are staying high, but just wanted to point this out.

    Digging a bit deeper into the original issue - can you post your updateADCReg(); function code? I would like to see what that function is doing.

    Also, can you tell me what frequency you are running the CPU at?

    Thanks!

    -Mitch

  • CPU @ 8 MHz

    void updateADCReg(void)
    {
        if (!doNotDisturb) 
        {
            interruptFlagReg |= (1 << SS_REG_INT_FLAG_ADC);
    
            // Clear the interrupt flag
            HWREG16(FUNCTIONTIMER__PERIPHERAL + OFS_TAxCCTL2) &= ~(COV | CCIFG);
            //set rollover to be 100ms from current timer val (actually more like 45-55ms)
            HWREG16(FUNCTIONTIMER__PERIPHERAL + OFS_TAxCCR2) =
                HWREG16(FUNCTIONTIMER__PERIPHERAL + OFS_TAxR)
                + TIMER_CYCLES_100MS;
            //enable timer
            HWREG16(FUNCTIONTIMER__PERIPHERAL + OFS_TAxCCTL2) |= CCIE;
    
            // Set the interrupt for the QCC to high (and set the direction to out.
            P1DIR |= GPIO_PIN5;
            P1OUT |= GPIO_PIN5;
        }
    }
    

  • Thanks Jacob,

    Can you show me where in your code App_adcInit() and setAdcLowPower() are called? Exploring Bruce's idea a little more, I would like to get a sense of how close these functions are called within each other.

    Also, can you confirm what your ACLK frequency is?

    Thanks,

    Mitch

  • I don't have an FR2533 to work with, but I expect (hope) the FR2311 is an adequate proxy.

    Some observations:

    1) You can't set ADCON=0 and ADCENC=0 in the same step. If you try, ADCON stays at 1. (I.e.: ADCON=1 always in this program.)

    2) If you set SC=1 when ENC=0, it stays stuck at 1, at least until ENC=1 again. No sign of BUSY=1.

    I haven't (soi far) been able to get a hang by switching on/off quickly.

  • Hi Mitch,

    ACLK is set to use REFO and thus is 32kHz.


    main() >> App_init() >> App_adcInit()


    void main(void)
    {
        WDTCTL = WDTPW | WDTHOLD;
        BSP_configureMCU();
        __bis_SR_register(GIE);
        App_init();
        CAPT_appStart();
    ...
    }

    setAdcLowPower(false) is called from App_init() before the call to App_adcInit()

    setAdcLowPower() is also called from the I2C ISR as the result of a command from our main processor.

  • Hi Jacob,

    Sorry for the delayed response. 

    Have you tried Bruce's suggestions and see if you still see the same behavior?

    Also, it may be worth changing the ADC clock to MODOSC (5MHz) and seeing if you still see failures. There may be an issue with the 30ms delay not being long enough for the 4kHz ADC clock.

    Thanks,

    Mitch

  • Hi Jacob,

    Wanted to check in and see if you were still experiencing issues here?

    Thanks,

    Mitch

  • Hi Mitch,

    Thanks for following up on this. Yes, I'm still having issues with this.

    I have made the changes per Bruce and your suggestions and was able to get interrupts to trigger again, but they are inconsistent. Sometimes when changing the voltage on the ADC, the interrupt will fire, sometimes it will not.

    Changes thus far:

    • No longer clearing ADCON and ADCENC simultansouly
      • Wait for until ADCBUSY is cleared before continuing 
    • No longer setting ADCON and ADCENC simultaneosly
    • Changed ADC clock to MODOSC
    • Set a flag in the ISR which indicates to the main loop that there is a HI/LO interrupt. The main loop will the read the MEM register and act accordingly.

    Is there a way to determine the current or previous LPM level using the debugger?

    -Jake

  • Hi Mitch,

    Something interesting to note here:

    I've reverted back to our original code (removed changes based on suggestions found on this thread) and only made the following change to the ADC clock:

        // Clock divided to ~10 kHz
        ADCCTL1 = ADCSHP | ADCSSEL_0 | ADCDIV_7;    // Signal sourced from sample timer | Clock source is MODCLK | Divide by 8
        ADCCTL2 = ADCSR  | ADCPDIV__64;             // predivide by 64; 8-bit resolution; binary unsigned read-back, 50 ksps sampling rate

    The original clock configuration was as follows:

        // Clock divided to 4 kHz
        ADCCTL1 = ADCSHP | ADCSSEL_1 | ADCDIV_1; // Signal sourced from sample timer | Clock source is ACLK | Divide by 2
        ADCCTL2 = ADCSR  | ADCPDIV__4;          // Predivide by 4; 8-bit resolution; binary unsigned readback, 50 ksps samplling rate

    With the new clock source running at ~10kHz, everything is working fine. I can't really explain why. I've tried using the ACLK as the ADC source at 8kHz and 16kHz and neither work. So it isn't simply the increased clock speed, must be something else going on.

  • Hi Jacob,

    I apologize for the delay here. 

    That is really interesting that changing the clock source seems to fix the issue. Just to clarify - the ADC bits stay set, but the device is not completely locking up, correct?

    I will run some tests with the SW you have provided and give an update next week. 

    Thanks,

    Mitch

  • Hi Mitch,

    Yes, everything is working as expected with the one single clock change.

    How about I email your our source code? I can even ship you one of our boards with the MSP430 in this state.

    -Jake

  • That will work! I sent you an E2E connect request.

    Thanks,

    Mitch

  • Hey Jacob, 

    Since we are moving this conversation offline, I will close the thread for now.

    Thanks,

    Mitch

**Attention** This is a public forum