버저, 피에조 스피커로 음악 만들기

 

버저 (Buzzer)

 

먼저 버저는 패시브 버저와 액티브 버저로 나눌 수 있습니다.

 

액티브 버저는 단일한 음을 만드는 간단한 장치입니다. 사용방법도 쉬워서 단순히 전원을 넣어주면 소리가 나고 전원을 끊으면 소리가 끊깁니다. 사용자가 출력되는 소리의 주파수를 선택할 수는 없습니다. 아래와 같이 연결해주면 됩니다.

  • S(Signal 혹은 +) 라인 ==> 아두이노의 디지털 8번 핀
  • GND (혹은 -) ==> 아두이노의 GND

그리고 아래 코드로 테스트 해볼 수 있습니다.

int speakerPin = 8;
void setup () {
  pinMode (speakerPin, OUTPUT);
}
void loop () {
  analogWrite (speakerPin, 255);
  delay (50);
  analogWrite (speakerPin, 0);
  delay (10);
}

 

반면에 패시브 버저는 다양한 음을 출력할 수 있습니다. 대신 AC sound signal을 만들어 줘야 합니다. 아두이노에서 이런 사운드 신호를 만들기 위해서는 디지털 핀으로 빠르게 on/off 를 반복해 주는데 on/off 가 유지되는 시간에 의해 다른 소리가 생성되게 됩니다.

피에조 스피커나 패시브 버저를 다음과 같이 연결해 줍니다.

  • S(Signal 혹은 +) ==> 아두이노 디지털 9번 핀
  • GND (혹은 -) ==> 아두이노의 GND

그리고 아래 코드로 테스트 해볼 수 있습니다. 연결된 디지털 핀으로 on/off 를 빠르게 반복해 줍니다. 주의해서 보실 점은 off 상태를 유지하는 시간이 출력하고자 하는 음에 따라 달라진다는 점입니다.

int buzzer = 9 ;/ / setting controls the digital IO foot buzzer
void setup ()
{
  pinMode (buzzer, OUTPUT) ;/ / set the digital IO pin mode, OUTPUT to output
}

void loop ()
{
  unsigned char i, j ;/ / define variables
  while (1)
  {
    for (i = 0; i <80; i + +) / / outputs a frequency sound
    {
      digitalWrite (buzzer, HIGH) ;/ / send voice
      delay (1) ;/ / Delay 1ms
      digitalWrite (buzzer, LOW) ;/ / do not send voice
      delay (1) ;/ / delay ms
    }

    for (i = 0; i <500; i + +) / / output to another frequency sound
    {
      digitalWrite (buzzer, HIGH) ;/ / send voice
      delay (2) ;/ / delay 2ms
      digitalWrite (buzzer, LOW) ;/ / do not send voice
      delay (2) ;/ / delay 2ms
    }
  }
}

 

이제 이걸 응용해서 다양한 사운드를 만들 수 있습니다. 조금 복잡하지만 arduino.cc 에서 제공하는 아래 예제를 보시죠. 아래 예제에서는 tone(음의 높낮이), beat(음의 지속시간) 를 통해 음악을 만들어 냅니다.

tone은 아두이노의 디지털 핀을 빠르게 on/off 할 때 on/off 각각의 상태를 유지하는 시간입니다. beat는 tone을 만드는 on/off 반복상태를 얼마나 오래 지속할 것인가를 결정합니다. 입력값에 따라 특정 음을 만들도록 이런 작업을 해주는 함수가 playTone() 입니다.

그리고 음악을 만들기 위해 미리 일련의 값들을 설정해 둔 것이 melody[], beats[] 배열입니다. loop 함수 안에서 배열에 저장된 음을 playTone() 함수로 재생합니다.

/* Play Melody
 * -----------
 *
 * Program to play a simple melody
 *
 * Tones are created by quickly pulsing a speaker on and off 
 *   using PWM, to create signature frequencies.
 *
 * Each note has a frequency, created by varying the period of 
 *  vibration, measured in microseconds. We'll use pulse-width
 *  modulation (PWM) to create that vibration.

 * We calculate the pulse-width to be half the period; we pulse 
 *  the speaker HIGH for 'pulse-width' microseconds, then LOW 
 *  for 'pulse-width' microseconds.
 *  This pulsing creates a vibration of the desired frequency.
 *
 * (cleft) 2005 D. Cuartielles for K3
 * Refactoring and comments 2006 clay.shirky@nyu.edu
 * See NOTES in comments at end for possible improvements
 */

// TONES  ==========================================
// Start by defining the relationship between 
//       note, period, &  frequency. 
#define  c     3830    // 261 Hz 
#define  d     3400    // 294 Hz 
#define  e     3038    // 329 Hz 
#define  f     2864    // 349 Hz 
#define  g     2550    // 392 Hz 
#define  a     2272    // 440 Hz 
#define  b     2028    // 493 Hz 
#define  C     1912    // 523 Hz 
// Define a special note, 'R', to represent a rest
#define  R     0

// SETUP ============================================
// Set up speaker on a PWM pin (digital 9, 10 or 11)
int speakerOut = 9;
// Do we want debugging on serial out? 1 for yes, 0 for no
int DEBUG = 1;

void setup() { 
  pinMode(speakerOut, OUTPUT);
  if (DEBUG) { 
    Serial.begin(9600); // Set serial out if we want debugging
  } 
}

// MELODY and TIMING  =======================================
//  melody[] is an array of notes, accompanied by beats[], 
//  which sets each note's relative length (higher #, longer note) 
int melody[] = {  C,  b,  g,  C,  b,   e,  R,  C,  c,  g, a, C };
int beats[]  = { 16, 16, 16,  8,  8,  16, 32, 16, 16, 16, 8, 8 }; 
int MAX_COUNT = sizeof(melody) / 2; // Melody length, for looping.

// Set overall tempo
long tempo = 10000;
// Set length of pause between notes
int pause = 1000;
// Loop variable to increase Rest length
int rest_count = 100; //<-BLETCHEROUS HACK; See NOTES

// Initialize core variables
int tone_ = 0;
int beat = 0;
long duration  = 0;

// PLAY TONE  ==============================================
// Pulse the speaker to play a tone for a particular duration
void playTone() {
  long elapsed_time = 0;
  if (tone_ > 0) { // if this isn't a Rest beat, while the tone has 
    //  played less long than 'duration', pulse speaker HIGH and LOW
    while (elapsed_time < duration) {

      digitalWrite(speakerOut,HIGH);
      delayMicroseconds(tone_ / 2);

      // DOWN
      digitalWrite(speakerOut, LOW);
      delayMicroseconds(tone_ / 2);

      // Keep track of how long we pulsed
      elapsed_time += (tone_);
    } 
  }
  else { // Rest beat; loop times delay
    for (int j = 0; j < rest_count; j++) { // See NOTE on rest_count
      delayMicroseconds(duration);  
    }                                
  }                                 
}

// LET THE WILD RUMPUS BEGIN =============================
void loop() {
  // Set up a counter to pull from melody[] and beats[]
  for (int i=0; i<MAX_COUNT; i++) {
    tone_ = melody[i];
    beat = beats[i];

    duration = beat * tempo; // Set up timing

    playTone(); 
    // A pause between notes...
    delayMicroseconds(pause);

    if (DEBUG) { // If debugging, report loop, tone, beat, and duration
      Serial.print(i);
      Serial.print(":");
      Serial.print(beat);
      Serial.print(" ");    
      Serial.print(tone_);
      Serial.print(" ");
      Serial.println(duration);
    }
  }
}

 

위 예제처럼 아두이노의 디지털 핀을 빠르게 on/off 반복시키는 것은 PWM의 원리와 같습니다. 그래서 PWM – analogWrite() 함수를 이용해서도 똑같이 구현을 할 수 있습니다. 아래는 PWM 을 이용한 소스코드 입니다.

/* Play Melody
 * -----------
 *
 * Program to play melodies stored in an array, it requires to know
 * about timing issues and about how to play tones.
 *
 * The calculation of the tones is made following the mathematical
 * operation:
 *
 *       timeHigh = 1/(2 * toneFrequency) = period / 2
 *
 * where the different tones are described as in the table:
 *
 * note         frequency       period  PW (timeHigh)   
 * c            261 Hz          3830    1915    
 * d            294 Hz          3400    1700    
 * e            329 Hz          3038    1519    
 * f            349 Hz          2864    1432    
 * g            392 Hz          2550    1275    
 * a            440 Hz          2272    1136    
 * b            493 Hz          2028    1014    
 * C            523 Hz          1912    956
 *
 * (cleft) 2005 D. Cuartielles for K3
 */

int ledPin = 13;
int speakerOut = 9;               
byte names[] = {'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C'};  
int tones[] = {1915, 1700, 1519, 1432, 1275, 1136, 1014, 956};
byte melody[] = "2d2a1f2c2d2a2d2c2f2d2a2c2d2a1f2c2d2a2a2g2p8p8p8p";
// count length: 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
//                                10                  20                  30
int count = 0;
int count2 = 0;
int count3 = 0;
int MAX_COUNT = 24;
int statePin = LOW;

void setup() {
 pinMode(ledPin, OUTPUT); 
}

void loop() {
  analogWrite(speakerOut, 0);     
  for (count = 0; count < MAX_COUNT; count++) {
    statePin = !statePin;
    digitalWrite(ledPin, statePin);
    for (count3 = 0; count3 <= (melody[count*2] - 48) * 30; count3++) {
      for (count2=0;count2<8;count2++) {
        if (names[count2] == melody[count*2 + 1]) {       
          analogWrite(speakerOut,500);
          delayMicroseconds(tones[count2]);
          analogWrite(speakerOut, 0);
          delayMicroseconds(tones[count2]);
        } 
        if (melody[count*2 + 1] == 'p') {
          // make a pause of a certain size
          analogWrite(speakerOut, 0);
          delayMicroseconds(500);
        }
      }
    }
  }
}

 

아두이노에서는 이것을 더 간단히 처리할 수 있도록 tone() 함수를 지원합니다.

/*
  Melody
 
 Plays a melody 
 
 circuit:
 * 8-ohm speaker on digital pin 8
 
 created 21 Jan 2010
 modified 30 Aug 2011
 by Tom Igoe 

This example code is in the public domain.
 
 http://arduino.cc/en/Tutorial/Tone
 
 */
 #include "pitches.h"

// notes in the melody:
int melody[] = {
  NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  4, 8, 8, 4,4,4,4,4 };

void setup() {
  // iterate over the notes of the melody:
  for (int thisNote = 0; thisNote < 8; thisNote++) {

    // to calculate the note duration, take one second 
    // divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000/noteDurations[thisNote];
    tone(8, melody[thisNote],noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(8);
  }
}

void loop() {
  // no need to repeat the melody.
}

 

tone() 함수로 더 간결하게 소스를 작성할 수 있습니다. 단, 이렇게 음을 출력하는 작업은 다른 복잡한 작업과 같이 배치해서 실행할 경우 원하는 만큼 정확히 동작하지 않을 수도 있다는 점을 참고하세요.

 

참고자료 : 아두이노로 멜로디 만들기아두이노 tone(), 패시브 버저, 패시브버저와 액티브버저의 차이

 

You may also like...