LED VU미터 타워

RESIZE_IMG_9572.JPG

LED Dual Volume Unit Meter Tower Rev.1 @ 2015.11

본 글에서 소개하는 VU미터 제작기는 AVR또는 아두이노(Arduino)를 활용하였으며, 약간의 손재주가 필요할 수 있습니다.

우선 VU 미터는 아래 사진과 같이 소리의 크기를 일정한 레벨에 따라 시각화시켜주는 것입니다.

RESIZE_P7070335.JPG.2560x2560_q85.jpg

위 작은 VU미터를 크고 아름답게 만든것이 아래 사진.

anigif.gif

준비물

VU미터가 스테레오이므로 총 2대 제작.

1. 회로도

0949.PNG

D1은 문턱전압이 높은 일반 정류 다이오드 사용하면 안 되고 1N5819와 같은 쇼트키 다이오드를 사용합니다.

일반적인 LED와 달리 본 포스트에서 사용하는 고휘도 LED는 전류소모가 많으니 반드시 TR로 전류를 증폭하여 제어하도록 합니다.

LED쪽 회로도는 다음과 같습니다.

TR base에는 1K 저항을 거쳐 MCU의 디지탈 포트와 연결합니다.

아두이노로 치면 D0 ~ D13의 디지탈 I/O 포트에 연결하도록 합니다.

LED 저항계산기는 아래 사이트를 이용하시기 바랍니다.

2. 아크릴 판재

아크릴 사이즈가 너무 크면 LED 빛이 이상하게 퍼져서 한 곳에 편중되고, 너무 작으면 눈이 아플 수 있습니다.
따라서 빛이 한 곳에 편중되지 않도록 적절한 사이즈를 정할 필요가 있습니다.

필자가 사용한 아크릴 사이즈는 다음과 같습니다.

  • 기판 고정시킬 아크릴 판재 110mm x 80mm x 5T * 4개
  • 실제 LED 바가 될 아크릴 판재 90mm x 60mm x 10T * 26개 (여분 2개)

아크릴 제작은 디바이스마트의 커스텀 아크릴 서비스를 사용했습니다.

3. 마이컴

아두이노 사용자 배려를 위해 아두이노 우노에 사용된 칩과 동일한 ATmega328P-PU를 사용합니다.

4. LED

일반 LED는 휘도가 약하므로 반드시 고휘도 LED를 쓰도록 합니다.

필자가 사용한 고휘도 LED 좌표는 다음과 같습니다.

5. 지지대

사람으로 치면 척추가 되는 부분이므로, 너무 얇으면 아크릴 무게 때문에 홀라당 자빠질 수 있으니.. 7 ~ 10파이 정도가 적당합니다.

자세한 사항은 후술하겠습니다.

제작

RESIZE_IMG_9423.JPGRESIZE_IMG_9427.JPG

도착한 기판 고정용 (110mm x 80mm x 5T) x 4개와 LED 바 전용 (90mm x 60mm x 10T) x 26개(여분 2개) 입니다.

RESIZE_IMG_9428.JPG

RESIZE_IMG_9440.JPG

LED 아크릴 바를 고정시켜줄 구멍과 LED를 두개씩 넣을 수 있도록 구멍을 뚫었습니다.

RESIZE_IMG_9444.JPG

타공 작업 후 지지대와 맞춰보는 모습입니다.

아크릴 LED 바는 총 13개에 20mm 간격으로 배치되고, 기판 지지대까지 합쳐서 LED VU 미터 타워 총 높이가 450mm 정도 됩니다.

LED VU미터의 지지대는 440 ~ 450mm 정도로 맞췄습니다.

RESIZE_IMG_9446.JPG

RESIZE_IMG_9460.JPG

RESIZE_IMG_9475.JPG

LED는 구멍에 잘 넣고 글루건이나 투명 실리콘으로 고정시킨 뒤 LED (+)는 공통으로 땜하고 (-)는 나중에 선을 연결할 수 있도록 적절한 길이로 자르도록 합니다.

RESIZE_IMG_9480.JPG

