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.

Reading from multiple channels using ADC10 and DTC

Other Parts Discussed in Thread: MSP430G2231

Hi,

I am trying to read voltage values on two channels (A1, A2; i.e. Pin 4 and Pin 5), and transfer the result to the AP connected to a PC. Although I can successfully read from single channel, I am unable to do two channels. 

 

Here is my code:

while (1) {
    __bis_SR_register(LPM3_bits+GIE);  // LPM3 with interrupts enabled   
      SMPL_Ioctl( IOCTL_OBJ_RADIO, IOCTL_ACT_RADIO_AWAKE, 0); 
//get radio ready...awakens in idle state

      ADC10CTL1 = INCH_2 + CONSEQ_3;
      ADC10CTL0 = REF2_5V + SREF_1 + ADC10SHT_2 + REFON + ADC10ON + ADC10IE;

      for (countDown = 240; countDown > 0; countDown--); //delay to allow references to settle
    
      ADC10AE0 |= 0x06; //110  
      ADC10DTC1 = 0x10;                         // 16 conversions
        
      while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
      ADC10SA = 0x200;                        // Data buffer start
       
      ADC10CTL0 |= ENC + ADC10SC; //Sampling and conversion start
      __bis_SR_register(CPUOFF + GIE);        // LPM0 with interrupts enabled
     
      //read result
      readPtr = (int *)0x200; //same value as ADC10SA
     
      int ri, ii;
      for (ri = 0, ii=0; ri <16; ri++) { //this loop is similar to sample temperature sensor code
        incomingVoltage[ii++] = (*(readPtr + ri))&0xFF;
        incomingVoltage[ii++] = ((*(readPtr + ri))>>8)&&0xFF;
      }
 

      ADC10CTL0 &= ~ENC;
      ADC10CTL0 &= ~(REFON + ADC10ON);        // turn off A/D to save power

      rc=SMPL_SendOpt(sLinkID1, incomingVoltage, sizeof(incomingVoltage), SMPL_TXOPTION_ACKREQ))) //send message
     

Following are my questions:

1. Is it a good idea to do all of this in a infinite loop?

2. My understanding is: the code after the line '__bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled' (i.e. the result reading part) will be hit only after the conversions are done and the results are written into memory. Is that correct? I am afraid my understanding of interrupts is still shaky..

3. I do get 'peer connected' message in the console, but none of the data transfer messages. Any help in debugging this would be greatly appreciated.

 

