ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아두이노 오르골만들기
    아두이노 2023. 1. 30. 22:19

    개요

     파에조 부저를 처음 사용할 때부터 만들고 싶었던 것을 만들어 보았다. 만들고 보니 노래하나 연주하는 데 아두이노 보드를 통째로 사용하는 것은 과하다고 생각되었고, 혹시 나중에 교육기부할 때 사용하면 좋을 것 같아서 납땜을 하지 않는 선에서 최대한 작고 컴팩트하게 만들어 보았다. (교육기부때 사용해볼려고 ppt를 만든걸 글쓰는데 활용하였다.)

     

    필요 부품

    이름 수량 단가
    터미널 블록 1 319.0
    47uF, 50V 알루미늄 캐패시터 1 55.0
    슬라이드 스위치 1 165.0
    파에조 부저 1 330.0
    ATtiny85-PU 1 4004.0
    BJT(BC337) 1 66.0
    1/4W 10k옴 저항 1 14.3
    1/4W 130옴 저항 1 14.3
    LED녹색 1 44.0
    미니 브래드 보드 1 880.0
    AA 건전지 홀더 3V 1 660.0
    AA 건전지 1.5V 2 660.0
      합계 ₩7,871.6
    • 파에조 부저(Piezo Buzzer)

     전압을 가할 때 재료가 물리적으로 변형되는 역압전효과를 이용한 것이 파에조 부저로 원하는 음의 반주기 마다 핀을 출력을 반전시키면 부저 내부 압전소자가 진동하며 음을 낸다. 대부분의 물질은 양전하와 음전하가 균형을 이뤄 전기적으로 중성인 상태로, 전압이 가해지면 전기장에 의해 내부 전하 분포가 바뀌고, 다시 전압이 없어지면 원래대로 돌아가게 된다. 그러나 대부분의 물질은 이러한 효과가 잘 드러나지 않으며, 대표적으로 잘 드러나는 석영은 전자시계를 비롯한 많은 회로에 QC(Quartz Clock)으로 사용된다. 아두이노 우노회로에 있는 16MHz 크리스탈도 압전&역압전효과를 이용한 것으로, 전압을 가해 크리스탈을 변형시켰다 탄성으로 돌아오면서 생기는 교류 신호의 주파수가 굉장히 일정하다는 성질을 활용한 것이다.

    • 평균률

    피타고라스는 음의 진동수 비가 간단한 정수비를 이룰 때 인간의 귀에 아름답게 들린다는 것을 발견하여 피타고라스 음계를 만들었는데 이것이 최초의 순정률이다. 그러나 순정률은 몇개의 음 조합을 제외하면 비를 이루는 정수가 커저서, 조성과 화음이 고정되는 문제가 있다. 순정률로 조율된 피아노 소리를 들어보면 몇 음을 제외하고 나머지는 소리가 죽은 느낌을 받을 것이다. 현대에는 이러한 문제를 해결하기 위해, 진동수비가 1:2인 옥타브 사이 음 비율을 12개로 등분한 평균률을 사용한다. 12번 곱해서 2배가 되는 것이니 음정이 1도 높아질 때 진동수는 2^(1/12)배 증가한다.

    코딩

    깃허브에 보면 다른 노래도 많이 만들어 놨습니다.

    https://github.com/sidreco214/MusicBox/blob/main/Always_With_Me.ino

     

    GitHub - sidreco214/MusicBox: Arduino MusicBox

    Arduino MusicBox. Contribute to sidreco214/MusicBox development by creating an account on GitHub.

    github.com

    /*
    MusicBos_Always_With_Me(센과 치히로의 행방불명 OST: 언제나 몇번이라도)
    악보 출처(첫번째 악보)
    https://yuri-blog.tistory.com/entry/%ED%94%BC%EC%95%84%EB%85%B8-%EC%95%85%EB%B3%B4%EB%8F%99%EC%98%81%EC%83%81-%EC%84%BC%EA%B3%BC-%EC%B9%98%ED%9E%88%EB%A1%9C%EC%9D%98-%ED%96%89%EB%B0%A9%EB%B6%88%EB%AA%85-%EC%96%B8%EC%A0%9C%EB%82%98-%EB%AA%87-%EB%B2%88%EC%9D%B4%EB%9D%BC%EB%8F%84
    */
    
    #define Buzzer 3  //부저 핀
    #define LED 4
    #define term 4800 //악보 한 마디 길이, 메트로늄 기호로부터 계산하며, 없는 경우 4/4박자 곡이 보통 2000인걸 감안하여 감으로 찾기
    
    //4옥타브 도면 c1, 5옥타브 파샵이면 fS5 이런식으로 명명되었다.
    const float c1 = 32.7032,   cS1 = 34.6478,   d1 = 36.7081,   dS1 = 38.8909,   e1 = 41.2034,   f1 = 43.6535,   fS1 = 46.2493,   g1 = 48.9994,   gS1 = 51.9131,   a1 = 55.0000,   aS1 = 58.2705,   b1 = 61.7354;
    const float c2 = 65.4064 ,  cS2 = 69.2957,   d2 = 73.4162,   dS2 = 77.7817,   e2 = 82.4069,   f2 = 87.3071,   fS2 = 92.4986,   g2 = 97.9989,   gS2 = 103.8262,  a2 = 110.0000,  aS2 = 116.5409,  b2 = 123.4708;
    const float c3 = 130.8128,  cS3 = 138.5913,  d3 = 146.8324,  dS3 = 155.5635,  e3 = 164.8138,  f3 = 174.6141,  fS3 = 184.9972,  g3 = 195.9977,  gS3 = 207.6523,  a3 = 220.0000,  aS3 = 233.0819,  b3 = 246.9417;
    const float c4 = 261.6256,  cS4 = 277.1826,  d4 = 293.6648,  dS4 = 311.1270,  e4 = 329.6276,  f4 = 349.2282,  fS4 = 369.9944,  g4 = 391.9954,  gS4 = 415.3047,  a4 = 440.0000,  aS4 = 466.1638,  b4 = 493.8833;
    const float c5 = 523.2511,  cS5 = 554.3653,  d5 = 587.3295,  dS5 = 622.2540,  e5 = 659.2551,  f5 = 698.4565,  fS5 = 739.9888,  g5 = 783.9909,  gS5 = 830.6094,  a5 = 880.0000,  aS5 = 932.3275,  b5 = 987.7666;
    const float c6 = 1046.5023, cS6 = 1108.7305, d6 = 1174.6591, dS6 = 1244.5079, e6 = 1318.5102, f6 = 1396.9129, fS6 = 1479.9777, g6 = 1567.9817, gS6 = 1661.2188, a6 = 1760.0000, aS6 = 1864.6550, b6 = 1975.5332;
    const float c7 = 2093.0045, cS7 = 2217.4610, d7 = 2349.3181, dS7 = 2489.0159, e7 = 2637.0205, f7 = 2793.8259, fS7 = 2959.9554, g7 = 3135.9635, gS7 = 3322.4376, a7 = 3520.0000, aS7 = 3729.3101, b7 = 3951.0664;
    const float c8 = 4186.0090, cS8 = 4434.9221, d8 = 4698.6363, dS8 = 4978.0317, e8 = 5274.0409, f8 = 5587.6517, fS8 = 5919.9108, g8 = 6271.9270, gS8 = 6644.8752, a8 = 7040.0000, aS8 = 7458.6202, b8 = 7902.1328;
    
    typedef struct _NOTE { //구조체 선언
        float melody;
        float tempo;
    } NOTE;
    
    //배열의 크기가 크기때문에 PROGMEM 매크로 사용
    const PROGMEM NOTE NoteSong[] = { //악보를 저장하는 구조체 배열, 이차원 배열인 NoteSong[][2]로 대체 가능
        {f2,8},{f4,8},{a4,8},{f2,8},{f4,8},{a4,8},
        {f4,8},{f4,8},{a4,8},{f4,8},{f4,16},{g4,16},
        {a4,16},{f4,16},{c5,16/3},{a4,16},{g4,8},{c5,8},{g4,8},
        {f4,16},{d4,16},{a4,16/3},{f4,16},{e4,4},{e4,8}, //1-1 
    
        {d4,8},{e4,8},{f4,16},{g4,16},{c4,8},{f4,8},{g4,16},{a4,16},
        {aS4,8},{aS4,16},{a4,16},{g4,16},{f4,16},{g4,4},{f4,16},{g4,16},
        {a4,16},{f4,16},{c5,16/3},{a4,16},{g4,8},{c5,8},{g4,8},
        {f4,16},{d4,16},{d4,8},{e4,16},{f4,16},{c4,4},{c4,16},{c4,16}, //1-2
    
        {d4,8},{e4,8},{f4,16},{g4,16},{c4,8},{f4,8},{g4,16},{a4,16},
        {aS4,8},{aS4,16},{a4,16},{g4,16},{f4,16},
        {f4,4},{0,8},{a4,16},{aS4,16},
        {c5,8},{c5,8},{c5,8},{c5,8},{c5,16},{d5,16},{c5,16},{aS4,16}, //1-3
    
        {a4,8},{a4,8},{a4,8},{a4,8},{a4,16},{aS4,16},{a4,16},{g4,16},
        {f4,8},{f4,8},{f4,16},{e4,16},{d4,8},{e4,8},{e4,16},{f4,16},
        {g4,8},{g4,16},{a4,16},{g4,16},{a4,16},{g4,4},{a4,16},{aS4,16},
        {c5,8},{c5,8},{c5,8},{c5,8},{c5,16},{d5,16},{c5,16},{aS4,16}, //1-4
    
        {a4,8},{a4,8},{a4,8},{a4,16},{aS4,16},{a4,16},{g4,16},{f4,16},{e4,16},
        {d4,8},{d4,16},{e4,16},{f4,16},{g4,16},{c4,8},{f4,8},{g4,16},{a4,16},
        {g4,16/3},{g4,16},{g4,16},{f4,16},{f4,8/3},
        {f2,8},{c3,8},{a4,8},{f2,8},{f3,8},{f5,16},{g5,16}, //2-1
    
        {a5,16},{f5,16},{c6,16/3},{a5,16},{g5,8},{c6,8},{g5,8},
        {f5,20},{g5,20},{f5,16},{d5,16},{a5,16/3},{f5,16},{e5,4},{f5,16},{e5,16},
        {d5,8},{e5,8},{f5,16},{g5,16},{c5,8},{f5,8},{g5,16},{a5,16},
        {aS5,8},{aS5,16},{a5,16},{g5,16},{f5,16},{g5,4},{f5,16},{g5,16}, //2-2
    
        {a5,16},{f5,16},{c6,16/3},{a5,16},{g5,8},{c6,8},{g5,8},
        {f5,20},{g5,20},{f5,16},{d5,16},{d5,8},{e5,16},{f5,16},{c5,4},{c5,8},
        {d5,8},{e5,8},{f5,16},{g5,16},{c5,8},{f5,8},{g5,16},{a5,16},
        {aS5,8},{aS5,16},{a5,16},{g5,16},{f5,16},{f5,8/3}, //2-3 
    
        {f2,8},{c3,8},{a4,8},{f2,8},{f3,8},{a4,16},{aS4,16},
        {c5,16},{c5,16},{c5,8},{c5,16},{c5,32},{d5,32},{c5,16},{aS4,16},
        {a4,16},{a4,16},{a4,8},{a4,16},{a4,32},{aS4,32},{a4,16},{g4,16},
        {f4,16},{f4,16},{f4,16},{e4,16},{d4,8},{e4,16},{e4,32},{f4,32}, //2-4
    
        {g4,16},{g4,16},{a4,16},{g4,16},{a4,16},{g4,4},{a4,16},{aS4,16},
        {c5,16},{c5,16},{c5,8},{c5,8},{c5,32},{d5,32},{c5,16},{aS4,16},
        {a4,16},{a4,16},{a4,8},{a4,32},{aS4,32},{a4,32},{g4,32},{f4,32},{e4,32},
        {d4,16},{e4,16},{f4,16},{g4,16},{c4,8},{f4,8},{g4,16},{a4,16}, //2-5
    
        {g4,4},{g4,16},{f4,16},
        {f4,8/3},{f2,16},{c3,16},{f3,16},{g3,16},{a3,16},{c4,16},
        {f2,16},{c3,16},{f3,16},{g3,16},{a3,16},{c4,16},{f4,16},{g4,16},{a4,16},{c5,16},{f5,16},{g5,16},
        {a5,16},{f5,16},{c6,16/3},{a5,16},{g5,8},{c6,8},{g5,8}, //2-6 
    
        {f5,16},{d5,16},{a5,16/3},{f5,16},{e5,4},{e5,8},
        {d5,8},{e5,8},{f5,16},{g5,16},{c5,8},{f5,8},{g5,16},{a5,16},
        {aS5,8},{aS5,16},{a5,16},{g5,16},{f5,16},{g5,4},{f5,16},{g5,16},
        {a5,16},{f5,16},{c6,16/3},{a5,16},{g5,8},{c6,8},{g5,8}, //3-1
    
        {f5,20},{g5,20},{f5,16},{d5,16},{d5,8},{e5,16},{f5,16},{c5,8},{c5,16},{c5,16},
        {d5,8},{e5,8},{f5,16},{g5,16},{c5,8},{f5,8},{g5,16},{a5,16},
        {aS5,8},{aS5,16},{a5,16},{g5,16},{f5,16},
        {f5,8/3},{f4,16},{g4,16},{a4,16},{c5,16},{a5,16},{aS5,16}, //3-2
    
        {c6,8},{c6,8},{c6,8},{c6,8},{c6,16},{d6,16},{c6,16},{aS6,16},
        {a5,8},{a5,8},{a5,8},{a5,8},{a5,16},{aS5,16},{a5,16},{g5,16},
        {f5,8},{f5,8},{f5,16},{e5,16},{d5,8},{e5,8},{e5,16},{f5,16},
        {g5,8},{g5,16},{a5,16},{g5,16},{a5,16},{g5,4},{a5,16},{aS5,16}, //3-3 
    
        {c6,8},{c6,8},{c6,8},{c6,8},{c6,16},{d6,16},{c6,16},{aS6,16},
        {a5,8},{a5,8},{a5,8},{a5,16},{aS5,16},{a5,16},{g5,16},{f5,16},{e5,16},
        {d5,8},{d5,16},{e5,16},{f5,16},{g5,16},{c5,8},{f5,8},{g5,16},{a5,16},
        {aS5,16/3},{a5,16},{g5,16},{f5,16}, //3-4 
    
        {f5,8/3},{f4,16},{g4,16},{a4,16},{c5,16},{f5,16},{g5,16},
        {a5,16/3},{c6,16},{f6,16},{f6,16},{c7,4},{f4,16},{g4,16},
        {a4,16},{f4,16},{c5,16/3},{a4,16},{g4,8},{c5,8},{g4,8},
        {f4,8},{a4,16/3},{f4,16},{e4,4},{f4,16},{e4,16}, //3-5 
    
        {d4,8},{e4,8},{f4,16},{g4,16},{c4,8},{f4,8},{g4,16},{a4,16},
        {aS4,8},{aS4,16},{a4,16},{g4,16},{f4,16},{g4,4},{f4,16},{g4,16},
        {a4,16},{f4,16},{c5,16/3},{a4,16},{g4,8},{c5,8},{g4,8},
        {f4,16},{d4,16},{d4,16},{d4,16},{e4,16},{f4,16},{c4,4},{c4,8}, //3-6 
    
        {d4,8},{e4,8},{f4,16},{g4,16},{c4,8},{f4,16/3},{e4,16},
        {d4,8},{e4,8},{f4,16},{g4,16},{c4,4},{f4,16},{e4,16},
        {d4,8},{e4,8},{f4,16},{g4,16},{c4,8},{f4,8},{g4,16},{a4,16},
        {aS4,8},{aS4,16},{a4,16},{g4,16},{f4,16}, //4-1 
    
        {f4,8/3},{f4,16},{g4,16},{a4,16},{c5,16},{f5,16},{a5,16},
        {c7,8/3},{f4,16},{g4,16},{a4,16},{c5,16},{a5,16},{aS5,16},
        {c6,8},{c6,8},{c6,8},{c6,8},{c6,16},{d6,16},{c6,16},{aS5,16}, //4-2
    
        {a5,8},{a5,8},{a5,8},{a5,8},{a5,16},{aS5,16},{a5,16},{g5,16},
        {f5,8},{f5,8},{f5,16},{e5,16},{d5,8},{e5,8},{e5,16},{f5,16},
        {g5,8},{g5,16},{a5,16},{g5,16},{a5,16},{g5,4},{a5,16},{aS5,16}, //4-3
    
        {c6,8},{c6,8},{c6,8},{c6,8},{c6,16},{d6,16},{c6,16},{aS5,16},
        {a5,8},{a5,8},{a5,16},{d5,16},{a5,16},{aS5,16},{a5,16},{g5,16},{f5,16},{e5,16},
        {d5,8},{d5,16},{e5,16},{f5,16},{g5,16},{c5,8},{f5,8},{g5,16},{a5,16}, //4-4
    
        {g5,4},{g5,16},{f5,16},
        {f5,8/3},{f4,16},{g4,16},{a4,16},{c5,16},{f5,16},{a5,16},
        {a5,4},{0,8},{f4,16},{g4,16},{a4,16},{c5,16},{f5,16},{d6,16},
        {f6,2}, //4-5
    };
    
    void setup() {
        pinMode(Buzzer, OUTPUT);
        pinMode(LED, OUTPUT);
        digitalWrite(LED,1);
    
        for(int i = 0; i < sizeof(NoteSong)/sizeof(NoteSong[0]); i++) {
            float pitch = pgm_read_float(&NoteSong[i].melody); //이차원 배열이면 &NoteSong[i][0]
            float beat = term/pgm_read_float(&NoteSong[i].tempo); //이차원 배열이면  &NoteSong[i][1]
            tone(Buzzer, pitch, beat);
            delay(beat*1.1); //*1.1인 이유는 음이 바로 이어서 나오면 조금 듣기 이상하기 때문에
        }
    };
    
    void loop() {};
     
    • 메트로늄 기호 계산법

    악보 첫페이지 상단에 이렇게 표시되어 있는 것이 매트로늄 기호이다. 이것이 의미는 1분에 사분음표 56개 연주된다는 의미이다. 역수를 취하면 사분음표 한개당 걸리는 시간(분)이 되며(단위: 분/사분음표), ms 단위로 바꿔주기 위해 60*1000을 곱하고, 4/4박자면 *4를 6/8박자면 *(1/2)*8=*4를 하면 된다. 6/8박자니 *(1/2)*6 아닌가 의문이 들 수도 있지만, 이리하면 8분 음표 한개 길이는 term/6이다. 사분음표는 4, 8분음표는 8이라고 악보와 일대일 대응되게 코딩하는게 수월한데 이리되면, 코딩하다 햇깔리기 쉬울 것이다.

    때에 따라서 낮은 음자리표 음을 가져오는 것보다 쉼표가 나을 때도 있으며, 1,2옥타브는 음이 깨끗하게 들리지 않기 때문에 다른 음으로 대체하거나 쉼표로 처리하는 게 더 나을 수도 있다.

    • 코딩 팁

     

     

    업로드Attiny85에 업로드하기

    그림처럼 연결해줍니다.

    https://raw.githubusercontent.com/sleemanj/optiboot/master/dists/package_gogo_diy_attiny_index.json 

     

    보드로 Attiny85를 선택

    밑에서 설정할 Processor Speed와 같이 8MHz로 설정한다.소비전력 감소같은 특별한 이유가 없다면 높은 클럭이 좋지만, 고주파회로로 갈수록 인덕턴스의 영향을 많이 받기 때문에 정밀한 회로가 필요하다. 아두이노 우노의 ATmega328도 크리스탈이 없으면 8MHz가 한계이니 8MHz 정도가 적당하다.

    이 회로에서는 BOD를 끄는 것을 권장한다. 정전압 레귤레이터라도 사용하면 모를까 컴팩트하게 만든다고 AA건전지 2개를 직렬로 연결하여 사용하니, 캐패시터가 있더라도 전원이 불안정하다. BOD를 설정하면 부저가 진동하면서 순간적으로 전압을 떨어뜨리기때문에 노래가 재생되다 중간에 리셋이 된다.

     

     

     

    포트 설정한 뒤 부트로더를 구워줍니다.(최초 1회만 하면 됨)

    그냥 업로드하면 안되고 프로그래머를 사용해 업로드를 눌러야 합니다.

    회로 조립

    부저와 ATtiny85를 바로 연결하니 소리가 작아 트랜지스터를 사용하였다. 그리고 증폭 목적이 아니더라도 트랜지스터로 전류를 조절하는 편이 전원도 안정화하고, 배터리도 오래쓸 수 있다. 베이스 단자의 저항값을 조절해서 소리크기를 조절할 수 있다.

     

     

    이 포스트는 출처가 표기된 사진을 제외하고 CC-BY-NC-SA에 따라 이용가능합니다.

     

    스케치 용량 부족 해결법

    아두이노 우노의 스케치 저장공간은 32KB이고, 여기서 사용한 ATtiny85는 8KB입니다. 악보 7장에 음표가 많은 호랑풍류가가 우노로 컴파일 하였을 때 12KB정도 사용하며, 여기서 사용한 센과치히로의 행방불명 OST는 우노로 컴파일 하였을 때 8KB, Attiny85로 컴파일하였을 때는 6KB를 사용하였습니다. 우노라면 교향곡정도 길이의 곡을 코딩하지 않는 이상 스케치 용량이 딱히 부족하지 않을테지만,  ATtiny85라면 용량에도 신경을 써야합니다. 하나씩 진행하면서 문제가 해결되는지 확인하면 됩니다.

    1. 필요한 음 진동수만 남기고 모두 삭제한다.
    2. tone 함수를 아래와 같이 대체한다.
    //아두이노 함수 이용, 이 방법을 사용하면 1KB정도 줄일 수 있다.
    void myTone(uint8_t pin, float freq, float dura) {
    	unsigned long time0 = 0;
    	while(millis()-time0 < dura) {
    		digitalWrite(pin,1);
    		delay(500.0/freq);
    		digitwlWrite(pin,0);
    		delay(500.0/freq);
        }
    };
    
    
    //좀더 용량을 줄이고 싶다면
    void myTone(float freq, float dura) {
    	unsigned long time0 = 0;
    	while(millis()-time0 < dura) {PORTD ^= (1 << PD3);} //아두이노 우노 3번 핀
    };
    
    /*
    실험해보니
    원래 tone 함수처럼 비교일치 인터럽트의 비교일치값을 조절하는 것보다
    이 방식이 용량을 더 줄일 수 있었다.
    */

         3.음 진동수의 소수점을 반올림한 뒤 숫자 크기에 맞추어 uint8_t(0~255), uint16_t(0~65,536) 자료형을 바꾼다.

           float(4byte) ->  uint8_t(1byte), uint16_t(2byte)

        4.아두이노에서 주어지는 것을 사용하지 않고, 마이크로칩 스튜디오에서 프로그래밍 하여 업로드 한다.(최적화 옵션에

          서 용량우선으로), (참고로 아두이노에서 제공하는 추상화된 함수는 추상화 때문에 용량이 크다.)

    댓글

Designed by Tistory.