MCU based LED Arduino VU Meter

From time to time, I see people trying to build an audio VU meter. In the analog era, that’s typically done with a voltage divider + a series of comparators; or using chips like LM3914/3915.

MCU based LED VU Meter

Those chips are harder and harder to find, or you may need more resolution, or a different output profile. What to do?

Modern MCUs offer an easy solution.

  1. their build-in ADC (10-bit or more) is more than enough for this type of applications;
  2. their fast speed allows easy multiplexing so a few pins can control lots of output;
  3. their PWM capabilities also allow them to drive analog coil meters.

In this post, we will explore #1 and #2 and leave #3 to those of you who want to explore more on their own.

Picking a MCU:

For the purposes of this application, we need to pick a MCU with built-in adc, and sufficient pins. Because we are using multiplexing, we also want to control the timing via a timer interrupt. So a mcu with the following features is needed:

  1. built-in adc;
  2. port operations;
  3. timer;
  4. interrupts.

That’s pretty much any modern MCU, :).

When I started coding for this project, I had a PIC16F684 in my mind, and I thought to pick 6 output pins (“segments”) over 4 multiplexing lines (“digit”, or “channel”) for one adc input, making it a VU-meter driving up to 24 leds. But to make the code more versatile, I decided to write one that supports 6 – 16 segments, 2 – 4 digits so it is good for a total of 12 – 64 LEDs, over 1 or 2 adc channels.

I actually didn’t find a PIC16F684 in my part box so instead I wrote it for a PIC16F690 – but I only used a few pins and used a random number generator so simulate the adc for demo purposes.

Basic Principles of Operations:

Essentially, we are going to take the adc input – in this case, 10-bit adc, and convert it to a 12-bit logarithmic value to be displayed on the LED. For flexibility, I want to support both bar patterns and dot patterns.

The code is written so that each output pin is individually configurable:

//hardware configuration
#define NUM_OF_SEGS 6 //number of segments. 

#define SEG0_DDR TRISC
#define SEG0 (1<<0)

#define SEG1_DDR TRISC
#define SEG1 (1<<1)

#define SEG2_DDR TRISC
#define SEG2 (1<<2)

#define SEG3_DDR TRISB
#define SEG3 (1<<4)

#define SEG4_DDR TRISB
#define SEG4 (1<<5)

#define SEG5_DDR TRISB
#define SEG5 (1<<6)

#define SEG6_DDR TRISB
//#define SEG6 (1<<6)

#define SEG7_DDR TRISB
//#define SEG7 (1<<7) //uncomment if not used

#define SEG8_DDR TRISB
//#define SEG8 (1<<0) //uncomment if not used

#define SEG9_DDR TRISB
//#define SEG9 (1<<0) //uncomment if not used

#define SEG10_PORT PORTB
#define SEG10_DDR TRISB
//#define SEG10 (1<<0) //uncomment if not used

#define SEG11_PORT PORTB
#define SEG11_DDR TRISB
//#define SEG11 (1<<0) //uncomment if not used

#define SEG12_PORT PORTB
#define SEG12_DDR TRISB
//#define SEG12 (1<<0) //uncomment if not used

#define SEG13_PORT PORTB
#define SEG13_DDR TRISB
//#define SEG13 (1<<0) //uncomment if not used

#define SEG14_PORT PORTB
#define SEG14_DDR TRISB
//#define SEG14 (1<<0) //uncomment if not used

#define SEG15_PORT PORTB
#define SEG15_DDR TRISB
//#define SEG15 (1<<0) //uncomment if not used

#define CH0_PORT PORTC
#define CH0_DDR TRISC
#define CH0 (1<<7)

#define CH1_PORT PORTB
#define CH1_DDR TRISB
#define CH1 (1<<7)

#define CH2_PORT PORTA
#define CH2_DDR TRISA
//#define CH2 (1<<4) //uncomment if not used

#define CH3_PORT PORTA
#define CH3_DDR TRISA
//#define CH3 (1<<5) //uncomment if not used

//end hardware configuration

The output is written so it can be active high or active low:

Read more: MCU based LED Arduino VU Meter


About The Author

Scroll to Top