Introduction: How to Create a Beer Bottle LED VU Meter
Create your own Beer Bottle LED Volume Unit Meter!
This instructable will show you how to create a LED beer bottle VU Meter. A volume unit meter is a device that is used to display a relative volume of audio signals. Basically, sound gets converted into electricity, then amplified and run into a dedicated IC (Integrated Circuit). The IC then determines the level of sound and outputs it accordingly onto its outputs. This particular VU Meter uses a LM3915 Dot/Bar Display Driver to determine the sound level. This circuit is more flexible than a regular VU Meter though, due to the fact that I incorporated a microcontroller into the project. This makes it a little more complex, but also way more flexible.
The main reason that I built this project is because I have always been fascinated with LEDs. There is just so many cool projects that a person can make with them. Also, a couple years ago I made a Chevrolet logo VU Meter which didn't use a microcontroller. Although it was really cool, there wasn't a lot that I could do with it once it was made. I had the options of bar mode or dot mode and that was it. With the help of a microcontroller, you can design a project so that you are able to turn on any individual LED or groups of LEDs at any of the 10 different sound levels. That makes things a little more challenging, but also more interesting.
By following each and every step, you will be able to complete your own beer bottle LED VU Meter just like this one. Or if you are feeling really ambitious, create your own customized LED VU Meter and just use this instructable as a guideline.
This instructable will show you how to create a LED beer bottle VU Meter. A volume unit meter is a device that is used to display a relative volume of audio signals. Basically, sound gets converted into electricity, then amplified and run into a dedicated IC (Integrated Circuit). The IC then determines the level of sound and outputs it accordingly onto its outputs. This particular VU Meter uses a LM3915 Dot/Bar Display Driver to determine the sound level. This circuit is more flexible than a regular VU Meter though, due to the fact that I incorporated a microcontroller into the project. This makes it a little more complex, but also way more flexible.
The main reason that I built this project is because I have always been fascinated with LEDs. There is just so many cool projects that a person can make with them. Also, a couple years ago I made a Chevrolet logo VU Meter which didn't use a microcontroller. Although it was really cool, there wasn't a lot that I could do with it once it was made. I had the options of bar mode or dot mode and that was it. With the help of a microcontroller, you can design a project so that you are able to turn on any individual LED or groups of LEDs at any of the 10 different sound levels. That makes things a little more challenging, but also more interesting.
By following each and every step, you will be able to complete your own beer bottle LED VU Meter just like this one. Or if you are feeling really ambitious, create your own customized LED VU Meter and just use this instructable as a guideline.
Step 1: Skills Required
This isn't a very difficult project to complete, however it does take quite a bit of time (especially if you make the PCB yourself). There are more than 330 drill holes in the PCB, 78 LEDs that have to be soldered into place and 224 SMD connections. Therefore you should possess the following skills:
- Basic knowledge of electronics
- Basic knowledge of boolean logic and digital circuits
- Know how to solder (This includes SMD parts)
- Know how to create a PCB
- Know how to program a PIC18F4550 microcontroller
Optional:
- Know how to use a multimeter (This is used if you run into errors)
- Know how to program in C
- Know how to use the MPLAB IDE and the C18 compiler
With enough time and patience, anyone can complete this project. If you are looking at this project right now and thinking that it may be too difficult for you, take baby steps and start building the smaller circuits inside of the main circuit and get accustomed to them. For example, play around with the PIC18F4550 and get to know how it works. Then play around with the LM3915 so you know how to operate it, etc. The schematic may seem intimidating, but its really not. Like most electronic circuits, it is comprised of a bunch of smaller electronic circuits all linked together.
- Basic knowledge of electronics
- Basic knowledge of boolean logic and digital circuits
- Know how to solder (This includes SMD parts)
- Know how to create a PCB
- Know how to program a PIC18F4550 microcontroller
Optional:
- Know how to use a multimeter (This is used if you run into errors)
- Know how to program in C
- Know how to use the MPLAB IDE and the C18 compiler
With enough time and patience, anyone can complete this project. If you are looking at this project right now and thinking that it may be too difficult for you, take baby steps and start building the smaller circuits inside of the main circuit and get accustomed to them. For example, play around with the PIC18F4550 and get to know how it works. Then play around with the LM3915 so you know how to operate it, etc. The schematic may seem intimidating, but its really not. Like most electronic circuits, it is comprised of a bunch of smaller electronic circuits all linked together.
Step 2: Tools Required
Here is a list of tools and supplies that were used to build this project:
- Solder
- Soldering Iron
- Flux Pen
- Multimeter
- Drill/Rotary Tool (If making a PCB)
- Needle Nose Pliers
- Side Cutters
- Scalpel/Utility Knife (Used for PCB touch-ups)
- PIC Programmer (I used a MPLAB ICD2 Clone)
- Anti-Static Tweezers for small SMD components
- Perfboard/Breadboard or Materials required to make a PCB
- Toothbrush and Rubbing Alcohol to clean the flux off the PCB
All of the tools are pretty basic and should be found on most electronic hobbyists workbenches.
- Solder
- Soldering Iron
- Flux Pen
- Multimeter
- Drill/Rotary Tool (If making a PCB)
- Needle Nose Pliers
- Side Cutters
- Scalpel/Utility Knife (Used for PCB touch-ups)
- PIC Programmer (I used a MPLAB ICD2 Clone)
- Anti-Static Tweezers for small SMD components
- Perfboard/Breadboard or Materials required to make a PCB
- Toothbrush and Rubbing Alcohol to clean the flux off the PCB
All of the tools are pretty basic and should be found on most electronic hobbyists workbenches.
Step 3: Components Required
Here is a list of the following electronic components required:
34x - 5mm Green LEDs
28x - 5mm Blue LEDs
16x - 3mm Yellow LEDs
2x - 1N4148 Fast Switching Diodes
1x - 1N4007 Rectifier Diode
1x - PIC18F4550 TQFP PIC Microcontroller
4x - 74HC595 SOIC 8-Bit Shift Registers
1x - LM3915N (DIP) Dot/Bar Display Driver
1x - LM7805 TO-220 +5V Regulator
2x - IRL510 TO-220 Logic Level MOSFETs
2x - MMBT2222A SOT23 Transistors
2x - 27pf 0805 Capacitors
1x - 0.1μF Ceramic Disc Capacitor
1x - 1μF Electrolytic Capacitor
1x - 4.7μF Electrolytic Capacitor
2x - 22μF Electrolytic Capacitors
1x - 47μF Electrolytic Capacitor
1x - 220μF Electrolytic Capacitor
36x - 100Ω 0805 SMD Resistors
8x - 10kΩ 0805 SMD Resistors
3x - 50Ω 0805 SMD Resistors
1x - 10kΩ SIL9 Resistor Network
1x - 10kΩ Trim Potentiometer
1x - 390Ω Through-Hole 1/4W Resistor
1x - 2.7kΩ 0805 SMD Resistor
1x - 2.2kΩ 0805 SMD Resistor
1x - 1kΩ 0805 SMD Resistor
1x - 4.7kΩ 0805 SMD Resistor
1x - RJ-11 6p6pc ICSP Connector
1x - 2-Pin 0.1" Molex Connector
1x - 2-Pin 0.1" Molex Header
1x - Electret Condenser Mic
1x - SMD Momentary Tactile Switch
1x - SPST Rocker Switch
1x - 2-Position DIP Switch
1x - 20MHz Through-Hole Crystal
1x - TO-220 Heat Sink
All these components I had already had on hand, but I would estimate the cost of this project to be around $40. I have shown a couple of the components used in this project in the photo.
34x - 5mm Green LEDs
28x - 5mm Blue LEDs
16x - 3mm Yellow LEDs
2x - 1N4148 Fast Switching Diodes
1x - 1N4007 Rectifier Diode
1x - PIC18F4550 TQFP PIC Microcontroller
4x - 74HC595 SOIC 8-Bit Shift Registers
1x - LM3915N (DIP) Dot/Bar Display Driver
1x - LM7805 TO-220 +5V Regulator
2x - IRL510 TO-220 Logic Level MOSFETs
2x - MMBT2222A SOT23 Transistors
2x - 27pf 0805 Capacitors
1x - 0.1μF Ceramic Disc Capacitor
1x - 1μF Electrolytic Capacitor
1x - 4.7μF Electrolytic Capacitor
2x - 22μF Electrolytic Capacitors
1x - 47μF Electrolytic Capacitor
1x - 220μF Electrolytic Capacitor
36x - 100Ω 0805 SMD Resistors
8x - 10kΩ 0805 SMD Resistors
3x - 50Ω 0805 SMD Resistors
1x - 10kΩ SIL9 Resistor Network
1x - 10kΩ Trim Potentiometer
1x - 390Ω Through-Hole 1/4W Resistor
1x - 2.7kΩ 0805 SMD Resistor
1x - 2.2kΩ 0805 SMD Resistor
1x - 1kΩ 0805 SMD Resistor
1x - 4.7kΩ 0805 SMD Resistor
1x - RJ-11 6p6pc ICSP Connector
1x - 2-Pin 0.1" Molex Connector
1x - 2-Pin 0.1" Molex Header
1x - Electret Condenser Mic
1x - SMD Momentary Tactile Switch
1x - SPST Rocker Switch
1x - 2-Position DIP Switch
1x - 20MHz Through-Hole Crystal
1x - TO-220 Heat Sink
All these components I had already had on hand, but I would estimate the cost of this project to be around $40. I have shown a couple of the components used in this project in the photo.
Step 4: Schematic/PCB/Software/Data Calculator
Before we go on to how it works, I have posted the downloadable zip file below which includes:
- Circuit Schematic
- PCB Top and Bottom Layers (PDF and PNG)
- PCB Top Silkscreen (PDF and PNG)
- VU Meter Data Calculator Program
- PIC18F4550 Firmware
I thought I'd post this right away in the instructable so you have it as we go through the steps, rather than get it at the last step when we're finished.
- Circuit Schematic
- PCB Top and Bottom Layers (PDF and PNG)
- PCB Top Silkscreen (PDF and PNG)
- VU Meter Data Calculator Program
- PIC18F4550 Firmware
I thought I'd post this right away in the instructable so you have it as we go through the steps, rather than get it at the last step when we're finished.
Attachments
Step 5: How It Works
I will try to explain how it works in the next couple of paragraphs, however if you are still unclear as to how it works after reading them, check out the photos (they look really unprofessional, but they might help you understand the concept better). Just take your time and if you have any questions feel free to ask.
The first stage of the circuit is an electret mic pre-amplifer. An electret mic is great for applications like this because it is small and versatile, but it has a drawback. When it picks up sound it only puts a small signal, much too small for the LM3915 to even notice it. Therefore we have to amplify the signal of the electret microphone. The two MMBT2222A transistors are the heart of the amplifier and bring the signal of the electret mic up to a large enough value for the LM3915 to work with. Now the sound that goes into the mic gets converted into AC electricity. In order for the LM3915 to be able to work with the amplified input signal, we need to convert the AC signal to a DC signal. This is taken care of by the 1N4148 diodes. Once the signal is converted to DC, it is fed into the signal line of the LM3915.
The second stage of the circuit is the LM3915 itself. We don't need to actually know how the LM3915 works, we just have to know how to use it. If your curious, on page 7 of the LM3915 datasheet it shows a simplistic version of the internal wiring diagram. So, we now have a valid signal going into the IC, but how does the MCU (Microcontroller Unit) know what sound level the LM3915 is at? Well if you look at the schematic you'll notice that each of the LM3915s outputs (LED1 - LED10) are fed into an input on the PIC18F4550. Each output is also tied high by a 10k resistor (I used a resistor network) because the LM3915s outputs are an open collector type. Meaning, the LM3915 can force its outputs into a low state (0v) but it cannot force them into a high state (+v). So when an output is not being forced low, the resistor is keeping it held high. If all of the outputs are in a high state, the LM3915 isn't detecting any sound. If any or all of the outputs are low, the PIC18F4550 can then interpret the intensity level of the sound.
The third stage of the circuit is the PIC18F4550. The LM3915 may be the heart of the circuit, but the microcontroller is definitely the brains. This stage reads the outputs of the LM3915 and turns on the LEDs according to the sound level. The nice thing about using a microcontroller is that we can turn on any of the inside LEDs (the 28 blue LEDs inside the "bottle") that we want. Therefore, there is an endless amount of animations that we can create and display (ok, maybe not endless...). I have also added an element to this circuit so that we can control the 16 3mm yellow LEDs which form the lemon at the top of the bottle and we can control the 34 5mm Green LEDs which form the bottle.
The fourth stage of the circuit is the 4x 74HC595's. Once the MCU has read the sound level, it will then figure out which LEDs it wants to turn on and which LEDs it wants to turn off (this is stored in 4 bytes of data), then it will serially transmit the 4 bytes of data (one byte to each 74HC595) and latch it. Once the 74HC595's have been latched, the individual LEDs will either turn on or off.
The first stage of the circuit is an electret mic pre-amplifer. An electret mic is great for applications like this because it is small and versatile, but it has a drawback. When it picks up sound it only puts a small signal, much too small for the LM3915 to even notice it. Therefore we have to amplify the signal of the electret microphone. The two MMBT2222A transistors are the heart of the amplifier and bring the signal of the electret mic up to a large enough value for the LM3915 to work with. Now the sound that goes into the mic gets converted into AC electricity. In order for the LM3915 to be able to work with the amplified input signal, we need to convert the AC signal to a DC signal. This is taken care of by the 1N4148 diodes. Once the signal is converted to DC, it is fed into the signal line of the LM3915.
The second stage of the circuit is the LM3915 itself. We don't need to actually know how the LM3915 works, we just have to know how to use it. If your curious, on page 7 of the LM3915 datasheet it shows a simplistic version of the internal wiring diagram. So, we now have a valid signal going into the IC, but how does the MCU (Microcontroller Unit) know what sound level the LM3915 is at? Well if you look at the schematic you'll notice that each of the LM3915s outputs (LED1 - LED10) are fed into an input on the PIC18F4550. Each output is also tied high by a 10k resistor (I used a resistor network) because the LM3915s outputs are an open collector type. Meaning, the LM3915 can force its outputs into a low state (0v) but it cannot force them into a high state (+v). So when an output is not being forced low, the resistor is keeping it held high. If all of the outputs are in a high state, the LM3915 isn't detecting any sound. If any or all of the outputs are low, the PIC18F4550 can then interpret the intensity level of the sound.
The third stage of the circuit is the PIC18F4550. The LM3915 may be the heart of the circuit, but the microcontroller is definitely the brains. This stage reads the outputs of the LM3915 and turns on the LEDs according to the sound level. The nice thing about using a microcontroller is that we can turn on any of the inside LEDs (the 28 blue LEDs inside the "bottle") that we want. Therefore, there is an endless amount of animations that we can create and display (ok, maybe not endless...). I have also added an element to this circuit so that we can control the 16 3mm yellow LEDs which form the lemon at the top of the bottle and we can control the 34 5mm Green LEDs which form the bottle.
The fourth stage of the circuit is the 4x 74HC595's. Once the MCU has read the sound level, it will then figure out which LEDs it wants to turn on and which LEDs it wants to turn off (this is stored in 4 bytes of data), then it will serially transmit the 4 bytes of data (one byte to each 74HC595) and latch it. Once the 74HC595's have been latched, the individual LEDs will either turn on or off.
Step 6: Power Supply
The power supply is the most important part of any electronic device. It doesn't matter how great the device is, having an unreliable power supply can ruin all of that. And having no power supply at all...well that just speaks for itself. In order for us to determine the requirements for a power supply we need to perform a few calculations.
In the schematic you will notice that there are 8 rows of 4x Green LEDs in series and 1 row with only two green LEDs in series, 3 rows of 5x Yellow LEDs in series and 1 row with only one yellow LED and 28x individually controlled Blue LEDs.
LED Voltage Drops:
Blue LED = ~3.3v
Green LED = ~2.2v
Yellow LED = ~1.9v
LED Current Calculations
Blue LED = (5.0v - 3.3v) / 100Ω
= 17mA per Blue LED
Green LED = (10.0v - (4 LEDs * 2.2v/LED)) / 100Ω
= 12mA per Green LED row
Yellow LED = (10.0v - (5 LEDs * 1.9v/LED)) / 50Ω
= 10mA per Yellow LED row
Max LED Current = (9 Green LED rows * 12mA/row) + (4 Yellow LED rows * 10mA/row) + (28 Blue LEDs * 17mA/LED)
= 624mA
74HC595 Current
I595 = Max 74HC595 Current Draw From All 4x 74HC595's
ICC (Io @ 0mA) = 80uA per 74HC595
I595 = 80uA * 4 74HC595's
= 320uA
Max PIC18F4550 Current @ 48MHz
IMCU = Max PIC18F4550 Current
IMCU = 50mA
Max LM3915 Current @ 20v (All outputs off)
ILM3915 = Max LM3915 Current
ILM3915 = 9.2mA (There was only ratings for 5v and 20v, so I chose 20v to be safe even though were running at 10v)
The last 3 values are found in each devices datasheets. I left out a few calculations where there is still current draw in the circuit just for the sake of simplicity (We got the main sources of current draw, the other few are next to negligible). We'll make sure that we use a power supply that can supply more than the bare minimum that we need. So an approximate value of our current draw is:
Current Drawn From +10v Power Supply
Isupply = Total Current Drawn From +10v Power Supply
Isupply = 624mA + 320uA + 50mA + 9.2mA
= ~683.5mA
Checking my VU Meter when it is running at its max power consumption (All LEDs on) it draws 660mA. The reason it is lower than what we calculated is because we used the maximum current values in our calculations and not the typical current values (all those values are located in the datasheets). So at the very least, we'd want a +10v power supply that can supply 700mA. If you wanted to be safer (as I do), we'd use a +10v power supply that can handle 1.0A of current draw. The one I am currently using can actually handle up to 2.0A, which is way more than we need.
I run this VU Meter at +10v, but it is capable of running reliably from +9v to +18v. Keep in mind that you will want to change the green and yellow LED resistor values if you use a higher voltage or you may put too much current through them and burn them out. Also, if you put a higher voltage through it you will need to use a heatsink that can dissipate the extra heat produced by the LM7805.
In the schematic you will notice that there are 8 rows of 4x Green LEDs in series and 1 row with only two green LEDs in series, 3 rows of 5x Yellow LEDs in series and 1 row with only one yellow LED and 28x individually controlled Blue LEDs.
LED Voltage Drops:
Blue LED = ~3.3v
Green LED = ~2.2v
Yellow LED = ~1.9v
LED Current Calculations
Blue LED = (5.0v - 3.3v) / 100Ω
= 17mA per Blue LED
Green LED = (10.0v - (4 LEDs * 2.2v/LED)) / 100Ω
= 12mA per Green LED row
Yellow LED = (10.0v - (5 LEDs * 1.9v/LED)) / 50Ω
= 10mA per Yellow LED row
Max LED Current = (9 Green LED rows * 12mA/row) + (4 Yellow LED rows * 10mA/row) + (28 Blue LEDs * 17mA/LED)
= 624mA
74HC595 Current
I595 = Max 74HC595 Current Draw From All 4x 74HC595's
ICC (Io @ 0mA) = 80uA per 74HC595
I595 = 80uA * 4 74HC595's
= 320uA
Max PIC18F4550 Current @ 48MHz
IMCU = Max PIC18F4550 Current
IMCU = 50mA
Max LM3915 Current @ 20v (All outputs off)
ILM3915 = Max LM3915 Current
ILM3915 = 9.2mA (There was only ratings for 5v and 20v, so I chose 20v to be safe even though were running at 10v)
The last 3 values are found in each devices datasheets. I left out a few calculations where there is still current draw in the circuit just for the sake of simplicity (We got the main sources of current draw, the other few are next to negligible). We'll make sure that we use a power supply that can supply more than the bare minimum that we need. So an approximate value of our current draw is:
Current Drawn From +10v Power Supply
Isupply = Total Current Drawn From +10v Power Supply
Isupply = 624mA + 320uA + 50mA + 9.2mA
= ~683.5mA
Checking my VU Meter when it is running at its max power consumption (All LEDs on) it draws 660mA. The reason it is lower than what we calculated is because we used the maximum current values in our calculations and not the typical current values (all those values are located in the datasheets). So at the very least, we'd want a +10v power supply that can supply 700mA. If you wanted to be safer (as I do), we'd use a +10v power supply that can handle 1.0A of current draw. The one I am currently using can actually handle up to 2.0A, which is way more than we need.
I run this VU Meter at +10v, but it is capable of running reliably from +9v to +18v. Keep in mind that you will want to change the green and yellow LED resistor values if you use a higher voltage or you may put too much current through them and burn them out. Also, if you put a higher voltage through it you will need to use a heatsink that can dissipate the extra heat produced by the LM7805.
Step 7: Wasted Power and Heat Dissipation
Now we need to calculate the current that is drawn from the LM7805 +5v regulator so that we can calculate the power that it wastes. If you look at the schematic you'll see that the green and yellow LEDs run right off of the +10v supply. The only components that use power from the +5v regulator are the 74HC595's, the PIC18F4550 and the 28 Blue LEDs. So we can calculate Ireg by adding up the current draw of those components.
Ireg = Total Current Drawn From +5v Voltage Regulator
Iblue_total = Total Current Drawn From The +5v Regulator With All Blue LEDs On
Iblue_total = 28 Blue LEDs * 17mA per LED
= 476mA
Ireg = IMCU + I595 + Iblue_total
= 50mA + 320uA + 476mA
= 526.3mA
Now, because we are using a linear voltage regulator to step our voltage down from +10v to +5v for the PIC18F4550 and the 74HC595's, we are going to have some wasted power. The larger the current draw through the linear regulator, the more power that will be wasted in the regulator. Here we will do some calculations and find the wasted power in the voltage regulator:
Vin = +10v
Vreg = +5v
Ireg = 526.3mA
Pwasted = (Vin - Vreg) * Ireg
= (+10v - +5v) * 526.3mA
= 2.63W
The power that is wasted by the LM7805 is 2.63W. Now you may be saying "Big deal, we're not running it on a battery so we can afford to waste that power." The only problem is that when a linear voltage regulator wastes power it turns that wasted power into heat. This is bad news for the LM7805, therefore we need to attach a heatsink to it to help it dissipate the heat that it generates. If I would have had a switching +5v regulator on hand I would have used it instead of the LM7805, simply because switching regulators are more efficient than linear regulators, which means less heat.
Ireg = Total Current Drawn From +5v Voltage Regulator
Iblue_total = Total Current Drawn From The +5v Regulator With All Blue LEDs On
Iblue_total = 28 Blue LEDs * 17mA per LED
= 476mA
Ireg = IMCU + I595 + Iblue_total
= 50mA + 320uA + 476mA
= 526.3mA
Now, because we are using a linear voltage regulator to step our voltage down from +10v to +5v for the PIC18F4550 and the 74HC595's, we are going to have some wasted power. The larger the current draw through the linear regulator, the more power that will be wasted in the regulator. Here we will do some calculations and find the wasted power in the voltage regulator:
Vin = +10v
Vreg = +5v
Ireg = 526.3mA
Pwasted = (Vin - Vreg) * Ireg
= (+10v - +5v) * 526.3mA
= 2.63W
The power that is wasted by the LM7805 is 2.63W. Now you may be saying "Big deal, we're not running it on a battery so we can afford to waste that power." The only problem is that when a linear voltage regulator wastes power it turns that wasted power into heat. This is bad news for the LM7805, therefore we need to attach a heatsink to it to help it dissipate the heat that it generates. If I would have had a switching +5v regulator on hand I would have used it instead of the LM7805, simply because switching regulators are more efficient than linear regulators, which means less heat.
Step 8: LED Connections
This step builds off of Step 4: How It Works, so make sure that you understand step 4 quite thoroughly before you continue on to this step. In this step we will have a look at the PCB board and see where all of the LEDs are placed. From there we can learn how to write data to the 74HC595 shift registers and how to control the state of each individual LED inside the "bottle" (the blue LEDs).
Before we can start writing any code to control our LEDs, we first need to find out where exactly each LED is placed on the PCB. If you look at photo #2 you will see a silkscreen of the PCB board. The outside LEDs that make up the bottle shape are all green LEDs. We cannot control these individually, we can either turn them all on or all off. The same goes for the yellow LEDs that make up the lemon at the top of the bottle. Right now we are going to concentrate on the inside LEDs which are the blue LEDs that we can control individually. On the schematic all of the LEDs are labeled in a left to right, bottom to top manner.
Now, to know which LEDs to turn on we need to figure out which LED connects to which pin on which 74HC595. I have it set up so that if you count in a left to right, bottom to top manner, every 8 blue LEDs will make up one 74HC595 IC. The top 4 blue LEDs are connected to the 4th 74HC595 (IC4) and only use half of its outputs. Here are the LED connections to the 1st 74HC595 (IC1), as seen in photo #3.
QH -> LED7
QG -> LED8
QF -> LED9
QE -> LED12
QD -> LED13
QC -> LED14
QB -> LED17
QA -> LED18
The first 8 blue LEDs connect to IC1, the next 8 connect to IC2, the next 8 connect to IC3 and the next 4 connect to IC4. They all follow the same format as IC1**. Now I know what you're thinking, "Why didn't he wire QA to LED7 and QB to LED8 and QC to LED9...etc. instead so they all go in a straight through order?" Seems kinda backwards huh? Well the reason I chose to do it the way I did is because it made the PCB much easier to route. So I made it easier to route the hardware, but now it may make the software a little harder to write (its still quite easy).
**IC4 is actually connected up differently, I should have put the four LEDs on the upper 4 bits not the lower 4 bits. Not sure why I did that.
Before we can start writing any code to control our LEDs, we first need to find out where exactly each LED is placed on the PCB. If you look at photo #2 you will see a silkscreen of the PCB board. The outside LEDs that make up the bottle shape are all green LEDs. We cannot control these individually, we can either turn them all on or all off. The same goes for the yellow LEDs that make up the lemon at the top of the bottle. Right now we are going to concentrate on the inside LEDs which are the blue LEDs that we can control individually. On the schematic all of the LEDs are labeled in a left to right, bottom to top manner.
Now, to know which LEDs to turn on we need to figure out which LED connects to which pin on which 74HC595. I have it set up so that if you count in a left to right, bottom to top manner, every 8 blue LEDs will make up one 74HC595 IC. The top 4 blue LEDs are connected to the 4th 74HC595 (IC4) and only use half of its outputs. Here are the LED connections to the 1st 74HC595 (IC1), as seen in photo #3.
QH -> LED7
QG -> LED8
QF -> LED9
QE -> LED12
QD -> LED13
QC -> LED14
QB -> LED17
QA -> LED18
The first 8 blue LEDs connect to IC1, the next 8 connect to IC2, the next 8 connect to IC3 and the next 4 connect to IC4. They all follow the same format as IC1**. Now I know what you're thinking, "Why didn't he wire QA to LED7 and QB to LED8 and QC to LED9...etc. instead so they all go in a straight through order?" Seems kinda backwards huh? Well the reason I chose to do it the way I did is because it made the PCB much easier to route. So I made it easier to route the hardware, but now it may make the software a little harder to write (its still quite easy).
**IC4 is actually connected up differently, I should have put the four LEDs on the upper 4 bits not the lower 4 bits. Not sure why I did that.
Step 9: Controlling the LEDs
Now that we understand how the LEDs are wired up, we can begin learning how to write data to each shift register so that we can control the LEDs. When we write data out of our PIC18F4550, it will first go into IC4. Then after IC4 fills up with 8 bits of data (1 byte), every bit shifted into IC4 after that will cause the MSb (Most Significant bit) of IC4 to be shifted into IC3 which will become the LSb (Least Significant bit) of IC3. So on so forth, all the way to IC1. I won't go into great detail with this because there are already quite a few instructables here that deal with shift registers, so if you don't fully understand the concept you can read up on them. I also posted a link to a good tutorial on 74HC595 shift registers at step #16.
Every LED is represented by 1 bit. In order to control each LED, we have to send 28 bits to the shift registers from the PIC18F4550. Seeing as we use the SPI protocol to transfer data to the shift registers, we actually end up sending 32 bits of data (4 bytes). This is because the SPI protocol only sends data 8 bits at a time with this particular microcontroller. Anyways, the extra 4 bits are not used and we will just ignore them (we'll always leave them as 0). Once we shift the data into the 74HC595's, we then "latch" them. Which means that whatever data we shifted into them, it will put that value (1 (high) or 0 (low)) onto its outputs. We'll head into some examples now which may clear up some questions.
Example 1
Say we want to turn all of the blue LEDs ON. What would be the values for each byte of data we shift into each register? This is where some basic boolean logic will come into play. Seeing as these LEDs are active high (they turn on when we write a 1 to them) we have to write a 1 to turn them on or a 0 to turn them off. So if we want them all on, we would have to write this to the shift registers:
MSb Lsb Hex
IC1 - 1st Byte Sent -> 11111111 0xFF
IC2 - 2nd Byte Sent -> 11111111 0xFF
IC3 - 3rd Byte Sent -> 11111111 0xFF
IC4 - 4th Byte Sent -> 00001111 0x0F
So that one was pretty easy. We just made every bit that represents an LED equal to 1 (+5v). Then we'd latch the data in and it'd turn on all of the blue LEDs. Of course the 4 bits that are 0 are those ones that we are going to ignore and aren't connected to any LEDs.
Example 2
Say we want to turn all of the blue LEDs OFF. This is another easy one, but what would we write to the shift registers?
MSb Lsb Hex
IC1 - 1st Byte Sent -> 00000000 0x00
IC2 - 2nd Byte Sent -> 00000000 0x00
IC3 - 3rd Byte Sent -> 00000000 0x00
IC4 - 4th Byte Sent -> 00000000 0x00
Now we just made every bit that represents an LED equal to 0 (0v). Then we'd latch the data in and it'd turn off all of the blue LEDs.
Example 3
What data would we send to turn on LED7, LED8, LED42, LED43, LED47 and LED52? All the other LEDs not mentioned should be turned off.
bit 7 ------ bit 0
MSb Lsb Hex
IC1 - 1st Byte Sent -> 11000000 0xC0
IC2 - 2nd Byte Sent -> 00000000 0x00
IC3 - 3rd Byte Sent -> 00000110 0x06
IC4 - 4th Byte Sent -> 00001001 0x09
Once we latch it, it'll turn on LED7 and LED8 which are bit 7 (QH) and bit 6 (QG) on IC1. Also bit 1 (QB) and bit 2 (QC) on IC3 will go high, turning on LED42 and LED 43. On IC4, bit 0 (QA) and bit 3 (QD) will go high, turning on LED47 and LED52.
Its really simple to turn on any of the inside LEDs. Just have a look at the schematic, figure out where the LEDs are that you want on and off, convert it into 4 bytes and your set. Next we'll learn how to use functions that have been written for the PIC18F4550 to transmit the data to the shift registers.
Every LED is represented by 1 bit. In order to control each LED, we have to send 28 bits to the shift registers from the PIC18F4550. Seeing as we use the SPI protocol to transfer data to the shift registers, we actually end up sending 32 bits of data (4 bytes). This is because the SPI protocol only sends data 8 bits at a time with this particular microcontroller. Anyways, the extra 4 bits are not used and we will just ignore them (we'll always leave them as 0). Once we shift the data into the 74HC595's, we then "latch" them. Which means that whatever data we shifted into them, it will put that value (1 (high) or 0 (low)) onto its outputs. We'll head into some examples now which may clear up some questions.
Example 1
Say we want to turn all of the blue LEDs ON. What would be the values for each byte of data we shift into each register? This is where some basic boolean logic will come into play. Seeing as these LEDs are active high (they turn on when we write a 1 to them) we have to write a 1 to turn them on or a 0 to turn them off. So if we want them all on, we would have to write this to the shift registers:
MSb Lsb Hex
IC1 - 1st Byte Sent -> 11111111 0xFF
IC2 - 2nd Byte Sent -> 11111111 0xFF
IC3 - 3rd Byte Sent -> 11111111 0xFF
IC4 - 4th Byte Sent -> 00001111 0x0F
So that one was pretty easy. We just made every bit that represents an LED equal to 1 (+5v). Then we'd latch the data in and it'd turn on all of the blue LEDs. Of course the 4 bits that are 0 are those ones that we are going to ignore and aren't connected to any LEDs.
Example 2
Say we want to turn all of the blue LEDs OFF. This is another easy one, but what would we write to the shift registers?
MSb Lsb Hex
IC1 - 1st Byte Sent -> 00000000 0x00
IC2 - 2nd Byte Sent -> 00000000 0x00
IC3 - 3rd Byte Sent -> 00000000 0x00
IC4 - 4th Byte Sent -> 00000000 0x00
Now we just made every bit that represents an LED equal to 0 (0v). Then we'd latch the data in and it'd turn off all of the blue LEDs.
Example 3
What data would we send to turn on LED7, LED8, LED42, LED43, LED47 and LED52? All the other LEDs not mentioned should be turned off.
bit 7 ------ bit 0
MSb Lsb Hex
IC1 - 1st Byte Sent -> 11000000 0xC0
IC2 - 2nd Byte Sent -> 00000000 0x00
IC3 - 3rd Byte Sent -> 00000110 0x06
IC4 - 4th Byte Sent -> 00001001 0x09
Once we latch it, it'll turn on LED7 and LED8 which are bit 7 (QH) and bit 6 (QG) on IC1. Also bit 1 (QB) and bit 2 (QC) on IC3 will go high, turning on LED42 and LED 43. On IC4, bit 0 (QA) and bit 3 (QD) will go high, turning on LED47 and LED52.
Its really simple to turn on any of the inside LEDs. Just have a look at the schematic, figure out where the LEDs are that you want on and off, convert it into 4 bytes and your set. Next we'll learn how to use functions that have been written for the PIC18F4550 to transmit the data to the shift registers.
Step 10: Using the PIC18F4550
To send data to the shift registers is really simple. In my code I setup the SPI module for Mode 03 (CLPH = 1 and CPOL = 1) and the speed is 12 MIPS (Million Instructions Per Second). We then use the function SPI_Send_595(uint32 data) to turn the LEDs on or off. You must arrange your data into a 32 bit variable, so use the function SPI_Send_595(byte1:byte2:byte3:byte4). Then follow the example below to send the data.
Figure out which LEDs you want ON and which LEDs you want OFF. We'll use the data from example 3 in Step #8.
MSb Lsb Hex
IC1 - 1st Byte Sent -> 11000000 0xC0
IC2 - 2nd Byte Sent -> 00000000 0x00
IC3 - 3rd Byte Sent -> 00000110 0x06
IC4 - 4th Byte Sent -> 00001001 0x09
This is how the data would be sent into the function: SPI_Send_595(0xC0000609);
Pretty simple. Just take each bytes hex value and arrange it into a 32-bit variable starting with the 1st byte and continuing through to the 4th byte. SPI_Send_595(uint32 data) takes care of sending and latching the data in the 74HC595's. All of the code is commented so I will only post some pseudo code below which explains the operation of the function.
Pseudo Code
- 32-bit variable is passed into the function SPI_Send_595(data)
- The MSB is the first byte transmitted out of the PIC18F4550 (It will end up in IC1)
- The second byte is transmitted out of the PIC (it will end up in IC2)
- The third byte is transmitted out of the PIC (it will end up in IC3)
- The LSB is the last byte transmitted out of the PIC (it ends up in IC4)
- RD2 goes high, which latches the data into the shift registers
- RD2 goes low, returning to its default state
Figure out which LEDs you want ON and which LEDs you want OFF. We'll use the data from example 3 in Step #8.
MSb Lsb Hex
IC1 - 1st Byte Sent -> 11000000 0xC0
IC2 - 2nd Byte Sent -> 00000000 0x00
IC3 - 3rd Byte Sent -> 00000110 0x06
IC4 - 4th Byte Sent -> 00001001 0x09
This is how the data would be sent into the function: SPI_Send_595(0xC0000609);
Pretty simple. Just take each bytes hex value and arrange it into a 32-bit variable starting with the 1st byte and continuing through to the 4th byte. SPI_Send_595(uint32 data) takes care of sending and latching the data in the 74HC595's. All of the code is commented so I will only post some pseudo code below which explains the operation of the function.
Pseudo Code
- 32-bit variable is passed into the function SPI_Send_595(data)
- The MSB is the first byte transmitted out of the PIC18F4550 (It will end up in IC1)
- The second byte is transmitted out of the PIC (it will end up in IC2)
- The third byte is transmitted out of the PIC (it will end up in IC3)
- The LSB is the last byte transmitted out of the PIC (it ends up in IC4)
- RD2 goes high, which latches the data into the shift registers
- RD2 goes low, returning to its default state
Step 11: Reading the LM3915's Outputs
Finally, we have trudged through all of that other stuff that is necessary to get the project working and now we get to actually start working on the VU Meter aspect of the project. The first thing that we need to do is figure out which outputs of the LM3915 feed into which inputs of the PIC18F4550. So we'll pull out that schematic (yet again) and notice that the connections are as follows:
Outputs:Inputs
LED1:RB7
LED2:RB6
LED3:RB5
LED4:RB4
LED5:RB3
LED6:RB2
LED7:RD7
LED8:RD6
LED9:RD5
LED10:RD4
Once again, if you're wondering why the inputs are kind of backwards its due to the routing of the PCB. Now that we have the connections, I will show you how we store the input data.
The first thing that we do is declare a 16-bit variable. We will only be using the lower 10 bits because we only have 10 different sound levels.
uint16 level; //We are declaring a 16-bit unsigned integer which will store the sound level from LED1 - LED10
Seeing as we don't use a full PORT (RB0 - RB7 or RD0 - RD7) to bring the data in, we're going to have to mask off the unwanted bits. The bits that we don't need are RB0, RB1, RD0, RD1, RD2 and RD3. The first 4 bits of data that we'll bring in is RD4 - RD7, so we'll mask off the bottom bits (RD0 - RD3) and set the variable 'level' equal to to upper nibble (RD4 - RD7).
level = 0x00F0 & PORTD; //Mask off the bottom bits and make 'level' equal to the upper nibble of PORTD
Now that we have the values stored that are on outputs LED7 - LED10, we have to get our variable 'level' ready to be able to store the next 6 bits of data that are on RB2 - RB7. In order to make room for these next 6 bits we have to left bit shift 'level' by 2. So right now 'level' would look something like this in binary form:
xxxx = RD4,RD5,RD6,RD7 respectively
level = 00000000xxxx0000 <= There is only enough room for 4 bits of data on the end. We need to fit 6 bits of data
So in order to bit shift 'level' two places, we put this as our next line of code:
level = level << 2; //Bit shift 'level' two places and ready it to accept the data from RB2 - RB7
This is now how 'level' would look after it has been bit shifted left two places:
level = 000000xxxx000000 <= Now we have enough room for RB2 - RB7
Finally, we can now retrieve the final 6 bits of data that are output from LED1 - LED6 and store it in 'level'. Here is the next line of code in order to do that:
level = level | ((0xFC & PORTB) >> 2); //Mask off bits RB0 and RB1, then bit shift RB2 - RB7 two places to the right and store it in the bottom 6 bits of 'level'
This line is a little more cryptic, but it does the exact same thing as we did with PORTD except it shifts the data two places to the right. In the inside brackets we mask off RB0 and RB1 and then shift the result to the right two places. Now that the data that we want is in the lowest 6 bits, we will OR it with 'level' which will simply place RB2 - RB7 into the lowest 6 bits of 'level'. Here is what it will end up looking like:
xxxx = RD4,RD5,RD6,RD7 respectively
yyyyyy = RB2,RB3,RB4,RB5,RB6,RB7 respectively
level = 000000xxxxyyyyyy
Next we will match the values of 'level' with the corresponding sound level.
Outputs:Inputs
LED1:RB7
LED2:RB6
LED3:RB5
LED4:RB4
LED5:RB3
LED6:RB2
LED7:RD7
LED8:RD6
LED9:RD5
LED10:RD4
Once again, if you're wondering why the inputs are kind of backwards its due to the routing of the PCB. Now that we have the connections, I will show you how we store the input data.
The first thing that we do is declare a 16-bit variable. We will only be using the lower 10 bits because we only have 10 different sound levels.
uint16 level; //We are declaring a 16-bit unsigned integer which will store the sound level from LED1 - LED10
Seeing as we don't use a full PORT (RB0 - RB7 or RD0 - RD7) to bring the data in, we're going to have to mask off the unwanted bits. The bits that we don't need are RB0, RB1, RD0, RD1, RD2 and RD3. The first 4 bits of data that we'll bring in is RD4 - RD7, so we'll mask off the bottom bits (RD0 - RD3) and set the variable 'level' equal to to upper nibble (RD4 - RD7).
level = 0x00F0 & PORTD; //Mask off the bottom bits and make 'level' equal to the upper nibble of PORTD
Now that we have the values stored that are on outputs LED7 - LED10, we have to get our variable 'level' ready to be able to store the next 6 bits of data that are on RB2 - RB7. In order to make room for these next 6 bits we have to left bit shift 'level' by 2. So right now 'level' would look something like this in binary form:
xxxx = RD4,RD5,RD6,RD7 respectively
level = 00000000xxxx0000 <= There is only enough room for 4 bits of data on the end. We need to fit 6 bits of data
So in order to bit shift 'level' two places, we put this as our next line of code:
level = level << 2; //Bit shift 'level' two places and ready it to accept the data from RB2 - RB7
This is now how 'level' would look after it has been bit shifted left two places:
level = 000000xxxx000000 <= Now we have enough room for RB2 - RB7
Finally, we can now retrieve the final 6 bits of data that are output from LED1 - LED6 and store it in 'level'. Here is the next line of code in order to do that:
level = level | ((0xFC & PORTB) >> 2); //Mask off bits RB0 and RB1, then bit shift RB2 - RB7 two places to the right and store it in the bottom 6 bits of 'level'
This line is a little more cryptic, but it does the exact same thing as we did with PORTD except it shifts the data two places to the right. In the inside brackets we mask off RB0 and RB1 and then shift the result to the right two places. Now that the data that we want is in the lowest 6 bits, we will OR it with 'level' which will simply place RB2 - RB7 into the lowest 6 bits of 'level'. Here is what it will end up looking like:
xxxx = RD4,RD5,RD6,RD7 respectively
yyyyyy = RB2,RB3,RB4,RB5,RB6,RB7 respectively
level = 000000xxxxyyyyyy
Next we will match the values of 'level' with the corresponding sound level.
Step 12: Matching Input Values With Sound Levels
As I'm sure you have already noticed, the input bits from the outputs LED1 to LED10 are not stored in order in the variable 'level'. It is in fact stored like this:
LM3915 Outputs => PIC18F4550 Inputs
bit # -> 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
level = 0 : 0 : 0 : 0 : 0 : 0 :LED7:LED8:LED9:LED10:LED1:LED2:LED3:LED4:LED5:LED6
Now I know that we could re-arrange the bits to make it easier to work with, but that would mean more instructions for the PIC every time it reads those inputs, which means more time wasted. So we'll just go ahead and figure out the value of 'level' at each sound level and leave the bits in the order that they are in right now. Remember that the LM3915 outputs are active low, meaning that when it detects sound at a certain level it will pull those outputs low.
Sound Level 0 (No sound)
level = 0000001111111111
= 0x3FF
Sound Level 1
level = 0000001111011111
= 0x3DF
Sound Level 2
level = 0000001111001111
= 0x3CF
Sound Level 3
level = 0000001111000111
= 0x3C7
Sound Level 4
level = 0000001111000011
= 0x3C3
Sound Level 5
level = 0000001111000001
= 0x3C1
Sound Level 6
level = 0000001111000000
= 0x3C0
Sound Level 7
level = 0000000111000000
= 0x1C0
Sound Level 8
level = 0000000011000000
= 0x0C0
Sound Level 9
level = 0000000001000000
= 0x040
Sound Level 10 (Max Sound)
level = 0000000000000000
= 0x000
The LM3915 is hardwired in bar mode, so these 10 values will never change. When the sound level is equal to output LED1 the variable 'level' will always be equal to 0x3DF, when the sound level is equal to output LED2 'level' will always be equal to 0x3CF, etc. So now that the PIC18F4550 can tell what the sound level is from the LM3915 IC, we can make all sorts of different shapes for the VU Meter. It is very easy to code too, as you will see in the next step.
LM3915 Outputs => PIC18F4550 Inputs
bit # -> 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
level = 0 : 0 : 0 : 0 : 0 : 0 :LED7:LED8:LED9:LED10:LED1:LED2:LED3:LED4:LED5:LED6
Now I know that we could re-arrange the bits to make it easier to work with, but that would mean more instructions for the PIC every time it reads those inputs, which means more time wasted. So we'll just go ahead and figure out the value of 'level' at each sound level and leave the bits in the order that they are in right now. Remember that the LM3915 outputs are active low, meaning that when it detects sound at a certain level it will pull those outputs low.
Sound Level 0 (No sound)
level = 0000001111111111
= 0x3FF
Sound Level 1
level = 0000001111011111
= 0x3DF
Sound Level 2
level = 0000001111001111
= 0x3CF
Sound Level 3
level = 0000001111000111
= 0x3C7
Sound Level 4
level = 0000001111000011
= 0x3C3
Sound Level 5
level = 0000001111000001
= 0x3C1
Sound Level 6
level = 0000001111000000
= 0x3C0
Sound Level 7
level = 0000000111000000
= 0x1C0
Sound Level 8
level = 0000000011000000
= 0x0C0
Sound Level 9
level = 0000000001000000
= 0x040
Sound Level 10 (Max Sound)
level = 0000000000000000
= 0x000
The LM3915 is hardwired in bar mode, so these 10 values will never change. When the sound level is equal to output LED1 the variable 'level' will always be equal to 0x3DF, when the sound level is equal to output LED2 'level' will always be equal to 0x3CF, etc. So now that the PIC18F4550 can tell what the sound level is from the LM3915 IC, we can make all sorts of different shapes for the VU Meter. It is very easy to code too, as you will see in the next step.
Step 13: Making a 10 Step Animation
Now comes the funnest part of this project, making your own animations. To start we'll just make a simple 10 step bar graph. There's also 10 different rows of blue LEDs inside the bottle, how convenient. ;)
First we need to figure out what LEDs we want on and what LEDs we want off at each step. If I'm gonna make a simple bar graph I'll want LED7, LED8 and LED9 to turn on when the sound is at level 1 (the bottom row of blue LEDs). When the sound is at level 2 I will want LED7, LED8, LED9, LED12, LED13 and LED14 to turn on while all the rest turn off (the two bottom rows of blue LEDs). So on so forth, up until we get to the 10th sound level in which we will want all of the blue LEDs on. So here is the function that we will use:
void Bar_Graph(void)
{
uint16 level;
level = (0xF0 & (uint16)PORTD) << 2; //Store the upper nibble of PORTD in 'level' from bit#9 - bit#6
level |= ((0xFC & PORTB) >> 2); //Mask off RB0 and RB1, bit shift the data right two places and store RB2 - RB7 in 'level'
switch (level)
{
case 0x03FF: SPI_Send_595(0x000000C0); break; //Sound Level 0
case 0x03DF: SPI_Send_595(0xE00000C0); break; //Sound Level 1
case 0x03CF: SPI_Send_595(0xFC0000C0); break; //Sound Level 2
case 0x03C7: SPI_Send_595(0xFF8000C0); break; //Sound Level 3
case 0x03C3: SPI_Send_595(0xFFF000C0); break; //Sound Level 4
case 0x03C1: SPI_Send_595(0xFFFE00C0); break; //Sound Level 5
case 0x03C0: SPI_Send_595(0xFFFFC0C0); break; //Sound Level 6
case 0x01C0: SPI_Send_595(0xFFFFF8C0); break; //Sound Level 7
case 0x00C0: SPI_Send_595(0xFFFFFFC0); break; //Sound Level 8
case 0x0040: SPI_Send_595(0xFFFFFFCC); break; //Sound Level 9
case 0x0000: SPI_Send_595(0xFFFFFFCF); break; //Sound Level 10
}
}
**Don't worry about bit#7 and bit#6 that make the 0xC0 right yet, its explained under the next paragraph
You can see that at sound level 0 I don't turn on any LEDs, so it sends out 0x000000C0. At sound level 1 I send out 0xE00000C0 which will turn on LED7, LED8 and LED9. At sound level 2 I send out 0xFC0000C0 which will turn on LED7, LED8, LED9, LED10, LED11 and LED12. And so on. Its quite simple, you just figure out which LEDs you want turned on or off and whatever level of sound, then just put it into the switch statement.
Now do you remember back in step #8 when I told you that we ignore those 4 upper bits of data that is sent to IC4? Well now we are actually going to use 2 of those unused bits when we are passing our 32-bit data value to SPI_Send_74HC595(*data*). You may have noticed in the schematic that we are able to control all of the green LEDs as a whole and all of the yellow LEDs as a whole thanks to the 2x IRL510 MOSFETs that are in the circuit. To turn on all of the green LEDs we simply write a 1 (+5v) to RC2 and to turn on all of the yellow LEDs we write a 1 to RC1. This turns on the IRL510's allowing current to flow through them, thus lighting the LEDs.
In order to turn the bottle and lemon on, when you send your data through the function SPI_Send_595(*data*) you will have to make bit#6 and bit#7 high. Bit#6 controls the bottle and bit#7 controls the lemon. In the SPI_Send_595 function I mask off bit#6 and bit#7 and set the output of RC1 and RC2 according to the state of those bits. The IRL510's are not controlled by the shift registers, don't get that confused. I just use bit#6 and bit#7 in software, they never get transferred to the 74HC595's. As you can see they get masked off when we send out byte #4.
#define LEMON PORTCbits.RC2
#define BOTTLE PORTCbits.RC1
void SPI_Send_595(uint32 data)
{
LEMON = (data >> 7) & 0x00000001; //Turn the lemon on if bit#7 is high and turn it off if bit#7 is low
BOTTLE = (data >> 6) & 0x00000001; //Turn the bottle on if bit#6 is high and turn it off it bit#6 is low
SPI_Send(data >> 24); //Shift out byte #1 first (IC1)
SPI_Send((data >> 16) & 0x000000FF); //Shift out byte #2 next (IC2)
SPI_Send((data >> 8) & 0x000000FF); //Shift out byte #3 next (IC3)
SPI_Send(data & 0x0000000F); //Shift out byte #4 last (IC4)
LAT_595 = 1; //Latch the data onto the outputs of the 74HC595's
Nop();
LAT_595 = 0; //Reset the latch to its default state
}
You may also notice that both RC1 and RC2 are PWM outputs on the PIC18F4550. They have been placed there in case somebody would like to control the brightness of the bottle or the lemon. I think it'd be pretty cool to use the bar graph for an LED animation and for every level of sound have a different brightness of light from the yellow LEDs. That way the lemon would also go to the beat of the music with the blue LEDs.
I ran into trouble when I designed the first version of the PCB and I can't control the LED bottle and lemon separately. I didn't find my error until I had already created and assembled the PCB. No worries for you though, I fixed that mistake in the PCB files as soon as I found it, so if you use my design to make this VU Meter you will be able to control the bottle and the lemon.
I eventually got sick of figuring out values for specific LEDs that I wanted to turn on so I whipped together a small program which will do all of the calculations for us. All we need to do is take that data hex value and put it in SPI_Send_595(*Data here*) and it will light up every LED that it shows on the program. It can be downloaded in the zip file in step #4. As far as I'm concerned this program is a life saver.
First we need to figure out what LEDs we want on and what LEDs we want off at each step. If I'm gonna make a simple bar graph I'll want LED7, LED8 and LED9 to turn on when the sound is at level 1 (the bottom row of blue LEDs). When the sound is at level 2 I will want LED7, LED8, LED9, LED12, LED13 and LED14 to turn on while all the rest turn off (the two bottom rows of blue LEDs). So on so forth, up until we get to the 10th sound level in which we will want all of the blue LEDs on. So here is the function that we will use:
void Bar_Graph(void)
{
uint16 level;
level = (0xF0 & (uint16)PORTD) << 2; //Store the upper nibble of PORTD in 'level' from bit#9 - bit#6
level |= ((0xFC & PORTB) >> 2); //Mask off RB0 and RB1, bit shift the data right two places and store RB2 - RB7 in 'level'
switch (level)
{
case 0x03FF: SPI_Send_595(0x000000C0); break; //Sound Level 0
case 0x03DF: SPI_Send_595(0xE00000C0); break; //Sound Level 1
case 0x03CF: SPI_Send_595(0xFC0000C0); break; //Sound Level 2
case 0x03C7: SPI_Send_595(0xFF8000C0); break; //Sound Level 3
case 0x03C3: SPI_Send_595(0xFFF000C0); break; //Sound Level 4
case 0x03C1: SPI_Send_595(0xFFFE00C0); break; //Sound Level 5
case 0x03C0: SPI_Send_595(0xFFFFC0C0); break; //Sound Level 6
case 0x01C0: SPI_Send_595(0xFFFFF8C0); break; //Sound Level 7
case 0x00C0: SPI_Send_595(0xFFFFFFC0); break; //Sound Level 8
case 0x0040: SPI_Send_595(0xFFFFFFCC); break; //Sound Level 9
case 0x0000: SPI_Send_595(0xFFFFFFCF); break; //Sound Level 10
}
}
**Don't worry about bit#7 and bit#6 that make the 0xC0 right yet, its explained under the next paragraph
You can see that at sound level 0 I don't turn on any LEDs, so it sends out 0x000000C0. At sound level 1 I send out 0xE00000C0 which will turn on LED7, LED8 and LED9. At sound level 2 I send out 0xFC0000C0 which will turn on LED7, LED8, LED9, LED10, LED11 and LED12. And so on. Its quite simple, you just figure out which LEDs you want turned on or off and whatever level of sound, then just put it into the switch statement.
Now do you remember back in step #8 when I told you that we ignore those 4 upper bits of data that is sent to IC4? Well now we are actually going to use 2 of those unused bits when we are passing our 32-bit data value to SPI_Send_74HC595(*data*). You may have noticed in the schematic that we are able to control all of the green LEDs as a whole and all of the yellow LEDs as a whole thanks to the 2x IRL510 MOSFETs that are in the circuit. To turn on all of the green LEDs we simply write a 1 (+5v) to RC2 and to turn on all of the yellow LEDs we write a 1 to RC1. This turns on the IRL510's allowing current to flow through them, thus lighting the LEDs.
In order to turn the bottle and lemon on, when you send your data through the function SPI_Send_595(*data*) you will have to make bit#6 and bit#7 high. Bit#6 controls the bottle and bit#7 controls the lemon. In the SPI_Send_595 function I mask off bit#6 and bit#7 and set the output of RC1 and RC2 according to the state of those bits. The IRL510's are not controlled by the shift registers, don't get that confused. I just use bit#6 and bit#7 in software, they never get transferred to the 74HC595's. As you can see they get masked off when we send out byte #4.
#define LEMON PORTCbits.RC2
#define BOTTLE PORTCbits.RC1
void SPI_Send_595(uint32 data)
{
LEMON = (data >> 7) & 0x00000001; //Turn the lemon on if bit#7 is high and turn it off if bit#7 is low
BOTTLE = (data >> 6) & 0x00000001; //Turn the bottle on if bit#6 is high and turn it off it bit#6 is low
SPI_Send(data >> 24); //Shift out byte #1 first (IC1)
SPI_Send((data >> 16) & 0x000000FF); //Shift out byte #2 next (IC2)
SPI_Send((data >> 8) & 0x000000FF); //Shift out byte #3 next (IC3)
SPI_Send(data & 0x0000000F); //Shift out byte #4 last (IC4)
LAT_595 = 1; //Latch the data onto the outputs of the 74HC595's
Nop();
LAT_595 = 0; //Reset the latch to its default state
}
You may also notice that both RC1 and RC2 are PWM outputs on the PIC18F4550. They have been placed there in case somebody would like to control the brightness of the bottle or the lemon. I think it'd be pretty cool to use the bar graph for an LED animation and for every level of sound have a different brightness of light from the yellow LEDs. That way the lemon would also go to the beat of the music with the blue LEDs.
I ran into trouble when I designed the first version of the PCB and I can't control the LED bottle and lemon separately. I didn't find my error until I had already created and assembled the PCB. No worries for you though, I fixed that mistake in the PCB files as soon as I found it, so if you use my design to make this VU Meter you will be able to control the bottle and the lemon.
I eventually got sick of figuring out values for specific LEDs that I wanted to turn on so I whipped together a small program which will do all of the calculations for us. All we need to do is take that data hex value and put it in SPI_Send_595(*Data here*) and it will light up every LED that it shows on the program. It can be downloaded in the zip file in step #4. As far as I'm concerned this program is a life saver.
Step 14: "Dot Mode" Animation
Even though the LM3915 is hardwired in bar mode, we can still make a dot mode animation through the software. The function is the same as the bar graph animation, only this one doesn't keep the levels underneath it still lit.
void Dot_Mode(void)
{
uint16 level;
level = (0xF0 & (uint16)PORTD) << 2;
level |= ((0xFC & PORTB) >> 2);
switch (level)
{
case 0x03FF: SPI_Send_595(0x000000C0); break;
case 0x03DF: SPI_Send_595(0xE00000C0); break;
case 0x03CF: SPI_Send_595(0x1C0000C0); break;
case 0x03C7: SPI_Send_595(0x038000C0); break;
case 0x03C3: SPI_Send_595(0x007000C0); break;
case 0x03C1: SPI_Send_595(0x000E00C0); break;
case 0x03C0: SPI_Send_595(0x0001C0C0); break;
case 0x01C0: SPI_Send_595(0x000038C0); break;
case 0x00C0: SPI_Send_595(0x000007C0); break;
case 0x0040: SPI_Send_595(0x000000CC); break;
case 0x0000: SPI_Send_595(0x000000C3); break;
}
}
To make any other animation, open up the VU Meter Data Calculator then turn the specific LEDs on or off and copy and paste the hex data into the switch statement above. Put that hex value into any of those SPI_Send_595(*hex data*) and it will light up at that sound level. You can make a bunch of animations and have them all controlled by the tactile switch (S2) on the PCB. Make it so that every time that you press S2 the VU Meter will change to a different animation. My PCB doesn't have a switch or I would have done that, that is another feature that I added to the PCB files after I had already assembled my VU Meter. Well if you've made it to this step then congrats, you've wrapped this project!
void Dot_Mode(void)
{
uint16 level;
level = (0xF0 & (uint16)PORTD) << 2;
level |= ((0xFC & PORTB) >> 2);
switch (level)
{
case 0x03FF: SPI_Send_595(0x000000C0); break;
case 0x03DF: SPI_Send_595(0xE00000C0); break;
case 0x03CF: SPI_Send_595(0x1C0000C0); break;
case 0x03C7: SPI_Send_595(0x038000C0); break;
case 0x03C3: SPI_Send_595(0x007000C0); break;
case 0x03C1: SPI_Send_595(0x000E00C0); break;
case 0x03C0: SPI_Send_595(0x0001C0C0); break;
case 0x01C0: SPI_Send_595(0x000038C0); break;
case 0x00C0: SPI_Send_595(0x000007C0); break;
case 0x0040: SPI_Send_595(0x000000CC); break;
case 0x0000: SPI_Send_595(0x000000C3); break;
}
}
To make any other animation, open up the VU Meter Data Calculator then turn the specific LEDs on or off and copy and paste the hex data into the switch statement above. Put that hex value into any of those SPI_Send_595(*hex data*) and it will light up at that sound level. You can make a bunch of animations and have them all controlled by the tactile switch (S2) on the PCB. Make it so that every time that you press S2 the VU Meter will change to a different animation. My PCB doesn't have a switch or I would have done that, that is another feature that I added to the PCB files after I had already assembled my VU Meter. Well if you've made it to this step then congrats, you've wrapped this project!
Step 15: Challenges
When I originally made this project I ran into a few challenges and a couple set backs. First off, there were a couple solder bridges and unwanted connections after I assembled the PCB. Those problems were minor and it didn't take long to fix them with the continuity function on my multimeter. Then I realized that I didn't allow enough room on the PCB for a heatsink which goes onto the LM7805. Luckily, there was enough room for it to be installed backwards. The LM7805 gets warm but it doesn't get hot, so I'm lucky that even though the heatsink is installed backwards it still has enough surface area touching the regulator to dissipate that extra heat. If you look at the PCB now I have added enough room for a heatsink on the LM7805 and it can be installed the right way.
Those last two setbacks were quite minor, I found the biggest setback when I noticed that I hooked up the green and yellow LEDs up to the IRL510's the wrong way! That is why my project lacks the functionality to control the lemon and the bottle. Now I just hardwired them in so that they are both completely on all of the time, but that takes a lot of fun out of the project. Once again, I fixed that in the PCB so that anybody who makes this project does have that functionality.
The error is posted in the photos to this step, but it happened because I had the LEDs connected to the source of the IRL510's and then grounded. Now I thought that this would work, but for some reason when I'd supply +5v from the PIC to the gate of the MOSFETs, the MOSFETs wouldn't allow enough current through and the LEDs wouldn't light up. So I got out a breadboard and started trying to figure out why it wouldn't work. Then I connected it up so that the LEDs were connected to the drain of the MOSFETs and voila! When I'd put +5v on the gate of either IRL510, their corresponding LEDs would light up. I should have wired it up this way from the start.
Those last two setbacks were quite minor, I found the biggest setback when I noticed that I hooked up the green and yellow LEDs up to the IRL510's the wrong way! That is why my project lacks the functionality to control the lemon and the bottle. Now I just hardwired them in so that they are both completely on all of the time, but that takes a lot of fun out of the project. Once again, I fixed that in the PCB so that anybody who makes this project does have that functionality.
The error is posted in the photos to this step, but it happened because I had the LEDs connected to the source of the IRL510's and then grounded. Now I thought that this would work, but for some reason when I'd supply +5v from the PIC to the gate of the MOSFETs, the MOSFETs wouldn't allow enough current through and the LEDs wouldn't light up. So I got out a breadboard and started trying to figure out why it wouldn't work. Then I connected it up so that the LEDs were connected to the drain of the MOSFETs and voila! When I'd put +5v on the gate of either IRL510, their corresponding LEDs would light up. I should have wired it up this way from the start.
Step 16: References
Here I have listed a couple websites that will help out with this project if you are a beginner or don't know how certain components work (such as the 74HC595). I have also listed the datasheets for some of the components used in this project.
Basic Electronics
http://www.kpsec.freeuk.com/map.htm
Tutorial on the 74HC595
http:// http://www.arduino.cc/en/Tutorial/ShiftOut
PIC18F4550 Datasheet
http://ww1.microchip.com/downloads/en/devicedoc/39632d.pdf
LM3915 Datasheet
http://www.national.com/ds/LM/LM3915.pdf
IRL510 Datasheet
http://www.vishay.com/docs/91297/91297.pdf
74HC595 Datasheet
http://www.nxp.com/documents/data_sheet/74HC_HCT595.pdf
Basic Electronics
http://www.kpsec.freeuk.com/map.htm
Tutorial on the 74HC595
http:// http://www.arduino.cc/en/Tutorial/ShiftOut
PIC18F4550 Datasheet
http://ww1.microchip.com/downloads/en/devicedoc/39632d.pdf
LM3915 Datasheet
http://www.national.com/ds/LM/LM3915.pdf
IRL510 Datasheet
http://www.vishay.com/docs/91297/91297.pdf
74HC595 Datasheet
http://www.nxp.com/documents/data_sheet/74HC_HCT595.pdf
Step 17: Conclusion (We're Done!)
All in all, I am quite pleased with how this LED VU Meter turned out. Its something original and I had fun making it. If I were to do it again I think I'd use SMD LEDs and try to make the PCB way smaller. If I was to use SMD LEDs I would be able to move the PIC18F4550 and the 74HC595's right underneath the LEDs on the bottom side of the PCB. I can't do that with through hole obviously because all of the 5mm and 3mm LEDs are soldered to the bottom layer of the PCB. Next time I would also omit the LM3915 and just run a signal into the PIC's ADC (Analog to Digital Converter). This would decrease the board size and lower the amount of components needed, but it would be a lot more software dependent (Which I actually think is a plus).
Also I am very aware that I need better lighting for my pictures. I apologize if some of them are dark but that's the best I could do for right now. I'll work on it :)
Well that concludes this instructable! If you have any questions or concerns about this project, send me a message or post a comment and I will get back to you as soon as I can. The more feedback I get, the better this Instructable will become. Thanks for having a look!
Also I am very aware that I need better lighting for my pictures. I apologize if some of them are dark but that's the best I could do for right now. I'll work on it :)
Well that concludes this instructable! If you have any questions or concerns about this project, send me a message or post a comment and I will get back to you as soon as I can. The more feedback I get, the better this Instructable will become. Thanks for having a look!