Capturing high frequency signals with AVR ADC

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Guys,

This is related to one of my earlier posts on capturing a relatively pure sine wave (from a signal generator). I know the ADC limitation of the 8 bit AVR's of 200ksps/13 clock cycles as the ADC sampling frequency limiting the input sine frequency to about 6 kHz for all 10 bits. Are there any ways around this? How can I capture a 22kHz sine wave (not just peak or rms but a full sine wave)?

I was thinking of maybe splitting the sine wave into two waves with a phase shift of 90 degrees and then sampling the positive part in one half cycle and then switching to the 90 degrees phase shifted wave (Q wave) in the next and somehow stitching the two to get a full wave. Would this idea work? Any other ideas to make this possible using only the Atmega ADC?

Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try 8 bit mode and speed up the prescaler and see what you get?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1) Why do you care? What is the purpose of this sampling? As you have surmised, this isn't the forte of the AVR microcontroller. If it is "pure", what will you be accomplishing? [We've been on too many wild goose chases for RES puzzles to fall for this.]
2) Xmega models can do it.
3) What is 200/13?
4) With decent signal drive and sitting on one channel, you can push the 200kHz somewhat.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lets run some numbers...

200KHz input sine wave so one period is 5us. Suppose that you want 10 samples per period (NOT very good for representing a "pure" sine wave). That mans a sample every 0.5us or 2MHz sample rate. With an ordinary AVR running at 16MHz, that is only 8 clock cycles per sample.

Now, what do you need to do in those 8 clocks? Even if it is an external ADC, you will need to get two bytes (you say that you want more than 8 bits) and store them. You will need to do this with parallel data transfer which will take a couple of clock cycles per byte. Then dealing with the write address and actually writing will take a couple more. So, basically, the processor would be 100% occupied while this is happening. By the time it triggers the ADC and counts the number of samples, you might not even be able to do 200KHz.

What are the solutions?

1) use a faster processor
2) use lower resolution
3) subsample to alias the signal to a lower frequency
4) use a fast ADC with a FIFO that will be able to buffer the data for
slower reading, later.

That is about it. There are no "tricks" that will give you a faster effective sample rate.

Jim

PS - subsampling means sampling with a FAST sampler at a slower sample rate. For example, suppose that you sample once every 5.1us. So, each sample is 0.1us later in the sine than the sample before., After 50 samples, you will have 50 points, taken through 50 periods and you will have a high quality reproduction of the original, but at a lower "frequency".

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

He DID say 200ksps, but Karnak The Magnificent told me he meant a/d clock. He REALLY meant 22ksps, which is barely doable, but if I knew it was a sine wave, all you need to know is freq and amplitude, and both those are easy, so whats the REAL problem?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

He DID say 200ksps

What he actually said was 200ksps/13 - that is that the max you can run the AVR ADC clock at is 200kHz and because it takes 13 ADC clocks to make a conversion the max 10 bit sample rate is 200/13 = 15.38kHz which, because of Nyquist, presumably means the max signal you can detect is 7.69kHz so it's well off he 22kHz required.

But as you suggested, switching from 10 to 8 bits allows the 200kHz ADC clock to be increased (though it's never clear by exactly how much) and this must have a fall through effect to the 7.69kHz rate - though I doubt it could be raised to 22kHz by any means.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

(though it's never clear by exactly how much)

Darn that Mr. Nyquist--things would be much easier without him.

There was a thread where someone ran some numbers. They weren't that bad IIRC implying that with decent signal drive OP might get his 45ksps. That would be ~600kHz ADC clock.) I've referred to it several times, but I haven't found the link for a while. Perhaps time to do some digging...

Lee

[edit] I can't for the life of me hunt out the thread with numbers from ADC overclocking. The closest I came was a reference from Lennart suggesting that it was done in early 2007:
https://www.avrfreaks.net/index.p...

Quote:
Several months ago someone tested to run ADC at different speeds above the recommended and posted his results. It DID seem to work surprisingly well when 'overclocked'.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You guys are right, I meant the 200 ksps as the maximum ADC sampling rate.

Is there a chart that shows the sampling frequency vs. number of bits that can be used? If there isn't I will try and characterize that on the breadboard. Of course there will be some error because I will be using the UART to look at the captured wave. So far with the UART, I have captured about 2kHz successively and haven't tried to go higher than that.

@Bob,
I will try 8 bit mode with smaller prescalar.

I guess my other option would be to alias the signal to a lower frequency and have a multiple in software to get the real frequency.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's my mental model: at 200k clocks per sec (5usec), the 1.5clocks/7.5usec alloted for acquire-sample-within-1/1024th-of-the-input-value allows time for a 10 bit acquisition, and the 13 clocks allows for a 10 bit conversion. If you select 8 bits, you only need to wait 8/10 as long for acquisition time AND for conversion time, so you can raise the a/d clock with the prescaler. Also, acquisition time is fastest when a/d input is driven from lo impedance source. 10K source impedance probably takes full settling time. 1k would take less.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What, EXACTLY, do you wish to measure?

The frequency, the amplitude, the On/Off time of the signal, etc.? Does the sin change, how fast do you need to track it?

Sampling it to reproduce it is very different from detecting it presence, or amplitude, etc., where other methods apply.

You could also inject the signal and a "local oscillator" signal into a mixer, down shift the frequency, and read the new output. Like building a radio. By then, however, you might just as well have used a faster micro.

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JC,

In a previous thread OP said he was just trying to detect the 0 crossings in order to get the period/frequency of a sine.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In my monster ATmega2560 app I occasionally want to use the ADC while running in 8-bit as fast as I can. My CPU clock is at 14.7456 MHz, so sys clock / 16 gives me an ADC clock of just under 1 MHZ (921.6 KHz).

Since I have an external memory I can pull in about 8,000 samples before I have to call it quits.

/* ADC Clock Pre-scaling constants */
#define ADC_CLK_SYS_MASK		( _BV( ADPS2 ) | _BV( ADPS1 ) | _BV( ADPS0 ) )
#define ADC_CLK_SYS_DIV_128		( _BV( ADPS2 ) | _BV( ADPS1 ) | _BV( ADPS0 ) )
#define ADC_CLK_SYS_DIV_64		( _BV( ADPS2 ) | _BV( ADPS1 ) )
#define ADC_CLK_SYS_DIV_16		( _BV( ADPS2 ) )

. . .
bool
GetFastAdcSeries( uint8_t AdcSignal, uint8_t* pBuffer, uint16_t Samples )
{
	uint16_t i;

	if ( Samples > 8192 )
	{
		/* too many samples! */
		return false;
	}
	else if ( Samples == 0 )
	{
		Samples = 1;
	}

	/* Grab the ADC resource */
	if ( ! xSemaphoreTake( adcAvailable, ADC_AVAILABLE_WAIT ) )
	{
		return false;
	}

	adcSamplesTaken = 0;

	/* Make the universe quiet */
	vTaskSuspendAll();

	/* set up the mux to read the given signal */
	AdmuxSetup( AdcSignal );
	
	/* set ADLAR in ADMUX since we only need to read the top byte */
	ADMUX |= _BV( ADLAR );

	/* Set A/D to conversion "free running mode"
	 * set the clock rate high (SysClk/16) to get just under 1 MHz,
	 * Make sure the AD Conversion Complete interrupt is OFF.
	 */
	ADCSRB &= ~( _BV( ADTS2 ) | _BV( ADTS1 ) | _BV( ADTS0  ) );
	ADCSRA &= ~( _BV( ADATE ) | _BV( ADIE )  | ADC_CLK_SYS_MASK );
	ADCSRA |= _BV( ADATE ) | ADC_CLK_SYS_DIV_16;

	/* Start conversions */
	ADCSRA |= _BV( ADSC );

	/* Pick up the requested samples */
	for ( i = 0; i < Samples; i++)
	{
		/* wait on the completion flag */
		while ( ! ( ADCSRA & _BV( ADIF ) ) );

		/* Clear the completion flag */
		ADCSRA |= _BV( ADIF );

		/* pick up the value */
		*pBuffer = ADCH;
		pBuffer++;
		adcSamplesTaken++;
	}

	/* Shut down converter */
	ADCSRA &= ~( _BV( ADSC ) | _BV( ADATE ) | _BV( ADIE )  | ADC_CLK_SYS_MASK );

	/* Set things back so a normal ADC run can happen */
	ADCSRA |= ADC_CLK_SYS_DIV_128;
	ADMUX  &= ~_BV( ADLAR );

	/* Release the rest of the universe */
	xTaskResumeAll();

	xSemaphoreGive( adcAvailable );

	return true;
}

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JC raises a very important point.

Is the intent to measure something about the signal, such as its frequency or amplitude? Or, is it reproduction? If it is reproduction and the source is spoken or musical, then subsampling is not appropriate because there aren't enough cycles at any one frequency to make subsampling work. I suggested subsampling, thinking that you were talking about 100's of KHz signal.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is more on the reproduction side. I need to capture all of the input sine wave. I agree that life would have been much easier if I had to just detect the zero crossings, or calculate the peak amplitude. And it also has to be real time so I cannot store it in external memory.

I will try looking at only the ADCH value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Give us the 'Big Picture' and we'll give you several alternative approaches.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you know that the signal is spire sine then you know alot already. The remaining variables are period and amplitude. I prsume you need both. While I am no expert on analog, I think you can get some external op amps to trigger t transition when the signal crosses zero. This can measure period by timing between pulses then you can use a peak and hold to measure the amplitude.

You did not mention how fast the amplitude might be changing.

-Tony

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The manual for the ATmega32(L) has a shortlist of frequencies vs absolute accuracy ("Absolute Accuracy (Including INL, DNL, Quantization Error, Gain, and Offset Error)") on page 293.

    Absolute accuracy for a "Single Ended Conversion"* when Vref = 4V, Vcc = 4V and... ADC clock = 200 kHz --> (10bit) 1.5 LSB
    ADC clock = 1 MHz --> (8bit) 3 LSB

    Clock frequency: min = 50 kHz; max = 1 MHz

This doesn't seem to be much of a roll-off in conversion accuracy...

[*as opposed to differential conversion.]