Thanks!

  • Chaitanya Kulkarni said:
    Is it a good idea to do all of this in a infinite loop?

    No. You usually init the ADC before enterign the loop.

    Also, after enablign the reference, it takes some time until the reference has settled. Your loop is probably not long enough, and has a high probability to be optimized away by teh compiler (it has no other effect than setting countDown to 0). user _-delay_cycles() instead, or bettet set up and use a timer for your timings and delays.

    Chaitanya Kulkarni said:
    My understanding is: the code after the line '__bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled' (i.e. the result reading part) will be hit only after the conversions are done and the results are written into memory. Is that correct?

    Partly. THis line holds the MSP until an interupt of any source occurs. The interrupt handler will then decide whether to revive the main execution or fall back to sleep on ISR exit.

    If you do not program teh ADC for triggering an interrupt on conversion complete (or a DTC transfer complete), and provide an interrupt function to handle this event, your code will fall to endless sleep here.

    If you do not use interrupts, replace the LPM line with a while loop that waits for the proper IFG bit to be set (e.g. DTC block transfer complete).

    You don't write which MSP you use. From the use of INCH_x in ADC10CTL1 I guess it is an 2x device (there is no INCH setting in ADC10CTL1 for 5x devices). However, without this info, I cannto check the pin usage. There are >300 different MSPs with different pinouts.

     

  •  Thanks for the detailed reply!

    You are right, it is a 2x device. Here's the Users' Guide I am following: http://rfic.eecs.berkeley.edu/ee42/labs/slau144e.pdf

    I have changed the code to utilize ADC10IFG setting. This is how it looks now:

    int * readPtr = (int *)0x200;
    ADC10CTL1 = INCH_2 + CONSEQ_1;
    ADC10CTL0 = REF2_5V + SREF_1 + ADC10SHT_2 + REFON + ADC10ON + ADC10IE;
    for (countDown = 240; countDown > 0; countDown--); //delay to allow references to settle
         
    ADC10AE0 |= 0x06; //110
         
    ADC10DTC1 = 0x02;                         // 2 conversions
    ADC10SA = 0x200;                        // Data buffer start, same as readPtr above
           
    ADC10CTL0 |= ENC + ADC10SC; //Sampling and conversion start
    while(!(ADC10CTL0 & ADC10IFG)); // wait till the block conversion is complete
    ADC10CTL0 &= ~ADC10IFG; //reset ADC10IFG
    //code for reading from readPtr

    There are no interrupts now, and I wait till ADC10IFG bit is set, indicating single conversion of input channels A2 and A1.

    However, this isn't giving any results; I never reach the 'code for reading from readPtr' part. It doesn't matter whether ADC10IE is set or not.

    Could you please have a look and let me know what is it that I am doing wrong?

    Thanks  a lot!

    EDIT: I get the results if I set ADC10DTC1 to 0x01... but then, I get only channel A2 readings and not channel A1.

  • Chaitanya Kulkarni said:
    You are right, it is a 2x device

    Which one? They all differ here and there, and there are tons of them. They all share the same pool of hardwar emodules, but implement only part of it and in different numbers, so the specific device might be important.

    Chaitanya Kulkarni said:
    int * readPtr = (int *)0x200;

    Why do you define a specific address? This way, you do not reserve any memory. The linker may put other variables at the same place. I'm not even sure whether there is ram at this location (depends on the specific MSP device).

    If you want to store two results there, just use
    unsigned int results[2];

    Chaitanya Kulkarni said:
    ADC10CTL1 = INCH_2 + CONSEQ_1;

    This will read Channel 0,1 and 2. So you'll get three conversions with a superfluous channel 0 as the first. There is, however, no way to avoid this as the sequence always starts with channel 0 and continues to the programmed channel. So you should increase teh index of results to three and also program the DTC to three. Adn igore the result that ends up in results[0].

    Chaitanya Kulkarni said:
    for (countDown = 240; countDown > 0; countDown--);

    THis is no reliable delay. The compiler might optimize it to a 'countdown=0' or even drop it totally, as countdown ins't use later. Use __delay_cycles() for a delay of a fixed number of processor cycles, or better use a timer for delays.

    Chaitanya Kulkarni said:
    ADC10CTL0 = REF2_5V + SREF_1 + ADC10SHT_2 + REFON + ADC10ON + ADC10IE;

    You should set ADC10ON in a signel instruction before doing anything else.

    Chaitanya Kulkarni said:
    ADC10DTC1 = 0x02;

    Change it to 3, as you'll get an unwanted conversion of Channel A0 as the first result.

    Chaitanya Kulkarni said:
    ADC10SA = 0x200;

    With the above array definition, use
    ADC10SA = (int)result;

    Chaitanya Kulkarni said:
    I wait till ADC10IFG bit is set, indicating single conversion of input channels A2 and A1

    No. Since you use the DTC, the individual conversion will not set this bit (DTC will immediately read the result which is resetting the bit before it can appear in the register). The bit is set when the DTC has done its job and copied (in your code) 2 conversion results to readPte and readPtr+2. There is, however, a third conversion still in the works at this point, and since the DTC is done, it will also set the IFG bit when finished.

     

  • After a bit of a struggle, I got this working. Answers here helped A LOT. In the end, I removed a lot of superfluous code, and wrote the ADC10 part similar to TI's samples.

    This is the code that can read successfully from 4 pins simultaneously:

    unsigned int res[5]; //for holding the conversion results
    void main (void)


      //code for connecting to AP
     
      //reading voltage
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
      ADC10CTL1 = INCH_4 + CONSEQ_1;            // A4/A3/A2/A1/A0, once multi channel
      ADC10CTL0 = REF2_5V + ADC10SHT_2 + MSC+ REFON + ADC10ON + ADC10IE; //2.5 reference voltage
      ADC10AE0 = 0x1F;                          // P2.0,1,2,3,4 ADC option select
      ADC10DTC1 = 0x5;                         // 5 conversions
      P1DIR |= 0x01;                            // Set P1.0 output

      for (;;)
      {
        P1OUT |= 0x01;                          // Set P1.0 LED on
        ADC10CTL0 &= ~ENC;
        while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
        ADC10SA = (int)res;                        // Data buffer start
        ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion ready
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit
        P1OUT &= ~0x01;                         // Clear P1.0 LED off
      }
     
    }

    /*------------------------------------------------------------------------------
    * ADC10 interrupt service routine
    ------------------------------------------------------------------------------*/
    #pragma vector=ADC10_VECTOR
    __interrupt void ADC10_ISR(void)
    {
        //code from reading from res and sending it to AP
      __bic_SR_register_on_exit(CPUOFF);        // Clear CPUOFF bit from 0(SR)
    }

     

    THANKS for all the help!

     

     

     

  • One thing to add: after activating the internal reference, it requires some settling time. Typically ~100µs. If you start conversion ebfore this, the first readings will be invalid (and the settling time expands). So unless your MCLK runs on 32kHz, you should add a small delay after enabling the reference.

    p.s.: your code actually reads from 5 pins, even if you don't need one of the 5 results :) And you shouldn't say 'simultaneously' as the samples are taken one after another. For a simultaneous sampling, the respective number of individual ADC units would be required. (for energy metering applications, this is an importand difference and the reason why there are specific metering chips with up to 7 SD16 units)

  • hi all..

    in my application too i have to read a 5  analog inputs have to take a average of that 5 inputs.. i had use the same code which u all posted earlier in that  fourm. i dont know how to read a 5 values from  memory.. because after reading the value from memory only i can able to take a avg of that 5 value.. can any one help for me to do the average of 5 values.. and also suggest a code to set a time delay as per mr.jens said earlier..


  • #include  <msp430g2231.h>

    unsigned int res[5]; //for holding the conversion results
    unsigned int x,y=0;
                           
    void main (void)
    {

      //code for connecting to AP
     
      //reading voltage
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
      ADC10CTL1 = INCH_4 + CONSEQ_3;            // A4/A3/A2/A1/A0, once multi channel
      ADC10CTL0 = REF2_5V + ADC10SHT_2 + MSC+ REFON + ADC10ON + ADC10IE; //2.5 reference voltage
      ADC10AE0 = 0x1F;                          // P2.0,1,2,3,4 ADC option select
      ADC10DTC1 = 0x05;                         // 5 conversions
      P1DIR |= 0x01;                            // Set P1.0 output

      for (;;)
      {
        P1OUT |= 0x01;                          // Set P1.0 LED on
        ADC10CTL0 &= ~ENC;
        while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
        ADC10SA = (int)res;                        // Data buffer start
        ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion ready
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit
       
        y = x/5;
       
        P1OUT &= ~0x01;                         // Clear P1.0 LED off
      }
     
    }

    /*------------------------------------------------------------------------------
    * ADC10 interrupt service routine
    ------------------------------------------------------------------------------*/
    #pragma vector=ADC10_VECTOR
    __interrupt void ADC10_ISR(void)
    {
        int i,x=0;
        for(i=0;i<5;i++)
        {
            x=x+res[i];
        }                                    //code from reading from res and sending it to AP
      __bic_SR_register_on_exit(CPUOFF);        // Clear CPUOFF bit from 0(SR)
    }

     i tried a coding like this to take a avg of five analog inputs.

     if i am doing single step  debugging means..based on the in put values .res[0],res[1],res[2],res[3],res[4]  it s not getting changed.. pls help me any one

  • Hi...

    My program also requires 4 analog input and the converted data is to be displayed on lcd. how can I display the adc output value on lcd infinite time?   Is it the case of multiple channel single conversion or multiple channel multiple conversion ?    

    OR can i display infinite times adc output usin while loop every time calling for converting the data..here is my main program :

    #include "Board.h"
    #include "LCD.h"
    #include "adc_humid.h"
    #include "msp430xG46x.h"
    #include "adc_temp.h"
    #include "light_adc.h"
    //
    // Function Declarations
    //
    void initBasicTimer(void);
    void initPortPins(void);
    int adc_humid(void);
    int adc_temp(void);
    int light_adc(void);

    int main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT
    FLL_CTL0 |= XCAP18PF; // Set load cap for 32k xtal
    //ADC12CTL1 = CONSEQ_2;
    initPortPins(); // Initialize port pins
    initBasicTimer(); // Initialize basic timer
    initLCD_A(); // Initialize LCD_A int num;
    int temp[5] ;

    // LCD Segment Mapping
    const UInt8 LCD_Char_Map[] =
    {
    LCD_A+LCD_B+LCD_C+LCD_D+LCD_E+LCD_F, // '0' or 'O'
    LCD_B+LCD_C, // '1' or 'I'
    LCD_A+LCD_B+LCD_D+LCD_E+LCD_G, // '2' or 'Z'
    LCD_A+LCD_B+LCD_C+LCD_D+LCD_G, // '3'
    LCD_B+LCD_C+LCD_F+LCD_G, // '4' or 'y'
    LCD_A+LCD_C+LCD_D+LCD_F+LCD_G, // '5' or 'S'
    LCD_A+LCD_C+LCD_D+LCD_E+LCD_F+LCD_G, // '6' or 'b'
    LCD_A+LCD_B+LCD_C, // '7'
    LCD_A+LCD_B+LCD_C+LCD_D+LCD_E+LCD_F+LCD_G, // '8' or 'B'
    LCD_A+LCD_B+LCD_C+LCD_F+LCD_G, // '9' or 'g'
    LCD_A+LCD_B+LCD_C+LCD_E+LCD_F+LCD_G, // 'A'
    LCD_A+LCD_D+LCD_E+LCD_F, // 'C'
    LCD_B+LCD_C+LCD_D+LCD_E+LCD_G, // 'd'
    LCD_A+LCD_D+LCD_E+LCD_F+LCD_G, // 'E'
    LCD_A+LCD_E+LCD_F+LCD_G, // 'F'
    LCD_B+LCD_C+LCD_E+LCD_F+LCD_G, // 'H'
    LCD_B+LCD_C+LCD_D+LCD_E, // 'J'
    LCD_D+LCD_E+LCD_F, // 'L'
    LCD_A+LCD_B+LCD_E+LCD_F+LCD_G, // 'P'
    LCD_B+LCD_C+LCD_D+LCD_E+LCD_F // 'U'
    };
    int i=0,l,num[3];
    P3DIR |= 0x41; //p3.6 and p3.0 on
    P7DIR |= 0x11; //p7.6 and p7.0 on
    P3DIR |= 0X90;
    P7DIR |= 0X20;


    while(1){
    // display of temperature
     num[0]= adc_temp();
    i=0;
    int temp_in=num[0];
    while(num[0]>0)
    {
    temp[i]=num[0]%10;
    num[0]=num[0]/10;
    i=i+1;
    }
    _BIS_SR(LPM3_bits + GIE); // LPM3, enable interrupts
    for(int j=0;j<i;j=j+1)
    {
    testChar(temp[j],j);
    }
    dispChar(6,12);
    dispChar(5,13);
    dispChar(4,9);
    dispChar(3,11);
    LCDMEM[2 + LCD_MEM_OFFSET] &= ~LCD_Char_Map[8];
    if(temp_in < 22)
    {
    P3OUT |= 0x41;
    P7OUT &=~(0x11);
    }
    else if(temp_in >25)
    {
    P7OUT |= 0x11;
    P3OUT &= ~(0x41); }
    else if(temp_in<=25&&temp_in>=22)
    { P7OUT &=~(0x11);
    P3OUT &= ~(0x41);
    }
    for(l=0;l<3600;l++) //delay
    for(l=0;l<3600;l++) //delay
    for(l=0;l<3600;l++); //delay
    // display tthe humidity
    i=0;
    num[1]= adc_humid();
    int humid_in= num[1];
    while(num[1]>0)
    {
    temp[i]=num[1]%10;
    num[1]=num[1]/10;
    i=i+1;
    }
    _BIS_SR(LPM3_bits + GIE); // LPM3, enable interrupts
    for(int j=0;j<i;j=j+1)
    {
    testChar(temp[j],j);
    }
    dispChar(6,15);
    dispChar(5,19);
    dispChar(4,18);
    LCDMEM[2 + LCD_MEM_OFFSET] &= ~LCD_Char_Map[8];
    LCDMEM[3 + LCD_MEM_OFFSET] &= ~LCD_Char_Map[8];
    if(humid_in < 60)
    {
    P3OUT |= 0x10;
    P3OUT &=~(0x80);
    }
    else if(humid_in >70)
    { P3OUT |= 0x80;
    P3OUT &= ~(0x01); }
    else if(humid_in>=60&&humid_in<=70)
    { P3OUT &=~(0x80);
    P3OUT &= ~(0x10);
    }
    for(l=0;l<3600;l++) //delay
    for(l=0;l<3600;l++) //delay
    for(l=0;l<3600;l++); //delay
    // display of light intensity
    i=0;
    num[2]= light_adc();
    int light_in= num[2];
    while(num[2]>0)
    {
    temp[i]=num[2]%10;
    num[2]=num[2]/10;
    i=i+1;
    }
    _BIS_SR(LPM3_bits + GIE); // LPM3, enable interrupts
    for(int j=0;j<i;j=j+1)
    {
    testChar(temp[j],j);
    }
    dispChar(6,17);
    dispChar(5,19);
    if(light_in <300)
    P7OUT= 0X20;
    else P7OUT= ~(0X20);

    for(l=0;l<3600;l++)
    for(l=0;l<3600;l++)
    for(l=0;l<3600;l++);


    }

    }


    // Basic Timer Interrupt Service Routine
    #pragma vector=BASICTIMER_VECTOR
    __interrupt void basic_timer_ISR(void)
    {
    //P2OUT ^= PIN2+PIN1; // Toggle P2.2,1
    P5OUT ^= PIN1; // Toggle P5.1

    LPM3_EXIT;
    }


    //
    // Initialize port pins
    //
    void initPortPins(void)
    {
    //P2DIR = PIN2+PIN1; // Set P2.2,1 as outputs
    P5DIR = PIN1; // Set P5.1 as output
    P2OUT = PIN1; // Set P2.1 to 1
    }


    //
    // Initialize basic timer
    //
    void initBasicTimer(void)
    {
    // Basic timer setup
    // Set ticker to 32768/(256*128)
    // Enable BT interrupt
    BTCTL = BT_fCLK2_DIV128 | BT_fCLK2_ACLK_DIV256;
    IE2 |= BTIE;
    }


    void testChar( int number ,int q)
    {
    static UInt8 pos = 0;
    if (q==0)
    pos=0;
    int index = number;

    dispChar(pos, index);

    if( pos++ >= LCD_NUM_DIGITS )
    {
    pos = 0;
    }

    }

    and all header files are written in the same way as posting for adc_temp()  :

    #include <msp430xG46x.h>

    volatile unsigned int i;
    volatile unsigned int ADCresult;
    volatile unsigned long int DegC;

    int adc_temp(void)
    {
    WDTCTL = WDTPW|WDTHOLD; // Stop watchdog timer
    P6SEL |= 0x01; // ADC sampled on P6.0
    ADC12CTL0 = ADC12ON | SHT0_6; // Setup ADC12, turn on ref
    // Sample and hold 128 ADC12CLK cycles
    ADC12CTL1 = SHP; // Use sampling timer
    ADC12MCTL0 = SREF_4; // Select Temp Sensor, Vref+
    ADC12IE = 0x01; // Enable ADC12IFG.0
    for (i = 0; i < 0x3600; i++); // Delay for reference start-up
    ADC12CTL0 |= ENC; // Enable conversions
    __enable_interrupt(); // Enable interrupts

    while(1)
    {
    ADC12CTL0 |= ADC12SC; // Start conversion
    __bis_SR_register(LPM0_bits); // Enter LPM0
    DegC = ((((long)ADCresult)*340)/4095);
    //DegF = ((DegC * 9/5)+32); // Calculate DegF
    return DegC;
    __no_operation(); // SET BREAKPOINT HERE
    }
    }

    #pragma vector=ADC12_VECTOR
    __interrupt void ADC12ISR(void)
    {
    ADCresult = ADC12MEM0; // Move results, IFG is cleared
    __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
    }

**Attention** This is a public forum