2015-03-11 16 views
6

Lise elektroniği sınıfım bazı arduino uno kitleri satın almaya karar verdi, ki bu son derece havalı. Bu konuda yeterli, şu anda sınıfta piezo buzzer ile deneme yapıyoruz (this gibi görünüyor). Piezo buzzer'ı kullanarak şarkı oluşturmayı öğrendik. Öğretmenimiz "yaratıcı" olmamızı söyledi. Katy Perry'nin "Havai fişek" i kullanmaktan daha yaratıcı olmanın daha iyi bir yolu.Arduino - Bir piezo buzzerde aynı anda iki veya daha fazla ton nasıl oluşturulur?

Bazı yaratıcı özgürlükler kullanarak, bu şarkının güzel bir piyano parçasını buldum (bağlantı here). Şimdi bir piyanistim (AP Müzik teorisini aldım), ve sahip olduğum problem ise sadece bir notu sadece piezo zili çalabildiğim. Şarkıyı bir piezo zilinde çalmak mümkün mü, bu bir piyanoda çalındığı gibi (ya da en azından yakın). Bas ve tiz nota anahtarı notaları eşzamanlı olarak sesli olarak çalındığı gibi.

Faz kaymaları ve notaların sıklığını eklemeyi içerdiğini biliyorum, ancak bunu bir piezo buzzer için nasıl kod haline getiriyorsunuz? Büyük ölçüde takdir edilecek bir örnek kod gönderebilirseniz. Değilse, mümkün olan en açık şekilde açıklayabilir misiniz? Programlamada usta değilim, ama ben de yeni başlayan biri değilim.

cevap

1

Bu üçüncü taraf Ton kütüphane birden iğneler üzerinde eşzamanlı kare dalgalar oynayabilir: tek bir hoparlör üzerinden tüm tonlarını almak için birden pimleri ve tek hoparlör arasında dirençler bağlayabilirsiniz Link

.

+1

Anladığım kadarıyla, eşzamanlı çalmak için başvurduğunuz üçüncü taraf Ton kütüphanesini nasıl kullanacağınızı açıklayan bir örnek kod yayınlayabilir misiniz? tek hoparlörde ses mi geliyor? –

14

Arduinolar sadece dijital çıkış sunar: çıkış açık (+ 5V) veya kapalı (0V). Bu noktaya girdiğinizi umduğum tone() işlevi, belirli bir frekansta bir kare dalga üretir.

100Hz ton istediğinizi varsayalım. 100Hz, çıkışın saniyenin 1/100'ü veya her 10 saniyede bir tekrarlanacağı anlamına gelir. Yani tone(PIN,100), her 5 ms'de çağrılacak bir zamanlayıcı kesmesi ayarlayacaktır. Interrupt ilk kez çağrıldığında, çıktıyı düşük olarak ayarlar ve programınız ne yapıyorsa geri döner. Bir dahaki sefere çıktıyı yüksek olarak ayarlar. Böylece her 5 ms'de bir çıkış değişir ve% 50 görev döngüsünde bir kare dalga elde edersiniz, bu da çıkışın tam olarak yarısı için açık olduğu anlamına gelir.

Bu her şey çok iyi, ancak çoğu ses dalga biçimi kare dalga değildir. Aynı anda iki kare dalga tonunu oynatmak ya da tek bir kare dalga tonunun sesini kontrol etmek istiyorsanız, sadece "açık" ve "kapalı" dan daha fazla değer elde edebilmeniz gerekir.

İyi haber şu ki, darbe genişlik modülasyonu (genellikle kısaltılmış PWM) olarak adlandırılan kullanabileceğiniz bir numara var. Buradaki fikir, çıktınızı yalnızca iki değerden birine ayarlayabilmenizdir, ancak bunu gerçekten hızlı yapabilirsiniz. İnsanlar yaklaşık 20kHz'e kadar ses frekanslarını duyabilir. Eğer çıktınızdan daha hızlı çıkarsanız, 200kHz'de (16MHz'de saat hızına sahip Arduino'nun yetenekleri dahilinde), ayrı çıkış geçişlerini duymuyorsunuz, fakat ortalama değeri daha uzun bir süre boyunca.

tone() ile 200 kHz'lik bir sesin üretildiğini düşünün. Duymak için çok yüksek, ama ortalama değer açık ve kapalı arasında yarı yarıya (% 50 görev döngüsü, hatırlıyor musun?). Yani şimdi üç olası çıkış değerlerine sahibiz: on, off ve halfway. Bu, aynı anda iki kare dalgaları oynatmamıza izin vermek için yeterlidir:

Yüksek kaliteli ses, bundan daha birçok değer gerektirir. CD'ler 16 bit ses depolar, yani 65536 olası değer vardır.Ve bir Arduino'dan CD kalitesinde ses elde etmeyecek olsak da,% 50'den başka bir görev döngüsü seçerek daha fazla çıktı değeri elde edebiliriz. Aslında, Arduino'nun bizim için donanımı var.

analogWrite() ile tanışın. Bu Arduino'nun yerleşik PWM donanımını kullanarak değişen çıkış seviyelerini gösterir. Kötü haber, PWM frekansının tipik olarak 500Hz'dir, bu da bir LED'i karartmak için iyidir, ancak ses için çok düşüktür. Bu yüzden, donanım kayıtlarını kendimiz programlamalıyız.

Secrets of Arduino PWM bazı daha fazla bilgiye sahiptir ve bir Arduino üzerinde bir PWM DAC'nin nasıl uygulanacağı konusunda bir detailed reference bulunmaktadır.

7 bit çözünürlük seçtim, yani çıkış 128 olası görev döngüsüyle 16MHz/128 = 125kHz kare dalgadır.

Tabii ki, PWM çıkışı çalışırken bir kere eğlence başlıyor. Birden fazla ses ile dalga formlarınızı frekansa ayarlamak için kesmelere güvenemezsiniz, bunları kendiniz uzatmanız gerekir. Temel dijital sinyal işleme (DSP) bilgisi oldukça kullanışlı olacaktır. Bir kesme işleyicisinde ses verileri oluşturmak için sıkı bir kod gerekecek ve doğru zamanda doğru notları tetiklemek için bir playroutine ihtiyacınız olacak. Gökyüzü sınır!

neyse, burada bazı kod:

#define PIN 9 

/* these magic constants were generated by the following perl script: 
    #!/usr/bin/perl -lw 
    my $freq = 16000000/256; 
    my $A4 = 440; 
    print int(128*$freq/$A4*exp(-log(2)*$_/12)) for (-9..2); 
*/ 
const uint16_t frtab[] = { 
    30578, 28861, 27241, 25712, 
    24269, 22907, 21621, 20408, 
    19262, 18181, 17161, 16198 
}; 

#define VOICES 4 

struct voice { 
    uint16_t freq; 
    int16_t frac; 
    uint8_t octave; 
    uint8_t off; 
    int8_t vol; 
    const uint8_t *waveform; 
} voice[VOICES]; 

#define PITCH 50 /* global pitch adjustment */ 

/* some waveforms. 16 samples each */ 
const uint8_t square_50[] = { 
    0, 0, 0, 0, 0, 0, 0, 0,15,15,15,15,15,15,15,15 
}; 
const uint8_t square_25[] = { 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15,15,15,15 
}; 
const uint8_t square_12[] = { 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15,15 
}; 
const uint8_t square_6[] = { 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15 
}; 
const uint8_t sawtooth[] = { 
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 
}; 
const uint8_t triangle[] = { 
    0, 2, 4, 6, 8,10,12,14,15,13,11, 9, 7, 5, 3, 1 
}; 
const uint8_t nicebass[] = { 
    0, 8,14,18,22,23,24,25,26,25,24,23,22,18,14, 8 
}; 

void setup() { 
    /* TIMER0 is used by the Arduino environment for millis() etc. 
    So we use TIMER1. 
*/ 
    pinMode(PIN, OUTPUT); 
    /* fast PWM, no prescaler */ 
    TCCR1A = 0x80; 
    TCCR1B = 0x11; 
    /* 7-bit precision => 125kHz PWM frequency */ 
    ICR1H = 0; 
    ICR1L = 0x7f; 
    /* enable interrupts on TIMER1 overflow */ 
    TIMSK1 = 1; 
    OCR1AH = 0; /* hi-byte is unused */ 
    for (uint8_t i=0; i<VOICES; i++) 
    clear_voice(i); 
} 

void set_voice(uint8_t v, uint8_t note, uint8_t volume, const uint8_t *waveform) { 
    note += PITCH; 
    voice[v].octave = note/12; 
    voice[v].freq = frtab[note%12]; 
    voice[v].frac = 0; 
    voice[v].off = 0; 
    voice[v].waveform = waveform; 
    voice[v].vol = volume; 
} 

void clear_voice (uint8_t v) { 
    voice[v].freq = 0; 
} 

uint8_t s = 0; 

ISR(TIMER1_OVF_vect) { 
    /* Calculate new data every 4 pulses, i.e. at 125/4 = 31.25kHz. 
    Being interrupted unnecessarily is kinda wasteful, but using another timer is messy. 
    */ 
    if (s++ & 3) 
    return; 

    int8_t i; 
    int8_t out = 0; 
    for (i=0; i<VOICES; i++) { 
    if (voice[i].freq) { 
     voice[i].frac -= 128<<voice[i].octave; 
     if (voice[i].frac < 0) { /* overflow */ 
     voice[i].frac += voice[i].freq; 
     voice[i].off++; 
     } 
     /* warning: vol isn't a real volume control, only for square waves */ 
     out += (voice[i].waveform[voice[i].off & 15]) & voice[i].vol; 
    } 
    } 

    /* out is in the range 0..127. With 4-bit samples this gives us headroom for 8 voices. 
    Or we could use more than 4-bit samples (see nicebass). 
    */ 
    OCR1AL = out; 
} 

/* tune data */ 
const uint8_t bass[8][4] = { 
    { 12, 19, 23, 24 }, 
    { 5, 12, 19, 21 }, 
    { 12, 19, 23, 24 }, 
    { 5, 12, 19, 21 }, 
    { 14, 16, 17, 21 }, 
    { 7, 19, 14, 19 }, 
    { 14, 16, 17, 21 }, 
    { 7, 19, 14, 19 } 
}; 

const uint8_t melody[2][8][16] = { 
    {/* first voice */ 
    {31, 0, 0, 0, 0, 1, 2, 3,31,29,28,29, 0,28,26,24 }, 
    { 0, 0, 0, 0, 0, 1, 2, 3,53,54,53,54, 0, 1, 2, 3 }, 
    {31, 0, 0, 0, 0, 1, 2, 3,31,29,28,29, 5,28, 5,26 }, 
    { 5,28,24, 0, 0, 1, 2, 3,53,54,56,54, 0, 1, 2, 3 }, 

    {29, 0, 0, 0, 0, 1, 2, 3,31,29,28,29, 5, 0,28, 5 }, 
    {28, 5, 0,26, 0, 1, 2, 3,54,56,58,56, 0, 1, 2, 3 }, 
    {29, 0, 0, 0, 0, 1, 2, 3,31,29,28,29, 5, 0,28, 5 }, 
    {28, 5, 0,26, 0, 1, 2, 3, 0,19,21,23,24,26,28,29 }, 
    }, 

    {/* second voice */ 
    {24, 0, 0, 0, 0, 1, 2, 3,24,24,24,24, 0,24,24,21 }, 
    { 0, 0, 0, 0, 0, 1, 2, 3,49,51,49,51, 0, 1, 2, 3 }, 
    {24, 0, 0, 0, 0, 1, 2, 3,24,24,24,24, 5,24, 5,24 }, 
    { 5,23,21, 0, 0, 1, 2, 3,49,51,53,51, 0, 1, 2, 3 }, 

    {26, 0, 0, 0, 0, 1, 2, 3,24,26,24,24, 5, 0,24, 5 }, 
    {24, 5, 0,24, 0, 0, 0, 0,51,51,54,54, 0, 1, 2, 3 }, 
    {26, 0, 0, 0, 0, 1, 2, 3,24,26,24,24, 5, 0,24, 5 }, 
    {24, 5, 0,23, 0, 1, 2, 3, 0, 5, 0,19,21,23,24,26 }, 
    } 
}; 

void loop() { 
    uint8_t pos, i, j; 

    for (pos=0; pos<8; pos++) { 
    for (i=0; i<16; i++) { 
     /* melody: voices 0 and 1 */ 
     for (j=0; j<=1; j++) { 
     uint8_t m = melody[j][pos][i]; 
     if (m>10) { 
      /* new note */ 
      if (m > 40) /* hack: new note, keep volume */ 
      set_voice(j, m-30, voice[j].vol, square_50); 
      else /* new note, full volume */ 
      set_voice(j, m, 15, square_50); 
     } else { 
      voice[j].vol--; /* fade existing note */ 
      switch(m) { /* apply effect */ 
      case 1: voice[j].waveform = square_25; break; 
      case 2: voice[j].waveform = square_12; break; 
      case 3: voice[j].waveform = square_6; break; 
      case 4: clear_voice(j); break; /* unused */ 
      case 5: voice[j].vol -= 8; break; 
      } 
      if (voice[j].vol < 0) 
      voice[j].vol = 0; /* just in case */ 
     } 
     } 

     /* bass: voices 2 and 3 */ 
     set_voice(2, bass[pos][i%4], 31, nicebass); 
     set_voice(3, bass[pos][0]-12, 15-i, sawtooth); 

     delay(120); /* time per event */ 
    } 
    } 
} 

Bu, dört sesli melodi çalar. Test etmek için sadece bir Arduino Leonardo'm var (iyi, Pro Micro), bu yüzden PIN'u hangi pinin TIMER1A'ya bağlandığına göre değiştirmeniz gerekebilir (eğer doğru okuyorsam bir Uno'da pin 9 ve pim 11'de bir Mega). Ne yazık ki kullanmak için hangi pin seçtiniz?

Ayrıca sadece kulaklık ile test ettik, bu yüzden bir piezo buzzer sesini edeceğiz nasıl hiçbir fikrim yok ...

Umarım bu size olabilirlik fikir verir ve potansiyel bir başlangıç ​​noktası kendi ayarınız için. Belirsiz olan her şeyi açıklamaktan mutluyum ve bana bunu yazmak için bir mazeret verdiğiniz için teşekkür ederim :)

+0

Sesleri 95'ten 102'ye nasıl karıştırdığınızı anlamıyorum. Frak nedir ve nasıl kullanılır? [Bu] (https://en.wikipedia.org/wiki/Twelfth_root_of_two) ve "frtab" daki değerleri anlıyorum ama bir periyottan ve oktavdan dalgadaki bir değere nasıl geçiyorsunuz? Ayrıca 102 satırını 'out + = (voice [i] .off & 15) <8 olarak değiştirdim. 0: 31; çünkü hacim ve diğer dalga formlarına ihtiyacım yok, işe yarayacak mı? –

İlgili konular