MSP430F6777A: Multi-channel ADC Reading Problem using DMA

Part Number: MSP430F6777A

I am doing a 6 channels ADC reading using the DMA method on the MSP430F6777A microprocessor. I show the array adc_table[] as Destination Address. I save the read ADC values to adc_table[] in the following order.

adc_table[] = [ADC_Channel5, ADC_Channel4, ADC_Channel3, ADC_Channel2, ADC_Channel1, ADC_Channel0]

But after a while in runtime, ADC values start to be recorded as below:

adc_table[] = [ADC_Channel3, ADC_Channel2, ADC_Channel1, ADC_Channel0, ADC_Channel5, ADC_Channel4]

How can I solve this problem?

  • Hi Keles

    Could you please refer and check the example code used single sequence and DMA?

    msp430f677xA_adc10_10.c of https://www.ti.com/tool/download/SLAC648

    Thanks

  • I solved the error by editing the interrupt as follows.

    #pragma vector = TIMER1_A1_VECTOR
    __interrupt void TIMER1_A1_ISR (void)
    {
    //Any access, read or write, of the TAIV register automatically resets the
    //highest "pending" interrupt flag
    switch ( __even_in_range(TA1IV,14) ){
    case 0: break; //No interrupt
    case 2: break;
    case 4: break; //CCR2 not used
    case 6: break; //CCR3 not used
    case 8: break; //CCR4 not used
    case 10: break; //CCR5 not used
    case 12: break; //CCR6 not used
    case 14: break;
    default: break;

    }
    __bis_SR_register(GIE);
    //LPM4_EXIT;
    while (ADC10_A_isBusy(ADC10_A_BASE)) ; // Wait for the ADC to be free (not busy)

    ADC10_A_startConversion(ADC10_A_BASE, ADC10_A_SEQOFCHANNELS); // Enable and start the ADC conversion

    __delay_cycles(5000); // Delay to allow the conversion to complete

    TA1IV = 0;
    }

    I waited for ADC to be free before start conversion and added delay after start conversion.

    In my current software, I also do different operations in Timer Interrupt. Will using while() and __delay_cycles() cause any problems?

  • Hi Keles

    Thanks for your feedback!

    >> I solved the error by editing the interrupt as follows.

    I am glad to know the error has been solved

    >> Will using while() and __delay_cycles() cause any problems?

    while (ADC10_A_isBusy(ADC10_A_BASE))

    __delay_cycles(5000); // Delay to allow the conversion to complete

    I think the while loop is OK

    I think delay is OK if the CPU loading is OK

  • The better way to fix this is to adjust the ADC settings (clock divider and such) so that it will always complete before the next trigger. Adding extra margin if the clock has a lot of intrinsic variability and is asynchronous to the timer clock.

    Waiting inside the timer interrupt is a bad idea. You could easily end up waiting past the next timer interrupt. Since you enable interrupts again within the ISR this could cause a cascade of stack frames exhausting the available SRAM.

  • Hi Li and Schultz,

    Thank you for your feedback.

    What do you think about following method?

    Using an Interrupt Flag: Set a flag within the ISR and handle the long-running operations in the main loop by checking this flag.

    For example:

    volatile bool adc_conversion_needed = false;
    
    #pragma vector = TIMER1_A1_VECTOR
    __interrupt void TIMER1_A1_ISR(void)
    {
        switch (__even_in_range(TA1IV, TAIV_TAIFG))
        {
            case TAIV_TACCR1:
                break;
            case TAIV_TACCR2:
                break;
            case TAIV_TACCR3:
                break;
            case TAIV_TACCR4:
                break;
            case TAIV_TACCR5:
                break;
            case TAIV_TACCR6:
                break;
            case TAIV_TAIFG:
                adc_conversion_needed = true; // Set the flag
                break;
            default:
                break;
        }
        TA1IV = 0; // Clear the interrupt flag
    }
    
    void main(void)
    {
        WDT_A_hold(WDT_A_BASE);
    
        // Timer and ADC initialization code
    
        while (1)
        {
            if (adc_conversion_needed)
            {
                adc_conversion_needed = false; // Clear the flag
    
                while (ADC10_A_isBusy(ADC10_A_BASE));
    
                ADC10_A_startConversion(ADC10_A_BASE, ADC10_A_SEQOFCHANNELS);
    
                __delay_cycles(5000); // Delay to allow the conversion to complete
            }
    
            // Other main loop code
        }
    }

  • Usually when you use DMA it is so that you can do something else at the same time. Instead of spinning around doing nothing, you could have just polled the ADC for the results.

    But this is just a code fragment and given the rest it might make some sense. Maybe.

    Oh, "TA1IV = 0" is at best pointless and at worst a hazard. Reading TA1IV automatically clears the interrupt.

  • In my code, I do different operations in the interrupt. Like this;

    case TAIV_TAIFG:

         my_function1();

         my_function2();

         my_function3();


         adc_conversion_needed = true; // Set the flag


    break;

**Attention** This is a public forum