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.

ADC10 and DTC with continuous read

I am unsuccessfully attempting to take successive measurements and store them to memory on the G2955. My goal is to use the timer to trigger the start of measurements and then rapidly take four and average the result.

My ADC initialization is as follows:

void ADC10_init(void)
{
      ADC10CTL0 &= ~ENC;

      ADC10CTL0 = ADC10IE + MSC + ADC10ON + REFON + REF2_5V + ADC10SHT_0 + SREF_1;

      ADC10AE0 |= 0x7;  // P2.0, P2.1, and P2.2 for ADC. (000111 = 0x7)

      //Continuous data transfer
      ADC10DTC0 = ADC10TB + ADC10CT;

      //Define the number of transfers in each block of data
      ADC10DTC1 = 2;    //Ch 1 down to 0.

      while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active

      ADC10SA = (unsigned int)&ADCData[0];        // Data buffer, start declared as: unsigned int ADCData[5];  

      ADC10CTL0 |= ENC;   // Enable ADC
}

My measurement code is:

     ADC10CTL1 = CONSEQ_2 + ADC10SSEL_2 + ADC10DIV_0 + SHS_2 + INCH_1;

     ADC10CTL0 |= ENC;                      //Enable ADC reading on next trigger of TIMER0_A

      while (!(ADC10CTL1 & BUSY)); //This statement holds the micro until Timer0 CCR0 triggers.
               {
                }

     int c = 0;
     while (ADC10CTL1 & BUSY);      //Micro should stay in this loop unitl I disable the ADC
                {

                while(c <= 4 )
                {
                     if(ADC10IFG && ADC10B1)           //This code should trigger when the first block is full
                         {
                         TempV1 = TempV1 + ADCData[0];  //Store readings 0,1
                         TempI1 = TempI1 + ADCData[1];
                          _bic_SR_register(ADC10IFG);        //_bic_SR_register(mask)  is used to Clear bits in the status register.
                          c++;

                          }

                       if(ADC10IFG && !ADC10B1) //This block should trigger when the second block is full
                           {
                           TempV1 = TempV1 + ADCData[2];    //Store readings
                           TempI1 = TempI1 + ADCData[3];
                            _bic_SR_register(ADC10IFG);
                            c++;
                            }
                   }
                 ADC10CTL0 &= ~ENC;                            //Disable after 4 readings.
                  }
 

Currently, when I step through the code I only enter the block one (first) if statement. For some reason when I step I just loop through that code four times instead of moving on to read block two data. I guess this could be because none of the interrupt timing works well when stepping. My temp variables are also not registering the test signal I am sending into the ADC.

Any advice would be appreciated!

