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.

>SOLVED< MSP430G2553 ADC10 repeated single channel with DTC block transfer - why is ADC10BUSY still set after block transfer has finished?

Hi everyone!

Question about the ADC10 and it's ADC10BUSY flag in ADC10CTL1:

I use the ADC10 with the Data Transfer Controller to sample a single channel multiple times and use the single block transfer mode to store the data. Here are some code lines

void main( void )
{
  ...

  // Vref+/Vss, 64 clocks S&H, ref output on, multiple sample and conversion, 2,5V ref, ref on, ADC10 on, interrupt enable
  ADC10CTL0  = (SREF_1 | ADC10SHT_3 | REFOUT | MSC | REF2_5V | REFON | ADC10ON | ADC10IE);
  // Channel A0 selected, ADC10CS bit as trigger, clock divider 0, ADC10OSC, repeat single channel
  ADC10CTL1  = (INCH_0 | SHS_0 | ADC10DIV_0 | ADC10SSEL_0 | CONSEQ_2);
  // Analog input A0
  ADC10AE0   = 0x01;

  ...

  __bis_SR_register( GIE ); // Enable global interrupts

  // Set number of samples
  ADC10DTC1 = 8;
  // Set start address
  ADC10SA = &adc_average_samples[0];
  // Enable and start conversion
  ADC10CTL0 |= (ENC | ADC10SC);

  while( 1 )
  {
    ...
  }
}

#pragma vector = ADC10_VECTOR
__interrupt void ADC10_ISR ( void )
{
  ADC10CTL0 &= ~ENC; // Disable conversions

  //while( ADC10CTL1 & ADC10BUSY ); // Only for testing

  if( ADC10CTL1 & ADC10BUSY )
  {
    err = 1; // <- Is entered directly after the first block transfer, so ADC10BUSY is still set when transfer is finished - WHY?
  }
}

I recognize that sometimes the ADC conversion stop randomly - there isn't another interrupt generated. In the ISR I set a flag to process the data in the main. After processing, another block transfer is initiated (not shown here). But this sometimes does not happen...there is no further interrupt - according to the user's guide, you should first check for ADC10BUSY to be cleared before changing anything of the ADC's configuration. This might be the problem, because I see ADC10BUSY set when entering the ISR and I then change things (switch to another channel - also not shown here).

But why is ADC10BUSY still set here? The IFG is set after a complete block transfer, then the conversions are stopped...what is the ADC still doing? I don't see any loss of interrupts if I insert the while( ADC10CTL1 & ADC10BUSY ); inside the ISR for testing purposes. The bit gets cleared soon after entering the ISR.

  • Well, maybe I have the answer to my own question...I think I misunderstood the working principle - since I set CONSEQ_2 for repeated single channel, the ADC would perform samples all the time if I would not disable ENC. By setting the DTC count to eight in my case, the DTC simply jumps in and takes the next eight samples that come from the ADC. After that, the DTC waits for another trigger, but the ADC would continue running in the background. Therefore the ADC10BUSY bit is set because the ADC is performing the next (9th) sample. By resetting ENC it stops after the currently running conversion. But since initiating a new transfer while busy is prohibited, I have to wait for UCBUSY to get cleared first.

    Am I correct with this consideration?

  • So I think I can close this thread as a monolog :) It works now without missing interrupts - I have changed my code and will share it with you:

    ...
    
    #define ADC_AVERAGE_COUNT 8
    
    #define ADC_FLG_WAIT_FOR_IDLE     0x80
    #define ADC_FLG_RESULTS_AVAILABLE 0x40
    #define ADC_FLG_CHANNEL_1_ACTIVE  0x01
    #define ADC_FLG_CHANNEL_2_ACTIVE  0x02
    
    volatile uint8_t adc_status = ADC_FLG_CHANNEL_1_ACTIVE;      // Start with channel (A0)
    
    ...
    
    volatile uint16_t adc_average_samples[2][ADC_AVERAGE_COUNT]; // Array for holding multiple samples of two channels
    
    ...
    
    void main( void )
    {
      ...
    
      // Vref+/Vss, 64 clocks S&H, ref output on, multiple sample and conversion, 2,5V ref, ref on, ADC10 on, interrupt enable
      ADC10CTL0  = (SREF_1 | ADC10SHT_3 | REFOUT | MSC | REF2_5V | REFON | ADC10ON | ADC10IE);
      // Channel A0 selected, ADC10CS bit as trigger, clock divider 0, ADC10OSC, repeat single channel
      ADC10CTL1  = (INCH_0 | SHS_0 | ADC10DIV_0 | ADC10SSEL_0 | CONSEQ_2);
      // Analog input A0 and A3 enabled
      ADC10AE0   = (0x01 | 0x08);
    
      ...
    
      __bis_SR_register( GIE );                               // Enable global interrupts
    
      ADC10DTC1 = ADC_AVERAGE_COUNT;                          // Set number of samples
      ADC10SA = &adc_average_samples[0][0];                   // Set start address
      ADC10CTL0 |= (ENC | ADC10SC);                           // Enable and start conversion
    
      while( 1 )
      {
        if( adc_status & ADC_FLG_WAIT_FOR_IDLE )              // Request to wait for ADC idle again
        {
          if( !(ADC10CTL1 & ADC10BUSY) )                      // Check if ADC is not busy
          {
            adc_status &= ~ADC_FLG_WAIT_FOR_IDLE;             // Clear flag
    
            if( adc_status == ADC_FLG_CHANNEL_1_ACTIVE )      // Channel 1
            {
              adc_status = ADC_FLG_CHANNEL_2_ACTIVE;          // Set new status
              ADC10CTL1 &= ~INCH_0;                           // Disable channel A0
              ADC10CTL1 |= INCH_3;                            // Enable channel A3
              ADC10SA = &adc_average_samples[1][0];           // Set start address
              ADC10CTL0 |= (ENC | ADC10SC);                   // Enable and start conversion
            }
            else if( adc_status == ADC_FLG_CHANNEL_2_ACTIVE ) // Channel 2
            {
              adc_status = ADC_FLG_CHANNEL_1_ACTIVE;          // Set new status
              ADC10CTL1 &= ~INCH_3;                           // Disable channel A3
              ADC10CTL1 |= INCH_0;                            // Enable channel A0
              ADC10SA = &adc_average_samples[0][0];           // Set start address
              adc_status |= ADC_FLG_RESULTS_AVAILABLE;        // Set flag to signal new data to process
            }
          }
        }
    
        if( adc_status & ADC_FLG_RESULTS_AVAILABLE )          // Request for processing data
        {
          ... // Process data
    
          adc_status &= ~ADC_FLG_RESULTS_AVAILABLE;           // Clear flag
          ADC10CTL0  |= (ENC | ADC10SC);                      // Enable and start conversion
        }
    
        ... // Do other things in main
      }
    }
    
    #pragma vector = ADC10_VECTOR
    __interrupt void ADC10_ISR ( void )
    {
      ADC10CTL0  &= ~ENC;                                     // Disable conversions
      adc_status |= ADC_FLG_WAIT_FOR_IDLE;                    // Set flag to signal waiting for ADC idle
    }

    Dennis

  • This is free sample code by the way :-)
  • Nice to see a thread with ‘loud thinking’ or ‘talking to myself’ <smile>.

    Something to the Average;
    Taking 8 (new) samples and average them is slow (need to wait for 8 samples) and the result is nearly the same as stripping off a few of the least significant bits. A much better way is ‘the moving 8’ method, but there are more methods depending on your needs.
    When randomly taking samples the result is in most cases useless. When for example the first 8 where ready in 100uS and the next 8 in 10mS (due to other processes in your system), you can’t compare both and when you want to use them to control a motor, the motor will be nervous and will be never running smoothly. Always use a timer to initiate a sampling cycle or another time-based way to collect data.

  • I have two independent pots that I read - for this it is OK. Otherwise I agree with you - timed samples are useful in most applications and the moving average is one of the methods I use very often, too.

    Dennis

**Attention** This is a public forum