Innehållsförteckning:

Basys3 FPGA Digital Audio Synthesizer: 5 steg
Basys3 FPGA Digital Audio Synthesizer: 5 steg

Video: Basys3 FPGA Digital Audio Synthesizer: 5 steg

Video: Basys3 FPGA Digital Audio Synthesizer: 5 steg
Video: 1-bit FPGA DAC working! Playing Music via the serial port! RISCV FPGA 2024, Juli
Anonim
Image
Image
Basys3 FPGA Digital Audio Synthesizer
Basys3 FPGA Digital Audio Synthesizer
Basys3 FPGA Digital Audio Synthesizer
Basys3 FPGA Digital Audio Synthesizer

Denna digitala sinusvågs tangentbordssynthesizer tar användarinmatningar via en serie momentana switchar som läggs ut som ett tangentbord och matar ut en ljudvåg genom en högtalare. Baserat på användarinmatningar kommer enheten att generera sinusvågor med olika frekvenser från C4 till C6. Användaren kan mata in anteckningar från C4 upp till C6 (25 noter totalt) och upp till fyra tangenter samtidigt - om fler än fyra tangenter trycks, spelas de fyra lägsta tonerna.

Detta projekt gjordes av Ryan Morris och Mavis Tsoi för vår Cal Poly CPE 133 Digital Design -klass:)

Steg 1: Teori

Ett FPGA -kort kan bara mata ut digitala signaler. Med andra ord kan den bara producera en hög (3,3V) spänning eller en låg (0V) spänning. Emellertid är ljudsignaler analoga och kan ha oändligt många steg i spänning. För att komma runt detta kommer vi att använda en PWM -signal (pulsbreddsmodulering) för att emulera en analog våg. Om du inte vet vad PWM är, kolla in det här:

Steg 2: Ingredienser och verktyg

  • Dator med Vivado installerad
  • Vi kommer att använda Vivado version 2017.2
  • Basys3 FPGA Board
  • 25 SPDT -gränslägesbrytare (vi använde dessa)
  • 30 bygelkablar (ena änden, den andra änden spelar ingen roll), 12 tum
  • Avbitartång
  • Wire strippers
  • Reservtråd för lödning
  • Harts-kärnlödning
  • Lödkolv
  • ¼”kvinnligt ljuduttag
  • Förstärkare/högtalare
  • Något att montera omkopplarna på (vi använde protoboard + trälåda)

Steg 3: Kabeldragning och hårdvaruinstallation

Kabeldragning och hårdvara
Kabeldragning och hårdvara
Kabeldragning och hårdvara
Kabeldragning och hårdvara
Kabeldragning och hårdvara
Kabeldragning och hårdvara

system arkitektur

Se figur 1: 25 tillgängliga ingångar → Basys3 -kort → förstärkare och högtalare.

Produktion

Se figur 2: Basys3 -kort → 1/2 kvinnligt ljuduttag → högtalare (med förstärkare)

Inmatning

PMOD -anslutningarna på Basys3 -kortet måste vara anslutna till jord för att se en låg ingång och fungerar inte korrekt om de lämnas som en öppen krets. På grund av detta måste vi använda SPDT -switchar för alla våra anteckningsnycklar. En SPDT -switch tillåter i princip användaren att växla mellan kretsar när den trycks in, så vi kommer att använda dem som våra "knappar" för att mata in låga (0V) eller höga (3.3V) signaler till Basys3 -kortet.

Varje omkopplare har NO (normalt öppnad) terminal ansluten till 3,3V, NC (normalt stängd) terminal ansluten till GND och COM (gemensam) terminal ansluten till FPGA -ingången. Se figur 3.

Eftersom vi har 25 gränslägesbrytare delar de alla en gemensam 3.3V -linje och en gemensam GND -linje. Sedan kommer signalledningen från varje gränslägesbrytare att samlas i grupper om 8 och anslutas till pmod -anslutningarna på Basys3 -kortet med hjälp av dragkedjiga trådar för att minimera den monumentala röra vi kommer att göra. Se figur 4 eller ett exempel på de första åtta tangenterna.

Steg 4: VHDL -inställning (Vivado)

VHDL -inställning (Vivado)
VHDL -inställning (Vivado)
VHDL -inställning (Vivado)
VHDL -inställning (Vivado)

