# Why does ADC conversion time vary?

I'm using TI Tiva C TM4C123GH6PM on a Launchpad combined with an AD 9850 DDS to create frequency modulator.

Currently I have an extremely simple program which basically sets up microcontroller's peripherals and the AD9850 and then goes into a loop in which it samples audio signal coming to the microcontroller's ADC and based on the results adjusts the output frequency of the DDS.

My problem is that the loop's execution time is not constant.

The loop is divided into three basic parts: In the first part, I get a sample from the ADC, in the second part, the required settings for the AD9850 are calculated and in the third part those settings are sent to the AD9850.

After doing some cycle counting, it turns out that the second and third part always last for 2670 clock cycles. This leaves me with the only suspect being the first part.

After measuring the execution time of the first part just by itself, I've noticed that it varies from as low as 100 cycles to as much as 8000 cycles.

Here's a nice graph I got from the debugger showing how the number of cycles changes with time:

I did look into the datasheet of the part and into the Tivaware Peripheral Driver Library User's guide and I couldn't find a reason why the sampling would have such drastic jitter. The ADC sampling plus conversion time is listed as 1 microsecond, which is around 80 processor cycles, since I'm running at 80 MHz clock frequency. This makes the loop duration in the area of around 100 to 200 cycles look OK, but I have absolutely no idea what's happening in the case when the time is in the thousands of cycles.

I have also read the errata for the ADC and as far as I can see, none of the numerous problems apply to my case. Also the part I have is an actual TM4 part, not an experimental XM4 part.

Here's the problematic part of the code:

c_start = HWREG(DWT_BASE + DWT_O_CYCCNT); // starts cycle counting
{//Busy wait to be replaced with an ISR at some point
}
//used to store results
c_stop = HWREG(DWT_BASE + DWT_O_CYCCNT); // Ends cycle count
c_dur=c_stop-c_start;//Provides number of cycles, refresh breakpoint goes here


ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);//Pin is PD1=>channel AIN6
//Averages 64 samples and stores them in one FIFO slot,
//has apparently no effect on the conversion time, same results when disabled

HWREG(ADC0_BASE+ 0x038) = 0x40;//This enables hardware dithering

//Uses ADC0, sequence 3=>FIFO with length of one, highest prioroty
//First and last step, selects ADC0, sequence 3, step zero, channel 6,
// enables interrupt and ends sequence
//Enables sequence


UPDATE: I went ahead and did the test with pin wiggling and I got relatively similar results. On this image, a pin goes high and then immediately low as soon as ADC is done. This is taken with dithering and hardware oversampling disabled, with sample rate of 1 MSa/s and FIFO buffer depth of 1.

• Doubt if it's causing the problem but I think to enable dithering the syntax should be more like HWREG(ADC0_BASE+ 0x038) = 0x40. Also I wonder in case it's a problem with the DWT unit setup if you haven't done anything similar already it might be worth toggling an I/O and measure with a scope to take that out of the equation. – PeterJ Jan 4 '14 at 12:13
• @PeterJ I myself am not sure about the syntax... Also what's DWT? I can wiggle pins with HWREG, but even using that, the time it takes for pin to go low after being set high is very long. – AndrejaKo Jan 4 '14 at 12:51
• OK, DWT seems to be Data Watchpoint and Trace and is mentioned in the datasheet. – AndrejaKo Jan 4 '14 at 19:33
• @PeterJ I did some testing and you were right about the dither bit setup. Thanks a lot for catching that bug! – AndrejaKo Jan 4 '14 at 20:43
• This isn't an answer, but the workaround is simple. Arm the ADC after checking for a result rather than before. If no result is ready when you check, re-use the previous one. There's no reason to wait for the ADC -- let it work on getting the next sample while you're processing the previous one. – David Schwartz Jan 5 '14 at 8:15

While there are reasons described by others concerning where your jitter may be coming from, being dependent upon things like conversion time (or even hidden branches in function libraries where each branch takes different times) is just poor practice for real time systems.

If you need low jitter loop time, you need to MAKE IT by creating a timer interrupt to give it to you. If you need speed, the timer should be set at your maximum time through your loops, plus a bit of head room. On a timer interrupt you should service everything that needs to be done based on your last ADC read, then do the next ADC read.

When this happens, the microcontroller does not reset itself. If it did, I would see that in the debugger.

I suspect that you are always running this under the debugger and you have set a watch on adcData because it's interesting data. The IDE is helpfully preserving that between runs. I will further guess that the TI processor supports hardware watchpoints on memory writes, and the debugger is using those to catch updates to adcData. Each time through your loop the debugger hits the watchpoint and stops for as long as it takes for the external debugger to read out the data and resume the chip.

If you kept the bit wiggling and ran without the debugger you could test this theory.

• The image from the oscilloscope was made with the debugger disabled. – AndrejaKo Mar 30 '14 at 19:59

As a sidenote, the ADC acquisition time is supposed to be finite, but I have seen some variance in characterizing components. The datasheet likely gives a typical or a "guaranteed by design". Also, the validity of each aquisition isn't guaranteed due to noise, either from layout or otherwise.

You don't mention interrupts, but I see that you're clearing an interrupt flag in the code. If you haven't yet, try disabling interrupts globally. If any other interrupts are going off, they may be causing this jitter.

With a software-based loop, there are lots of things that can cause irregular execution. The most reliable way to get precisely sampled data is to trigger the ADC using a reliable source like a timer (which I believe you can do with no external hardware on this device). The same timer signal could trigger an ISR, where you can collect and use the result at greater leisure, or it could trigger DMA to copy samples into a buffer of some sort.

I suggest caution with libraries like TivaWare. They are usually written for convenience, not high efficiency. If you can track down the source code, you will probably find that they are doing a remarkable amount of housekeeping in those routines. Very often you will get much better performance by hitting the registers directly.

There are 3 most used types of analog to digital converters.

In this type the input is compared with a reference voltage digitally generated.the reference voltage is increased step by step til the reference voltage is greater than input voltage.So for different input voltages the time take for conversion is different.

In this type the same principle is used but in a smart manner. first input voltage is compared with Va/2 (where Va is the upper limit of input voltage) to find in which half it lies . if it is greater than Va/2 its is compared with 3Va/4 else it is compared with Va/4 to find in which quarter it lies.Something like a binary search in computer programs.

In this type the input voltage is compared with all the voltage levels at the same time and corresponding output is given . The time taken in this type of conversion is constant and does not depend upon the input voltage.

I suggest a Flash ADC would be best for your circuit. If you want work on the same hardware there are two thing you could do.

1. find the maximum time (Ta) the ADC takes for conversion . Set a timer interrupt to any value more than Ta. Each time you request a conversion start the timer, and execute the rest of the code only after the interrupt has occurred . this is not the best way but you will get ADC conversion in a constant time. code structure will look something like this:

• (part 1):
• Request for conversion.
• Set up timer interrupt.
• i=0;
• while (i==0){} //no need of polling