Tabla de contenido:

Sintetizador de audio digital Basys3 FPGA: 5 pasos
Sintetizador de audio digital Basys3 FPGA: 5 pasos

Video: Sintetizador de audio digital Basys3 FPGA: 5 pasos

Video: Sintetizador de audio digital Basys3 FPGA: 5 pasos
Video: AMD Xilinx Arty A7, Artix 7 FPGA Evaluation Board - Getting Started 2024, Mes de julio
Anonim
Image
Image
Sintetizador de audio digital Basys3 FPGA
Sintetizador de audio digital Basys3 FPGA
Sintetizador de audio digital Basys3 FPGA
Sintetizador de audio digital Basys3 FPGA

Este sintetizador de teclado de onda sinusoidal digital tomará las entradas del usuario a través de una serie de interruptores momentáneos dispuestos como un teclado y emitirá una onda de audio a través de un altavoz. Según las entradas del usuario, el dispositivo generará ondas sinusoidales de varias frecuencias de C4 a C6. El usuario puede ingresar notas desde C4 hasta C6 (25 notas en total), y hasta cuatro teclas al mismo tiempo; si se presionan más de cuatro teclas, se reproducirán los cuatro tonos más bajos.

Este proyecto fue realizado por Ryan Morris y Mavis Tsoi para nuestra clase de diseño digital Cal Poly CPE 133:)

Paso 1: Teoría

Una placa FPGA solo puede emitir señales digitales. En otras palabras, solo puede producir un voltaje alto (3.3V) o un voltaje bajo (0V). Sin embargo, las señales de audio son analógicas y pueden tener infinitos incrementos de voltaje. Para evitar esto, usaremos una señal PWM (modulación de ancho de pulso) para emular una onda analógica. Si no sabe qué es PWM, consulte esto:

Paso 2: ingredientes y herramientas

  • Computadora con Vivado instalado
  • Usaremos Vivado versión 2017.2
  • Placa FPGA Basys3
  • 25 interruptores de límite SPDT (los usamos)
  • 30 cables de puente (un extremo macho, el otro extremo no importa), 12 pulgadas
  • Cortadores de alambre
  • Pelacables
  • Alambre de repuesto para soldar
  • Soldadura con núcleo de resina
  • Soldador
  • Conector de audio hembra de ¼”
  • Amplificador / altavoz
  • Algo para montar los interruptores (usamos protoboard + caja de madera)

Paso 3: Configuración de cableado y hardware

Configuración de cableado y hardware
Configuración de cableado y hardware
Configuración de cableado y hardware
Configuración de cableado y hardware
Configuración de cableado y hardware
Configuración de cableado y hardware

Arquitectura del sistema

Consulte la Figura 1: 25 entradas disponibles → Placa Basys3 → amplificador y altavoz.

Producción

Consulte la Figura 2: Placa Basys3 → Conector de audio hembra de 1/2 → Altavoz (con amplificador)

Aporte

Las conexiones pmod en la placa Basys3 deben estar conectadas a tierra para ver una entrada baja y no funcionarán correctamente si se dejan en circuito abierto. Debido a esto, tenemos que usar interruptores SPDT para todas nuestras teclas de notas. Un interruptor SPDT básicamente permite al usuario cambiar entre circuitos cuando se presiona, por lo que los usaremos como nuestros “botones” para ingresar señales bajas (0V) o altas (3.3V) a la placa Basys3.

Cada interruptor tendrá el terminal NO (normalmente abierto) conectado a 3.3V, el terminal NC (normalmente cerrado) conectado a GND y el terminal COM (común) conectado a la entrada FPGA. Ver figura 3.

Debido a que tenemos 25 interruptores de límite, todos compartirán una línea común de 3.3V y una línea GND común. Luego, la línea de señal de cada interruptor de límite se agrupará en grupos de 8 y se conectará a las conexiones pmod en la placa Basys3 utilizando cables de puente con cremallera para minimizar el desorden monumental que haremos. Consulte la Figura 4 o un ejemplo de las primeras ocho teclas.

Paso 4: Configuración de VHDL (Vivado)

Configuración de VHDL (Vivado)
Configuración de VHDL (Vivado)
Configuración de VHDL (Vivado)
Configuración de VHDL (Vivado)

Primero se probaron el generador de onda sinusoidal y el generador PWM para asegurarnos de que nuestro concepto funcionara, luego se integraron el limitador de entrada y el sumador / cambiador de amplitud. Los detalles de la función y E / S de cada bloque de proceso se muestran en la Figura. El código se muestra a continuación, pero también se adjunta como archivos VHD y txt. Si hay discrepancias, vaya con los archivos VHD.

Por cierto: probablemente deberíamos haber acortado nuestras líneas, pero la incrustación de código en Instructables también resultó ser bastante molesta de manejar, por lo que el espaciado no es el mayor y no hay resaltado de sintaxis. Si tiene Vivado y le gustaría seguir el código, le recomendamos que simplemente descargue el archivo.

Primero, veamos el módulo Sine Wave Generator.

biblioteca IEEE; use IEEE. STD_LOGIC_1164. ALL; use IEEE. NUMERIC_STD. ALL; La entidad Wave_Generator es Port (Trigger: en STD_LOGIC; - Pulsar la tecla Freq_Cnt: en STD_LOGIC_VECTOR (15 downto 0); - Valor del contador = 100MHz / (Note Frequency * 64 Divisions of Sine Wave) (redondeado al número más cercano) - renombrado de Freq wavegenCLK: en STD_LOGIC; - Basys3 100MHz CLK WaveOut: out STD_LOGIC_VECTOR (9 hacia abajo 0)); - Amplitud firmada del final de onda Wave_Generator; arquitectura El comportamiento de Wave_Generator es la señal i: rango entero de 0 a 64: = 0; - índice de amplitud tipo de banco de memoria memory_type es una matriz (0 a 63) de un rango de números enteros -64 a 63; - crear un banco de memoria (ROM) para contener valores de amplitud - ¿esta RAM o ROM simplemente se pregunta … amplitud de la señal: tipo_de_memoria: = (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 memoria de amplitud para el proceso de inicio de onda sinusoidal (wavegenCLK, Trigger) contador de variables: unsigned (15 downto 0): = to_unsigned (0, 16); - contador del divisor de reloj, renombrado de count1 begin if (rising_edge (wavegenCLK)) luego si (Trigger = '1') entonces - se presiona la tecla contador: = contador + 1; if (contador = unsigned (Freq_Cnt)) entonces - Freq_Cnt = 100Mhz / (note freq * 64 divisiones de la onda sinusoidal) - reinicie el contador y asigne datos de amplitud al contador de salida: = to_unsigned (0, 16); WaveOut <= STD_LOGIC_VECTOR (to_signed (amplitud (i), 10)); - incrementar i para la siguiente lectura i <= i + 1; - resetear i si se ha completado una onda sinusoidal si (i = 63) entonces i <= 0; terminara si; terminara si; - (contador = unsigned (Freq_Cnt)) else - la tecla no está presionada - restablece la salida, el índice de amplitud y el contador WaveOut <= "0000000000"; i <= 0; contador: = to_unsigned (0, 16); - Amplitud de salida = -64 cuando no se toca ninguna nota end if; - (Trigger = '1') end if; - (borde ascendente (CLK)) finaliza el proceso; end Behavioral;

Generaremos una onda sinusoidal digital en el Basys3 utilizando el reloj interno y una ROM. Esta ROM almacenará 64 valores que representan 64 amplitudes en una onda sinusoidal. Consulte la Figura 1. Los 64 valores que usamos emulan una onda sinusoidal con una resolución bastante buena.

Usando el reloj interno, contamos hasta un valor que representa la velocidad del reloj dividida por la frecuencia de la onda que queremos y 64: Clk div = 100MHz / (Freq * 64) Cada vez que nuestro contador alcanza ese valor, llamamos a un número de la ROM y enviarla desde nuestro módulo generador de ondas. La frecuencia de nuestra onda dependerá de qué tan rápido llamemos a estas amplitudes.

Tendremos 25 submódulos, cada uno asociado a una frecuencia / nota.

Aquí está el resto del código que llama a los módulos del generador de onda sinusoidal:

biblioteca IEEE; use IEEE. STD_LOGIC_1164. ALL; use IEEE. NUMERIC_STD. ALL; la entidad Two_Octave_Synth es el puerto (CLK: en STD_LOGIC; O4: en STD_LOGIC_VECTOR (11 hacia abajo a 0); O5: en STD_LOGIC_VECTOR (12 hacia abajo a 0); salida: hacia fuera STD_LOGIC); end Two_Octave_Synth; arquitectura El comportamiento de Two_Octave_Synth es el componente Wave_Generator es Port (Trigger: en STD_LOGIC; Freq_Cnt: en STD_LOGIC_VECTOR (15 downto 0); wavegenCLK: en STD_LOGIC; WaveOut: out STD_LOGIC_VECTOR (9 downto 0)); componente final; --------------------------- señales de salida del generador de ondas ------------------ ----- señal WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, WaveGs5, WaveA5, WaveAs5, WaveB5, WaveC6: firmado (9 hacia abajo a 0); -------------------------------- para la lógica de selección de notas -------------- ------ señal 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: sin signo (4 hacia abajo a 0); señal cntC4, cntCs4, cntD4, cntDs4, cntE4, cntF4, cntFs4, cntG4, cntGs4, cntA4, cntAs4, cntB4, cntC5, cntCs5, cntD5, cntDs5, cntG5, cntB5, cntDs5, cntE5, cntB5, cntF5, cntE5, cntG5: sin firmar (4 hacia abajo a 0); error de señal: STD_LOGIC; ----------------------------------- para agregar ondas sinusoidales ----------- --------------- señal Wave0, Wave1, Wave2, Wave3: firmado (9 hacia abajo a 0); - señales de la señal de salida del módulo generador de ondas WaveSum: STD_LOGIC_VECTOR (9 hacia abajo 0); --señal para ondas sinusoidales sumadas (complemento de 2 -512 a 511) señal positiveWaveSum: STD_LOGIC_VECTOR (9 hacia abajo a 0); - sin firmar de 0 a 1023, para usar en el generador PWM ----------------------------------- para generar PWM ------------------------------- señal ping_length: unsigned (9 downto 0): = unsigned (positiveWaveSum); --signal off_length: unsigned (6 downto 0): = to_unsigned (127, 7) - unsigned (WAVE); señal PWM: unsigned (9 downto 0): = to_unsigned (0, 10); comenzar Note_C4: Mapa de puertos Wave_Generator (Trigger => O4 (0), Freq_Cnt => X "1755", wavegenCLK => CLK, firmado (WaveOut) => WaveC4); --5973, 261,63 Hz Note_Cs4: mapa de puertos Wave_Generator (Trigger => O4 (1), Freq_Cnt => X "1606", wavegenCLK => CLK, firmado (WaveOut) => WaveCs4); - 5638, 277,18 Hz Note_D4: Mapa de puertos Wave_Generator (Trigger => O4 (2), Freq_Cnt => X "14C9", wavegenCLK => CLK, firmado (WaveOut) => WaveD4); --5321, 293,66 Hz Note_Ds4: mapa de puertos Wave_Generator (Trigger => O4 (3), Freq_Cnt => X "139F", wavegenCLK => CLK, firmado (WaveOut) => WaveDs4); - 5023, 311,13 Hz Note_E4: Mapa de puertos Wave_Generator (Trigger => O4 (4), Freq_Cnt => X "1285", wavegenCLK => CLK, firmado (WaveOut) => WaveE4); --4741, 329,63 Hz Note_F4: mapa de puertos Wave_Generator (Trigger => O4 (5), Freq_Cnt => X "117B", wavegenCLK => CLK, firmado (WaveOut) => WaveF4); --4475, 349,23 Hz Note_Fs4: mapa de puertos Wave_Generator (Trigger => O4 (6), Freq_Cnt => X "1080", wavegenCLK => CLK, firmado (WaveOut) => WaveFs4); - 4224, 369,99 Hz Note_G4: Mapa de puertos Wave_Generator (Trigger => O4 (7), Freq_Cnt => X "0F92", wavegenCLK => CLK, firmado (WaveOut) => WaveG4); --3986, 392,00 Hz Note_Gs4: mapa de puertos Wave_Generator (Trigger => O4 (8), Freq_Cnt => X "0EB3", wavegenCLK => CLK, firmado (WaveOut) => WaveGs4); - 3763, 415,30 Hz Note_A4: Mapa de puertos Wave_Generator (Trigger => O4 (9), Freq_Cnt => X "0DE0", wavegenCLK => CLK, firmado (WaveOut) => WaveA4); --3552, 440,00 Hz Note_As4: mapa de puertos Wave_Generator (Trigger => O4 (10), Freq_Cnt => X "0D18", wavegenCLK => CLK, firmado (WaveOut) => WaveAs4); - 3352, 466,16 Hz Note_B4: Mapa de puertos Wave_Generator (Trigger => O4 (11), Freq_Cnt => X "0C5C", wavegenCLK => CLK, firmado (WaveOut) => WaveB4); --3164, 493,88 Hz -------------------------------------------- -------------------------------------------------- --------------------------- Note_C5: Mapa de puertos Wave_Generator (Trigger => O5 (0), Freq_Cnt => X "0BAB", wavegenCLK => CLK, firmado (WaveOut) => WaveC5); --2987, 523,25 Hz Note_Cs5: mapa de puertos Wave_Generator (Trigger => O5 (1), Freq_Cnt => X "0B03", wavegenCLK => CLK, firmado (WaveOut) => WaveCs5); - 2819, 554,37 Hz Note_D5: Mapa de puertos Wave_Generator (Trigger => O5 (2), Freq_Cnt => X "0A65", wavegenCLK => CLK, firmado (WaveOut) => WaveD5); --2661, 587,33 Hz Note_Ds5: mapa de puertos Wave_Generator (Trigger => O5 (3), Freq_Cnt => X "09D0", wavegenCLK => CLK, firmado (WaveOut) => WaveDs5); - 2512, 622,25 Hz Note_E5: Mapa de puertos Wave_Generator (Trigger => O5 (4), Freq_Cnt => X "0943", wavegenCLK => CLK, firmado (WaveOut) => WaveE5); --2371, 659,25 Hz Note_F5: mapa de puertos Wave_Generator (Trigger => O5 (5), Freq_Cnt => X "08Be", wavegenCLK => CLK, firmado (WaveOut) => WaveF5); --2238, 698,46 Hz Note_Fs5: mapa de puertos Wave_Generator (Trigger => O5 (6), Freq_Cnt => X "0840", wavegenCLK => CLK, firmado (WaveOut) => WaveFs5); - 2112, 739,99 Hz Note_G5: Mapa de puertos Wave_Generator (Trigger => O5 (7), Freq_Cnt => X "07CA", wavegenCLK => CLK, firmado (WaveOut) => WaveG5); --1994, 783,99 Hz Note_Gs5: mapa de puertos Wave_Generator (Trigger => O5 (8), Freq_Cnt => X "075A", wavegenCLK => CLK, firmado (WaveOut) => WaveGs5); - 1882, 830,61 Hz Note_A5: Mapa de puertos Wave_Generator (Trigger => O5 (9), Freq_Cnt => X "06F0", wavegenCLK => CLK, firmado (WaveOut) => WaveA5); --1776, 880,00 Hz Note_As5: mapa de puertos Wave_Generator (Trigger => O5 (10), Freq_Cnt => X "068C", wavegenCLK => CLK, firmado (WaveOut) => WaveAs5); - 1676, 932,33 Hz Note_B5: Mapa de puertos Wave_Generator (Trigger => O5 (11), Freq_Cnt => X "062E", wavegenCLK => CLK, firmado (WaveOut) => WaveB5); --1582, 987,77 Hz Note_C6: mapa de puertos Wave_Generator (Trigger => O5 (12), Freq_Cnt => X "05D6", wavegenCLK => CLK, firmado (WaveOut) => WaveC6); --1494, 1046,5 Hz ------------ lógica de selección de nota ------------ C4 <= "0000" & O4 (0); Cs4 <= "0000" & O4 (1); D4 <= "0000" y O4 (2); Ds4 <= "0000" y O4 (3); E4 <= "0000" & O4 (4); F4 <= "0000" & O4 (5); Fs4 <= "0000" & O4 (6); G4 <= "0000" y O4 (7); Gs4 <= "0000" y O4 (8); A4 <= "0000" & O4 (9); As4 <= "0000" & O4 (10); B4 <= "0000" & O4 (11); C5 <= "0000" & O5 (0); Cs5 <= "0000" & O5 (1); D5 <= "0000" y O5 (2); Ds5 <= "0000" & O5 (3); E5 <= "0000" & O5 (4); F5 <= "0000" & O5 (5); Fs5 <= "0000" & O5 (6); G5 <= "0000" y O5 (7); Gs5 <= "0000" y O5 (8); A5 <= "0000" y O5 (9); As5 <= "0000" & O5 (10); B5 <= "0000" & O5 (11); C6 <= "0000" y 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; Selección: proceso (WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, WaveGs5, WaveFs5, WaveG5, WaveGs5, WaveFs5 WaveB5, WaveC6) comienzan si (cntC6 = "00000") luego --------------- si no se generan señales Wave0 <= "0000000000"; Wave1 <= "0000000000"; Wave2 <= "0000000000"; Wave3 <= "0000000000"; de lo contrario, si (O4 (0) = '1') entonces ------------------- nota C4 reproducida 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 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 < = WaveC6; Wave1 <= "0000000000"; Wave2 <= "0000000000"; Wave3 Wave1 <= WaveC6; Wave2 <= "0000000000"; Wave3 Wave2 <= WaveC6; Wave3 Wave3 error Wave1 <= "0000000000"; Wave2 <= "0000000000"; Wave3 Wave2 <= "0000000000"; Wave3 Error de Wave3 <= '1'; caso final; terminara si; terminara si; proceso finalizado; ------------- sumador de onda sinusoidal -------------------- WaveSum <= STD_LOGIC_VECTOR (Wave0 + Wave1 + Wave2 + Wave3); --------- hacer que la onda sinusoidal sea positiva para pwm --------------------- PositiveWaveSum <= no WaveSum (9) y WaveSum (8 downto 0); ------------- Generador de PWM --------------------- proceso (CLK) - recuento de variables: unsigned (1 downto 0): = to_unsigned (0, 2); comenzar si (borde_reciente (CLK)) luego --contar: = contar + 1; --if (cuenta = to_unsigned (4, 2)) entonces --count: = to_unsigned (0, 2); --if (PWM = to_ if (PWM <ping_length) entonces salida <= '1'; de lo contrario salida <= '0'; end if; PWM <= PWM + 1; ping_length <= unsigned (positiveWaveSum); --end if; end if; end process; end Behavioral;

Selector de 4 notas La parte más complicada de este proyecto es seleccionar solo cuatro frecuencias. Lo hicimos con una gran cantidad de declaraciones IF, y usamos señales en lugar de variables para que el proceso se pueda simular y depurar. Probamos otros métodos usando variables y bucles FOR, pero encontramos errores en tiempo de ejecución. Entonces, al final, decidimos que si funciona, lo dejaremos en paz.¿No arregles lo que no está roto?

Las cuatro ondas de salida están etiquetadas como Wave0, Wave1, Wave2, Wave3; estas son las que se sumarán para formar la salida final.

Mirando el código, verá un montón de señales etiquetadas C4, Cs4, D4, Ds4, etc. Estas son señales de 5 bits que toman el disparador correspondiente de O4 (octava 4) u O5 (octava 5) y las convierten 5 bits para agregar.

A continuación, las variables cntC4, cntCs4, etc. representan cuántas notas más bajas que la nota de destino se han tocado, incluida la nota de destino. Por ejemplo, si se tocan C4, E4, G4, A # 4 y D5 (acorde C9), cntC4 será 1, cntE4 será 2, cntG4 será 3, etc.

Luego, siempre que se toque una nota, se examinará el recuento de la nota de destino para ver dónde conectar la señal de nota. Por ejemplo, si se toca la nota D5 (lo que significa que O5 (2) es alta) y cntD5 es 3, entonces actualmente se están reproduciendo 3 notas, con 2 notas más bajas que D5, por lo que conectaremos waveD5 a Wave2 (la tercera onda recuento de señales de Wave0). Alternativamente, si cntD5 es 5, entonces actualmente se están reproduciendo 5 notas, con 4 notas más bajas que D5, por lo que simplemente dejaremos waveD5 colgando y no haremos nada con él.

Las declaraciones IF se repiten luego para cubrir los casos de las 25 notas.

Sumador de amplitud

Después de seleccionar las 4 ondas más bajas, tenemos que sumarlas. La razón por la que solo agregaremos cuatro notas juntas es porque la idea de PWM que estamos usando para nuestra salida solo puede tener una cierta resolución hasta que el PWM funcione demasiado lento y el altavoz comenzará a captar la onda cuadrada de PWM. Por ejemplo, si usáramos una resolución de 8192 (13 bits), cada uno de esos 8192 puntos debe corresponder a un flanco ascendente del reloj integrado. Entonces, 100MHz / 8192 = 12.2kHz, que está dentro del rango de audición humana.

La suma real de las amplitudes es súper simple, solo debes asegurarte de que pueda funcionar realmente rápido.

Salida PWM

El ciclo de trabajo del PWM representará la amplitud de nuestra onda de salida en ese instante. Por ejemplo, si tenemos un rango de amplitud de 0 a 128, 0 sería un ciclo de trabajo de 0%, 64 sería 50%, 128 sería 100%, etc. Este PWM funcionará extremadamente rápido (el nuestro es 97.6 kHz), tan rápido que el hablante no reconocerá las ondas cuadradas individuales y, en cambio, observará el voltaje promedio, creando nuestra señal "analógica".

Archivo de restricciones

Es posible que haya conectado su hardware de manera diferente, así que asegúrese de que el archivo de restricciones coincida.

Paso 5: Descargas de código

A continuación se muestra el código, tanto en formato.txt como en.vhd para Vivado. Wave_Generator es el submódulo generador de ondas, y Two_Octave_Synth es el módulo superior con todo lo demás.

Recomendado: