This shows you the differences between two versions of the page.
pm:prj2025:rnedelcu:cosmin.croitoriu [2025/05/27 09:00] cosmin.croitoriu |
pm:prj2025:rnedelcu:cosmin.croitoriu [2025/05/28 15:56] (current) cosmin.croitoriu |
||
---|---|---|---|
Line 47: | Line 47: | ||
===== Software Design ===== | ===== Software Design ===== | ||
+ | **Overview** | ||
- | <note tip> | + | Partea software a proiectului se bazează pe următoarele etape: |
- | Descrierea codului aplicaţiei (firmware): | + | * Citește semnalul audio de la intrarea analogică A0, folosind în spate ADC-ul integrat. |
- | * mediu de dezvoltare (if any) (e.g. AVR Studio, CodeVisionAVR) | + | * Aplică unul din cele 4 efecte. |
- | * librării şi surse 3rd-party (e.g. Procyon AVRlib) | + | * Scalează semnalul rezultat în funcție de valoarea luminozității ambientale, detectată cu un LDR pe A1. |
- | * algoritmi şi structuri pe care plănuiţi să le implementaţi | + | * Trimite semnalul procesat către DAC-ul extern, scalând mai întâi output-ul pentru a fi pe 12 biți. Comunicarea cu DAC-ul se face protocolul I2C. |
- | * (etapa 3) surse şi funcţii implementate | + | * Efectele, cât și intensitatea lor sunt schimbate cu ajutorul butoanelor prin întreruperi |
- | </note> | + | Pentru comunicarea cu DAC-ul am folosit biblioteca Adafruit_MCP4725. |
- | ===== Rezultate Obţinute ===== | ||
- | <note tip> | + | **Implementarea Efectelor Audio** |
- | Care au fost rezultatele obţinute în urma realizării proiectului vostru. | + | * **Clean**: nu se aplică nimic asupra semnalului și este transmis nemodificat. |
- | </note> | + | * **Distorsion**: Inputul este amplificat și apoi limitat la o anumită valoare maximă/minimă |
- | ===== Concluzii ===== | + | <code> |
+ | int distortionEffect(int input) { | ||
+ | // Centrare în jurul lui 0 | ||
+ | int centered = input - 512; | ||
- | ===== Download ===== | + | // Partea de amplificare a semnalului (între 1.5 și 5) |
+ | float gain = map(intensity, minIntensity, maxIntensity, 150, 500) / 100.0; | ||
+ | int boosted = centered * gain; | ||
- | <note warning> | + | // Partea de tăiere a semnalului după un anumit threshold |
- | O arhivă (sau mai multe dacă este cazul) cu fişierele obţinute în urma realizării proiectului: surse, scheme, etc. Un fişier README, un ChangeLog, un script de compilare şi copiere automată pe uC crează întotdeauna o impresie bună ;-). | + | int threshold = map(intensity, minIntensity, maxIntensity, 512, 128); |
+ | if (boosted > threshold) boosted = threshold; | ||
+ | if (boosted < -threshold) boosted = -threshold; | ||
- | Fişierele se încarcă pe wiki folosind facilitatea **Add Images or other files**. Namespace-ul în care se încarcă fişierele este de tipul **:pm:prj20??:c?** sau **:pm:prj20??:c?:nume_student** (dacă este cazul). **Exemplu:** Dumitru Alin, 331CC -> **:pm:prj2009:cc:dumitru_alin**. | + | // Recentrare in 512 |
- | </note> | + | return constrain(boosted + 512, 0, 1023); |
+ | } | ||
+ | </code> | ||
+ | |||
+ | * **Tremolo**: Modifică volumul în timp folosind o undă sinusoidală. Pentru a face codul mai eficient, nu am folosit funția sin din math.h, ci am salvat valorile sinusoidei într-un fișier separat, într-un vector. Fiecare valoare din acest vector reprezintă un coeficient de volum între 0 și 1, care este aplicat semnalului audio pentru a-l face mai tare sau mai slab în mod ciclic. Dacă parcurgem vectorul pas cu pas, adică câte un eșantion la fiecare buclă, vom avea o frecvență joasă de modulare (efectul tremolo va suna lent). | ||
+ | |||
+ | Dar dacă dorim să creștem frecvența tremolo-ului (adică să facem vibrația volumului mai rapidă), avem două opțiuni: | ||
+ | |||
+ | * Scădem timpul între eșantioane (frecvența cu care se face sample++) (rate) | ||
+ | |||
+ | * Parcurgem mai rapid vectorul – adică sări peste eșantioane: sample += tremoloStepSize | ||
+ | <code> | ||
+ | int tremoloEffect(int input) { | ||
+ | // Nr de frame-uri ca valoarea sinusoidei sa ramana constanta. | ||
+ | int rate = map(intensity, minIntensity, maxIntensity, 16, 1); | ||
+ | |||
+ | // Nr de step-uri sa fie sarite intre sample-uri | ||
+ | int tremoloStepSize = map(intensity, minIntensity, maxIntensity, 2, 8); | ||
+ | |||
+ | step++; | ||
+ | if (step >= rate) { | ||
+ | step = 0; | ||
+ | sample += tremoloStepSize; | ||
+ | if (sample >= 1024) sample -= 1024; | ||
+ | } | ||
+ | |||
+ | // Calculul factorului de scalare pe baza valoriii sinusoidei din fisier | ||
+ | float tremolo = map(waveform[sample], 0, 255, 0, 1000) / 1000.0; | ||
+ | return input * tremolo; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | * **BitCrusher**: Efectul bitcrusher constă în scăderea rezoluției semnalului audio, adică în reducerea numărului de biți folosiți pentru a reprezenta fiecare eșantion. În loc de un semnal fluid de 10 biți (0–1023, pe Arduino), semnalul este „zdrobit” în trepte mari, ceea ce duce la un sunet digital, agresiv. În proiect am folosit folsit între 4 și 64 de nivele. | ||
+ | <code> | ||
+ | int bitcrusherEffect(int input) { | ||
+ | // Nr de nivele | ||
+ | int bitDepth = map(intensity, minIntensity, maxIntensity, 1, 4); | ||
+ | int levels = 1 << bitDepth; | ||
+ | |||
+ | // Diferenta dintre 2 nivele | ||
+ | int stepSize = 1024 / levels; | ||
+ | |||
+ | // Maparea inputului la un nivel din semnal | ||
+ | int crushed = (input / stepSize) * stepSize; | ||
+ | return constrain(crushed, 0, 1023); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | **Controlul intensității și schimbarea de efecte**: | ||
+ | Pentru un răspuns rapid și a elimina overhead-ul, am utilizat întreruperi: | ||
+ | Trei butoane sunt folosite: | ||
+ | * D4: schimbă efectul (prin întrerupere **PCINT**): Folosim PCINT, deoarce D4 nu are întrerupere INT. Astfel, avem nevoie de o condiție suplimentară pentru a nu se activa întreruperea pe ambele fronturi. | ||
+ | <code> | ||
+ | // Enable la PCINTs | ||
+ | PCICR |= (1 << PCIE2); | ||
+ | |||
+ | // Enable pt D4 | ||
+ | PCMSK2 |= (1 << PCINT20); | ||
+ | </code> | ||
+ | * D2: scade intensitatea (prin întrerupere **INT0**): Întreruperea este activată pe frontul negativ HIGH to LOW. | ||
+ | * D3 crește intensitatea (prin întrerupere **INT1**) Întreruperea este activată pe frontul negativ HIGH to LOW. | ||
+ | În toate cazurile am folosit o măsură de a evita debounce-ul folosind niște variabile care rețin timestamp-ul ultimei schimbări. | ||
+ | |||
+ | **Conversia Analog-Digital**: Pentru prelucrarea semnalului de la chitară, am folosit convertorul analog-digital intern al Arduino-ului, configurat manual pentru a controla mai precis performanța și viteza de eșantionare. | ||
+ | Inițializarea se face în funcția setupADC() prin care se se setează referința la AVcc, se activează ADC-ul și se se setează prescaler-ul la 64. | ||
+ | <code> | ||
+ | void setupADC() { | ||
+ | // AVcc ca referinta si calanalul 0 by default | ||
+ | ADMUX = (1 << REFS0); | ||
+ | |||
+ | // Enable la ADC su Prescaler 64 | ||
+ | ADCSRA = (1 << ADEN) | ||
+ | | (1 << ADPS2) | (1 << ADPS1); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Pentru citire în bucla de loop, se schimbă canalul între A0 și A1 (folosim A1 pentru fotorezistor), iar apoi se porneste conversia și se asteaptă terminarea. | ||
+ | <code> | ||
+ | uint16_t readADC(uint8_t pin) { | ||
+ | // Setam canalul, 0 sau 1. | ||
+ | ADMUX = (ADMUX & 0xF0) | (pin & 0x0F); | ||
+ | |||
+ | // Start conversie | ||
+ | ADCSRA |= (1 << ADSC); | ||
+ | |||
+ | // Se asteapta pana conversia este terminata | ||
+ | while (ADCSRA & (1 << ADSC)); | ||
+ | |||
+ | return ADC; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | **Conversia Digital-Analog**: Semnalele procesate sunt trimise la DAC-ul **MCP4725**, care are implicit adresa 0x60, și care funcționează pe **protocolul I2C**. Valoarea de 10 biți este transformată într-una de 12 biți prin shiftare la stânga. | ||
+ | |||
+ | <code> | ||
+ | dac.setVoltage(scaled << 2, false); // false = fără scriere în EEPROM | ||
+ | </code> | ||
+ | |||
+ | **Controlul Volumului prin Fotorezistor**: La pinul A1 este conectat potențialul divizorului de tensiune din care face parte fotorezistorului, astfel încât, când fotorezistorul nu este acoperit, potențialul este mic, iar atunci când este acoperit, potențialul este mare. Acest potențial ajustează volumul semnalului. | ||
+ | <code> | ||
+ | float getVolumeFromLDR() { | ||
+ | int ldr = analogRead(A1); | ||
+ | int volumeInt = map(ldr, 300, 700, 1000, 0); | ||
+ | volumeInt = constrain(volumeInt, 0, 1000); | ||
+ | return volumeInt / 1000.0; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ===== Concluzii și Rezultat ===== | ||
+ | Implementarea finală oferă un procesor de efecte simplu și modular, care imită decent cele 3 efecte. Rezultatul este unul satisfăcător, sunetul fiind destul de bun pentru procesarea cu un Arduino Uno. Drept îmbunătățire, aș încerca să fac codul low-level, fără bibliotecile Arduino, astfel putând să obțin atât o procesare mai bună a sunetului, dar și posibilitatea de a adăuga noi efecte. | ||
+ | Proiectul a fost unul foarte interesant, care m-a făcut să înțeleg mai bine noțiunile de la laboratorul de PM, și să îmi aprofundez cunoștiințele de electronică. Cel mai mult m-am distrat la partea hardware, la lipit. Nu pot să exprim în cuvinte fericirea pe care am simțit-o atunci când am văzut că trece semnalul de chitară nedistorsionat :-D | ||
===== Jurnal ===== | ===== Jurnal ===== | ||
Line 78: | Line 196: | ||
* 12.4.2025: Am facut partea de output doar cu un singur filtru trece jos, pentru a testa sunetul. Deoarece sunetul nu era grozav, am decvis să folosesc filtrul Sallen Key. | * 12.4.2025: Am facut partea de output doar cu un singur filtru trece jos, pentru a testa sunetul. Deoarece sunetul nu era grozav, am decvis să folosesc filtrul Sallen Key. | ||
* 14.4.2025: Am făcut output stage-ul, folosind filtrul Sallen Key. Drept amplificator am folosit TL972, pe care l-am pus pe PCB cu ajutorul unui adaptor SMD, după **multe ore de chin** pentru a-l lipi. Neașteptat, chiar a funcționat! | * 14.4.2025: Am făcut output stage-ul, folosind filtrul Sallen Key. Drept amplificator am folosit TL972, pe care l-am pus pe PCB cu ajutorul unui adaptor SMD, după **multe ore de chin** pentru a-l lipi. Neașteptat, chiar a funcționat! | ||
+ | {{:pm:prj2025:rnedelcu:adaptor.png?200|}} | ||
* 16.4.2025: Am implementat primele versiuni ale distorsion-ului si al tremolo-ului. Observ ca sunetul este destul de "muffled", probabil din cauza filtrelor de 5kHz. | * 16.4.2025: Am implementat primele versiuni ale distorsion-ului si al tremolo-ului. Observ ca sunetul este destul de "muffled", probabil din cauza filtrelor de 5kHz. | ||
- | * 18.4.2025: Adaugare a butoanelor care modifica intensitatea efectului si a fotorezistorului. | + | * 18.4.2025: Adaugare a butoanelor care modifica intensitatea efectului si a fotorezistorului. |
+ | * 23.4.2025: Am observat că ceva s-a întâmplat cu procesorul plăcii, deoarece nu mai trimite bine semnalul. cu același cod folosit... A trebuit să renunț l ecran pentru a obține un sunet decent. | ||
+ | * 25.4.2025: Finalizare proiect + pagină de OCW. | ||
===== Bibliografie/Resurse ===== | ===== Bibliografie/Resurse ===== | ||
+ | [[https://ww1.microchip.com/downloads/en/devicedoc/22039d.pdf|Datasheet DAC MCP4725]] | ||
[[https://www.electrosmash.com/pedalshield-uno|Pedal Shield Uno]] | [[https://www.electrosmash.com/pedalshield-uno|Pedal Shield Uno]] | ||
Line 90: | Line 212: | ||
[[https://ocw.cs.pub.ro/courses/pm/prj2017/ddragomir/cmihalache|Proiect inspiratie]] | [[https://ocw.cs.pub.ro/courses/pm/prj2017/ddragomir/cmihalache|Proiect inspiratie]] | ||
+ | |||
+ | {{:pm:prj2025:rnedelcu:cosmin_proiect_pm.zip|Arhiva cod}} | ||
<html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | <html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | ||