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.

MSP430FR5989: ADC12TOVIFG recovery issues

Part Number: MSP430FR5989

Hello,

I have been having a heck of a time trying to recover from an ADC Conversion time overflow.   My problem is not in that I am getting the error, it is trying to recover from the error.  I purposely setup the sampling rate to be faster than can be supported, forcing the conversion time overflow flag.  However, after I get this error, no matter how slow I setup the sampling rate afterwards, it ALWAYS generates this error until I power cycle the board.

I have it setup so that TimerB0 feeds into the ADC module, which then feeds the DMA module.  I suspect the issue is after I have collected all the samples I need, I take too long to stop the timer, which then triggers another conversion that I forcibly stop.  

I use the following code to force stoppage of the collection.  I believe this conforms to the recommendations from the users guide:

ADC12CTL1 &= ~(ADC12CONSEQ_3); // According to users guide, to halt any conversion, need to clear these 3 bits
ADC12CTL0 &= ~(ADC12ENC); // Need to disable conversions before any other bits can be changed
while (ADC12CTL1 & ADC12BUSY);
ADC12CTL0 &= ~(ADC12ON);
TB0CTL &= ~MC_3;

Is there any noticable flaws in my logic or the order of operations?

Thanks,

-Chris

  • Hi Chris,

    You may be experiencing the ADC42 Errata. Can you take a look into this errata and it's workarounds and let me know if they fix your issue?

    Best regards,

    Caleb Overbay

  • Thanks for the response Caleb. I also suspected this errata might be the culprit. The fact that I am getting the error is not the issue as much as I can't seem to recover from it once it occurs.

    The errata sheet seems to indicate that the following steps would recover from this:

    To recover the conversion halt:
    1. Disable ADC module (ADC12CTL0.ADC12ENC = 0 and ADC12CTL0.ADC12ON = 0)
    2. Re-enable ADC module (ADC12CTL0.ADC12ON = 1 and ADC12CTL0.ADC12ENC =
    1)
    3. Re-enable conversion

    I believe I am following these steps in shutting down the module, but I am not confident that I am doing it correctly:

    ADC12CTL1 &= ~(ADC12CONSEQ_3); // According to users guide, to halt any conversion, need to clear these 3 bits
    ADC12CTL0 &= ~(ADC12ENC); // Need to disable conversions before any other bits can be changed
    while (ADC12CTL1 & ADC12BUSY);
    ADC12CTL0 &= ~(ADC12ON);
    TB0CTL &= ~MC_3;
  • Hi Chris,

    It doesn't look like you're following the steps outlined above. I recommend the following:

    ADC12CTL1 &= ~(ADC12CONSEQ_3);      // Reset to single channel mode
    ADC12CTL0 &= ~(ADC12ENC + ADC12ON); // Reset ENC and ON bits
    TB0CTL &= ~MC_3;                    // Stop timer
    ADC12CTL0 |= ADC12ENC + ADC12ON;    // Set ENC and ON bits

    It's not much different than what you had before. I reset the ENC and ON bits in the same line of code, then set them later on. There is no need to pend on the busy bit. If you have optimization on, the compiler might see that you set the ENC and ON bits later in the code and optimize out the reset. Keep an eye on the disassembly to check if this is the case.

    Also the ADC12TOVIFG should be reset when the interrupt is serviced. When the issue occurs, can you double check that this flag gets cleared?

    Best regards 

    Caleb Overbay

  • Unfortunately that did not work for me.  However, when I observed these differences on the debugger I found that by doing

    ADC12CTL0 &= ~(ADC12ENC + ADC12ON);

    The debugger indicated that the ADC12ON bit did not actually clear.  Furthermore, if I did 

    ADC12CTL0 &= ~(ADC12ENC);
    ADC12CTL0 &= ~(ADC12ON);

    The debugger indicated that both bits got cleared.  Not sure if this helps, but I do find it odd.

    None-the-less I am still having the issue where once I get the overflow, I cannot recover from it and anytime I tried to take another sample It immediately overflows again.

  • Hi Chris,

    Can you post a reduced set of code that reproduces the issue? I'd like to test this out on my setup.

    Best regards,
    Caleb Overbay
  • Hey Caleb.

    First off I just want to thank you again for taking the time to help me out.

    I'm attaching some sample code that I generated that recreates this error that I am having.  To give an overview of what is (trying) to be achieved, I am trying to sample 2 analog channels "simultaneously" as fast as I possibly can.  Again, I do not mind if we end up going too fast that overflow occurs, but it is a big problem that I cannot seem to recover from the error once it occurs.

    In this project I start out by sampling way too fast.  I get the overflow flag, so I change the rate down to a much slower rate (that I know is plenty slow enough for the module to keep up).  However, after decreasing the rate, I still get continuous Overflow interrupts (even though the flags are being cleared and the ADC module is being reset).  

    The setup is as follows.  I have TB0.1 feeding the ADC module.  The ADC module is setup extended sampling mode, so the timer drives when each channel is sampled and for how long.  I have 2 DMAs set up, each one to transfer data from one of the ADC12MEMx registers and into a buffer.  I found through experimentation (and through research) that the ADC12IFG cannot trigger both modules, therefore I setup the timer to also trigger the DMA modules (in this case I realize the trigger is too soon, but this is just to prove the point that I cannot recover from the overflow once it occurs).  Also, TimerB0 and ADC modules are running the SMCLK (8MHz).  

    Thanks again,

    -Chris

    #include <msp430.h> 
    #include <stdint.h>
    
    
    
    #define _N_POINTS		1024
    
    
    #pragma LOCATION(_data_0, 0x15000)
    static volatile uint16_t _data_0[_N_POINTS];
    #pragma LOCATION(_data_1, 0x17000)
    static volatile uint16_t _data_1[_N_POINTS];
    
    static volatile uint16_t _n_tov = 0;
    static volatile uint16_t _n_ov = 0;
    
    /* Static function declarations */
    static void _startAdcCollection(void);
    static void _stopAdcCollection(void);
    
    int _system_pre_init(void)
    {
        /*
         * Disable the GPIO power-on default high-impedance mode to activate
         * previously configured port settings
         */
        PM5CTL0 &= ~LOCKLPM5;
    
        /* Unlock all segments for Write access - note, because all variables are in FRAM, we need to do this before setting any variables */
        MPUSAM |= (MPUSEGIWE | MPUSEG3WE | MPUSEG2WE | MPUSEG1WE);
    
        return 1;
    }
    /*
     * main.c
     */
    int main(void)
    {
        WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
    
        /* Setup the External VRef */
        P1SEL1 |= (BIT1);
        P1SEL0 |= (BIT1);
    
        /* Setup Analog Channels 8 and 9 */
        P9SEL1 |= (BIT0 | BIT1);
        P9SEL0 |= (BIT0 | BIT1);
    
        /* Setup the external crystal */
    	PJSEL1 &= ~BIT4;
    	PJSEL0 |= BIT4;
    
    
    
        /* Configure the wait states before setting the clocks */
        FRCTL0 = FRCTLPW | NWAITS_1;
    
        // Clock System Setup (external 32768 crystal)
        CSCTL0_H = CSKEY >> 8;                    // Unlock CS registers
        CSCTL1 = DCOFSEL_4 | DCORSEL;            // Set DCO to 16MHz
        CSCTL2 = SELA__LFXTCLK | SELS__DCOCLK | SELM__DCOCLK; // Set SMCLK = MCLK = DCO,
        CSCTL3 = DIVA__1 | DIVS__2 | DIVM__1;     // Set all dividers
        CSCTL4 &= ~(LFXTBYPASS | LFXTOFF);                      // Ensure crystal not in bypass
        /*
         * Wait for faults to clear.  NOTE: if you don't clear the fault flag, XT1 clock will default to 37.5 KHz
         */
        do
        {
            CSCTL5 &= ~LFXTOFFG;                    // Clear XT1 fault flag
            SFRIFG1 &= ~OFIFG;
        } while (SFRIFG1&OFIFG);                   // Test oscillator fault flag
        CSCTL0_H = 0;                             // Lock CS registers
    
        /*
         * Setup TimerA0 as a 1 second timer.
         */
        TA0CTL = TASSEL__ACLK | TACLR;
        TA0CCTL0 = CCIE;
        TA0CCR0 = 32768;
    
        /*
         * Setup Timer B0.1 to feed ADC module in extended sampling mode [using extended mode to prevent having to toggle the ENC bit]
         *  The initial timer is set to run 2 channels at 150 Khz.  The timer source is 8MHz SMCLK, but 2 channels requires the PWM
         *  to trigger each channel, so need to cut the period in half.
         *  TB0.2 will trigger the DMAs to read the data.
         */
        TB0CTL = (TBSSEL__SMCLK | TBCLR);
        TB0CCTL0 = 0x0000;
        TB0CCR0 = 20; //26;           // Period = 3.26us [x2 for each channel]
        TB0CCR1 = 8;            // 8 clocks for sampling
        TB0CCTL1 = 0x0000;      // Clear the output line first to drive low
        TB0CCTL1 = OUTMOD_7;    // Set output unit to outmod 7
        TB0CCR2 = TB0CCR0 - 4; //TB0CCR1 + 16;		// Per the users guide, conversion time takes 15 clocks plus a sync
        TB0CCTL2 = 0x0000;      // Clear the output line first to drive low
    
        /*
         * Setup ADC Module.  ADC12CLK => SMCLK.
         */
        ADC12CTL0 = ADC12SHT1_1 | ADC12SHT0_1;        // Should not matter since we are using extended sampling mode
        ADC12CTL1 = ADC12SHS_3 | ADC12SSEL_3;         // Extended sampling mode, TimerB0.1 SAMPCON, 8MHz ADC12CLK
        ADC12CTL2 = ADC12RES_2;                       // 12-Bit
        ADC12CTL3 = 0x0000;
        /* Setup Analog channels 8 and 9 [these are channels getting sampled] */
        ADC12MCTL0 = (ADC12VRSEL_4 | ADC12INCH_8);              // External Reference
        ADC12MCTL1 = (ADC12VRSEL_4 | ADC12INCH_9 | ADC12EOS);
        ADC12IER0 = 0x0000;
        ADC12IER1 = 0x0000;
        ADC12IER2 = (ADC12TOVIE | ADC12OVIE);
        ADC12IFGR0 = ADC12IFGR1 = ADC12IFGR2 = 0x0000;
    
        /*
         * Setup DMA0 and DMA1 to pipe data from the ADC12 registers into the data buffers.
         * Triggered by TB0.2, not by the ADC12IFG.  It was found that the ADC12IFG cannot
         * be used to trigger BOTH DMAs.  This however means every other sample for each
         * channel is going to be garbage, but that is OK.
         */
        DMACTL0 = (DMA1TSEL_8 | DMA0TSEL_8);
        DMA0CTL = (DMADT_0 | DMADSTINCR_3 | DMASRCINCR_0);
        *((uint32_t*)&DMA0SA) = (uint32_t)&ADC12MEM0;
        *((uint32_t*)&DMA0DA) = (uint32_t)&_data_0;
    //    __data20_write_long((uint32_t)&DMA0SA, (uint32_t)&ADC12MEM0);
    //    __data20_write_long((uint32_t)&DMA0DA, (uint32_t)&_data_0);
        DMA1CTL = (DMADT_0 | DMADSTINCR_3 | DMASRCINCR_0 | DMAIE);
    //    __data20_write_long((uint32_t)&DMA1SA, (uint32_t)&ADC12MEM1);
    //    __data20_write_long((uint32_t)&DMA1DA, (uint32_t)&_data_1);
        *((uint32_t*)&DMA1SA) = (uint32_t)&ADC12MEM1;
        *((uint32_t*)&DMA1DA) = (uint32_t)&_data_1;
    
    
    	/* Enable Interrupts */
    	_enable_interrupts();
    	_no_operation(); // Needed because the next instruction after enable is always executed due to pipelined architecture
    
        _startAdcCollection();
        while (1)
        {
    		__bis_SR_register(LPM0_bits);
    		__no_operation();
        }
    
    	return 0;
    }
    
    
    static void _startAdcCollection(void)
    {
    	TB0CCTL0 = 0x0000;
    	TB0CCTL1 = 0x0000;	// Clear the output line
    	TB0CCTL1 = OUTMOD_7;
    	TB0CCTL2 = 0x0000;
        TB0R = TB0CCR0 - 1;     // Start 1 tick behind CCR0 value, so that the timer is triggered high immediately
    
    	/* Setup the DMAs */
    	DMA0CTL &= ~(DMAIFG);
    	DMA0SZ = _N_POINTS;		// Multiply by 2 to account for garbage
    	DMA1CTL &= ~(DMAIFG);
        DMA1SZ = _N_POINTS;
        DMA0CTL |= DMAEN;
        DMA1CTL |= DMAEN;
    
        /* Enable the ADC Module */
        ADC12CTL1 |= ADC12CONSEQ_3;
        ADC12CTL0 |= (ADC12ENC | ADC12ON);
    
        /* Start the timer */
        TB0CTL |= (MC__UP);
    }
    
    static void _stopAdcCollection(void)
    {
    	/* Stop */
    	ADC12CTL1 &= ~(ADC12CONSEQ_3);      // Reset to single channel mode
    	ADC12CTL0 &= ~(ADC12ENC + ADC12ON); // Reset ENC and ON bits
    	TB0CTL &= ~MC_3;                    // Stop timer
    
    	ADC12IFGR0 = 0x0000;
    	ADC12IFGR1 = 0x0000;
    	ADC12IFGR2 = 0x0000;
    
    	/* Clear the CCTL registers (to clear the output lines) */
    	TB0CCTL0 = 0x0000;
    	TB0CCTL1 = 0x0000;
    	TB0CCTL2 = 0x0000;
    
    	DMA0CTL &= ~(DMAEN | DMAIFG);
    	DMA1CTL &= ~(DMAEN | DMAIFG);
    
    }
    
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void timerA0_ccr0_isr(void)
    {
    	TA0CTL &= ~(MC_3);
    
    	/* Start the Timer/ADC/DMA */
    	_startAdcCollection();
    }
    
    #pragma vector=ADC12_VECTOR
    __interrupt void adc12Isr(void)
    {
    	switch (__even_in_range(ADC12IV, 76))
     	{
     		case 0:
     			/* no interrupt */
     			break;
     		case 2:
     			/* MEMx Overflow error */
     			_n_ov += 1;
    
     			/* Simulate delay in software */
    // 			__delay_cycles(160);
     			_stopAdcCollection();
    
     			/* Change the collection parameters to much a slower rate, to show that the overflows still occur */
     			TB0CCR0 = 488;		// 8Khz, 2 channels
    
    			/* Delay 1 second */
    			TA0CTL |= MC__UP;
     			break;
     		case 4:
     			/* Conversion Time Overflow error */
     			_n_tov += 1;
    
     			/* Simulate delay in software */
    // 			__delay_cycles(160);
     			_stopAdcCollection();
    
     			/* Change the collection parameters to much a slower rate, to show that the overflows still occur */
     			TB0CCR0 = 488;		// 8Khz, 2 channels
     			TB0CCR2 = TB0CCR0 - 4;
    
    			/* Delay 1 second */
    			TA0CTL |= MC__UP;
     			break;
     		case 6:
     		case 8:
     		case 10:
     			break;
     		case 12:
     		case 14:
     		case 16:
     		case 18:
     		case 20:
     		case 22:
     		case 24:
     		case 26:
     		case 28:
     		case 30:
     		case 32:
     		case 34:
     		case 36:
     		case 38:
     		case 40:
     		case 42:
     		case 44:
     		case 46:
     		case 48:
     		case 50:
     		case 52:
     		case 54:
     		case 56:
     		case 58:
     		case 60:
     		case 62:
     		case 64:
     		case 66:
     		case 68:
     		case 70:
     		case 72:
     		case 74:
    			/* ADC12MEMx Interrupt - ADC12IFGx */
     			break;
     		case 76:
    			/* ADC12RDY Interrupt */
     			break;
     		default:
     			break;
     	}
    }
    
    #pragma vector=DMA_VECTOR
    __interrupt void dmaIsr(void)
    {
     	switch(__even_in_range(DMAIV, 16))
     	{
     		case 0:
     			break;
     		case 2:
            	/* DMA0 */
            	break;
     		case 4:
            	/* DMA1 */
    
     			/* Simulate delay */
    // 			__delay_cycles(160);
    
     			_stopAdcCollection();
    
    			/* Delay 1 second */
    			TA0CTL |= MC__UP;
    
            	break;
     		case 6:
            	/* DMA2 */
            	break;
     		case 8:
     		case 10:
     		case 12:
     		case 14:
     		case 16:
     			break;
     		default:
     			break;
     	}
    }
    

  • Hey Caleb,

    I just wanted to check in and see if you had a chance to look over the problem I have been experiencing and/or if there is anything else I can provide to help?

    -Chris

  • Hey Chris,

    Caleb is on vacation and will not be responding to E2E posts until 6/19.

    First, MPU registers can only be accessed after the correct password is given so I suggest that you set MPUCTL0 = MPUPW; before and after altering MPUSAM (you could already be altering this from the CCS project properties' MSP430 MPU tab). I also observed that the issue does not occur if MODOSC or SMCLK/2 is used as the ADC clock source instead of SMCLK, the Datasheet does state that the maximum ADC clock frequency for specified performance is 5.4 MHz. Such a high input frequency could be affecting the internal ADC sample and convert machine.

    Regards,
    Ryan
  • Hi Chris,

    Were Ryan's suggestions able to get things back on track?

    Best regards,
    Caleb Overbay

**Attention** This is a public forum