Inhaltsverzeichnis:

Basys3 FPGA Digital Audio Synthesizer - Gunook
Basys3 FPGA Digital Audio Synthesizer - Gunook

Video: Basys3 FPGA Digital Audio Synthesizer - Gunook

Video: Basys3 FPGA Digital Audio Synthesizer - Gunook
Video: Digital Audio Delay on an FPGA. 2024, November
Anonim
Image
Image
Basys3 FPGA Digital Audio Synthesizer
Basys3 FPGA Digital Audio Synthesizer
Basys3 FPGA Digital Audio Synthesizer
Basys3 FPGA Digital Audio Synthesizer

Dieser digitale Sinuswellen-Tastatur-Synthesizer nimmt Benutzereingaben über eine Reihe von Tastern entgegen, die wie eine Tastatur angeordnet sind, und gibt eine Audiowelle über einen Lautsprecher aus. Basierend auf Benutzereingaben erzeugt das Gerät Sinuswellen mit verschiedenen Frequenzen von C4 bis C6. Der Benutzer kann Noten von C4 bis C6 (insgesamt 25 Noten) und bis zu vier Tasten gleichzeitig eingeben – wenn mehr als vier Tasten gedrückt werden, werden die vier tiefsten Töne gespielt.

Dieses Projekt wurde von Ryan Morris und Mavis Tsoi für unsere Cal Poly CPE 133 Digital Design Klasse durchgeführt:)

Schritt 1: Theorie

Ein FPGA-Board kann nur digitale Signale ausgeben. Mit anderen Worten, es kann nur eine hohe (3,3 V) Spannung oder eine niedrige (0 V) Spannung erzeugen. Audiosignale sind jedoch analog und können unendlich viele Spannungsstufen aufweisen. Um dies zu umgehen, verwenden wir ein PWM-Signal (Pulsweitenmodulation), um eine analoge Welle zu emulieren. Wenn Sie nicht wissen, was PWM ist, sehen Sie sich dies an:

Schritt 2: Zutaten & Werkzeuge

  • Computer mit installiertem Vivado
  • Wir werden die Vivado-Version 2017.2 verwenden
  • Basys3 FPGA-Board
  • 25 SPDT-Endschalter (wir haben diese verwendet)
  • 30 Überbrückungsdrähte (ein Ende männlich, das andere Ende spielt keine Rolle), 12 Zoll
  • Kabelschneider
  • Abisolierzangen
  • Ersatzdraht zum Löten
  • Harzkernlot
  • Lötkolben
  • ¼-Zoll-Audiobuchse
  • Verstärker/Lautsprecher
  • Etwas zum Anbringen der Schalter (wir haben Protoboard + Holzkiste verwendet)

Schritt 3: Verkabelung & Hardware-Setup

Verkabelung & Hardware-Setup
Verkabelung & Hardware-Setup
Verkabelung & Hardware-Setup
Verkabelung & Hardware-Setup
Verkabelung & Hardware-Setup
Verkabelung & Hardware-Setup

Systemarchitektur

Siehe Abbildung 1: 25 verfügbare Eingänge → Basys3 Board → Verstärker & Lautsprecher.

Ausgabe

Siehe Abbildung 2: Basys3-Platine → 1/2 Audiobuchse → Lautsprecher (mit Verstärker)

Eingang

Die pmod-Anschlüsse auf der Basys3-Platine müssen mit Masse verbunden werden, um einen niedrigen Eingang zu sehen und funktionieren nicht richtig, wenn sie als offener Stromkreis belassen werden. Aus diesem Grund müssen wir für alle unsere Notentasten SPDT-Schalter verwenden. Ein SPDT-Schalter ermöglicht es dem Benutzer grundsätzlich, beim Drücken zwischen den Schaltkreisen umzuschalten, daher werden wir sie als unsere "Tasten" verwenden, um niedrige (0 V) oder hohe (3,3 V) Signale an die Basys3-Platine einzugeben.