Thanks,

  • Christopher Barth said:
         ADC10CTL1 = CONSEQ_2 + ADC10SSEL_2 + ADC10DIV_0 + SHS_2 + INCH_1;
         ADC10CTL0 |= ENC;                      //Enable ADC reading on next trigger of TIMER0_A

    All those settings except CONSEQx can only be done when ENC is clear. But you have set it right after the initial configuration.

    Christopher Barth said:
                         if(ADC10IFG && ADC10B1)           //This code should trigger when the first block is full

    This is probably not what you want. ADC10IFG is a constant. It's value is 0x04 (BIT2). So this part of the expression is always true.
    What you want is (ADC10CTL0&ADC10IFG) instead, which is 4 or 0 (true or false), depending on whether the ADC10IFG bit is set or not.

    What is ADC10B1?

  • Thank you for the information.

    Yes, I certainly messed up my setting of ADC10CTL1 at the beginning of my read routine. That part of the code was working because I had correctly set the trigger source in the initialization routine.  I am running in a loop taking measurements at two different point triggered by two different CCRs in the timer. The other times I correctly clear ENC.

    "What you want is (ADC10CTL0&ADC10IFG) instead"

    Thank you, I guess that should have been obvious. Now the problem I am having seems to be that ADC10IFG is only triggered once. My understanding is that the ADC and DTC should keep sampling these two channels and moving the data into the array I assigned until I stop the process.  

    I have repeat sequence of channels mode set (conseq_3 bit in ADC10CTL1 register) . Multiple sample and conversion mode is set (MSC bit in ADC10CTL0 register). ADC continuous transfer set (ADC10CT bit in ADC10DTC0 register).

    Is it alright to read and reset the ADC10IFG flag directly? I would rather use a loop the way I am than deal with passing data to and from interrupts. Why might the ADC10IFG flag not be triggering repeatedly?

    My latest code is attached. Thank you again for your advice!

    Chris

     

    TempV1 = 0;  //Reset variables
    TempI1 = 0;
    TempV2 = 0;
    TempI2 = 0;
    
    //Setup ADC for first trigger
    ADC10CTL1 = CONSEQ_3 + ADC10SSEL_2 + ADC10DIV_0 + SHS_2 + INCH_1;
    
    int c = 0; //Counter variable
    
    ADC10CTL0 |= ENC;							//Enable ADC reading on next trigger of TIMER0_AO
    
    while (!(ADC10CTL1 & BUSY));				//This statement holds the micro until Timer0 CCR0 triggers.
    	{
     	}
    
    P3OUT |= BIT2;								  //Set port 2 pin 3. Flag for checking timing. 
    
    while(c < 8 )
    	{
    	while(!(ADC10CTL0 & ADC10IFG))			//ADC10B1: 0  Block 2 is filled.
    		{									//		   1  Block 1 is filled
    		}									//ADC10IFG is set when either block becomes full
    											//ADC10B1 is set when block 1 is full
    	if(ADC10DTC0 & ADC10B1)
    		{
    		TempV1 = TempV1 + ADCData[0];  //Store readings
    		TempI1 = TempI1 + ADCData[1];
    		_bic_SR_register(ADC10IFG);		//_bic_SR_register(mask)  is used to Clear bits in the status register.
    		c++;								
    		}
    
    	else
    		{
    		TempV1 = TempV1 + ADCData[2];	//Store readings
    		TempI1 = TempI1 + ADCData[3];
    		_bic_SR_register(ADC10IFG);
    		c++;
    		}
    	}
    P3OUT &= ~BIT2;
    ADC10CTL0 &= ~ENC;							//Disable after desired number of readings.
    ADC10CTL1 &= ~CONSEQ_3;
    
    while ((ADC10CTL1 & BUSY))				//Verify that ADC has stopped. 
    {
    {
    
    //Change trigger source here. 
    ADC10CTL1 = CONSEQ_3 + ADC10SSEL_2 + ADC10DIV_0 + SHS_1 + INCH_1;
    
    ADC10CTL0 |= ENC;							//Enable ADC reading on next trigger of TIMER0_A1
    
    while (!(ADC10CTL1 & BUSY));				//This statement holds the micro until Timer0 CCR0 triggers at the start of dithering.
    	{
     	}
    P3OUT |= BIT3;
    c = 0;
    
    while(c < 8 )
    	{
    	while(!(ADC10CTL0 & ADC10IFG))
    		{
    		}
    
    	if(ADC10DTC0 & ADC10B1)
    		{
            TempV2 = TempV2 + ADCData[0];  //Store readings
            TempI2 = TempI2 + ADCData[1];
            _bic_SR_register(ADC10IFG);		//_bic_SR_register(mask)  is used to Clear bits in the status register.
            c++;								
            }
    
        else
        	{
        	TempV2 = TempV2 + ADCData[2];	//Store readings
        	TempI2 = TempI2 + ADCData[3];
        	_bic_SR_register(ADC10IFG);
        	c++;
        	}
        }
        
    P3OUT &= ~BIT3;
    ADC10CTL0 &= ~ENC;							//Disable after desired number of readings.
    ADC10CTL1 &= ~CONSEQ_3;
    
    
    Voltage1 = TempV1 >> 3;						//Divide by 8 to average.
    Current1 = TempI1 >> 3;
    Voltage2 = TempV2 >> 3;
    Current2 = TempI2 >> 3;
    

  • Christopher Barth said:
    My understanding is that the ADC and DTC should keep sampling these two channels and moving the data into the array I assigned until I stop the process.  

    I'm assuming you're using a 2x family MSP.

    Christopher Barth said:
    Now the problem I am having seems to be that ADC10IFG is only triggered once.

    When the DTC is active, an interrupt will only happen when the DTC is done. Not for every single conversion (actually, the DTC is 'eating' the interrupts and generates its own when done). Independently of the sequence length.

    Christopher Barth said:
    Is it alright to read and reset the ADC10IFG flag directly?

    Yes, if you don't want to call an ISR and want to do busy waiting instead... Otherwise, the interrupt is automatically cleared on ISR entry or (if not using the DTC) when ADC10MEM is read.

    Christopher Barth said:
    while (ADC10CTL1 & BUSY);      //Micro should stay in this loop unitl I disable the ADC

    I guess, the ';' is not really wanted here. Or else the code will stall at this position

    Christopher Barth said:
    if(ADC10IFG && ADC10B1)           //This code should trigger when the first block is full

    to answer my own question: for ADC10B1, the same is true as for ADC10IFG: it is a constant and therefore always true. It must be replaced by (ADC10DTC0&ADC10B1) in the IF cases.

    And something that completely slipped my attention:

    Christopher Barth said:
    _bic_SR_register(ADC10IFG);

    Sorry, this is complete nonsense. The _bic_SR_register intrinsic is for clearing bits in the processor status register (which is outside the scope of the C language, which does not know about processors or their registers).
    So you are clearing BIT2 of the status register. (ADC10IFG is just an alias for BIT2, as it is BIT2 in the ADC10CTL0 register.) Luckily, it doesn't do any harm. But won't clear the ADC10 interrupt flag. To do so, use

    ADC10CTL0 &= ~ADC10IFG;

  • Thanks a LOT for the help. Yes, I am very inexperienced at this type of thing. Either it has been too long since I was in programming classes, or they don't really cover these types of details.

     

    All the best!

     

  • Christopher Barth said:
    Either it has been too long since I was in programming classes, or they don't really cover these types of details.

    Intrinsics are compiler/platform specific. They are not covered by any programming classes, except if they are focused on this platform. So no wonder if you don't know how to use them.
    The critical thing is that you did use them without knowing what they really do :)

    The other things, well, the users guide covers them, but if you didn't read it completely, you'll miss many details. There are many cross-dependencies you'll only see if you collected some experience or did read all chapters a few times (best both, but most people won't). So you're not alone. That's why the forum exists.

**Attention** This is a public forum