Sinusvågsgeneratorn och PWM -generatorn testades först för att se till att vårt koncept fungerade, sedan integrerades ingångsbegränsaren och amplitudadderaren/växlaren. Detaljer om funktionen och I/O för varje processblock är som visas i figuren. Koden visas nedan, men bifogas även som VHD- och txt -filer. Om det finns avvikelser, gå med VHD -filerna.

BTW: vi borde förmodligen ha gjort våra rader kortare men kodinbäddning på Instructables visade sig också vara ganska irriterande att hantera, så avståndet är inte det största och det finns ingen syntaxmarkering. Om du har Vivado och vill följa koden rekommenderar vi starkt att du bara laddar ner filen.

Låt oss först titta på Sine Wave Generator -modulen.

bibliotek IEEE; använd IEEE. STD_LOGIC_1164. ALL; använd IEEE. NUMERIC_STD. ALL; enhet Wave_Generator är Port (Trigger: i STD_LOGIC; - Tangenttryckning Freq_Cnt: i STD_LOGIC_VECTOR (15 ner till 0); - Räknarvärde = 100MHz / (Notera Frekvens*64 divisioner av Sine Wave) (runda till närmaste nummer) - döpt om från Freq wavegenCLK: i STD_LOGIC; - Basys3 100MHz CLK WaveOut: ut STD_LOGIC_VECTOR (9 ner till 0)); - Signerad amplitud för vågänd Wave_Generator; arkitektur Beteende för Wave_Generator är signal i: heltalsområde 0 till 64: = 0; -index för amplitudminnesbanks typ memory_type är array (0 till 63) med heltalsområdet -64 till 63; - skapa minnesbank (ROM) för att hålla amplitudvärden- undrar detta RAM eller ROM … signalamplitud: 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); - amplitudminnesbank för sinusvåg börjar process (wavegenCLK, Trigger) variabel räknare: osignerad (15 ner till 0): = to_unsigned (0, 16); - klockavdelningsräknare, bytt namn från count1 börjar om (rising_edge (wavegenCLK)) sedan om (Trigger = '1') sedan- tangenten trycks in counter: = counter + 1; om (räknare = osignerad (Freq_Cnt)) då - Freq_Cnt = 100Mhz / (notera frekvens * 64 divisioner av sinusvågen) - återställ räknaren och tilldela amplituddata till utmatningsräknaren: = to_unsigned (0, 16); WaveOut <= STD_LOGIC_VECTOR (to_signed (amplitud (i), 10)); - öka i för nästa läsning i <= i + 1; - återställ i om en sinusvåg har slutförts om (i = 63) då i <= 0; sluta om; sluta om; - (räknare = osignerad (Freq_Cnt)) else- tangenten är inte nedtryckt- återställ utmatning, amplitudindex och räknare WaveOut <= "0000000000"; i <= 0; räknare: = to_unsigned (0, 16); --output Amplitude = -64 när ingen ton spelas slutar om; - (Trigger = '1') slut om; - (stigande_kant (CLK)) slutprocess; slut Beteende;

Vi kommer att generera en digital sinusvåg i Basys3 med hjälp av den interna klockan och en ROM. Denna ROM kommer att lagra 64 värden som representerar 64 amplituder på en sinusvåg. Se figur 1. De 64 värden vi använder efterliknar en sinusvåg med ganska bra upplösning.

Med hjälp av den interna klockan räknar vi till ett värde som representerar klockhastigheten dividerat med frekvensen för den våg vi vill ha och 64: Clk div = 100MHz / (Freq * 64) Varje gång vår räknare når det värdet ringer vi ett nummer från ROM -skivan och skicka ut den från vår våggeneratormodul. Frekvensen för vår våg beror på hur snabbt vi kallar dessa amplituder.

Vi kommer att ha 25 delmoduler, var och en associerad med en frekvens/not.

Här är resten av koden som kallar Sine Wave Generator -modulerna:

bibliotek IEEE; använd IEEE. STD_LOGIC_1164. ALL; använd IEEE. NUMERIC_STD. ALL; entitet Two_Octave_Synth är Port (CLK: i STD_LOGIC; O4: i STD_LOGIC_VECTOR (11 ner till 0); O5: i STD_LOGIC_VECTOR (12 ner till 0); output: ut STD_LOGIC); avsluta Two_Octave_Synth; arkitektur Beteende för Two_Octave_Synth är komponenten Wave_Generator är Port (Trigger: i STD_LOGIC; Freq_Cnt: i STD_LOGIC_VECTOR (15 ner till 0); wavegenCLK: i STD_LOGIC; WaveOut: ut STD_LOGIC_VECTOR (9 nedåt 0)); slutkomponent; --------------------------- utsignaler från våggenerator ------------------ ----- signal WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, Wave5, Wave5, Wave5, Wave5 WaveAs5, WaveB5, WaveC6: signerad (9 ner till 0); -------------------------------- för anteckningsval logik -------------- ------ 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: osignerad (4 ner till 0); signal cntC4, cntCs4, cntD4, cntDs4, cntE4, cntF4, cntFs4, cntG4, cntGs4, cntA4, cntAs4, cntB4, cntC5, cntCs5, cntD5, cnt5s, cnt5, cnt5, cnt5, cnt5, cnt5s: osignerad (4 ner till 0); signalfel: STD_LOGIC; ----------------------------------- för att lägga till sinusvågor ----------- --------------- signal Wave0, Wave1, Wave2, Wave3: signerad (9 ner till 0); -signaler från Wave Generator-modulens utsignal WaveSum: STD_LOGIC_VECTOR (9 ner till 0); -signal för summerade sinusvågor (2: s komplimang -512 till 511) signal positiveWaveSum: STD_LOGIC_VECTOR (9 ner till 0); --avsatt 0 till 1023, för användning i PWM-generator ----------------------------------- för att generera 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: osignerad (9 ner till 0): = to_unsigned (0, 10); börja Note_C4: Wave_Generator -portkarta (Trigger => O4 (0), Freq_Cnt => X "1755", wavegenCLK => CLK, signerad (WaveOut) => WaveC4); --5973, 261,63 Hz Note_Cs4: Wave_Generator-portkarta (Trigger => O4 (1), Freq_Cnt => X "1606", wavegenCLK => CLK, signerad (WaveOut) => WaveCs4);-5638, 277,18 Hz Note_D4: Wave_Generator -portkarta (Trigger => O4 (2), Freq_Cnt => X "14C9", wavegenCLK => CLK, signerad (WaveOut) => WaveD4); --5321, 293,66 Hz Note_Ds4: Wave_Generator-portkarta (Trigger => O4 (3), Freq_Cnt => X "139F", wavegenCLK => CLK, signerad (WaveOut) => WaveDs4);-5023, 311,13 Hz Note_E4: Wave_Generator -portkarta (Trigger => O4 (4), Freq_Cnt => X "1285", wavegenCLK => CLK, signerad (WaveOut) => WaveE4); --4741, 329,63 Hz Obs_F4: Wave_Generator-portkarta (Trigger => O4 (5), Freq_Cnt => X "117B", wavegenCLK => CLK, signerad (WaveOut) => WaveF4); --4475, 349,23 Hz Note_Fs4: Wave_Generator-portkarta (Trigger => O4 (6), Freq_Cnt => X "1080", wavegenCLK => CLK, signerad (WaveOut) => WaveFs4);-4224, 369,99 Hz Note_G4: Wave_Generator -portkarta (Trigger => O4 (7), Freq_Cnt => X "0F92", wavegenCLK => CLK, signerad (WaveOut) => WaveG4); --3986, 392,00 Hz Note_Gs4: Wave_Generator-portkarta (Trigger => O4 (8), Freq_Cnt => X "0EB3", wavegenCLK => CLK, signerad (WaveOut) => WaveGs4);-3763, 415,30 Hz Not_A4: Wave_Generator -portkarta (Trigger => O4 (9), Freq_Cnt => X "0DE0", wavegenCLK => CLK, signerad (WaveOut) => WaveA4); --3552, 440,00 Hz Note_As4: Wave_Generator-portkarta (Trigger => O4 (10), Freq_Cnt => X "0D18", wavegenCLK => CLK, signerad (WaveOut) => WaveAs4);-3352, 466,16 Hz Note_B4: Wave_Generator -portkarta (Trigger => O4 (11), Freq_Cnt => X "0C5C", wavegenCLK => CLK, signerad (WaveOut) => WaveB4); --3164, 493,88 Hz -------------------------------------------- ---------------------------------------------------------- --------------------------- Not_C5: Wave_Generator-portkarta (Trigger => O5 (0), Freq_Cnt => X "0BAB", wavegenCLK => CLK, signerad (WaveOut) => WaveC5); --2987, 523,25 Hz Note_Cs5: Wave_Generator-portkarta (Trigger => O5 (1), Freq_Cnt => X "0B03", wavegenCLK => CLK, signerad (WaveOut) => WaveCs5);-2819, 554,37 Hz Obs_D5: Wave_Generator -portkarta (Trigger => O5 (2), Freq_Cnt => X "0A65", wavegenCLK => CLK, signerad (WaveOut) => WaveD5); --2661, 587,33 Hz Note_Ds5: Wave_Generator-portkarta (Trigger => O5 (3), Freq_Cnt => X "09D0", wavegenCLK => CLK, signerad (WaveOut) => WaveDs5);-2512, 622,25 Hz Note_E5: Wave_Generator -portkarta (Trigger => O5 (4), Freq_Cnt => X "0943", wavegenCLK => CLK, signerad (WaveOut) => WaveE5); --2371, 659,25 Hz Not_F5: Wave_Generator-portkarta (Trigger => O5 (5), Freq_Cnt => X "08Be", wavegenCLK => CLK, signerad (WaveOut) => WaveF5); --2238, 698,46 Hz Note_Fs5: Wave_Generator-portkarta (Trigger => O5 (6), Freq_Cnt => X "0840", wavegenCLK => CLK, signerad (WaveOut) => WaveFs5);-2112, 739,99 Hz Note_G5: Wave_Generator -portkarta (Trigger => O5 (7), Freq_Cnt => X "07CA", wavegenCLK => CLK, signerad (WaveOut) => WaveG5); --1994, 783,99 Hz Note_Gs5: Wave_Generator-portkarta (Trigger => O5 (8), Freq_Cnt => X "075A", wavegenCLK => CLK, signerad (WaveOut) => WaveGs5);-1882, 830,61 Hz Obs_A5: Wave_Generator -portkarta (Trigger => O5 (9), Freq_Cnt => X "06F0", wavegenCLK => CLK, signerad (WaveOut) => WaveA5); --1776, 880,00 Hz Note_As5: Wave_Generator-portkarta (Trigger => O5 (10), Freq_Cnt => X "068C", wavegenCLK => CLK, signerad (WaveOut) => WaveAs5);-1676, 932,33 Hz Note_B5: Wave_Generator -portkarta (Trigger => O5 (11), Freq_Cnt => X "062E", wavegenCLK => CLK, signerad (WaveOut) => WaveB5); --1582, 987,77 Hz Not_C6: Wave_Generator-portkarta (Trigger => O5 (12), Freq_Cnt => X "05D6", wavegenCLK => CLK, signerad (WaveOut) => WaveC6); --1494, 1046,5 Hz ------------ noteringsvallogik ------------ 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; Urval: process (WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, Wave5, Wave5, Wave5, Wave5, Wave5, Wave5 WaveB5, WaveC6) börjar om (cntC6 = "00000") sedan --------------- om inga signaler genereras Wave0 <= "0000000000"; Wave1 <= "0000000000"; Wave2 <= "0000000000"; Wave3 <= "0000000000"; annars om (O4 (0) = '1') då ------------------- notera C4 spelade Wave0 Wave0 Wave1 fel Wave0 Wave1 Wave2 fel Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave2 Wave2 Wave2 Wave2 Wave Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave1 Wave2 Wave3 Wave0 Wave1 Wave2 Wave3 fel Wave0 Wave2 Wave2 Wave2 Wave2 Wave2 Wave = WaveC6; Wave1 <= "0000000000"; Wave2 <= "0000000000"; Wave3 Wave1 <= WaveC6; Wave2 <= "0000000000"; Wave3 Wave2 <= WaveC6; Wave3 Wave3 -fel Wave1 <= "0000000000"; Wave2 <= "0000000000"; Wave3 Wave2 <= "0000000000"; Wave3 Wave3 -fel <= '1'; slutfall; sluta om; sluta om; avsluta process; ------------- sinusvågadderare -------------------- WaveSum <= STD_LOGIC_VECTOR (Wave0 + Wave1 + Wave2 + Wave3); --------- gör sinusvåg positivt för pwm --------------------- positiveWaveSum <= inte WaveSum (9) & WaveSum (8 ner till 0); ------------- PWM generator --------------------- process (CLK)-variabelt antal: osignerat (1 ner till 0): = to_unsigned (0, 2); börja om (rising_edge (CLK)) sedan --count: = count + 1; --if (count = to_unsigned (4, 2)) then --count: = to_unsigned (0, 2); --if (PWM = to_ if (PWM <ping_length) sedan output <= '1'; else output <= '0'; end if; PWM <= PWM + 1; ping_length <= unsigned (positiveWaveSum); --end if; avsluta om; avsluta process; avsluta Beteende;

4 Not Selector Den svåraste delen av detta projekt är att välja bara fyra frekvenser. Vi gjorde det med en hel del IF -uttalanden, och vi använde signaler istället för variabler så att processen kan simuleras och felsökas. Vi försökte andra metoder med variabler och FOR-slingor, men stötte på körtidsfel. Så till slut bestämde vi oss för att om det fungerar lämnar vi det ifred. Fixar du inte det som inte är trasig amirit?

De fyra utgångsvågorna är märkta Wave0, Wave1, Wave2, Wave3 - det här är vad som kommer att läggas ihop för att bilda den slutliga utgången.

Om du tittar på koden ser du ett gäng signaler märkta C4, Cs4, D4, Ds4, etc. Dessa är 5-bitars signaler som tar motsvarande trigger från O4 (oktav 4) eller O5 (oktav 5) och gör dem 5-bitars för att lägga till.

Därefter representerar variablerna cntC4, cntCs4, etc hur många toner som är lägre än målnoten, inklusive målnoten. Till exempel, om C4, E4, G4, A#4 och D5 spelas (C9 -ackord) kommer cntC4 att vara 1, cntE4 blir 2, cntG4 kommer att vara 3, etc.

Sedan, när en ton spelas, kommer räkningen för målnoten att undersökas för att se var du ska ansluta notsignalen till. Till exempel, om D5 -ton spelas (vilket betyder att O5 (2) är hög) och cntD5 är 3, så spelas det för närvarande 3 noter, med 2 toner lägre än D5, så vi kommer att koppla waveD5 till Wave2 (den tredje vågen signalräkning från Wave0). Alternativt, om cntD5 är 5, spelas det för närvarande 5 noter, med 4 toner lägre än D5, så vi kommer bara att låta waveD5 hänga och inte göra någonting med det.

IF -uttalandena upprepas sedan för att täcka fallen för alla 25 anteckningarna.

Amplitude Adder

Efter att de lägsta 4 vågorna har valts måste vi lägga ihop dem. Anledningen till att vi bara kommer att lägga till fyra anteckningar är att PWM -idén vi använder för vår produktion bara kan ha en viss upplösning tills PWM körs för långsamt och högtalaren kommer att börja plocka upp PWM -fyrkantvåg. Om vi till exempel skulle använda en upplösning på 8192 (13 bitar) måste var och en av dessa 8192 punkter motsvara en stigande kant på den inbyggda klockan. Så, 100MHz / 8192 = 12,2kHz, vilket ligger långt inom området för mänsklig hörsel.

Själva tillägget av amplituderna är superenkelt, du måste bara se till att det kan köra riktigt snabbt.

PWM -utgång

PWM: s arbetscykel kommer att representera amplituden för vår utgångsvåg i det ögonblicket. Till exempel, om vi har ett amplitudintervall på 0 till 128, skulle 0 vara en 0%arbetscykel, 64 skulle vara 50%, 128 skulle vara 100%, etc. Denna PWM körs extremt snabbt (vårt är 97,6 kHz), så snabbt att högtalaren inte känner igen de enskilda kvadratiska vågorna och istället tittar på medelspänningen och skapar vår "analoga" signal.

Begränsningsfil

Du kanske har anslutit din maskinvara annorlunda, så se bara till att begränsningsfilen matchar.

Steg 5: Kodnedladdningar

Nedan finns koden, både i.txt -format och.vhd för Vivado. Wave_Generator är våggeneratorundermodulen och Two_Octave_Synth är den översta modulen med allt annat.

Rekommenderad: