Índice:
- Etapa 1: Teoria
- Etapa 2: Ingredientes e ferramentas
- Etapa 3: Instalação de fiação e hardware
- Etapa 4: configuração de VHDL (Vivado)
- Etapa 5: Downloads de código
Vídeo: Sintetizador de áudio digital Basys3 FPGA: 5 etapas
2024 Autor: John Day | [email protected]. Última modificação: 2024-01-30 11:39
Este sintetizador de teclado digital de onda senoidal recebe as entradas do usuário por meio de uma série de interruptores momentâneos dispostos como um teclado e produz uma onda de áudio por meio de um alto-falante. Com base nas entradas do usuário, o dispositivo irá gerar ondas senoidais de várias frequências de C4 a C6. O usuário pode inserir notas de C4 a C6 (total de 25 notas) e até quatro teclas ao mesmo tempo - se mais de quatro teclas forem pressionadas, os quatro tons mais baixos serão reproduzidos.
Este projeto foi realizado por Ryan Morris e Mavis Tsoi para nossa aula de design digital Cal Poly CPE 133:)
Etapa 1: Teoria
Uma placa FPGA pode emitir apenas sinais digitais. Em outras palavras, ele só pode produzir uma tensão alta (3,3 V) ou uma tensão baixa (0 V). No entanto, os sinais de áudio são analógicos e podem ter infinitos incrementos de voltagem. Para contornar isso, usaremos um sinal PWM (modulação por largura de pulso) para emular uma onda analógica. Se você não sabe o que é PWM, verifique:
Etapa 2: Ingredientes e ferramentas
- Computador com Vivado instalado
- Estaremos usando Vivado versão 2017.2
- Placa FPGA Basys3
- 25 Chaves de limite SPDT (nós as usamos)
- 30 fios de jumper (uma extremidade macho, a outra extremidade não importa), 12 polegadas
- Cortadores de arame
- Decapantes de arame
- Fio sobressalente para solda
- Solda com núcleo de resina
- Ferro de solda
- Entrada de áudio fêmea de ¼”
- Amplificador / alto-falante
- Algo para montar os interruptores (usamos protoboard + caixa de madeira)
Etapa 3: Instalação de fiação e hardware
Arquitetura do Sistema
Veja a Figura 1: 25 entradas disponíveis → Placa Basys3 → amplificador e alto-falante.
Saída
Veja a Figura 2: Placa Basys3 → 1/2 Conector de áudio feminino → Alto-falante (com amplificador)
Entrada
As conexões pmod na placa Basys3 devem ser conectadas ao aterramento para ver uma entrada baixa e não funcionarão corretamente se deixadas como um circuito aberto. Por causa disso, temos que usar interruptores SPDT para todas as nossas teclas de nota. Uma chave SPDT basicamente permite que o usuário alterne entre os circuitos quando pressionada, então vamos usá-los como nossos “botões” para inserir sinais baixos (0V) ou altos (3,3V) para a placa Basys3.
Cada chave terá o terminal NO (normalmente aberto) conectado a 3,3 V, o terminal NC (normalmente fechado) conectado ao GND e o terminal COM (comum) conectado à entrada do FPGA. Veja a Figura 3.
Como temos 25 interruptores de limite, todos compartilharão uma linha comum de 3,3 V e uma linha GND comum. Em seguida, a linha de sinal de cada interruptor de limite será agrupada em grupos de 8 e ligada às conexões pmod na placa Basys3 usando fios de jumper zippable para minimizar a confusão monumental que faremos. Veja a Figura 4 ou um exemplo das primeiras oito chaves.
Etapa 4: configuração de VHDL (Vivado)
O gerador de onda senoidal e o gerador PWM foram testados primeiro para garantir que nosso conceito funcionasse, depois o limitador de entrada e o somador / deslocador de amplitude foram integrados. Os detalhes da função e E / S de cada bloco de processo são mostrados na Figura. O código é mostrado abaixo, mas também anexado como arquivos VHD e txt. Se houver discrepâncias, vá com os arquivos VHD.
BTW: provavelmente deveríamos ter encurtado nossas linhas, mas a incorporação de código em Instructables também se tornou um tanto chata de lidar, então o espaçamento não é o maior e não há realce de sintaxe. Se você possui o Vivado e gostaria de acompanhar o código, recomendamos que você apenas baixe o arquivo.
Primeiro, vamos dar uma olhada no módulo gerador de onda senoidal.
biblioteca IEEE; use IEEE. STD_LOGIC_1164. ALL; use IEEE. NUMERIC_STD. ALL; entidade Wave_Generator é a porta (Trigger: em STD_LOGIC; - Pressionar tecla Freq_Cnt: em STD_LOGIC_VECTOR (15 até 0); - Valor do contador = 100 MHz / (Nota de frequência * 64 divisões da onda sinusoidal) (arredondado para o número mais próximo) - renomeado de Freq wavegenCLK: em STD_LOGIC; - Basys3 100 MHz CLK WaveOut: fora STD_LOGIC_VECTOR (9 para 0)); - Amplitude sinalizada de final de onda Wave_Generator; arquitetura O comportamento de Wave_Generator é o sinal i: intervalo inteiro de 0 a 64: = 0; - o índice de amplitude do tipo de banco de memória memory_type é uma matriz (0 a 63) de intervalo inteiro de -64 a 63; - crie banco de memória (ROM) para manter os valores de amplitude - esta RAM ou ROM está apenas imaginando … amplitude do sinal: 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 de memória de amplitude para o processo de início da onda senoidal (wavegenCLK, Trigger) contador de variável: sem sinal (15 até 0): = to_unsigned (0, 16); - contador divisor de relógio, renomeado de count1 begin if (rise_edge (wavegenCLK)) then if (Trigger = '1') then - tecla é pressionada counter: = counter + 1; if (counter = unsigned (Freq_Cnt)) then - Freq_Cnt = 100Mhz / (note freq * 64 divisões da onda senoidal) - reinicie o contador e atribua os dados de amplitude ao contador de saída: = to_unsigned (0, 16); WaveOut <= STD_LOGIC_VECTOR (to_signed (amplitude (i), 10)); - incremento i para a próxima leitura i <= i + 1; - redefinir i se uma onda senoidal foi concluída se (i = 63) então i <= 0; fim se; fim se; - (contador = sem sinal (Freq_Cnt)) else - a tecla não foi pressionada - reinicializar a saída, o índice de amplitude e o contador WaveOut <= "0000000000"; i <= 0; contador: = to_unsigned (0, 16); --output Amplitude = -64 quando nenhuma nota é tocada termina se; - (Trigger = '1') fim se; - processo de finalização (crescente_edge (CLK)); fim Comportamental;
Iremos gerar uma onda senoidal digital no Basys3 usando o relógio interno e um ROM. Esta ROM armazenará 64 valores que representam 64 amplitudes em uma onda senoidal. Veja a Figura 1. Os 64 valores que usamos emulam uma onda senoidal com resolução muito boa.
Usando o relógio interno, contamos até um valor que representa a velocidade do relógio dividida pela frequência da onda que queremos e 64: Clk div = 100 MHz / (Freq * 64) Cada vez que nosso contador atinge esse valor, chamamos um número de a ROM e envie-a de nosso módulo gerador de ondas. A frequência de nossa onda dependerá de quão rápido chamamos essas amplitudes.
Teremos 25 submódulos, cada um associado a uma frequência / nota.
Aqui está o restante do código que chama os módulos do gerador de onda senoidal:
biblioteca IEEE; use IEEE. STD_LOGIC_1164. ALL; use IEEE. NUMERIC_STD. ALL; entidade Two_Octave_Synth é a porta (CLK: em STD_LOGIC; O4: em STD_LOGIC_VECTOR (11 para 0); O5: em STD_LOGIC_VECTOR (12 para 0); saída: para fora STD_LOGIC); fim Two_Octave_Synth; arquitetura Behavioral of Two_Octave_Synth é o componente Wave_Generator é Port (Trigger: in STD_LOGIC; Freq_Cnt: in STD_LOGIC_VECTOR (15 downto 0); wavegenCLK: in STD_LOGIC; WaveOut: out STD_LOGIC_VECTOR (9 downto 0)); componente final; --------------------------- sinais de saída do gerador de onda ------------------ ----- sinal WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, WaveGs5, WaveA5, WaveA5, WaveAs5, WaveB5, WaveC6: assinado (9 até 0); -------------------------------- para lógica de seleção de notas -------------- ------ sinal 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: sem sinal (4 até 0); sinal cntC4, cntCs4, cntD4, cntDs4, cntE4, cntF4, cntFs4, cntG4, cntGs4, cntA4, cntAs4, cntB4, cntC5, cntCs5, cntD5, cntDs5, cntE5, cntF5, cntFs5, cntG5, cntGs5, cntA5, cntAs5, cntB5, cntC6: sem sinal (4 até 0); erro de sinal: STD_LOGIC; ----------------------------------- para adicionar ondas senoidais ----------- --------------- sinal Wave0, Wave1, Wave2, Wave3: assinado (9 até 0); --sinais do sinal de saída do módulo WaveSum do módulo WaveSum: STD_LOGIC_VECTOR (9 até 0); --sinal para ondas senoidais somadas (complemento de 2 -512 a 511) sinal positivoWaveSum: STD_LOGIC_VECTOR (9 até 0); --unsigned 0 a 1023, para uso no gerador PWM ----------------------------------- para gerar PWM ------------------------------- sinal ping_length: sem sinal (9 para 0): = sem sinal (positiveWaveSum); --signal off_length: unsigned (6 downto 0): = to_unsigned (127, 7) - unsigned (WAVE); sinal PWM: não assinado (9 até 0): = até_insignado (0, 10); começar Note_C4: Mapa da porta Wave_Generator (Trigger => O4 (0), Freq_Cnt => X "1755", wavegenCLK => CLK, assinado (WaveOut) => WaveC4); --5973, 261,63 Hz Note_Cs4: Mapa da porta Wave_Generator (Trigger => O4 (1), Freq_Cnt => X "1606", wavegenCLK => CLK, assinado (WaveOut) => WaveCs4); - 5638, 277,18 Hz Note_D4: Mapa da porta Wave_Generator (Trigger => O4 (2), Freq_Cnt => X "14C9", wavegenCLK => CLK, assinado (WaveOut) => WaveD4); --5321, 293,66 Hz Note_Ds4: Mapa da porta Wave_Generator (Trigger => O4 (3), Freq_Cnt => X "139F", wavegenCLK => CLK, assinado (WaveOut) => WaveDs4); - 5023, 311,13 Hz Note_E4: Mapa da porta Wave_Generator (Trigger => O4 (4), Freq_Cnt => X "1285", wavegenCLK => CLK, assinado (WaveOut) => WaveE4); --4741, 329,63 Hz Note_F4: Mapa da porta Wave_Generator (Trigger => O4 (5), Freq_Cnt => X "117B", wavegenCLK => CLK, assinado (WaveOut) => WaveF4); - 4475, 349,23 Hz Note_Fs4: Mapa da porta Wave_Generator (Trigger => O4 (6), Freq_Cnt => X "1080", wavegenCLK => CLK, assinado (WaveOut) => WaveFs4); - 4224, 369,99 Hz Note_G4: Mapa da porta Wave_Generator (Trigger => O4 (7), Freq_Cnt => X "0F92", wavegenCLK => CLK, assinado (WaveOut) => WaveG4); --3986, 392,00 Hz Note_Gs4: Mapa da porta Wave_Generator (Trigger => O4 (8), Freq_Cnt => X "0EB3", wavegenCLK => CLK, assinado (WaveOut) => WaveGs4); - 3763, 415,30 Hz Note_A4: Mapa da porta Wave_Generator (Trigger => O4 (9), Freq_Cnt => X "0DE0", wavegenCLK => CLK, assinado (WaveOut) => WaveA4); --3552, 440,00 Hz Note_As4: Mapa da porta Wave_Generator (Trigger => O4 (10), Freq_Cnt => X "0D18", wavegenCLK => CLK, assinado (WaveOut) => WaveAs4); - 3352, 466,16 Hz Note_B4: Mapa da porta Wave_Generator (Trigger => O4 (11), Freq_Cnt => X "0C5C", wavegenCLK => CLK, assinado (WaveOut) => WaveB4); - 3164, 493,88 Hz -------------------------------------------- -------------------------------------------------- --------------------------- Note_C5: Mapa da porta Wave_Generator (Trigger => O5 (0), Freq_Cnt => X "0BAB", wavegenCLK => CLK, assinado (WaveOut) => WaveC5); --2987, 523,25 Hz Note_Cs5: Mapa da porta Wave_Generator (Trigger => O5 (1), Freq_Cnt => X "0B03", wavegenCLK => CLK, assinado (WaveOut) => WaveCs5); - 2819, 554,37 Hz Note_D5: Mapa da porta Wave_Generator (Trigger => O5 (2), Freq_Cnt => X "0A65", wavegenCLK => CLK, assinado (WaveOut) => WaveD5); --2661, 587,33 Hz Note_Ds5: Mapa da porta Wave_Generator (Trigger => O5 (3), Freq_Cnt => X "09D0", wavegenCLK => CLK, assinado (WaveOut) => WaveDs5); - 2512, 622,25 Hz Note_E5: Mapa da porta Wave_Generator (Trigger => O5 (4), Freq_Cnt => X "0943", wavegenCLK => CLK, assinado (WaveOut) => WaveE5); --2371, 659,25 Hz Note_F5: Mapa da porta Wave_Generator (Trigger => O5 (5), Freq_Cnt => X "08Be", wavegenCLK => CLK, assinado (WaveOut) => WaveF5); --2238, 698,46 Hz Note_Fs5: Mapa da porta Wave_Generator (Trigger => O5 (6), Freq_Cnt => X "0840", wavegenCLK => CLK, assinado (WaveOut) => WaveFs5); - 2112, 739,99 Hz Note_G5: Mapa da porta Wave_Generator (Trigger => O5 (7), Freq_Cnt => X "07CA", wavegenCLK => CLK, assinado (WaveOut) => WaveG5); - 1994, 783,99 Hz Note_Gs5: Mapa da porta Wave_Generator (Trigger => O5 (8), Freq_Cnt => X "075A", wavegenCLK => CLK, assinado (WaveOut) => WaveGs5); - 1882, 830,61 Hz Note_A5: Mapa da porta Wave_Generator (Trigger => O5 (9), Freq_Cnt => X "06F0", wavegenCLK => CLK, assinado (WaveOut) => WaveA5); --1776, 880,00 Hz Note_As5: Mapa da porta Wave_Generator (Trigger => O5 (10), Freq_Cnt => X "068C", wavegenCLK => CLK, assinado (WaveOut) => WaveAs5); - 1676, 932,33 Hz Note_B5: Mapa da porta Wave_Generator (Trigger => O5 (11), Freq_Cnt => X "062E", wavegenCLK => CLK, assinado (WaveOut) => WaveB5); --1582, 987,77 Hz Note_C6: Mapa da porta Wave_Generator (Trigger => O5 (12), Freq_Cnt => X "05D6", wavegenCLK => CLK, assinado (WaveOut) => WaveC6); --1494, 1046,5 Hz ------------ lógica de seleção de notas ------------ 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" e 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; Seleção: processo (WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, WaveGs5, WaveA WaveB5, WaveC6) começa se (cntC6 = "00000") então --------------- se nenhum sinal está sendo gerado Wave0 <= "0000000000"; Wave1 <= "0000000000"; Wave2 <= "0000000000"; Wave3 <= "0000000000"; else if (O4 (0) = '1') then ------------------- nota C4 reproduzida Onda0 Onda0 Onda1 erro Onda0 Onda1 Onda2 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Erro Onda2 Onda3 Erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Erro Onda2 Onda3 Erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 erro Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 Onda1 Onda2 Onda3 erro Onda0 < = WaveC6; Wave1 <= "0000000000"; Wave2 <= "0000000000"; Wave3 Wave1 <= WaveC6; Wave2 <= "0000000000"; Onda3 Onda2 <= OndaC6; Erro Wave3 Wave3 Wave1 <= "0000000000"; Wave2 <= "0000000000"; Wave3 Wave2 <= "0000000000"; Erro Wave3 Wave3 <= '1'; caso final; fim se; fim se; fim do processo; ------------- somador de onda senoidal -------------------- WaveSum <= STD_LOGIC_VECTOR (Onda0 + Onda1 + Onda2 + Onda3); --------- tornar a onda senoidal positiva para pwm --------------------- positiveWaveSum <= não WaveSum (9) & WaveSum (8 downto 0); ------------- gerador PWM --------------------- processo (CLK) --contagem variável: não assinado (1 até 0): = to_unsigned (0, 2); começar if (rise_edge (CLK)) then --count: = count + 1; --if (contagem = 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 Seletor de notasA parte mais complicada deste projeto é selecionar apenas quatro frequências. Fizemos isso com uma grande quantidade de instruções IF e usamos sinais em vez de variáveis para que o processo pudesse ser simulado e depurado. Tentamos outros métodos usando variáveis e loops FOR, mas encontramos erros em tempo de execução. Então, no final, decidimos que, se funcionar, vamos deixar pra lá. Não conserte o que não está quebrado?
As quatro ondas de saída são rotuladas Onda0, Onda1, Onda2, Onda3 - essas são as que serão somadas para formar a saída final.
Olhando para o código, você verá um monte de sinais rotulados C4, Cs4, D4, Ds4, etc. Estes são sinais de 5 bits que pegam o gatilho correspondente de O4 (oitava 4) ou O5 (oitava 5) e os tornam 5 bits para adicionar.
Em seguida, as variáveis cntC4, cntCs4, etc. representam quantas notas abaixo da nota alvo foram tocadas, incluindo a nota alvo. Por exemplo, se C4, E4, G4, A # 4 e D5 são tocados (acorde C9), cntC4 será 1, cntE4 será 2, cntG4 será 3, etc.
Então, sempre que uma nota é tocada, a contagem da nota alvo será examinada para ver onde conectar o sinal da nota. Por exemplo, se a nota D5 é tocada (o que significa O5 (2) é alto) e cntD5 é 3, então existem atualmente 3 notas sendo tocadas, com 2 notas mais baixas do que D5, então ligaremos a onda D5 à Wave2 (a terceira onda contagem de sinal de Wave0). Alternativamente, se cntD5 for 5, então existem atualmente 5 notas sendo tocadas, com 4 notas abaixo de D5, portanto, deixaremos o waveD5 suspenso e não faremos nada com ele.
As declarações IF são então repetidas para cobrir os casos de todas as 25 notas.
Amplitude Adder
Depois que as 4 ondas mais baixas são selecionadas, temos que adicioná-las. O motivo pelo qual adicionaremos apenas quatro notas juntas é porque a ideia do PWM que estamos usando para nossa saída pode ter apenas uma determinada resolução até que o PWM esteja lento demais e o alto-falante comece a captar a onda quadrada do PWM. Por exemplo, se formos usar uma resolução de 8192 (13 bits), cada um desses 8192 pontos deve corresponder a uma borda ascendente do relógio integrado. Portanto, 100 MHz / 8192 = 12,2 kHz, o que está bem dentro da faixa de audição humana.
A adição real das amplitudes é super simples, você só precisa ter certeza de que pode funcionar muito rápido.
Saída PWM
O ciclo de trabalho do PWM representará a amplitude de nossa onda de saída naquele instante. Por exemplo, se tivermos uma faixa de amplitude de 0 a 128, 0 seria um ciclo de trabalho de 0%, 64 seria 50%, 128 seria 100%, etc. Este PWM funcionará extremamente rápido (o nosso é 97,6 kHz), tão rápido que o alto-falante não reconhecerá as ondas quadradas individuais e, em vez disso, olhará para a voltagem média, criando nosso sinal “analógico”.
Arquivo de restrições
Você pode ter conectado seu hardware de maneira diferente, então certifique-se de que o arquivo de restrições corresponda.
Etapa 5: Downloads de código
Abaixo está o código, tanto no formato.txt quanto.vhd para Vivado. Wave_Generator é o submódulo gerador de ondas, e Two_Octave_Synth é o módulo superior com todo o resto.
Recomendado:
Oh Christmas Tree (Oh Tannenbaum) Com MakeyMakey no sintetizador de água: 7 etapas
Oh Christmas Tree (Oh Tannenbaum) Com MakeyMakey no sintetizador de água: Esta canção de natal é boa para brincar com o makeymakey no sintetizador de água. Você pode jogar com nove tons.Para o ambiente é bom ter um pouco de luz de natal :-) Divirta-se
Feliz aniversário no sintetizador de água com MakeyMakey e Scratch: 5 etapas
Feliz aniversário no sintetizador de água com MakeyMakey e Scratch: em vez de flores e canto, você pode construir esta instalação como uma grande surpresa para aniversários
Sintetizador de água com MakeyMakey e Scratch: 6 etapas (com imagens)
Sintetizador de água com MakeyMakey e Scratch: Usar o MakeyMakey para transformar diferentes materiais em interruptores ou botões e, assim, acionar movimentos ou sons no computador é um assunto fascinante. A pessoa aprende qual material conduz o impulso de corrente fraca e pode inventar e experimentar com i
Órgão / sintetizador analógico incrível usando apenas componentes discretos: 10 etapas (com imagens)
Órgão / sintetizador analógico incrível usando apenas componentes discretos: os sintetizadores analógicos são muito legais, mas também muito difíceis de fazer. Então, eu queria fazer um o mais simples possível, para que seu funcionamento fosse facilmente compreensível. Para que funcione, você precisa de alguns sub-circuitos básicos: Um oscilador simples com resis
Esteira DIY VR - Concurso Basys3 FPGA-Digilent: 3 etapas
DIY VR Treadmill- Basys3 FPGA-Digilent Contest: Você quer construir uma esteira VR na qual você possa executar seus aplicativos e jogos de desktop? Então você veio ao lugar certo! Nos jogos convencionais você usa o mouse e o teclado para interagir com o ambiente. Portanto, precisamos enviar o