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 Single Channel Repeat Mode: Help Required

Other Parts Discussed in Thread: MSP430F6736

Hello everyone, I have a sinewave of 50HZ 500mVPP and 500mV DC offset. I want to measure it through ADC of MSP430F6736. The problem i am facing is my ADC doesnt go according to sampling rate i want because of following reasons.

1. Using internal clock of ADC shows range of 4.6 to 5.4 MHZ. How to get exactly 5 MHZ ?

2. To give negative terminal to AVSS. wont it make your ground connection oscillate too ? And with that i will have my ADC conversion values wrong. 

3. Any suggestions about getting sampling such that i get 120 points per period ? i will have 156microseconds per sample and 128 samples in 20 mseconds(50Hz).

 

Following code is basically just a check to see if i get repeating values of Sinewave. But instead , i get overflows in ADC and constant values for many conversions .. in range of 37 and 40

#include "msp430f6736.h"

volatile unsigned int ADC_Result[200];

static char i=0;
void main(void)
{
  WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
 
  P9SEL |=BIT1;                            // Enable A/D channel A5.
 
  /* Initialize ADC12_A */
  ADC10CTL0 = ADC10ON+ADC10SHT_12+ADC10MSC; // Turn on ADC12, set sampling time
                                            // set multiple sample conversion
  ADC10CTL1 = ADC10SHP+ADC10CONSEQ_2 + ADC10DIV_7;       // Use sampling timer, set mode
  ADC10MCTL0 |= ADC10INCH_5;
  ADC10IE = 0x01;                           // Enable ADC12IFG.0
  ADC10CTL0 |= ADC10ENC;                    // Enable conversions
  ADC10CTL0 |= ADC10SC;                     // Start conversion
 
  __bis_SR_register(LPM4_bits + GIE);       // Enter LPM4, Enable interrupts
  __no_operation();                         // For debugger 
}

#pragma vector=ADC10_VECTOR
__interrupt void ADC10ISR (void)
{
 
  while (i<200 && ADC10IFG0)
  {
    ADC_Result[i] = ADC10MEM0;             // Move results
    i++;                                // Increment results index, modulo; Set Breakpoint1 here
  }
   
 
}
Any help would be great. Later on i ll take FFT and then will perform Power Calculations. I did all this in MATLAB very quickly, MSP430 is whole new ball game. Please suggest something .

Thnx

  • Sarmad Hanif said:
    Using internal clock of ADC shows range of 4.6 to 5.4 MHZ. How to get exactly 5 MHZ ?

    Two ways:

    Either you use SMCLK or ACLK as ADC10CLK. It can be configured. Then it's up to you to ensure that the one you pick has the desired 5MHz. However, you will then control the sampling rate over the S&H time (plus the 11 clock cycles for the conversion). The ADC10SSEL bits control the clock source.

    The other way is to use a tiemr to start the conversion. Then you can control the moment in which a conversion start, independently of the used ADC10CLK (which must be high enough, of course, to finish the conversion before the next trigger comes).
    When using a timer, you can even use teh timers duty cycle to exactly control the sample time. When the timer PWM signal is high, the samplign input is open, when the signal goes low, the samplign input is closed and the conversion begins. 11 ADC10CLK cycles late you have the result.

    The available triggers for ADC10SHSx are listed in the device datasheet, in the timer I/O table found under short-form description->Peripherals->TA0 (or TB0). Usually they are TA0CCR1, TB0CCR0 and TB0CCR1.

    How to set up a timer for PWM is explained in the family users guide, timer section. And in several threads in this forum.

    Sarmad Hanif said:
    To give negative terminal to AVSS. wont it make your ground connection oscillate too ? And with that i will have my ADC conversion values wrong. 

    AVSS is internally connected to DVSS through a clamp diode. The reason for different VCC/VSS pins is to allow a better filtered version of the digital supply voltage for the analog circuitry, so that the small (<0.1V) voltage ripples caused by the CPU and other components do not affect the analog work.

    MSPs are not made for any negative input voltage on any pin, except for the few devices especially designed for power metering. (with multiple 1-channel SD16 ADCs)
    You'll need to use a level shifter for the input if you have symmetrical signals.

    Sarmad Hanif said:
    Any suggestions about getting sampling such that i get 120 points per period ? i will have 156microseconds per sample and 128 samples in 20 mseconds(50Hz).


    For the timing, I already answered.
    128 samples per full wave means 128*50 = 6400 samples per second or one sample every 156,25µs. with timer PWM control that's simple. You can even control how long you want the sampling input open.
    When using the S&H timer, things are more complicated. On 4MHz clock, you'll have 625 ticks per conversion. 614 if you remove the 11 ticks for the conversion itself. There is no '614' setting for the S&H time. Other frequencies won't give you an integer number of ticks for your desired frequency, causing a drift in the sample frequency to 50Hz ratio. Well, the 50Hz aren't that precise anyway. So you might be fine with this.

    You can try to figure out a clock frequency you can create (DCO isn't stable and precise enough), a clock divider(1..8) and an available S&H time (plus 11 ticks)  so it makes up to your 6400Hz sampling frequency.
    However, using a timer is much easier.

    With a 4MHz clock for the tiemr, you can start a conversion exactly every 156,25µs. And it is you choice whether you want to use one of the available S&H settings or directly control the S&H tiem with the timer PWm duty cycle. (then with 1/4µs precision, from 1/2µs to 150µs)

    Sarmad Hanif said:
    Later on i ll take FFT and then will perform Power Calculations.

    Then I'd suggest using an MSP430FE42x device. They are optimized for energy metering and have even hardware support for some calculations. And allow negative (full-wave) input. They even have LCD drivers. And a much higher resolution (16 bit) and therefore precision for the power calculation.

    Anyway, your code doesn't do what you think it is doing.

    Yu set up the ADC, but then inside the ISR, which is entered when the ADC has done one conversion loop, you simply copy the result of the last conversion 200 times into the result array. While during this time only a few conversions have been done.at all, so it is most the same value over and over again until the next conversion is ready.
    it's even worse. ADC10IFG0 is a constant with the value 1, and i, which is a signed char, is always <200, so your while loop never ends, filling the buffer from -128 to +128, causing havoc on the negative index.

    The right way would be:

    volatile unsigned char i; // static is superfluous, all globals are static. But a (signed) char can never be >127.

    __interrupt void ADC10ISR(void)
    {
      ADC_Result[i] = ADC10MEM0; // this clears the ADC10IFG0 bit)
      i++;
      if(i==200)
      {
        disable ADC, exit LPM, hit a breakpoint or whatever
      }
    } // exit the ISR so it can be called again when the next conversion is done.

  • Hi, i have done as you said .. I am getting values like a sinewave now . but i again have some questions.

    1. How to make these values as accurate as possible. What you told before i could nt understand it .its converting into integers when clearly values are float.

    2. My samples i am not sure that it takes exactly 128 sample in one cycle or it take 126 or 129 .. i only give 50 usecond in total for S&H and 74usecond for 5MHz ADC10Clock for conversion. plus i assume 1-2 useconds for settling and all. which makes 156 microsecond apporximately. But i want to make this more accurate.

    3. And is there a faster way to check if my values are plotted perfectly . I take printf (which is very slow) and then copy to excel and then plot a graph.

    #include "msp430f6736.h"
    #include "stdio.h"
    volatile long int ADC_Result[1000];
    volatile unsigned long int i=0;
    //void configure_clocks(void);
    void ADC_configure(void);
    void main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT 
      //  ACLK = REFO = 32.768kHz, MCLK = SMCLK = (Default DCO)/2 = (2MHz/2) = 1MHz
      ADC_configure();
    }

    void ADC_configure(void)
    {
      P9DIR &= ~BIT1;
      P9SEL |= BIT1;  // using P9.1 as an input to channel 5 of ADC.      
      
      ADC10CTL0 &= ~ADC10ENC;
      ADC10CTL0 |= ADC10MSC + ADC10ON;  // repeated single channel conversion and turning on ADC
      ADC10CTL1 |= ADC10SHS_1 + ADC10CONSEQ_2; //   repeat seq. single channel used Ta0.1 trig sample start
      ADC10MCTL0 |= ADC10INCH_5 + ADC10SREF_1; // channel 5 selected Vref = 1.5
     
      // By default, REFMSTR=1 => REFCTL is used to configure the internal reference
      while(REFCTL0 & REFGENBUSY);              // If ref generator busy, WAIT                                         
      REFCTL0 |= REFVSEL_0+REFON;               // Select internal ref = 1.5V
                                                // Internal Reference ON  
      __delay_cycles(40000);                      // ref delay                
      ADC10IE |= ADC10IE0;                        // Enable ADC conv complete interrupt
      ADC10CTL0 |= ADC10ENC;
      // ADC10 Enable
      // ADC conversion trigger signal - TimerA0.0
      TA0CCR0 = 155-1;                           // 1ms period for 1MHZ SMLCK
      TA0CCR1 = 50;                           // TA0.1 ADC trigger
      TA0CCTL1 = OUTMOD_6;                      // TA0CCR0 reset/set                                        
      TA0CTL = TASSEL_2 + MC_1 + TACLR;         // SMCLK, upmode, clear TAR
      __bis_SR_register(LPM0_bits + GIE);                       //enable interrupts
      __no_operation();                         // For debugger
    }
    // ADC10 interrupt service routine
    #pragma vector=ADC10_VECTOR
    __interrupt void ADC10ISR(void)
    {
      ADC_Result[i] = ADC10MEM0; // this clears the ADC10IFG0 bit)
      i++;
      if(i==1000)
      {
       ADC10CTL0 &= ~ADC10ENC;
       for (i=0;i<=1000;i++)
       {
         printf("\n%ld",ADC_Result[i]);
       }
      }
    }

  • and also i get some irregularities (very small at the positive and negative peaks) how can that be solved ?

  • Sarmad Hanif said:
    and also i get some irregularities (very small at the positive and negative peaks) how can that be solved ?


    How do these irregularities look like? Do you have spikes on the inputs? Or do you have latchup effects due to too-high voltage? Is your referecnes decoupled properly? (some devices require an external reference decoupling/bufferign even for low samplign rates)

    Sarmad Hanif said:
    What you told before i could nt understand it .its converting into integers when clearly values are float.


    Float is a way to express fractional numbers by a mantisse and an exponent. Basically you have the numbers stored as if they are an integer, and a second number that tells you how many of these digits are behind the decimal point.
    However, workign with these nubmers is complex and slow.

    The alternative is to make it yourself, limited to the precision you need.

    So you can multiply a value by 8 (which is basically a shift by 3 bits, a fast operation).
    Then each 'count' is 0.125 worth instead of '1'.

    If you add or subtract these values, all is straight. If you multiply or divide them, you need to do a shift by 3 bits after the operation. Hwoever, it is still with the speed of an integer operation.

    Instead of using 8 as a factor for 1/8 precision (3 bits), you can go for 10 bits for 1/1024 precision (~ 3 decimal digits)or even higher.
    As long as you keep an eye on the total nubmer of bits your intermediate calculations results take (mus tnot exceed 32 if using unsigned long ot 31 for signed long), the results are sufficiently precise and lightning fast. Using float or double makes things easier, but by magnitudes slower. And blows up the code quite a bit for the requried multiplicaiton/division functions for floating point math.

    Using float isn't always the highest precision.
    In our metering software, we were counting energy meter pulses. Using a float as a counter (default under Java).
    It worked well until some fine day, one device stopped counting.
    We discovered that this particular device did have a high pulse rate and the software was running for almos ttwo years without reboot.
    Our float variable has reached a point, where due to the high exponent, the minimum step size expressable by teh mantisse was >1. So the result was "x+1 = x".
    Depending on the absolute value, the precision of floats decreases  In many cases, it is far more precise than required, wasting CPU power and space for a precision that is only virtual (your ADC does only produce 10 bit results with an error of +-0.7mV for 1.5V reference, so calculating on 0.0000001mV level won't do you any good). And in other case it is far less than expected (see my story).

  • the values are like ADC's noise. like they are not fully smoothened. And one more thing i get minimum value .1 and maximum value .489 which should be .5 Volts. Should i change the reference ? And while i was doing exact 156 microsecond timer the result was not perfect 128 points in one cycle. i had to manipulate randomly some +-3 to TAXCCR1 to get 128 points . But i want to get it mathematically right too. And precision is required i guess because i will be taking FFT afterwards, which should be manipulated on double i guess. but after the values are stored in integers .. because i take average of 8 cycles to smoothened a little bit our sinewave.

    so i want your opinion on

    1. Perfect sampling , no noise.(suggestions) because then FFT will show unnecessary frequency components.(taking average of cycles is not fast)

    2. Why is that my timer is not acting perfectly and i need to play a bit with it to get desired result . (150microsecond total/55microsecond S&H time)

    Thankyou very much for your time and replies.

    Sarmad Hanif

  • Sarmad Hanif said:
    Why is that my timer is not acting perfectly

    THe timer acts perfectly. It's your clock that doesn't. I don't see you setting up any clock source, so the MSP runs form the internal DCO, a digitally controlled RC oscillator. Which has a quite large tolerance and temperature coefficient.

    So you actually don't know at all on which exact frequency your MSP is running. Nor does the timer. It just gets clock ticks of unknown (and unimportant for the timer) length and counts them.

  • Sarmad Hanif said:
    Why is that my timer is not acting perfectly

    THe timer acts perfectly. It's your clock that doesn't. I don't see you setting up any clock source, so the MSP runs form the internal DCO, a digitally controlled RC oscillator. Which has a quite large tolerance and temperature coefficient.

    So you actually don't know at all on which exact frequency your MSP is running. Nor does the timer. It just gets clock ticks of unknown (and unimportant for the timer) length and counts them.

**Attention** This is a public forum