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.

Msp430G2553 multiple ADC operating at the same time?

Hi,

I'm trying to ADC sample at least two pins simultaneously, but I saw only one ADC10MEM register. 

I also tried this code but it does not seem to be working. the values in memory address 0x0200 to 0x0206 does not make any sense. 

One way to work around it might be use ADC10 and ADC12 at the same time? 

#include <msp430.h>

int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
ADC10CTL1 = INCH_3 + CONSEQ_1; // A3/A2/A1, single sequence
ADC10CTL0 = ADC10SHT_2 + MSC + ADC10ON + ADC10IE;
ADC10DTC1 = 0x03; // 3 conversions
ADC10AE0 |= 0x0E; // P1.3,2,1 ADC10 option select
P1DIR |= 0x40; // Set P1.0 output

while(1)
{
ADC10CTL0 &= ~ENC;
while (ADC10CTL1 & BUSY); // Wait if ADC10 core is active
ADC10SA = 0x200; // Data buffer start
P1OUT |= 0x40; // P1.0 = 1
ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
//__bis_SR_register(CPUOFF + GIE); // LPM0, ADC10_ISR will force exit
P1OUT &= ~0x40; // P1.0 = 0
}
}

// ADC10 interrupt service routine
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
//__bic_SR_register_on_exit(CPUOFF); // Clear CPUOFF bit from 0(SR)
}

Thanks!

Holly

  • Holly Liang said:
    ADC10CTL1 = INCH_3 + CONSEQ_1; // A3/A2/A1, single sequence

    A3 A2 A1 A0

    Since you programmed the DTC for 3 values only, you'll get an ADC10 interrupt when the DTC is done, and one more after the ADC has done the last conversion of the sequence.

    Holly Liang said:
    I'm trying to ADC sample at least two pins simultaneously, but I saw only one ADC10MEM register. 

    Sampling is done sequentially, by multiplexing the input pins. Each conversion result is presented in the same ADC10MEM register until the next conversion is complete. The DTC intercepts the conversion interrupt and performs a DMA transfer on each conversion, triggering an interrupt when done with the given number of conversions. If the DTC is inactive, each conversion triggers an interrupt.

    Holly Liang said:
    the values in memory address 0x0200 to 0x0206 does not make any sense. 

    What do you get and what do you expect?

    The values are (or should be) in the range 0 to 1023, where 0 is 0V and 1023 is VCC. At least with your configuration.

    You have disabled the LPM in main. As a result, main doesn't wait for the conversions to be done, and resets and restarts the ADC endlessly before it has time for even one conversion. Entering LPM and letting the ADC10 ISR ending LPM will stop main until the DTC has done its job. Currently (without the synchronization by LPM or alternatively by checking whether the ADC is still busy), you might not get any result at all, as no conversion will ever complete.

  • Hi Jens, 

    What if I don't want to turn off CPU since I'm running a bunch of other things at the same time? Should I put  _NOP after each loop? How many should I put?

    The program is about sample two pins at the same time, and output two different PWM accordingly(for motor control). Now I can sample two channels, or I can output two PWM, but when I put them together, the motor stopped moving. 

    Thank you so much!

    -Holly

  • Holly Liang said:
    Should I put  _NOP after each loop? How many should I put?

    No. The ADC tells you with its interrupt when it is done. In your ISR you can set a global flag telling main that the work is done.

    If you want to do something else in the meantime, then do something else and come back when the ADC has done its job. Don't waste time in waiting.

  • Holly Liang said:
    The program is about sample two pins at the same time

    Not at the same time, but sequentially, one right after the other. Just to make sure you understand the difference.

  • Yes I see. Thanks!

  • Sorry about the delay of reply, but do you mean something like this:

    int done;

    main()

    {

    done =1;// ADC flag

    while(1)
    {

    if(done)
    {

    done=0;
    ADC10CTL0 &= ~ENC;
    while (ADC10CTL1 & BUSY); // Wait if ADC10 core is active
    ADC10SA = 0x200; // Data buffer start
    P1OUT |= 0x40; // P1.0 = 1
    ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start

    }

    pwm1();
    pwm2();

    }

    // ADC10 interrupt service routine
    #pragma vector=ADC10_VECTOR
    __interrupt void ADC10_ISR(void)
    {
    p13= *(int *)0x0200;
    p12= *(int *)(0x0202);
    p11= *(int *)(0x0204);


    done=1;

    }

    }

    pwm1(){blah blah}

    pwm2(){blah blah}

    I set a breakpoint in the ADC isr, but the program never stopped, so I think the ISR never entered.. it never interrupted...

  • Holly Liang said:
    int done;

    volatile int done; Else the compiler might just load done into a register and never see it changing (because in the visible program flow, it is never changed). Interrupts do magic: they may change the system state without any apparent (for the compiler) reason. So anything that doe snot behave like it should from the sequential program execution, must be flagged as 'volatile' to the compiler. Like all hardware registers. And all global variables that are used (changed, but also just read!) by an ISR.

    Holly Liang said:
    I set a breakpoint in the ADC isr, but the program never stopped,

    In your code, I don't see that you
    1) enabled the ADC10 interrupt (set the ADC10IE bit) nor
    2) globally enable interrupts.
    3) tell the ADC to do any move operation by telling it the number of results to move.
    So once the interrupts are enabled, you will get one interrupt after each conversion, and the result will be in ADC10MEM register.

    Holly Liang said:
    ADC10SA = 0x200; // Data buffer start

    Don't use absolute addresses this way. In your case, likely the linker placed 'done' at 0x200 (the start of ram), not knowing that you instructed the DTC to overwrite this location with your ADC results.

    Do something like this:
    short int results[3];
    [...]
    ADC10SA = (int)(void*)(&results);

  • Hi!

    I configured ADC10IE in the main loop but forgot to include it in the last post:

    WDTCTL = WDTPW + WDTHOLD; // Stop WDT
    //ConfigClocks();
    ConfigTimerA();

    ADC10CTL1 = INCH_3 + CONSEQ_3; // A3/A2/A1, single sequence
    ADC10CTL0 = ADC10SHT_2 + MSC + ADC10ON + ADC10IE;
    ADC10DTC1 = 0x03; // 3 conversions
    ADC10AE0 |= 0x0E; // P1.3,2,1 ADC10 option select

    But there were no interrupt.

    Then I added   _enable_interrupt();   and everything works now! But in the example code(original post of this thread), there is no   _enable_interrupt();   yet interrupts are still generated.

    (the original code does not have turn off/on CPU commented out)

    Thank you! 

  • Holly Liang said:

    Then I added   _enable_interrupt();   and everything works now! But in the example code(original post of this thread), there is no   _enable_interrupt();   yet interrupts are still generated.

    (the original code does not have turn off/on CPU commented out)

    Thank you! 

    __bis_SR_register(CPUOFF + GIE); // LPM0, ADC10_ISR will force exit

    The GIE part of that code enables interrupts.

    Also you are actually reading four channels:

    ADC10CTL1 = INCH_3 + CONSEQ_3; // A3/A2/A1, single sequence

    because it reads consecutively down through A0, so you're reading A3/A2/A1/A0

**Attention** This is a public forum