조립 후 LED 점등이 잘 되는지 확인합니다.

RESIZE_IMG_9488.JPG

RESIZE_IMG_9518.JPG

RESIZE_IMG_9522.JPG

기판은 위 사진 처럼 고정했습니다.

가능하면 조립 전에 커넥터 먼저 연결해놓고 하는 것이 정신건강에 이롭습니다.

RESIZE_IMG_9529.JPG

RESIZE_IMG_9500.JPG

기판에서 나온 선들을 각 LED에 연결해주고 케이블 타이로 정리해주면 깔끔합니다.

Firmware 작성

AVR Studio에서 작성된 AVR용 코드입니다.

#define F_CPU 16000000UL
#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#define SAMPLING_NUM    100
#define SENSITIVITY     10
int ledLevelNow = 0;
int ledLevelOld = 0;
uint32_t ledLevelCount = 0;
uint8_t barLed[14][2] = {
    /* PORTB, PORTD */
    /* Port No. 10, 9, 19, 18, 17, 16, 15, 14, 13, 12, 11, 6, 5, 4, 3, 2 */
    { 0b00000000, 0b00000000 }, // 0
    { 0b00000010, 0b00000000 }, // 1
    { 0b00000010, 0b01000000 }, // 2
    { 0b00000110, 0b01000000 }, // 3
    { 0b00000110, 0b01100000 }, // 4
    { 0b00001110, 0b01100000 }, // 5
    { 0b00101110, 0b01100000 }, // 6
    { 0b00111110, 0b01100000 }, // 7
    { 0b00111110, 0b01110000 }, // 8
    { 0b00111111, 0b01110000 }, // 9
    { 0b00111111, 0b01111000 }, // 10
    { 0b00111111, 0b11111000 }, // 11
    { 0b00111111, 0b11111100 }, // 12
    { 0b00111111, 0b11111110 }  // 13
};
uint8_t dotLed[14][2] = {
    /* PORTB, PORTD */
    { 0b00000000, 0b00000000 }, // 0
    { 0b00000010, 0b00000000 }, // 1
    { 0b00000000, 0b01000000 }, // 2
    { 0b00000100, 0b00000000 }, // 3
    { 0b00000000, 0b00100000 }, // 4
    { 0b00001000, 0b00000000 }, // 5
    { 0b00100000, 0b00000000 }, // 6
    { 0b00010000, 0b00000000 }, // 7
    { 0b00000000, 0b00010000 }, // 8
    { 0b00000001, 0b00000000 }, // 9
    { 0b00000000, 0b00001000 }, // 10
    { 0b00000000, 0b10000000 }, // 11
    { 0b00000000, 0b00000100 }, // 12
    { 0b00000000, 0b00000010 }  // 13
};
void initGPIO()
{
    PORTB = 0x00;
    PORTD = 0x00;
    DDRB = 0xFF;
    DDRD = 0xFF;
}
void initAdc()
{
    ADMUX |= (1<<REFS0);
    ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADEN);
}
uint16_t readAdc(uint8_t adcChannel)
{
    ADMUX = (ADMUX & 0xF0) | (adcChannel & 0x0F);
    ADCSRA |= (1<<ADSC);
    while (ADCSRA & (1<<ADSC));
    return ADC;
}
int main()
{
    initGPIO();
    initAdc();
    while (true)
    {
        for (int i = 0; i < SAMPLING_NUM; i++) ledLevelCount += readAdc(5);
        ledLevelCount /= SAMPLING_NUM;
        ledLevelNow = ledLevelCount / SENSITIVITY;
        if (ledLevelNow > 13) ledLevelNow = 13;
        if (ledLevelOld < ledLevelNow) _delay_ms(10);
        
        PORTB = barLed[ledLevelNow][0];
        PORTD = barLed[ledLevelNow][1];
        ledLevelOld = ledLevelNow;
    }
}

아두이노용 코드입니다.

#define F_CPU 16000000UL
#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#define SAMPLING_NUM    100
#define SENSITIVITY     10
int ledLevelNow = 0;
int ledLevelOld = 0;
uint32_t ledLevelCount = 0;
uint8_t barLed[14][2] = {
    /* PORTB, PORTD */
    /* Port No. 10, 9, 19, 18, 17, 16, 15, 14, 13, 12, 11, 6, 5, 4, 3, 2 */
    { 0b00000000, 0b00000000 }, // 0
    { 0b00000010, 0b00000000 }, // 1
    { 0b00000010, 0b01000000 }, // 2
    { 0b00000110, 0b01000000 }, // 3
    { 0b00000110, 0b01100000 }, // 4
    { 0b00001110, 0b01100000 }, // 5
    { 0b00101110, 0b01100000 }, // 6
    { 0b00111110, 0b01100000 }, // 7
    { 0b00111110, 0b01110000 }, // 8
    { 0b00111111, 0b01110000 }, // 9
    { 0b00111111, 0b01111000 }, // 10
    { 0b00111111, 0b11111000 }, // 11
    { 0b00111111, 0b11111100 }, // 12
    { 0b00111111, 0b11111110 }  // 13
};
uint8_t dotLed[14][2] = {
    /* PORTB, PORTD */
    { 0b00000000, 0b00000000 }, // 0
    { 0b00000010, 0b00000000 }, // 1
    { 0b00000000, 0b01000000 }, // 2
    { 0b00000100, 0b00000000 }, // 3
    { 0b00000000, 0b00100000 }, // 4
    { 0b00001000, 0b00000000 }, // 5
    { 0b00100000, 0b00000000 }, // 6
    { 0b00010000, 0b00000000 }, // 7
    { 0b00000000, 0b00010000 }, // 8
    { 0b00000001, 0b00000000 }, // 9
    { 0b00000000, 0b00001000 }, // 10
    { 0b00000000, 0b10000000 }, // 11
    { 0b00000000, 0b00000100 }, // 12
    { 0b00000000, 0b00000010 }  // 13
};
void initGPIO()
{
    PORTB = 0x00;
    PORTD = 0x00;
    DDRB = 0xFF;
    DDRD = 0xFF;
}
void initAdc()
{
    ADMUX |= (1<<REFS0);
    ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADEN);
}
uint16_t readAdc(uint8_t adcChannel)
{
    ADMUX = (ADMUX & 0xF0) | (adcChannel & 0x0F);
    ADCSRA |= (1<<ADSC);
    while (ADCSRA & (1<<ADSC));
    return ADC;
}
void setup()
{
}
void loop()
{
    initGPIO();
    initAdc();
    while (true)
    {
        for (int i = 0; i < SAMPLING_NUM; i++) ledLevelCount += readAdc(5);
        ledLevelCount /= SAMPLING_NUM;
        ledLevelNow = ledLevelCount / SENSITIVITY;
        if (ledLevelNow > 13) ledLevelNow = 13;
        if (ledLevelOld < ledLevelNow) _delay_ms(10);
        
        PORTB = barLed[ledLevelNow][0];
        PORTD = barLed[ledLevelNow][1];
        ledLevelOld = ledLevelNow;
    }
}

C언어가 가능하단 가정하게 간단히 코드를 설명드리면,

  1. ADC 샘플링 횟수만큼 평균치내어 노래파형을 안정화시키고,
  2. 평균치된 값을 연산에서 LED 몇개를 킬 것인지 계산합니다.

SENSITIVITY가 낮을수록 Level 반응이 커지는데, 그만큼 정확도가 높아질수도, 작아질수도 있으므로 적당히 조절하는것이 중요합니다.

마무리

질문은 댓글을 남겨주시기 바랍니다.

9 comments

      1. 참치님이 만드신 LED VU 미터 타워에 만들고 싶은 생각이 들었는데 실례가 안된다면 LED를 연결하는 구성이 어떻게 되는지 여쭈어봐도 될까요? 저는 무선 VU미터를 구입했거든요..빠른 답변 정말 감사드립니다. 

        1. 1382.PNG

           

          제가 제작한 VU 미터 회로도이고, 아크릴 위치는 위에서 바라본 평면도입니다.

          참고되시기 바랍니다.

           

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.