Sommario:

Sintetizzatore audio digitale FPGA Basys3: 5 passaggi
Sintetizzatore audio digitale FPGA Basys3: 5 passaggi

Video: Sintetizzatore audio digitale FPGA Basys3: 5 passaggi

Video: Sintetizzatore audio digitale FPGA Basys3: 5 passaggi
Video: Real-time Audio Processing via FIR Filters on Basys-3 FPGA 2024, Luglio
Anonim
Image
Image
Sintetizzatore audio digitale FPGA Basys3
Sintetizzatore audio digitale FPGA Basys3
Sintetizzatore audio digitale FPGA Basys3
Sintetizzatore audio digitale FPGA Basys3

Questo sintetizzatore a tastiera digitale a onda sinusoidale prenderà gli input dell'utente tramite una serie di interruttori momentanei disposti come una tastiera e emetterà un'onda audio attraverso un altoparlante. In base agli input dell'utente, il dispositivo genererà onde sinusoidali di varie frequenze da C4 a C6. L'utente può inserire note da C4 fino a C6 (25 note in totale) e fino a quattro tasti contemporaneamente: se vengono premuti più di quattro tasti, verranno suonati i quattro toni più bassi.

Questo progetto è stato realizzato da Ryan Morris e Mavis Tsoi per il nostro corso di design digitale Cal Poly CPE 133:)

Fase 1: Teoria

Una scheda FPGA può emettere solo segnali digitali. In altre parole, può produrre solo una tensione alta (3,3 V) o una tensione bassa (0 V). Tuttavia, i segnali audio sono analogici e possono avere infiniti incrementi di tensione. Per aggirare questo problema, utilizzeremo un segnale PWM (modulazione di larghezza di impulso) per emulare un'onda analogica. Se non sai cos'è il PWM, dai un'occhiata a questo:

Passaggio 2: ingredienti e strumenti

  • Computer con Vivado installato
  • Useremo la versione Vivado 2017.2
  • Scheda FPGA Basys3
  • 25 finecorsa SPDT (abbiamo usato questi)
  • 30 fili per ponticelli (un'estremità maschio, l'altra estremità non importa), 12 pollici
  • Pinza tagliafili
  • Spelafili
  • Filo di ricambio per saldatura
  • Saldatura con nucleo in resina
  • Saldatore
  • Jack audio femmina da”
  • Amplificatore/altoparlante
  • Qualcosa per montare gli interruttori (abbiamo usato la scheda prototipi + la scatola di legno)

Passaggio 3: configurazione del cablaggio e dell'hardware

Cablaggio e configurazione hardware
Cablaggio e configurazione hardware
Cablaggio e configurazione hardware
Cablaggio e configurazione hardware
Cablaggio e configurazione hardware
Cablaggio e configurazione hardware

Architettura di sistema

Vedere la Figura 1: 25 ingressi disponibili → Scheda Basys3 → amplificatore e altoparlante.

Produzione

Vedere la Figura 2: Scheda Basys3 → Jack audio femmina da 1/2 → Altoparlante (con amplificatore)

Ingresso

Le connessioni pmod sulla scheda Basys3 devono essere collegate a terra per vedere un ingresso basso e non funzioneranno correttamente se lasciate come circuito aperto. Per questo motivo, dobbiamo utilizzare gli interruttori SPDT per tutti i nostri tasti di nota. Un interruttore SPDT consente fondamentalmente all'utente di passare da un circuito all'altro quando viene premuto, quindi li useremo come nostri "pulsanti" per inviare segnali bassi (0 V) o alti (3,3 V) alla scheda Basys3.

Ciascun interruttore avrà il terminale NO (normalmente aperto) collegato a 3,3 V, il terminale NC (normalmente chiuso) collegato a GND e il terminale COM (comune) collegato all'ingresso FPGA. Vedi figura 3.

Poiché abbiamo 25 interruttori di limite, condivideranno tutti una linea comune da 3,3 V e una linea GND comune. Quindi, la linea di segnale da ciascun finecorsa verrà raggruppata in gruppi di 8 e collegata alle connessioni pmod sulla scheda Basys3 utilizzando cavi jumper zippabili per ridurre al minimo il disordine monumentale che faremo. Vedere la Figura 4 o un esempio dei primi otto tasti.

Passaggio 4: configurazione VHDL (Vivado)

Configurazione VHDL (Vivado)
Configurazione VHDL (Vivado)
Configurazione VHDL (Vivado)
Configurazione VHDL (Vivado)

Il generatore di onde sinusoidali e il generatore PWM sono stati prima testati per assicurarsi che il nostro concetto funzionasse, quindi sono stati integrati il limitatore di ingresso e il sommatore/cambio di ampiezza. I dettagli della funzione e degli I/O di ciascun blocco di processo sono mostrati nella figura. Il codice è mostrato di seguito, ma anche allegato come file VHD e txt. Se ci sono discrepanze, vai con i file VHD.

A proposito: probabilmente avremmo dovuto accorciare le righe, ma anche l'incorporamento del codice su Instructables si è rivelato piuttosto fastidioso da gestire, quindi la spaziatura non è la massima e non c'è l'evidenziazione della sintassi. Se hai Vivado e desideri seguire il codice, ti consigliamo vivamente di scaricare solo il file.

Per prima cosa, diamo un'occhiata al modulo Sine Wave Generator.

libreria IEEE; usa IEEE. STD_LOGIC_1164. ALL; usa IEEE. NUMERIC_STD. ALL; entità Wave_Generator è la porta (Trigger: in STD_LOGIC; -- Freq_Cnt pressione tasto: in STD_LOGIC_VECTOR(15 fino a 0); -- Valore contatore = 100MHz / (Nota frequenza*64 divisioni dell'onda sinusoidale) (arrotondato al numero più vicino) -- rinominato da Freq wavegenCLK: in STD_LOGIC; -- Basys3 100MHz CLK WaveOut: out STD_LOGIC_VECTOR(9 fino a 0)); -- Ampiezza con segno dell'estremità dell'onda Wave_Generator; architettura Il comportamento di Wave_Generator è il segnale i: intervallo intero da 0 a 64:= 0; -- l'indice del tipo di banco di memoria di ampiezza memory_type è un array (da 0 a 63) dell'intervallo intero da -64 a 63; -- creare un banco di memoria (ROM) per contenere i valori di ampiezza -- questa RAM o ROM si sta solo chiedendo… ampiezza del segnale: memory_type:= (0, 7, 13, 19, 25, 30, 35, 40, 45, 49, 52, 55, 58, 60, 62, 63, 63, 63, 62, 60, 58, 55, 52, 49, 45, 40, 35, 30, 25, 19, 13, 7, 0, -7, -13, -19, -25, -30, -35, -40, -45, -49, -52, -55, -58, -60, -62, -63, -63, -63, -62, - 60, -58, -55, -52, -49, -45, -40, -35, -30, -25, -19, -13, -7); -- banco di memoria di ampiezza per l'inizio del processo di onda sinusoidale (wavegenCLK, Trigger) contatore variabile: unsigned (15 fino a 0):= to_unsigned (0, 16); -- contatore del divisore dell'orologio, rinominato da count1 inizia se (rising_edge(wavegenCLK)) poi se (Trigger = '1') allora -- viene premuto il tasto contatore:= contatore + 1; if (counter = unsigned(Freq_Cnt)) then -- Freq_Cnt = 100Mhz / (nota freq * 64 divisioni dell'onda sinusoidale) -- resetta il contatore e assegna i dati di ampiezza al contatore di uscita:= to_unsigned(0, 16); WaveOut <= STD_LOGIC_VECTOR (to_signed(ampiezza(i), 10)); -- incrementa i per la lettura successiva i <= i + 1; -- resetta i se un'onda sinusoidale è stata completata if(i = 63) allora i <= 0; finisci se; finisci se; -- (counter = unsigned(Freq_Cnt)) else -- il tasto non viene premuto -- ripristina l'uscita, l'indice di ampiezza e il contatore WaveOut <= "0000000000"; io <= 0; contatore:= to_unsigned(0, 16); --output Amplitude = -64 quando non viene suonata nessuna nota end if; -- (Trigger = '1') end if; -- (rising_edge(CLK)) processo di fine; fine comportamentale;

Genereremo un'onda sinusoidale digitale nel Basys3 utilizzando l'orologio interno e una ROM. Questa ROM memorizzerà 64 valori che rappresentano 64 ampiezze su un'onda sinusoidale. Vedere la Figura 1. I 64 valori che usiamo emulano un'onda sinusoidale con una risoluzione abbastanza buona.

Utilizzando il clock interno, contiamo fino a un valore che rappresenta la velocità di clock divisa per la frequenza dell'onda che vogliamo e 64: Clk div = 100MHz / (Freq * 64) Ogni volta che il nostro contatore raggiunge quel valore, chiamiamo un numero da la ROM e inviarla dal nostro modulo generatore di onde. La frequenza della nostra onda dipenderà da quanto velocemente chiamiamo queste ampiezze.

Avremo 25 sottomoduli, ciascuno associato ad una frequenza/nota.

Ecco il resto del codice che chiama i moduli Sine Wave Generator:

libreria IEEE; usa IEEE. STD_LOGIC_1164. ALL; usa IEEE. NUMERIC_STD. ALL; entità Two_Octave_Synth è Port (CLK: in STD_LOGIC; O4: in STD_LOGIC_VECTOR(11 fino a 0); O5: in STD_LOGIC_VECTOR(12 fino a 0); output: out STD_LOGIC); fine Two_Octave_Synth; architettura Comportamento di Two_Octave_Synth è il componente Wave_Generator è Port (Trigger: in STD_LOGIC; Freq_Cnt: in STD_LOGIC_VECTOR(15 fino a 0); wavegenCLK: in STD_LOGIC; WaveOut: out STD_LOGIC_VECTOR(9 fino a 0)); componente finale; ---------------------------segnali in uscita dal generatore di onde---- ----- segnale WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, WaveGs,5 WaveAs5, WaveB5, WaveC6: firmato (9 fino a 0); --------------------------------per logica di selezione delle note -------------- ------ segnale C4, Cs4, D4, Ds4, E4, F4, Fs4, G4, Gs4, A4, As4, B4, C5, Cs5, D5, Ds5, E5, F5, Fs5, G5, Gs5, A5, As5, B5, C6: senza segno (4 fino a 0); segnale cntC4, cntCs4, cntD4, cntDs4, cntE4, cntF4, cntFs4, cntG4, cntGs4, cntA4, cntAs4, cntB4, cntC5, cntCs5, cntC5, cntDs5, cntA5, cntGs, cnt5, cntA5, cntG, cnt5: senza segno(4 fino a 0); errore di segnale: STD_LOGIC; -----------------------------------per l'aggiunta di onde sinusoidali ----------- --------------- segnale Wave0, Wave1, Wave2, Wave3: con segno (9 fino a 0); --segnali dal segnale di uscita del modulo Wave Generator WaveSum: STD_LOGIC_VECTOR(9 fino a 0); --segnale per onde sinusoidali sommate (complemento di 2 da -512 a 511) segnale positiveWaveSum: STD_LOGIC_VECTOR(9 fino a 0); --unsigned da 0 a 1023, per l'uso nel generatore PWM -----------------------------------per la generazione di PWM ----------------- signal ping_length: unsigned (9 downto 0):= unsigned(positiveWaveSum); --signal off_length: unsigned (6 fino a 0):= to_unsigned(127, 7) - unsigned(WAVE); segnale PWM: unsigned (9 downto 0):= to_unsigned(0, 10); begin Note_C4: mappa delle porte Wave_Generator (Trigger => O4(0), Freq_Cnt => X"1755", wavegenCLK => CLK, firmato(WaveOut) => WaveC4); --5973, 261,63 Hz Nota_Cs4: Mappa porta Wave_Generator (Trigger => O4(1), Freq_Cnt => X"1606", wavegenCLK => CLK, firmato(WaveOut) => WaveCs4);--5638, 277,18 Hz Nota_D4: Mappa delle porte Wave_Generator (Trigger => O4(2), Freq_Cnt => X"14C9", wavegenCLK => CLK, firmato(WaveOut) => WaveD4); --5321, 293,66 Hz Note_Ds4: Mappa porta Wave_Generator (Trigger => O4(3), Freq_Cnt => X"139F", wavegenCLK => CLK, firmato(WaveOut) => WaveDs4);--5023, 311.13 Hz Note_E4: Mappa delle porte Wave_Generator (Trigger => O4(4), Freq_Cnt => X"1285", wavegenCLK => CLK, firmato(WaveOut) => WaveE4); --4741, 329,63 Hz Note_F4: Mappa della porta Wave_Generator (Trigger => O4(5), Freq_Cnt => X"117B", wavegenCLK => CLK, firmato(WaveOut) => WaveF4); --4475, 349,23 Hz Note_Fs4: Mappa porta Wave_Generator (Trigger => O4(6), Freq_Cnt => X"1080", wavegenCLK => CLK, firmato(WaveOut) => WaveFs4);--4224, 369,99 Hz Note_G4: Mappa delle porte Wave_Generator (Trigger => O4(7), Freq_Cnt => X"0F92", wavegenCLK => CLK, firmato(WaveOut) => WaveG4); --3986, 392,00 Hz Note_Gs4: Mappa della porta Wave_Generator (Trigger => O4(8), Freq_Cnt => X"0EB3", wavegenCLK => CLK, firmato(WaveOut) => WaveGs4);--3763, 415.30 Hz Note_A4: Mappa delle porte Wave_Generator (Trigger => O4(9), Freq_Cnt => X"0DE0", wavegenCLK => CLK, firmato(WaveOut) => WaveA4); --3552, 440,00 Hz Nota_As4: Mappa porta Wave_Generator (Trigger => O4(10), Freq_Cnt => X"0D18", wavegenCLK => CLK, firmato(WaveOut) => WaveAs4);--3352, 466,16 Hz Nota_B4: Mappa delle porte Wave_Generator (Trigger => O4(11), Freq_Cnt => X"0C5C", wavegenCLK => CLK, firmato(WaveOut) => WaveB4); ---3164, 493,88 Hz -------------------------------------------- --------------------------------------------------- --------------------------- Note_C5: Mappa delle porte Wave_Generator (Trigger => O5(0), Freq_Cnt => X"0BAB", wavegenCLK => CLK, firmato(WaveOut) => WaveC5); --2987, 523,25 Hz Nota_Cs5: Mappa porta Wave_Generator (Trigger => O5(1), Freq_Cnt => X"0B03", wavegenCLK => CLK, firmato(WaveOut) => WaveCs5);--2819, 554,37 Hz Nota_D5: Mappa delle porte Wave_Generator (Trigger => O5(2), Freq_Cnt => X"0A65", wavegenCLK => CLK, firmato(WaveOut) => WaveD5); --2661, 587,33 Hz Note_Ds5: Mappa porta Wave_Generator (Trigger => O5(3), Freq_Cnt => X"09D0", wavegenCLK => CLK, firmato(WaveOut) => WaveDs5);--2512, 622,25 Hz Note_E5: Mappa delle porte Wave_Generator (Trigger => O5(4), Freq_Cnt => X"0943", wavegenCLK => CLK, firmato(WaveOut) => WaveE5); --2371, 659,25 Hz Note_F5: Mappa delle porte Wave_Generator (Trigger => O5(5), Freq_Cnt => X"08Be", wavegenCLK => CLK, firmato(WaveOut) => WaveF5); ---2238, 698,46 Hz Note_Fs5: Mappa porta Wave_Generator (Trigger => O5(6), Freq_Cnt => X"0840", wavegenCLK => CLK, firmato(WaveOut) => WaveFs5);--2112, 739,99 Hz Note_G5: Mappa delle porte Wave_Generator (Trigger => O5(7), Freq_Cnt => X"07CA", wavegenCLK => CLK, firmato(WaveOut) => WaveG5); ---1994, 783,99 Hz Note_Gs5: Mappa porta Wave_Generator (Trigger => O5(8), Freq_Cnt => X"075A", wavegenCLK => CLK, firmato(WaveOut) => WaveGs5);--1882, 830,61 Hz Note_A5: Mappa delle porte Wave_Generator (Trigger => O5(9), Freq_Cnt => X"06F0", wavegenCLK => CLK, firmato(WaveOut) => WaveA5); ---1776, 880,00 Hz Note_As5: Mappa porta Wave_Generator (Trigger => O5(10), Freq_Cnt => X"068C", wavegenCLK => CLK, firmato(WaveOut) => WaveAs5);--1676, 932,33 Hz Note_B5: Mappa delle porte Wave_Generator (Trigger => O5(11), Freq_Cnt => X"062E", wavegenCLK => CLK, firmato(WaveOut) => WaveB5); ---1582, 987,77 Hz Note_C6: Wave_Generator mappa porta (Trigger => O5(12), Freq_Cnt => X"05D6", wavegenCLK => CLK, firmato(WaveOut) => WaveC6); ---1494, 1046.5 Hz ------------logica di selezione delle note------------ DO4 <= "0000" & O4(0); Cs4 <= "0000" & O4(1); D4 <= "0000" & O4(2); Ds4 <= "0000" & O4(3); E4 <= "0000" & O4(4); F4 <= "0000" & O4(5); Fs4 <= "0000" & O4(6); G4 <= "0000" & O4(7); Gs4 <= "0000" & O4(8); A4 <= "0000" & O4(9); As4 <= "0000" & O4(10); B4 <= "0000" & O4(11); C5 <= "0000" & O5(0); Cs5 <= "0000" & O5(1); D5 <= "0000" & O5(2); Ds5 <= "0000" & O5(3); E5 <= "0000" & O5(4); F5 <= "0000" & O5(5); Fs5 <= "0000" & O5(6); G5 <= "0000" & O5(7); Gs5 <= "0000" & O5(8); A5 <= "0000" & O5(9); As5 <= "0000" & O5(10); B5 <= "0000" & O5(11); C6 <= "0000" & O5(12); cntC4 <= C4; cntCs4 <= C4 + Cs4; cntD4 <= C4 + Cs4 + D4; cntDs4 <= DO4 + DO4 + RE4 + RE4; cntE4 <= DO4 + DO4 + RE4 + RE4 + MI4; cntF4 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4; cntFas4 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4; cntG4 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4; cntGs4 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4; cntLA4 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4; cntLas4 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4; cntB4 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4; cntDO5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5; cntDos5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5; cntRE5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5; cntDs5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5; cntME5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5 + MI5; cntFA5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5 + MI5 + FA5; cntFa5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5 + MI5 + FA5 + FA5; cntSol5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5 + MI5 + FA5 + FA5 + SOL5; cntSol5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5 + MI5 + FA5 + FA5 + SOL5 + SOL5; cntLA5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5 + MI5 + FA5 + FA5 + SOL5 + SOL5 + LA5; cntLas5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5 + MI5 + FA5 + FA5 + SOL5 + SOL5 + LA5 + LA5; cntB5 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5 + MI5 + FA5 + FA5 + SOL5 + SOL5 + LA5 + LA5 + SI5; cntDO6 <= DO4 + DO4 + RE4 + RE4 + MI4 + FA4 + FA4 + SOL4 + SOL4 + LA4 + LA4 + SI4 + DO5 + DO5 + RE5 + RE5 + MI5 + FA5 + FA5 + SOL5 + SOL5 + LA5 + LA5 + SI5 + C6; Selezione: processo (WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveAG5, Wave5, Waves WaveB5, WaveC6) iniziano se (cntC6 = "00000") poi ---------------se non vengono generati segnali Wave0 <= "000000000"; Onda1 <= "0000000000"; Onda2 <= "0000000000"; Wave3 <= "0000000000"; else if (O4(0) = '1') then -------------------nota C4 riprodotta Wave0 Wave0 Wave1 errore Wave0 Wave1 Wave2 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 Wave1 Wave2 Wave3 errore Wave0 < = OndaC6; Onda1 <= "0000000000"; Onda2 <= "0000000000"; Onda3 Onda1 <= OndaC6; Onda2 <= "0000000000"; Onda3 Onda2 <= OndaC6; Wave3 Errore Wave3 Wave1 <= "00000000000"; Onda2 <= "0000000000"; Onda3 Onda2 <= "00000000000"; Wave3 Errore Wave3 <= '1'; caso finale; finisci se; finisci se; fine del processo; ------------- sommatore dell'onda sinusoidale----- WaveSum <= STD_LOGIC_VECTOR(Wave0 + Wave1 + Wave2 + Wave3); ---------rendere l'onda sinusoidale positiva per pwm -------- positiveWaveSum <= non WaveSum(9) e WaveSum(8 fino a 0); -------------Generatore PWM ------- process (CLK) --variable count: unsigned (da 1 a 0):= to_unsigned(0, 2); inizia se (rising_edge(CLK)) then --count:= count + 1; --if (count = to_unsigned(4, 2)) then --count:= to_unsigned(0, 2); --if (PWM = to_ if (PWM < ping_length) then output <= '1'; else output <= '0'; end if; PWM <= PWM + 1; ping_length <= unsigned(positiveWaveSum); --end if; end if; end process; end Behavioral;

4 Note Selector La parte più difficile di questo progetto è selezionare solo quattro frequenze. Lo abbiamo fatto con un sacco di istruzioni IF e abbiamo usato segnali invece di variabili in modo che il processo possa essere simulato e sottoposto a debug. Abbiamo provato altri metodi utilizzando variabili e cicli FOR, ma abbiamo riscontrato errori di runtime. Quindi, alla fine, abbiamo deciso che se funziona, lo lasceremo stare. Non aggiustare ciò che non è l'amirite rotta?

Le quattro onde di uscita sono etichettate Wave0, Wave1, Wave2, Wave3 -- queste sono quelle che verranno sommate per formare l'output finale.

Guardando il codice, vedrai un gruppo di segnali etichettati C4, Cs4, D4, Ds4, ecc. Questi sono segnali a 5 bit che prendono il trigger corrispondente da O4 (ottava 4) o O5 (ottava 5) e li rendono 5-bit per l'aggiunta.

Successivamente le variabili cntC4, cntCs4, etc rappresentano quante note inferiori alla nota target sono state suonate, inclusa la nota target. Ad esempio, se si suonano C4, E4, G4, A#4 e D5 (accordo C9) cntC4 sarà 1, cntE4 sarà 2, cntG4 sarà 3, ecc.

Quindi, ogni volta che viene suonata una nota, verrà esaminato il conteggio della nota di destinazione per vedere dove agganciare il segnale della nota. Ad esempio, se viene suonata la nota D5 (che significa O5(2) è alto) e cntD5 è 3, allora ci sono attualmente 3 note in esecuzione, con 2 note inferiori a D5, quindi collegheremo waveD5 a Wave2 (la terza wave conteggio del segnale da Wave0). In alternativa, se cntD5 è 5, al momento ci sono 5 note in esecuzione, con 4 note inferiori a D5, quindi lasceremo solo waveD5 in sospeso e non faremo nulla con esso.

Le dichiarazioni IF vengono quindi ripetute per coprire i casi per tutte le 25 note.

Sommatore di ampiezza

Dopo aver selezionato le 4 onde più basse, dobbiamo sommarle. Il motivo per cui aggiungeremo solo quattro note insieme è perché l'idea PWM che stiamo usando per il nostro output può avere solo una certa risoluzione fino a quando il PWM non funziona troppo lentamente e l'altoparlante inizia a rilevare l'onda quadra PWM. Ad esempio, se dovessimo utilizzare una risoluzione di 8192 (13 bit), ciascuno di questi 8192 punti deve corrispondere a un fronte di salita del clock di bordo. Quindi, 100 MHz / 8192 = 12,2 kHz, che è ben all'interno della gamma dell'udito umano.

L'effettiva aggiunta delle ampiezze è semplicissima, devi solo assicurarti che possa funzionare molto velocemente.

Uscita PWM

Il ciclo di lavoro del PWM rappresenterà l'ampiezza della nostra onda di uscita in quell'istante. Ad esempio, se abbiamo un intervallo di ampiezza da 0 a 128, 0 sarebbe un ciclo di lavoro dello 0%, 64 sarebbe il 50%, 128 sarebbe il 100%, ecc. Questo PWM funzionerà estremamente velocemente (il nostro è 97,6 kHz), così velocemente che l'altoparlante non riconoscerà le singole onde quadre e guarderà invece la tensione media, creando il nostro segnale "analogico".

File dei vincoli

Potresti aver collegato il tuo hardware in modo diverso, quindi assicurati che il file dei vincoli corrisponda.

Passaggio 5: download del codice

Di seguito il codice, sia in formato.txt che.vhd per Vivado. Wave_Generator è il sottomodulo del generatore di onde e Two_Octave_Synth è il modulo principale con tutto il resto.

Consigliato: