This shows you the differences between two versions of the page.
pm:prj2025:fstancu:cristina.drinciu [2025/05/25 16:43] cristina.drinciu [Software Design] |
pm:prj2025:fstancu:cristina.drinciu [2025/05/30 13:42] (current) cristina.drinciu [Hardware Design] |
||
---|---|---|---|
Line 103: | Line 103: | ||
{{:pm:prj2025:fstancu:poza2_amp.jpg?350 |}} {{:pm:prj2025:fstancu:poza4_amp.jpg?350|}} | {{:pm:prj2025:fstancu:poza2_amp.jpg?350 |}} {{:pm:prj2025:fstancu:poza4_amp.jpg?350|}} | ||
{{:pm:prj2025:fstancu:poza3_amp.jpg?350 |}} {{:pm:prj2025:fstancu:poza1_amp.jpg?350|}} | {{:pm:prj2025:fstancu:poza3_amp.jpg?350 |}} {{:pm:prj2025:fstancu:poza1_amp.jpg?350|}} | ||
+ | {{:pm:prj2025:fstancu:final_project1.jpeg?350|}} {{:pm:prj2025:fstancu:final_project.jpeg?350|}} | ||
Link catre poze si video demonstrativ: [[https://drive.google.com/drive/folders/1RUL_uXa5XXPFie1JvUg6ljKmbEH5EQQS | aici ]] | Link catre poze si video demonstrativ: [[https://drive.google.com/drive/folders/1RUL_uXa5XXPFie1JvUg6ljKmbEH5EQQS | aici ]] | ||
Line 143: | Line 144: | ||
=== I2C (TWI) === | === I2C (TWI) === | ||
__//Rol://__ Interfața I²C (TWI) generică, folosită pentru a comunica cu DAC-ul MCP4725 la ~400 kHz. | __//Rol://__ Interfața I²C (TWI) generică, folosită pentru a comunica cu DAC-ul MCP4725 la ~400 kHz. | ||
+ | |||
+ | {{:pm:prj2025:fstancu:amp_twcr.png?500|}} | ||
Ce face fiecare functie: | Ce face fiecare functie: | ||
- | - ''twi_init()'' | + | |
+ | **1)** ''twi_init()'' | ||
* ''TWSR = 0x00;'' → prescaler = 1 | * ''TWSR = 0x00;'' → prescaler = 1 | ||
* ''TWBR = 12;'' → bit-rate generator pentru SCL ≈ 400 kHz la 16 MHz | * ''TWBR = 12;'' → bit-rate generator pentru SCL ≈ 400 kHz la 16 MHz | ||
* ''TWCR = (1<<TWEN);'' → activează modul TWI (SDA/SCL) | * ''TWCR = (1<<TWEN);'' → activează modul TWI (SDA/SCL) | ||
- | - ''twi_start(address)'' | + | **2)** ''twi_start(address)'' |
* ''TWCR = (1<<TWSTA)|(1<<TWEN)|(1<<TWINT);'' → emite condiția START | * ''TWCR = (1<<TWSTA)|(1<<TWEN)|(1<<TWINT);'' → emite condiția START | ||
Line 156: | Line 160: | ||
* | * | ||
* Scrie TWDR = address; și trimiți SLA+R/W | * Scrie TWDR = address; și trimiți SLA+R/W | ||
- | ===== Rezultate Obţinute ===== | ||
- | <note tip> | + | **3)** ''twi_write(data)'' |
- | Care au fost rezultatele obţinute în urma realizării proiectului vostru. | + | * ''TWDR = data;'' |
- | </note> | + | * |
+ | * ''TWCR = (1<<TWEN)|(1<<TWINT);'' → începe transmiterea | ||
+ | * | ||
+ | * Așteaptă TWINT = 1 | ||
- | ===== Concluzii ===== | + | **4)** ''twi_stop()'' |
+ | * ''TWCR = (1<<TWSTO)|(1<<TWEN);'' → emite STOP | ||
- | ===== Download ===== | ||
- | <note warning> | + | === DAC === |
- | 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ă ;-). | + | __//Rol://__ : Strat de abstracție peste TWI pentru a scrie rapid valori 12-biți în MCP4725. |
- | 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**. | + | Ce fac functiile: |
- | </note> | + | |
+ | **1)** ''dac_init()'' | ||
+ | * Apel ''twi_init()'' | ||
+ | |||
+ | **2)** ''mcp4725_write(val)'' | ||
+ | * ''twi_start(addr|0)'' → trimite pe magistrală condiția START, apoi adresa de 7 biți a DAC-ului (0x62) urmată de bitul R/W=0 (scriere). | ||
+ | * | ||
+ | * ''twi_write(0x40)'' → fast-mode command | ||
+ | * | ||
+ | * ''twi_write(val>>4)'' → cei 8 biți ai codului DAC de 12 biți, adică biții de ordin înalt D11…D4 | ||
+ | * | ||
+ | * ''twi_write((val&0xF)<<4))'' → Trimite următorii 4 biți (D3…D0), puși în nibble-ul superior al octetului: | ||
+ | * astfel DAC va primii bitii in felul urmator: | ||
+ | ''Octet 1 = [ D11 D10 D9 D8 D7 D6 D5 D4 ] \\ | ||
+ | |||
+ | Octet 2 = [ D3 D2 D1 D0 0 0 0 0 ]'' | ||
+ | * | ||
+ | * ''twi_stop()'' - trimite conditia de STOP si elibereaza magistrala I2C | ||
+ | |||
+ | MCP4725 în fast-mode aşteaptă un octet de comandă, apoi doi octeți de date ce conțin cei 12 biți de nivel pe DAC. Primul transportă biții cei mai semnificativi (MSB), al doilea transportă cei mai puțin semnificativi (LSB) “aliniati” în nibble-ul de sus, pentru ca DAC-ul să știe exact cum să le reasambleze. | ||
+ | |||
+ | |||
+ | === Efecte === | ||
+ | __//Rol://__ Procesarea semnalului convertit in digital, aplicand efectele stabilite: **distorsiune, tremolo, delay** | ||
+ | |||
+ | == Distortion == | ||
+ | Creaza o forma de unda distorsionata si ascutita, cu tranzitiii rapide pentru un sunet "heavy". | ||
+ | |||
+ | * //Pre-emphasis// | ||
+ | |||
+ | Amplifică ușor frecvențele medii și înalte pentru mai mult „bite” | ||
+ | |||
+ | **''y=x⋅(0.7+0.3⋅∣x∣)''** | ||
+ | |||
+ | * //Soft clipping// | ||
+ | |||
+ | Tranziție netedă la limită. | ||
+ | |||
+ | ''**y=tanh(gain⋅y)**'' | ||
+ | |||
+ | * //Hard limit// | ||
+ | Taie vârfurile peste ±0.8 pentru senzație agresivă. | ||
+ | |||
+ | ''**y=max(min(y,+0.8),−0.8)**'' | ||
+ | |||
+ | Pentru că DAC-ul MCP4725 lucrează cu rezoluţie de 12 biţi (domeniu numeric 0…4095), trebuie să mapăm semnalul nostru normalizat (−1…+1) pe acest interval. | ||
+ | |||
+ | |||
+ | Astfel, pentru un semnal normalizat x∈[−1,1]x∈[−1,1], codul final este | ||
+ | |||
+ | **''DAC_code=⌊x⋅2047⌋+2048''** | ||
+ | |||
+ | asigurând conversia corectă pe 12 biţi. | ||
+ | |||
+ | |||
+ | == Tremolo == | ||
+ | Acest efect oscileaza volumul semnalului la o frecvență joasă (LFO) pentru „vibrato de volum”. | ||
+ | |||
+ | Generăm un LFO sinusoidal pe 16 biți de fază si modulam aplitudinea cu factorul 2 pentru a compensa atenuarea la min(LFO). | ||
+ | |||
+ | Vom modifica periodic volumul semnalului audio pentru a crea un efect de „pulsare”. | ||
+ | |||
+ | În cod ținem o variabilă de fază pe 16 biți care se crește constant de fiecare dată când vine un eşantion nou. | ||
+ | |||
+ | La fiecare eşantion, din această fază calculăm o valoare între 0 și 1 (LFO) care oscilează la frecvența tremolo-ului (de exemplu 5 pulsuri pe secundă). | ||
+ | |||
+ | Apoi multiplicăm semnalul de intrare cu un amestec între volumul original și valoarea LFO: | ||
+ | |||
+ | Dacă LFO este aproape de 1, semnalul are volum aproape normal. | ||
+ | |||
+ | Dacă LFO este aproape de 0, semnalul este atenuat (mai silențios). | ||
+ | |||
+ | Prin schimbarea „adâncimii” (depth) poți controla cât de puternică este această variație de volum. | ||
+ | |||
+ | Totul se întâmplă foarte rapid (mii de ori pe secundă), astfel încât urechea percepe un „vibrato de volum” constant și sincronizat, fără distorsiuni suplimentare. | ||
+ | |||
+ | == Delay == | ||
+ | __//Rol://__ acest efect functioneaza ca un ecou, deci a fost nevoie de un buffer pentru a memora semnalul, deci memorie RAM. | ||
+ | |||
+ | Stocăm fiecare eşantion de semnal într-un buffer circular de mărime fixă (de exemplu 980 poziții). | ||
+ | |||
+ | Când procesăm un eşantion nou, citim din buffer eşantionul aflat cu un anumit „pas” înapoi (de exemplu, 800 poziții) — acesta este semnalul întârziat. | ||
+ | |||
+ | Combinăm semnalul întârziat cu semnalul curent: o parte din ecou (feedback) se adaugă peste semnalul nou. | ||
+ | |||
+ | Scriem în buffer valoarea rezultatului (semnal direct + ecou) pentru ca, la următoarea trecere, să putem crea ecouri în buclă. | ||
+ | |||
+ | Prin ajustarea mărimii buffer-ului (delay time) și a procentului de feedback, controlăm cât de lung și cât de intens este ecoul. | ||
+ | |||
+ | Practic, buffer-ul reţine „urmele” semnalului și le redă cu întârziere, creând senzația de reverberație și ecou. | ||
+ | |||
+ | === ADC === | ||
+ | |||
+ | __//Rol://__ Preluare continuă de semnal analogic și declanșare ISR pentru procesare. | ||
+ | |||
+ | {{:pm:prj2025:fstancu:amp_admux.png?500|}} | ||
+ | |||
+ | {{:pm:prj2025:fstancu:amp_adcsra.png?500|}} | ||
+ | |||
+ | {{:pm:prj2025:fstancu:amp_adcsrb.png?500|}} | ||
+ | |||
+ | |||
+ | Avem urmatoarele setari pentru registre: | ||
+ | |||
+ | * ''ADMUX = (1<<REFS0);'' - REFS0=1 → referință AVcc, canal ADC0 | ||
+ | * ''ADCSRA = ADEN|ADIE|ADATE|(ADPS2|ADPS1);'' care este responsabil de: | ||
+ | |||
+ | ''ADEN = enable ADC'' | ||
+ | |||
+ | ''ADIE = enable ADC interrupt'' | ||
+ | |||
+ | ''ADATE = auto-trigger → modul Free-Running'' | ||
+ | '' | ||
+ | ADPS2:1 = prescaler 64 → ADC clock ~250 kHz'' | ||
+ | |||
+ | * ''ADCSRB = 0;'' → Free-Run | ||
+ | * ''DIDR0 = (1<<ADC0D)''; → dezactivează intrare digitală pe ADC0 | ||
+ | * ''ADCSRA |= (1<<ADSC);'' → pornește prima conversie | ||
+ | |||
+ | Intreruperile folosite: | ||
+ | |||
+ | ''**ISR(ADC_vect)**'' | ||
+ | |||
+ | * Citește ADC (0–1023), recentrează la ±512 | ||
+ | * | ||
+ | * Normalizează la ±1.0 | ||
+ | * | ||
+ | * Aplică efectele active | ||
+ | * | ||
+ | * Clamp și convert la 0–4095 → mcp4725_write() | ||
+ | |||
+ | De ce Free-Running? Vrem eșantionare continuă la frecvență fixă, fără să declanșăm singur fiecare conversie. Asa obtinem eficienta mai buna. | ||
+ | |||
+ | === Butoane === | ||
+ | //__Rol:__// Gestionare butoane cu PCINT + debounce cu Timer0. De asemnea va trata si activarea butoanelor. | ||
+ | |||
+ | Acestea functioneaza pe modul activer low, deci 0 activate, 1 dezactivate. | ||
+ | |||
+ | **ISR(PCINT2_vect) (Pin Change)** | ||
+ | * Setat pe PD2..PD4 (PCINT18..20) | ||
+ | * La trecerea HIGH→LOW pornește counter deb once | ||
+ | * eficient pentru ca nu asteptam intr-un loop apasarea butoanelor, doar intra rutina de tratare a intreruperilor pentru a schimba starea butoanelor si a LED-urilor. | ||
+ | |||
+ | **ISR(TIMER0_COMPA_vect)** | ||
+ | * (apelat in functia main), aceasta rutina va apela procesarea semnalului prin efecte, in functie daca au fost activate sau nu | ||
+ | * Timer0 CTC: prescaler 64, OCR0A=249 → 1 kHz interrupt | ||
+ | * decrementeaza counterele, iar la zero toggle LED și flag efect | ||
+ | |||
+ | De ce PCINT + timer? Detectăm rapid schimbarea de buton și facem debounce fără blocări în bucla principală. | ||
+ | |||
+ | === Main === | ||
+ | Inițializează toate modulele și intră în buclă infinită. | ||
+ | |||
+ | Flux general: | ||
+ | |||
+ | * Butoane → PCINT + Timer0 → update flags | ||
+ | |||
+ | * ADC Free-Run → ISR(ADC_vect) → apply_effects → DAC | ||
+ | |||
+ | * DAC → ieșire audio analogic | ||
+ | |||
+ | ===== Rezultate Obţinute si Performanta ===== | ||
+ | |||
+ | Amplificatorul functioneaza destul de bine, putand sa genereze un semnal audio destul de bun. | ||
+ | Chiar daca am incercat sa lucrez la o frecventa destul de ridicata pentru a avea un sample rate cat mai mare, notele joase sunt redate foarte clar iar cele mai inalte sunt redate o idee mai prost, avand o frecventa destul de mare si masuratorile nu sunt atat de accurate. | ||
+ | |||
+ | === Optimizarile folosite pentru a obtine performante si imbunatiri care pot fi adaugate === | ||
+ | |||
+ | **1) Free-Running ADC** | ||
+ | |||
+ | * Folosim ADC în Free-Running Mode pentru conversii continue la rata stabilită de prescaler, fără a reporni manual conversia la fiecare eşantion. | ||
+ | |||
+ | * Avantaj: latență minimă între eşantioane și utilizare eficientă a CPU în ISR | ||
+ | |||
+ | |||
+ | **2) Intreruperi** | ||
+ | |||
+ | * toată logica de procesare audio (distorsiune, tremolo, delay) se execută în ISR(ADC_vect), eliminând buclele de polling | ||
+ | |||
+ | * debounce butoane și toggle LED se fac în ISR-uri separate (PCINT2 si TIMER0), evitând blocarea buclei principale. | ||
+ | |||
+ | |||
+ | **3) Math Library** | ||
+ | |||
+ | * Tremolo folosește sinf() din <math.h>, costisitor în cicluri CPU | ||
+ | |||
+ | * Pentru o versiune și mai rapidă, se poate înlocui cu un tabel de valori pre-calculate (lookup table) pentru LFO. | ||
+ | |||
+ | |||
+ | **4) Buffer Circular Fix-Size pentru Delay** | ||
+ | |||
+ | * Delay folosește un buffer circular simplu, cu indexare modulară, pentru acces și scriere în timp constant (O(1)). | ||
+ | * Dimensiunea buffer-ului (980) este un compromis între intârziere perceptibilă și consum de memorie SRAM (≈2 KB). | ||
+ | |||
+ | === Frecvente ale proiectului === | ||
+ | |||
+ | **1) Rata de eşantionare ADC: ~10 kHz** | ||
+ | |||
+ | Prescaler ADC = 64 la 16 MHz → ADC clock ≈ 250 kHz → 25 cicluri pe conversie → ~10 k conversii/s, fiind suficient pentru frecventele relevante ala chitarii. | ||
+ | |||
+ | **2) LFO Tremolo: 5 Hz** | ||
+ | |||
+ | Frecvență perceptibilă plăcută de modulare de volum. Phase increment pre-calculatează saltul de fază pe eşantion, fără recalcule de frecvență în timp real. | ||
+ | |||
+ | **3) Timer Debounce: 1 kHz (1 ms tick)** | ||
+ | |||
+ | Asigură o rata de debounce robustă în maxim 50 ms fără pierderi de evenimente. | ||
+ | |||
+ | **4) I2C (TWI): ~400 kHz** | ||
+ | |||
+ | Comunicarea cu DAC la fast-mode pentru a corespunde celor 10 k eşantioane/s și overhead-ul I2C. | ||
+ | |||
+ | === Estimarea Consumului de Putere === | ||
+ | |||
+ | * Microntroller ATmega328P in mod normal (16 MHz, Vcc = 5 V): ~10–20 mA | ||
+ | | ||
+ | * DAC MCP4725: ~0.5 mA | ||
+ | | ||
+ | * LM358: fiecare canal ~0.7 mA | ||
+ | |||
+ | * LED-uri și Butoane: LED-uri aprinse (3 × 2 mA) = ~6 mA maxim | ||
+ | | ||
+ | **Total:** | ||
+ | I_total≈15mA (MCU)+1mA (DAC)+2mA (LM358)+6mA (LED)**≈24mA** | ||
+ | |||
+ | **Putere:** | ||
+ | P=V×I≈5V×24mA=**120mW** | ||
+ | |||
+ | |||
+ | ===== Concluzii ===== | ||
+ | |||
+ | Acest proiect a fost o modalitate perfecta de a aprofunda cunostiintele atat de proiectarea microprocesoarelor, a laboratoarelor de la aceasta materie, cat si mai important, cunostiintele de electronica. Constructia partii de HARDWARE a fost o provocare. In final, sunt multumita de outcome. :-D | ||
===== Jurnal ===== | ===== Jurnal ===== | ||
Line 178: | Line 414: | ||
18 Mai: Descriere detaliata pentru hardware si modificare liste de componente. | 18 Mai: Descriere detaliata pentru hardware si modificare liste de componente. | ||
+ | 25 Mai: Decriere detaliata Software + estimarea puterii consumate si optimizarile facute | ||
===== Bibliografie/Resurse ===== | ===== Bibliografie/Resurse ===== | ||
- | <note> | + | Datasheet LM358: [[https://www.onsemi.com/download/data-sheet/pdf/lm358-d.pdf | LINK]] |
- | Listă cu documente, datasheet-uri, resurse Internet folosite, eventual grupate pe **Resurse Software** şi **Resurse Hardware**. | + | |
- | </note> | + | Datasheet MCP4725: [[https://ww1.microchip.com/downloads/en/devicedoc/22039d.pdf | LINK]] |
+ | |||
+ | Datasheet ArduinoUno ATmega328p: [[https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf | LINK]] | ||
<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> | ||
+ | |||
+ | {{:pm:prj2025:fstancu:pm_amp.zip| Arhiva aici}} |