Bei jedem Schalter ist der NO-Anschluss (normalerweise geöffnet) mit 3,3 V verbunden, der NC-Anschluss (normalerweise geschlossen) mit GND und der COM-Anschluss (gemeinsam) mit dem FPGA-Eingang verbunden. Siehe Abbildung 3.

Da wir 25 Endschalter haben, teilen sich alle eine gemeinsame 3,3-V-Leitung und eine gemeinsame GND-Leitung. Dann wird die Signalleitung von jedem Endschalter in 8er-Gruppen gebündelt und mit den pmod-Anschlüssen auf der Basys3-Platine mit zipierbaren Überbrückungsdrähten verbunden, um das monumentale Durcheinander zu minimieren, das wir anrichten werden. Siehe Abbildung 4 oder ein Beispiel der ersten acht Tasten.

Schritt 4: VHDL-Setup (Vivado)

VHDL-Setup (Vivado)
VHDL-Setup (Vivado)
VHDL-Setup (Vivado)
VHDL-Setup (Vivado)

Der Sinusgenerator und der PWM-Generator wurden zuerst getestet, um sicherzustellen, dass unser Konzept funktioniert, dann wurden der Eingangsbegrenzer und der Amplitudenaddierer/-schieber integriert. Einzelheiten zu Funktion und E/A jedes Prozessblocks sind in der Abbildung dargestellt. Der Code wird unten gezeigt, aber auch als VHD- und TXT-Dateien angehängt. Bei Unstimmigkeiten gehen Sie zu den VHD-Dateien.

Übrigens: Wir hätten unsere Zeilen wahrscheinlich kürzer machen sollen, aber das Einbetten von Code in Instructables erwies sich auch als ziemlich nervig, so dass der Abstand nicht der größte ist und es keine Syntaxhervorhebung gibt. Wenn Sie Vivado haben und dem Code folgen möchten, empfehlen wir Ihnen dringend, die Datei einfach herunterzuladen.

Schauen wir uns zunächst das Modul Sinus Wave Generator an.

Bibliothek IEEE; IEEE. STD_LOGIC_1164. ALL verwenden; Verwenden Sie IEEE. NUMERIC_STD. ALL; Entität Wave_Generator ist Port (Trigger: in STD_LOGIC; -- Tastendruck Freq_Cnt: in STD_LOGIC_VECTOR(15 bis 0); -- Zählerwert = 100MHz / (Notenfrequenz*64 Divisionen der Sinuswelle) (auf nächste Zahl aufrunden) -- umbenannt von Freq wavegenCLK: in STD_LOGIC; -- Basys3 100MHz CLK WaveOut: aus STD_LOGIC_VECTOR(9 bis 0)); -- Vorzeichenbehaftete Amplitude des Wellenendes Wave_Generator; Architektur Verhalten von Wave_Generator ist Signal i: ganzzahliger Bereich 0 bis 64:= 0; -- Index des Amplitudenspeicherbanktyps memory_type ist ein Array (0 bis 63) des ganzzahligen Bereichs -64 bis 63; -- Speicherbank (ROM) erstellen, um Amplitudenwerte zu halten -- fragt sich dieser RAM oder ROM nur … Signalamplitude: 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); -- Amplitudenspeicherbank für Sinuswellenbeginnprozess (wavegenCLK, Trigger) variabler Zähler: unsigned (15 downto 0):= to_unsigned(0, 16); -- Taktteilerzähler, umbenannt von count1 begin if (rising_edge(wavegenCLK)) then if (Trigger = '1') then -- Taste wird gedrückt counter:= counter + 1; if (counter = unsigned(Freq_Cnt)) then -- Freq_Cnt = 100Mhz / (beachte freq * 64 Divisionen der Sinuswelle) -- setze den Zähler zurück und weise dem Ausgangszähler die Amplitudendaten zu:= to_unsigned(0, 16); WaveOut <= STD_LOGIC_VECTOR (to_signed(amplitude(i), 10)); -- inkrementiere i für das nächste Lesen i <= i + 1; -- i zurücksetzen, wenn eine Sinuswelle abgeschlossen wurde if(i = 63) dann i <= 0; Ende, wenn; Ende, wenn; -- (Zähler = unsigned(Freq_Cnt)) else -- Taste ist nicht gedrückt -- Ausgang, Amplitudenindex und Zähler zurücksetzen WaveOut <= "0000000000"; ich <= 0; Zähler:= to_unsigned(0, 16); --output Amplitude = -64 wenn keine Note gespielt wird end if; -- (Trigger = '1') end if; -- (rising_edge(CLK)) Prozess beenden; Ende Verhalten;

Wir werden im Basys3 eine digitale Sinuswelle erzeugen, indem wir die interne Uhr und ein ROM verwenden. Dieses ROM speichert 64 Werte, die 64 Amplituden auf einer Sinuswelle darstellen. Siehe Abbildung 1. Die 64 von uns verwendeten Werte emulieren eine Sinuswelle mit ziemlich guter Auflösung.

Mit der internen Uhr zählen wir bis zu einem Wert, der die Taktfrequenz geteilt durch die Frequenz der gewünschten Welle und 64 darstellt: Clk div = 100MHz / (Freq * 64) Jedes Mal, wenn unser Zähler diesen Wert erreicht, rufen wir eine Zahl von das ROM und senden Sie es aus unserem Wave-Generator-Modul. Die Frequenz unserer Welle hängt davon ab, wie schnell wir diese Amplituden nennen.

Wir werden 25 Untermodule haben, die jeweils einer Frequenz/Note zugeordnet sind.

Hier ist der Rest des Codes, der die Sinuswellengenerator-Module aufruft:

Bibliothek IEEE; IEEE. STD_LOGIC_1164. ALL verwenden; Verwenden Sie IEEE. NUMERIC_STD. ALL; Entität Two_Octave_Synth ist Port (CLK: in STD_LOGIC; O4: in STD_LOGIC_VECTOR(11 bis 0); O5: in STD_LOGIC_VECTOR(12 bis 0); Ausgang: aus STD_LOGIC); Ende Two_Octave_Synth; Architektur Verhalten von Two_Octave_Synth ist Komponente Wave_Generator ist Port (Trigger: in STD_LOGIC; Freq_Cnt: in STD_LOGIC_VECTOR(15 bis 0); wavegenCLK: in STD_LOGIC; WaveOut: aus STD_LOGIC_VECTOR(9 bis 0); Endkomponente; --------------------------Ausgangssignale vom Wellengenerator ------------------ ----- Signal WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, Wave5Gs5, WaveA WaveAs5, WaveB5, WaveC6: Vorzeichen (9 bis 0); --------------------------------für die Notenauswahllogik -------------- ------ Signal 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: unsigned(4 bis 0); Signal cntC4, cntCs4, cntD4, cntDs4, cntE4, cntF4, cntFs4, cntG4, cntGs4, cntA4, cntAs4, cntB4, cntC5, cntCs5, cntD5, cntDs5, cntE5, cntCnt5, cnt, cnt5, cnt, cnt,: unsigned(4 bis 0); Signalfehler: STD_LOGIC; ----------------------------------- zum Hinzufügen von Sinuswellen ----------- -------------- Signal Wave0, Wave1, Wave2, Wave3: Vorzeichen (9 bis 0); --Signale vom Ausgangssignal des Wave Generator-Moduls WaveSum: STD_LOGIC_VECTOR(9 bis 0); --Signal für summierte Sinuswellen (2er Komplement -512 bis 511) signal positiveWaveSum: STD_LOGIC_VECTOR(9 bis 0); --unsigned 0 bis 1023, zur Verwendung im PWM-Generator ---------------------------------- zum Erzeugen von PWM ------------------------------------------- signal ping_length: unsigned (9 downto 0):= unsigned(positiveWaveSum); --signal off_length: unsigned (6 downto 0):= to_unsigned(127, 7) - unsigned(WAVE); Signal PWM: unsigned (9 downto 0):= to_unsigned(0, 10); begin Note_C4: Wave_Generator-Port-Map (Trigger => O4(0), Freq_Cnt => X"1755", wavegenCLK => CLK, signed(WaveOut) => WaveC4); --5973, 261.63 Hz Note_Cs4: Wave_Generator-Port-Map (Trigger => O4(1), Freq_Cnt => X"1606", wavegenCLK => CLK, signed(WaveOut) => WaveCs4);--5638, 277.18 Hz Note_D4: Wave_Generator-Port-Map (Trigger => O4(2), Freq_Cnt => X"14C9", wavegenCLK => CLK, signed(WaveOut) => WaveD4); --5321, 293,66 Hz Note_Ds4: Wave_Generator-Port-Map (Trigger => O4(3), Freq_Cnt => X"139F", wavegenCLK => CLK, signed(WaveOut) => WaveDs4);--5023, 311,13 Hz Note_E4: Wave_Generator-Port-Map (Trigger => O4(4), Freq_Cnt => X"1285", wavegenCLK => CLK, signed(WaveOut) => WaveE4); --4741, 329.63 Hz Note_F4: Wave_Generator-Port-Map (Trigger => O4(5), Freq_Cnt => X"117B", wavegenCLK => CLK, signed(WaveOut) => WaveF4); --4475, 349,23 Hz Note_Fs4: Wave_Generator-Port-Map (Trigger => O4(6), Freq_Cnt => X"1080", wavegenCLK => CLK, signed(WaveOut) => WaveFs4);--4224, 369,99 Hz Note_G4: Wave_Generator-Port-Map (Trigger => O4(7), Freq_Cnt => X"0F92", wavegenCLK => CLK, signed(WaveOut) => WaveG4); --3986, 392,00 Hz Note_Gs4: Wave_Generator-Port-Map (Trigger => O4(8), Freq_Cnt => X"0EB3", wavegenCLK => CLK, signed(WaveOut) => WaveGs4);--3763, 415,30 Hz Note_A4: Wave_Generator-Port-Map (Trigger => O4(9), Freq_Cnt => X"0DE0", wavegenCLK => CLK, signed(WaveOut) => WaveA4); --3552, 440,00 Hz Note_As4: Wave_Generator-Port-Map (Trigger => O4(10), Freq_Cnt => X"0D18", wavegenCLK => CLK, signed(WaveOut) => WaveAs4);--3352, 466,16 Hz Note_B4: Wave_Generator-Port-Map (Trigger => O4(11), Freq_Cnt => X"0C5C", wavegenCLK => CLK, signed(WaveOut) => WaveB4); ---3164, 493,88 Hz ---------------------------------------------- -------------------------------------------------- --------------------------- Note_C5: Wave_Generator-Port-Map (Trigger => O5(0), Freq_Cnt => X"0BAB", wavegenCLK => CLK, signiert(WaveOut) => WaveC5); --2987, 523,25 Hz Note_Cs5: Wave_Generator-Port-Map (Trigger => O5(1), Freq_Cnt => X"0B03", wavegenCLK => CLK, signed(WaveOut) => WaveCs5);--2819, 554,37 Hz Note_D5: Wave_Generator-Port-Map (Trigger => O5(2), Freq_Cnt => X"0A65", wavegenCLK => CLK, signed(WaveOut) => WaveD5); --2661, 587,33 Hz Note_Ds5: Wave_Generator-Port-Map (Trigger => O5(3), Freq_Cnt => X"09D0", wavegenCLK => CLK, signed(WaveOut) => WaveDs5);--2512, 622,25 Hz Note_E5: Wave_Generator-Port-Map (Trigger => O5(4), Freq_Cnt => X"0943", wavegenCLK => CLK, signed(WaveOut) => WaveE5); --2371, 659,25 Hz Note_F5: Wave_Generator-Port-Map (Trigger => O5(5), Freq_Cnt => X"08Be", wavegenCLK => CLK, signed(WaveOut) => WaveF5); ---2238, 698,46 Hz Note_Fs5: Wave_Generator-Port-Map (Trigger => O5(6), Freq_Cnt => X"0840", wavegenCLK => CLK, signed(WaveOut) => WaveFs5);--2112, 739,99 Hz Note_G5: Wave_Generator-Port-Map (Trigger => O5(7), Freq_Cnt => X"07CA", wavegenCLK => CLK, signed(WaveOut) => WaveG5); ---1994, 783,99 Hz Note_Gs5: Wave_Generator-Port-Map (Trigger => O5(8), Freq_Cnt => X"075A", wavegenCLK => CLK, signed(WaveOut) => WaveGs5);--1882, 830,61 Hz Note_A5: Wave_Generator-Port-Map (Trigger => O5(9), Freq_Cnt => X"06F0", wavegenCLK => CLK, signed(WaveOut) => WaveA5); --1776, 880,00 Hz Note_As5: Wave_Generator-Port-Map (Trigger => O5(10), Freq_Cnt => X"068C", wavegenCLK => CLK, signed(WaveOut) => WaveAs5);--1676, 932,33 Hz Note_B5: Wave_Generator-Port-Map (Trigger => O5(11), Freq_Cnt => X"062E", wavegenCLK => CLK, signed(WaveOut) => WaveB5); --1582, 987,77 Hz Note_C6: Wave_Generator-Port-Map (Trigger => O5(12), Freq_Cnt => X"05D6", wavegenCLK => CLK, signed(WaveOut) => WaveC6); --1494, 1046,5 Hz ------------ Auswahllogik beachten------------ C4 <= "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 <= C4 + Cs4 + D4 + Ds4; cntE4 <= C4 + Cs4 + D4 + Ds4 + E4; cntF4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4; cntFs4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4; cntG4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4; cntGs4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4; cntA4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4; cntAs4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4; cntB4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4; cntC5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5; cntCs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5; cntD5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5; cntDs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5; cntE5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5; cntF5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5; cntFs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5; cntG5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5; cntGs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5 + Gs5; cntA5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5 + Gs5 + A5; cntAs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5 + Gs5 + A5 + As5; cntB5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5 + Gs5 + A5 + As5 + B5; cntC6 <= 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; Auswahl: Prozess (WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, As WaveFs5, WaveG5, Wave5Gs5 WaveFs5, WaveG5, Wave5Gs5 WaveB5, WaveC6) beginnen wenn (cntC6 = "00000") dann --------------- wenn keine Signale erzeugt werden Wave0 <= "0000000000"; Welle1 <= "00000000000"; Welle2 <= "00000000000"; Welle3 <= "00000000000"; else if (O4(0) = '1') then ------------------- Hinweis C4 gespielt Wave0 Wave0 Wave1 error Wave0 Wave1 Wave2 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 < = WelleC6; Welle1 <= "00000000000"; Welle2 <= "00000000000"; Welle3 Welle1 <= WelleC6; Welle2 <= "00000000000"; Welle3 Welle2 <= WelleC6; Wave3 Wave3-Fehler Wave1 <= "0000000000"; Welle2 <= "00000000000"; Welle3 Welle2 <= "0000000000"; Wave3 Wave3-Fehler <= '1'; Endfall; Ende, wenn; Ende, wenn; Prozess beenden; ------------- Sinuswellenaddierer-------- WaveSum <= STD_LOGIC_VECTOR(Wave0 + Wave1 + Wave2 + Wave3); --------- Sinuswelle für PWM positiv machen ---------------------- positivWaveSum <= nicht WaveSum(9) & WaveSum(8 bis 0); -------------PWM-Generator--------------------- process(CLK) --Variablenzähler: unsigned (1 downto 0):= to_unsigned(0, 2); begin if (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 Ausgabe <= '1'; else Ausgabe <= '0'; end if; PWM <= PWM + 1; ping_length <= unsigned(positiveWaveSum); --end if, end if, end process, end Behavioral;

4 Note SelectorDer schwierigste Teil dieses Projekts besteht darin, nur vier Frequenzen auszuwählen. Wir haben es mit einer ganzen Menge IF-Anweisungen gemacht und Signale anstelle von Variablen verwendet, damit der Prozess simuliert und debuggt werden kann. Wir haben andere Methoden mit Variablen und FOR-Schleifen ausprobiert, sind aber auf Laufzeitfehler gestoßen. Also haben wir am Ende beschlossen, dass wir es in Ruhe lassen, wenn es funktioniert. Reparieren Sie nicht, was nicht gebrochener Amirit ist?

Die vier Ausgangswellen sind mit Wave0, Wave1, Wave2, Wave3 gekennzeichnet – diese werden zusammengefügt, um den endgültigen Ausgang zu bilden.

Wenn Sie sich den Code ansehen, sehen Sie eine Reihe von Signalen mit der Bezeichnung C4, Cs4, D4, Ds4 usw. Dies sind 5-Bit-Signale, die den entsprechenden Trigger von O4 (Oktave 4) oder O5 (Oktave 5) nehmen und daraus machen 5-Bit zum Hinzufügen.

Als nächstes stellen die Variablen cntC4, cntCs4 usw. dar, wie viele Noten tiefer als die Zielnote gespielt wurden, einschließlich der Zielnote. Wenn beispielsweise C4, E4, G4, A#4 und D5 gespielt werden (C9-Akkord), ist cntC4 1, cntE4 ist 2, cntG4 ist 3 usw.

Wenn dann eine Note gespielt wird, wird der Zähler für die Zielnote überprüft, um zu sehen, wo das Notensignal angeschlossen werden soll. Wenn zum Beispiel die Note D5 gespielt wird (was bedeutet, dass O5(2) hoch ist) und cntD5 3 ist, werden derzeit 3 Noten gespielt, mit 2 Noten tiefer als D5, also werden wir waveD5 an Wave2 (die dritte Welle Signalzählung von Wave0). Wenn cntD5 alternativ 5 ist, werden derzeit 5 Noten gespielt, mit 4 Noten tiefer als D5, also lassen wir waveD5 einfach hängen und machen nichts damit.

Die IF-Anweisungen werden dann wiederholt, um die Fälle für alle 25 Noten abzudecken.

Amplitudenaddierer

Nachdem die niedrigsten 4 Wellen ausgewählt wurden, müssen wir sie zusammenzählen. Der Grund, warum wir nur vier Noten zusammenfügen, liegt darin, dass die PWM-Idee, die wir für unsere Ausgabe verwenden, nur eine bestimmte Auflösung haben kann, bis die PWM zu langsam läuft und der Lautsprecher beginnt, die PWM-Rechteckwelle aufzunehmen. Wenn wir beispielsweise eine Auflösung von 8192 (13 Bit) verwenden, muss jeder dieser 8192 Punkte einer steigenden Flanke des Onboard-Takts entsprechen. Also 100MHz / 8192 = 12,2kHz, was gut im Bereich des menschlichen Gehörs liegt.

Die eigentliche Addition der Amplituden ist super einfach, man muss nur sicherstellen, dass es wirklich schnell laufen kann.

PWM-Ausgang

Das Tastverhältnis der PWM repräsentiert die Amplitude unserer Ausgangswelle zu diesem Zeitpunkt. Wenn wir beispielsweise einen Amplitudenbereich von 0 bis 128 haben, wäre 0 ein 0%-Arbeitszyklus, 64 wäre 50%, 128 wäre 100% usw. Diese PWM läuft extrem schnell (unsere ist 97,6 kHz), so schnell, dass der Lautsprecher die einzelnen Rechteckwellen nicht erkennt und stattdessen auf die durchschnittliche Spannung schaut, wodurch unser „analoges“Signal entsteht.

Einschränkungsdatei

Möglicherweise haben Sie Ihre Hardware anders angeschlossen, stellen Sie also nur sicher, dass die Einschränkungsdatei übereinstimmt.

Schritt 5: Code-Downloads

Unten ist der Code, sowohl im.txt-Format als auch im.vhd-Format für Vivado. Wave_Generator ist das Untermodul des Wellengenerators und Two_Octave_Synth ist das oberste Modul mit allem anderen.

Empfohlen: