Page 1 of 1

VU meter with AVR

Posted: Fri May 30, 2008 11:47 pm
by xcesmess
I decided to break down and ask for some help :) I've been googling for the past week trying to find some sample code for a VU meter using the ADC on an AVR. I've found several schematics where people have used ICs such as the LM3915 with LEDs but I'd like to display the meter on an LCD instead.

If anyone has any code they'd like to contribute or maybe some ideas on how to do this I'd love to hear it.

Thats one half of the question... the other is what would be the proper way to port the audio into the ADC on the avr? For testing I put the audio from a headphone output directly into ADC0 and it gives me a reading but it is very jumpy and doesn't seem consistent. Probably doesn't give much of a good description there... I saw some others putting the audio through a couple of capacitors for filtering so maybe this is what I need to do...

As for code samples... I have two environments available... Bascom and avr-gcc. I'm currently running a m48 on a STK500. My LCD is a 16*2 running in 4-bit mode.

Any help would be appreciated! Thanks!

Kelly

Re: VU meter with AVR

Posted: Thu Jun 26, 2008 10:44 pm
by bikeNomad
xcesmess wrote: Thats one half of the question... the other is what would be the proper way to port the audio into the ADC on the avr? For testing I put the audio from a headphone output directly into ADC0 and it gives me a reading but it is very jumpy and doesn't seem consistent. Probably doesn't give much of a good description there... I saw some others putting the audio through a couple of capacitors for filtering so maybe this is what I need to do...
Well, you need to understand how a VU meter works first. Since the meter readings are not changing fast (even an analog meter has to be "damped" to slow it appropriately) you can't just read the audio with the ADC a few times a second and expect it to work well. You need to average the amplitude of the "envelope" before you display it.

Typically, VU meter circuits consist of a full-wave rectifier and a peak-detecting circuit, followed by a log converter.

This would probably be the easiest way to do the job, as it would take a lot of CPU (and maybe a faster ADC) to do the job in software.

Remember too that VU meters are logarithmic in response (more or less) since our ears' response to audio is more or less logarithmic.

However...

If you want to do it in software, you have to sample fast enough to actually catch the peaks of the waveforms; this typically requires at least 5x the highest bandwidth of interest. If you want to capture the peaks of the signal up to (say) 10Khz you'd need a 50 Khz sample rate. This is 20 usec/sample.

But the ADC of (say) the Atmega48/88/168 will only give you 15000 samples/second at the maximum resolution. Not to worry; if you arrange for an appropriate clock (650Khz?) to the ADC you'll be able to do this. You don't need that many bits of resolution for a VU meter!

Then you have to do the rest of the work in 20 usec.

This would consist of:

* getting the sample (you can grab either 8 or 16 bits here but you probably want 16 to get the full 10 bits of ADC result)
* full-wave rectifying it (if it's less than 0, negate it)
* peak-detecting it: if the ADC value is greater than the current peak-detect signal, then keep this ADC value as a peak value

Meanwhile, you have to decay the peak-detect value; you can do this during a timer interrupt routine (I recommend an exponential filter here; subtracting (say) 1/256 of the value per interrupt would give you a smooth decay. And dividing by 256 is just a shift.

And at the same time (possibly from the same interrupt routine) you have to update the LCD readings. Though you could probably ignore the audio briefly during that period.

You'll want to drive the LCD from the log of the peak-detector output. The easiest way to do this is to look at the highest bit that is set in the peak-detector accumulator; each bit location is worth 1/2 of the one before, or 6dB (since you're measuring voltage).

Have fun!

Posted: Thu Jun 26, 2008 10:57 pm
by macegr
This is the circuit I developed for approximating VU readings of an audio input, controlling a bunch of ShiftBrites. It works quite well with very little processing on the microcontroller, a few samples averaged is about all; decay is exponential due to the drain on the peak detection capacitor. I have not attempted to make it a standardized VU meter, it's just an approximation. It may not even have a logarithmic response. The diodes are BAT42. Due to the low cost of the op-amp, it will not reach the 5V rail, so you sacrifice some ADC resolution. A solution would be to set a 4V ADC reference or supply the op-amp with around 6 or 7 volts.

I have tested this circuit with a sweep from 40 to 20000 Hz and constant audio power. The output is virtually flat; this circuit responds equally to all audio frequencies. Below is a simulation with an actual audio clip injected.

Image

Circuit in action (YouTube video)

Posted: Thu Jul 31, 2008 12:12 am
by SMT
Hi macegr,

How can I average 100 samples? (I use assembly language)

Thanks in advance,
SMT

Posted: Thu Jul 31, 2008 12:07 pm
by inventore123
SMT wrote:Hi macegr,

How can I average 100 samples? (I use assembly language)

Thanks in advance,
SMT
In C you'll do something like this:

uint16_t calc_average()
{
//This should hold 100*max_value without overflowing
// 100*1023=102300, which is > 65535, so you need a 32 bit int
uint32_t sum=0;
uint8_t i;
for(i=0;i<100;i++) sum+=get_sample_from_adc();
return sum/100;
}

In assembler it will be more difficult, especially because the AVR is an 8 bit cpu, so you can't use a 32bit int, you need to use 4 bytes and propagate the carry manually. If you're *really* looking for speed, you can use only 3bytes and make a 24 bit integer in assembler, since it can hold 102300 (a thing you can't do in C)

If possible, no matter if you're using assembler or C, try to average 64 or 128 samples for greater code speed, since the compiler will optimize the slow division with a right shift.

Posted: Sat Aug 02, 2008 7:37 pm
by SMT
Hi inventore123,
Thank you for the reply.

I want to avarage 64 samples for speed... but I don't know how to make a 24 bit integer in assembler. :(

Would you please guide more? (specially propagating the carry manually)

Best regards,
SMT

Posted: Sat Aug 02, 2008 8:01 pm
by lou
Addition, in 3 bytes, on an 8 bit machine, is a basic assembly language exercise. I'll give you a hint, it involves the carry bit and the version of the add instruction which pays heed to it. It's more a way of thinking, than a bit of code. Shifting (which you'll be doing when you divide by 64) will be similar. Anther hint there is to shift left by 2 and ignore the bottom byte instead of shifting right 6 times. It'll save time.