Proiectul realizează o mini-harpă laser.
Fiecare coardă va fi reprezentată de raza unui laser ce luminează o photodiodă.
La întreruperea unei raze vom genera nota corespunzătoare.
Proiectul va fi compus din 3 module hardware :
Modulul software va trata întreruperea razelor și va implementa un algoritm de generare note.
Pentru a mări acuratețea semnalului, pentru fiecare senzor vom folosi câte
două photodiode legate în serie.Adăugăm un amplificator pentru a mări tensiunea
dată de cele două diode.
De asemenea, vom cupla un comparator ce va analiza tensiunea
primită și va stabili dacă raza a fost întreruptă sau nu. Acesta primește ca input
tensiunea amplificată de la cele două photodiode și o tensiune reglabilă dată de un
trimmer.În funcție de acestea, va trimite microcontrollerului 0V dacă raza a fost atinsă
și 5V în caz contrar.
Am folosit o rezistență reglabilă pentru a stabili de fiecare dată un prag dependent
de lumina ambientală din încăpere.Astfel, la întreruperea luminii, nu vom fi influențați
de lumina naturală ce afectează photodiodele.
În poza de mai jos avem circuitul pentru 2 corzi. La fiecare 2 corzi vom folosi
quad amplificatorul LM324AN din care obținem cele 2 amplificatoare de tensiune și 2 comparatoare.
Sunetul primit de la microcontroller e reprezentat de o valoare pe 8 biți, ce indică
amplitudinea undei sonore la un moment dat. Acest output fiind incompatibil cu un difuzor
analogic, am inclus în circuit un DAC ce convertește semnalul digital primit într-unul analogic[3].
Pentru un sunet mai bun, am trecut outputul printr-un amplificator și un filtru low pass.
De asemenea, cum placuța de bază nu putea oferi alimentarea necesară DAC-ului(-12/12V), am folosit o sursă ATX.
Programul rulează într-o buclă și așteaptă o întrerupere de ceas la un anumit interval.
Când timerul expiră, va interoga fiecare pin legat la o photodioda(pooling) pentru a depista corzile atinse.
Prin intermediul unui algoritm de generare a notelor, va calcula semnalul corespunzător
și îl va trimite la DAC.
Algoritmul de generare note poate fi împărțit în 3 mari părți :
DDS
Pentru generarea notelor, am ales ca metodă DDS(Sinteza digitală directă), ce constă în
crearea unor forme de undă de frecvențe arbitrare pornind de la o singură frecvență fixă[1].
Să luăm ca exemplu forma de undă sinusoidală și nota LA(440Hz). Dacă vrem să obținem o perioadă a undei
ce reprezintă nota selectată, vom aplica funcția de generare a undei pe un interval de valori, pornind
de la frecvența notei. Astfel, pentru fiecare notă, folosim un registru acumulator în care stocăm valoarea
curentă ce va fi folosită pentru generarea undei. La fiecare întrerupere de ceas(128), incrementăm
acumulatoarele pentru a obține o nouă valoare din forma de undă. Resetarea acumulatorului se va realiza
automat la overflow.
Valoarea cu care incrementăm acumulatorul e calculată cu formula:
⇒ | inc = frecventa * 549.756; |
iar pentru fiecare notă vom avea[2]:
Notă | Frecvență(Hz) | Increment |
---|---|---|
A | 440 | 241892640L |
C# | 554.4 | 304784726L |
E | 659.3 | 362454131L |
A | 880 | 483785280L |
C# | 1108.7 | 609514477L |
Scăderea în timp a amplitudinii:
Algoritmul foloseste un mecanism de “aging”(bazat tot pe timer) ce va oferi corzii un sunet prelungit a cărui
amplitudine scade în timp, specific instrumentelor de acest tip. Astfel, pentru fiecare coardă, vom păstra
într-un vector(clepsidra), vechimea(“age”) acesteia(cât a trecut de când a fost accesată ultima dată).
Vechimea va lua valori de la 0 la 255. Pentru a determina amplitudinea semnalului, vom folosi următoarea
funcție bazată pe timp(vechime) :
În final, nota va fi generată după formula:
valoare = funcție_undă(accumulator(coardă)) * ampl(vechime(coarda))
unde ampl asigură atenuarea în timp a sunetului.
Generarea simultană a mai multor note:
În semnalul final vom stoca până la 4 note, permițând astfel atingerea simultană a mai multor corzi.
Astfel, în cei 8 biți trimiși DAC-ul vom avea 4 note, fiecare cu 64 valori posibile. Algoritmul alocă 4 sloturi
în care ținem corzile cântate în același timp. Dacă avem mai mult de 4 corzi și o a 5-a este accesată, noua
notă o va înlocui pe cea mai veche din sloturi. Vechimea va fi determinată de asemenea cu ajutorul vectorului
clepsidră de mai sus.
Pentru proiect am ales sa folosesc forma de undă sinusoidală și 5 corzi cu note din gama A major(A, C#, E, A, C#).
Pe viitor, voi atașa un buton ce va putea comuta între diferite forme de undă(gaussiană, dinți de fierăstrău,etc).
Programul de test folosit pentru testarea DAC-ului și a algoritmului de generare note se poate downloada de la acest link
Pseudocod :
Initializări :
void initialize() { acumulator[i] - valoarea acumulator a slotului i nota[i] = val increment nota i inc_slot[i] = val increment a notei din slotul i slot[i] = slotul corzii i , > 4 => coarda nu este intr-un slot /* Init tabelă formă de undă sinusoidală */ for i = 0 to 256 sine[i] = (char)(32.0 + (31.0 * sin(6.283*((float)i)/256.0))) /* Init tabelă amplitudine în funcție de vechime */ for v = 0 to 9 ampl[v] = (255-10*v); for v = 10 to 173 ampl[v] = (174-v); for v = 174 to 256 ampl[v] = 0; /* Init corzi */ for i = 0 to 4 acumulator[i] = 0; vechime[i] = 256; //nu a fst atinsă niciodată slot[i] = i; inc_slot[i] = nota[i]; /* Init timere */ }
Întrerupere ceas (la 128us) :
ISR (TIMER0_OVF_vect) { /* DDS - pt fiecare nota incrementez acumulator */ for i = 0 to 4 acumulator[i] += inc_slot[i] ; highByte[i] = (char)(acumulator[i] >> 24) ; /* Semnal pentru DAC */ SIGNALPORT = ((sine[highByte[0]]*ampl[vechime[0]])>>8) + ((sine[highByte[1]]*ampl[vechime[1]])>>8) + ((sine[highByte[2]]*ampl[vechime[2]])>>8) + ((sine[highByte[3]]*ampl[vechime[3]])>>8); /* Incrementez vechime coardă, dacă e cazul */ if < au trecut 10 ms > //verific un counter for i = 0 : 4 vechime[i]++; }
Main :
main() { /* Initializari */ initialize() while 1 if <au trecut 50 ms> /* Poll pe outputul de la senzori */ for i = 0 to 4 if < coarda i a fost accesata > if < este intr-unul din cele 4 sloturi slot[i]<4 > //resetez vechime vechime[i] = 0; else < nu este intr-unul din cele 4 sloturi > //preemtează coarda cea mai veche coardă din sloturi slot[i] = slotul in care era coarda veche; slot[coarda_veche] = 4; inc_slot[slotul in care era coarda veche] = nota[i]; //resetez vechime vechime[i] = 0; }
Resurse Software:
http://www.phy.mtu.edu/~suits/notefreqs.html
http://www.electricdruid.net/index.php?page=info.dds
http://courses.cit.cornell.edu/ee476/FinalProjects/s2009/kac99/ECE_4760_Final_Project%20saf42%20kac99%20page%20v4.htm
Resurse Hardware:
http://www.ece.ualberta.ca/~elliott/ee552/studentAppNotes/1999f/DACDesign/DAC.html
http://en.wikipedia.org/wiki/ATX#Power_supply
http://www.datasheetcatalog.org/datasheet/philips/DAC08E.pdf
http://www.datasheetcatalog.org/datasheet/philips/LM324N.pdf