Tabla de contenido:

Sintetizador de arpa láser en tablero Zybo: 10 pasos (con imágenes)
Sintetizador de arpa láser en tablero Zybo: 10 pasos (con imágenes)

Video: Sintetizador de arpa láser en tablero Zybo: 10 pasos (con imágenes)

Video: Sintetizador de arpa láser en tablero Zybo: 10 pasos (con imágenes)
Video: Crokett's Theme / Harp Laser Performance by Arkonis 2024, Mes de julio
Anonim
Sintetizador de arpa láser en tablero Zybo
Sintetizador de arpa láser en tablero Zybo

En este tutorial crearemos un arpa láser completamente funcional utilizando sensores IR con una interfaz en serie que permitirá al usuario cambiar la afinación y el tono del instrumento. Este arpa será la nueva versión del siglo XXI del antiguo instrumento. El sistema se creó utilizando una placa de desarrollo Xilinx Zybo junto con Vivado Design Suites. Qué necesitará para completar el proyecto:

  • 12 sensores y emisores de infrarrojos (se pueden utilizar más o menos según el número de cadenas)
  • Placa de desarrollo Zybo Zynq-7000
  • RTOS gratis
  • Suite Vivado Design
  • Cable (para conectar los sensores a la placa)
  • 3 piezas de tubería de PVC ((2) de 18 pulgadas y (1) de 8 pulgadas)
  • 2 codos de PVC

Paso 1: Obtenga la demostración de audio Zybo DMA de Digilent

El lado FPGA de este proyecto se basa en gran medida en el proyecto de demostración que se encuentra aquí. Utiliza acceso directo a la memoria para enviar datos directamente desde la memoria en la que el procesador puede escribir sobre AXI Stream a un bloque de audio I2S. Los siguientes pasos le ayudarán a poner en marcha el proyecto de demostración de audio de DMA:

  1. Puede ser necesaria una nueva versión del archivo de placa para la placa Zybo. Siga estas instrucciones para obtener nuevos archivos de placa para Vivado.
  2. Siga los pasos 1 y 2 de las instrucciones de esta página para abrir el proyecto de demostración en Vivado. Utilice el método Vivado, no la transferencia de hardware SDK.
  3. Es posible que reciba un mensaje que diga que algunos de sus bloques de direcciones IP deben actualizarse. Si es así, seleccione "Mostrar estado de IP" y luego en la pestaña de estado de IP seleccione todas las IP desactualizadas y haga clic en "Actualizar seleccionado". Cuando finalice y aparezca una ventana que le pregunte si desea generar un producto de salida, siga adelante y haga clic en "Generar". Si recibe un mensaje de advertencia crítico, ignórelo.
  4. Cambie del diseño a la pestaña de fuentes en Vivado para ver los archivos fuente. Haga clic con el botón derecho en el diseño de bloque "design_1" y seleccione "Crear envoltura HDL". Cuando se le solicite, seleccione "copiar contenedor generado para permitir las ediciones del usuario". Se generará un archivo contenedor para el proyecto.
  5. Ahora que se completaron los pasos críticos que de alguna manera se omitieron en el otro tutorial, puede volver al tutorial previamente vinculado y continuar desde el paso 4 hasta el final y asegurarse de que el proyecto de demostración se ejecute correctamente. Si no tiene una forma de ingresar audio para grabar, simplemente grabe con los auriculares puestos y escuche un sonido difuso de 5 a 10 segundos cuando presione el botón de reproducción. Siempre que salga algo de la toma de auriculares cuando presiona el botón de reproducción, probablemente esté funcionando correctamente.

Paso 2: realice algunos cambios en Vivado

Haga algunos cambios en Vivado
Haga algunos cambios en Vivado

Así que ahora tiene funcionando la demostración de audio DMA de Digilent, pero ese no es el objetivo final aquí. Así que tenemos que volver a Vivado y hacer algunos cambios para que nuestros sensores se puedan conectar a los encabezados PMOD y podamos usar su valor en el lado del software.

  1. Abre el diagrama de bloques en Vivado
  2. Cree un bloque GPIO haciendo clic derecho en un espacio vacío en el diagrama de bloques y seleccionando "Agregar IP" en el menú. Busque y seleccione "AXI GPIO".
  3. Haga doble clic en el nuevo bloque de IP y en la ventana de volver a personalizar IP vaya a la pestaña de configuración de IP. Seleccione todas las entradas y establezca el ancho en doce, ya que tendremos 12 "cuerdas" en nuestro arpa y, por lo tanto, necesitaremos 12 sensores. Si desea utilizar menos o más sensores, ajuste este número de forma adecuada. También configure habilitar interrupción.
  4. Haga clic con el botón derecho en el nuevo bloque de IP GPIO y seleccione "ejecutar automatización de conexión". Marque la casilla AXI y presione OK. Esto debería conectar la interfaz AXI automáticamente, pero dejar las salidas del bloque desconectadas.
  5. Para dejar espacio para la interrupción adicional, haga doble clic en el bloque IP xlconcat_0 y cambie el número de puertos de 4 a 5. Luego puede conectar el pin ip2intc_irpt del nuevo bloque GPIO al nuevo puerto no utilizado en el bloque xlconcat.
  6. Haga clic derecho en la salida "GPIO" del nuevo bloque de IP GPIO y seleccione "hacer externo". Encuentre a dónde va la línea y haga clic en el pequeño pentágono lateral y, a la izquierda, se abrirá una ventana donde puede cambiar el nombre. Cambie el nombre a "SENSORES". Es importante usar el mismo nombre si desea que el archivo de restricciones que proporcionamos funcione; de lo contrario, tendrá que cambiar el nombre en el archivo de restricciones.
  7. De vuelta en la pestaña de fuentes, busque el archivo de restricciones y reemplácelo con el que le proporcionamos. Puede optar por reemplazar el archivo o simplemente copiar el contenido de nuestro archivo de restricciones y pegarlo sobre el contenido del anterior. Una de las cosas importantes que hace nuestro archivo de restricciones es habilitar las resistencias pullup en los encabezados PMOD. Esto es necesario para los sensores particulares que usamos, sin embargo, no todos los sensores son iguales. Si sus sensores requieren resistencias pulldown, puede cambiar cada instancia de "set_property PULLUP true" por "set_property PULLDOWN true". Si requieren un valor de resistencia diferente al que está en la placa, entonces puede quitar estas líneas y usar resistencias externas. Los nombres de los pines están en los comentarios en el archivo de restricciones y corresponden a las etiquetas en el primer diagrama en los esquemas de Zybo página que se puede encontrar aquí. Si desea utilizar diferentes pines pmod, simplemente haga coincidir los nombres en el archivo de restricción con las etiquetas en el esquema. Usamos encabezados PMOD JE y JD, y usamos seis pines de datos en cada uno, omitiendo los pines 1 y 7. Esta información es importante al conectar sus sensores. Como se muestra en el esquema, los pines 6 y 12 del PMODS son VCC y los pines 5 y 11 están a tierra.
  8. Vuelva a generar el contenedor HDL como antes, y copie y sobrescriba el anterior. Cuando haya terminado, genere bitstream y exporte hardware como antes, y reinicie el SDK. Si le preguntan si desea reemplazar el archivo de hardware antiguo, la respuesta es sí. Probablemente sea mejor tener el SDK cerrado al exportar hardware para que se reemplace correctamente.
  9. Inicie el SDK.

Paso 3: Ejecute FreeRTOS

El siguiente paso es hacer que FreeRTOS se ejecute en la placa Zybo.

  1. Si aún no tiene una copia, descargue FreeRTOS aquí y extraiga los archivos.
  2. Importe la demostración de FreeRTOS Zynq ubicada en FreeRTOSv9.0.0 / FreeRTOS / Demo / CORTEX_A9_Zynq_ZC702 / RTOSDemo. El proceso de importación es prácticamente el mismo que para el otro proyecto de demostración; sin embargo, debido a que la demostración de FreeRTOS Zynq se basa en otros archivos de la carpeta FreeRTOS, no debe copiar los archivos en su espacio de trabajo. En su lugar, debe colocar toda la carpeta FreeRTOS dentro de la carpeta de su proyecto.
  3. Cree un nuevo paquete de soporte de placa yendo a "archivo" -> "nuevo" -> "paquete de soporte de placa". Asegúrese de que esté seleccionado independiente y haga clic en finalizar. Después de un momento, aparecerá una ventana, marque la casilla junto a lwip141 (esto evita que una de las demostraciones de FreeRTOS no se compile) y presione Aceptar. Después de que se complete, haga clic con el botón derecho en el proyecto RTOSdemo y vaya a "propiedades", vaya a la pestaña "referencias del proyecto" y marque la casilla junto al nuevo bsp que creó. Es de esperar que se reconozca, pero a veces el SDK de Xilinx puede ser extraño con este tipo de cosas. Si aún obtiene un error después de este paso que indica que falta xparameters.h o algo así, intente repetir este paso y tal vez salir y reiniciar el SDK.

Paso 4: agregue el código de arpa láser

Ahora que se ha importado FreeRTOS, puede traer los archivos del proyecto de arpa láser a la demostración de FreeRTOS

  1. Cree una nueva carpeta en la carpeta src en la demostración de FreeRTOS y copie y pegue todos los archivos c proporcionados, excepto main.c, en esta carpeta.
  2. Reemplace el RTOSDemo main.c con el main.c. proporcionado
  3. Si todo se hace correctamente, debería poder ejecutar el código del arpa láser en este punto. Para propósitos de prueba, la entrada de botón que se usó en el proyecto de demostración DMA ahora se usa para reproducir sonidos sin sensores conectados (cualquiera de los cuatro botones principales funcionará). Tocará una cuerda cada vez que la presione y recorra todas las cuerdas del sistema en varias pulsaciones. Conecte algunos auriculares o altavoces a la toma de auriculares en la placa Zybo y asegúrese de que puede escuchar los sonidos de las cuerdas cuando presiona un botón.

Paso 5: Acerca del código

Es probable que muchos de los que lean este tutorial aprendan a configurar el audio o usar DMA para hacer algo diferente, o para crear un instrumento musical diferente. Por esa razón, las siguientes secciones se dedican a describir cómo funciona el código proporcionado en conjunto con el hardware descrito anteriormente para obtener una salida de audio funcional usando DMA. Si comprende por qué las piezas de código están allí, entonces debería poder ajustarlas para lo que sea que desee crear.

Interrupciones

Primero mencionaré cómo se crean las interrupciones en este proyecto. La forma en que lo hicimos fue creando primero una estructura de tabla de vectores de interrupciones que realiza un seguimiento de la ID, el controlador de interrupciones y una referencia al dispositivo para cada interrupción. Los ID de interrupción provienen de xparameters.h. El controlador de interrupciones es una función que escribimos para DMA y GPIO, y la interrupción I2C proviene del controlador Xlic I2C. La referencia del dispositivo apunta a instancias de cada dispositivo que inicializamos en otro lugar. Cerca del final de la función _init_audio, un bucle pasa por cada elemento de la tabla de vectores de interrupción y llama a dos funciones, XScuGic_Connect () y XScuGic_Enable () para conectar y habilitar las interrupciones. Hacen referencia a xInterruptController, que es un controlador de interrupciones creado en FreeRTOS main.c de forma predeterminada. Entonces, básicamente, adjuntamos cada una de nuestras interrupciones a este controlador de interrupciones que FreeRTOS ya creó para nosotros.

DMA

El código de inicialización de DMA comienza en lh_main.c. Primero se declara una instancia estática de una estructura XAxiDma. Luego, en la función _init_audio () se configura. Primero se llama a la función de configuración del proyecto de demostración, que está en dma.c. Está bastante bien documentado y viene directamente de la demostración. Luego, la interrupción se conecta y habilita. Para este proyecto, solo se requiere la interrupción de maestro a esclavo, porque el DMA envía todos los datos al controlador I2S. Si desea grabar audio, también necesitará la interrupción de esclavo a maestro. La interrupción de maestro a esclavo se llama cuando el DMA termina de enviar los datos que le dijo que enviara. Esta interrupción es increíblemente importante para nuestro proyecto porque cada vez que el DMA termina de enviar un búfer de muestras de audio, debe comenzar inmediatamente a enviar el siguiente búfer, o de lo contrario se produciría un retraso audible entre los envíos. Dentro de la función dma_mm2s_ISR () puede ver cómo manejamos la interrupción. La parte importante está cerca del final donde usamos xSemaphoreGiveFromISR () y portYIELD_FROM_ISR () para notificar a _audio_task () que puede iniciar la siguiente transferencia DMA. La forma en que enviamos datos de audio constantes es alternando entre dos búferes. Cuando un búfer se transmite al bloque I2C, el otro búfer tiene sus valores calculados y almacenados. Luego, cuando la interrupción proviene del DMA, el búfer activo cambia y el búfer escrito más recientemente comienza a transferirse, mientras que el búfer transferido anteriormente comienza a sobrescribirse con nuevos datos. La parte clave de la función _audio_task es donde se llama a fnAudioPlay (). fnAudioPlay () toma la instancia de DMA, la longitud del búfer y un puntero al búfer desde el que se transferirán los datos. Se envían algunos valores a los registros I2S para informarle que vienen más muestras. Luego, se llama a XAxiDma_SimpleTransfer () para iniciar la transferencia.

Audio I2S

audio.cy audio.h son donde tiene lugar la inicialización de I2S. El código de inicialización de I2S es un fragmento de código bastante común que está flotando en varios lugares, es posible que encuentre ligeras variaciones de otras fuentes, pero esta debería funcionar. Está bastante bien documentado y no es necesario cambiar mucho para el proyecto del arpa. La demostración de audio DMA de la que proviene tiene funciones para cambiar a las entradas de micrófono o línea para que pueda usarlas si necesita esa funcionalidad.

Síntesis de sonido

Para describir cómo funciona la síntesis de sonido, voy a enumerar cada uno de los modelos de sonido utilizados en el desarrollo que llevaron al método final, ya que le dará una idea de por qué se hace de la forma en que se hace.

Método 1: Se calcula un período de valores de seno para cada cuerda a la frecuencia correspondiente para la nota musical de esa cuerda y se almacena en una matriz. Por ejemplo, la longitud de la matriz será el período de la onda sinusoidal en muestras, lo que equivale a # de muestras / ciclo. Si la frecuencia de muestreo es de 48 kHz y la frecuencia de la nota es de 100 Hz, entonces hay 48, 000 muestras / segundo y 100 ciclos / segundo que conducen a 4800 muestras por ciclo, y la longitud de la matriz será de 4800 muestras y contendrá los valores de una completa. período de onda sinusoidal. Cuando se reproduce la cuerda, el búfer de muestra de audio se llena tomando un valor de la matriz de ondas sinusoidales y poniéndolo en el búfer de audio como muestra, luego incrementando el índice en la matriz de ondas sinusoidales para usar nuestro ejemplo anterior durante el curso. de 4800 muestras, se coloca un ciclo de onda sinusoidal en el búfer de audio. Se usa una operación de módulo en el índice de la matriz para que siempre esté entre 0 y la longitud, y cuando el índice de la matriz supera un cierto umbral (como quizás 2 segundos de muestras), la cadena se apaga. Para reproducir varias cuerdas al mismo tiempo, realice un seguimiento del índice de matriz de cada cuerda por separado y agregue el valor de la onda sinusoidal de cada cuerda para obtener cada muestra.

Método 2: Para crear un tono más musical, comenzamos con el modelo anterior y agregamos armónicos a cada frecuencia fundamental. Las frecuencias armónicas son frecuencias que son múltiplos enteros de la frecuencia fundamental. A diferencia de cuando se suman dos frecuencias no relacionadas, lo que da como resultado la reproducción simultánea de dos sonidos distintos, cuando se suman armónicos, sigue sonando como un solo sonido, pero con un tono diferente. Para lograr esto, cada vez que agregamos el valor de la onda sinusoidal en la ubicación (índice de matriz% longitud de matriz) a la muestra de audio, también agregamos (2 * índice de matriz% longitud de matriz) y (3 * índice de matriz% longitud de matriz), y así sucesivamente para la cantidad de armónicos que se deseen. Estos índices multiplicados atravesarán la onda sinusoidal a frecuencias que son múltiplos enteros de la frecuencia original. Para permitir un mayor control del tono, los valores de cada armónico se multiplican por una variable que representa la cantidad de ese armónico en el sonido general. Por ejemplo, la onda sinusoidal fundamental puede tener todos sus valores multiplicados por 6 para que sea un factor más importante en el sonido general, mientras que el quinto armónico puede tener un multiplicador de 1, lo que significa que sus valores contribuyen mucho menos al sonido general.

Método 3: Bien, ahora tenemos un tono muy agradable en las notas, pero todavía hay un problema bastante crucial: se reproducen a un volumen fijo durante una duración fija. Para que suene como un instrumento real, el volumen de una cuerda que se toca debe disminuir suavemente con el tiempo. Para lograr esto, una matriz se llena con los valores de una función que decae exponencialmente. Ahora, cuando se crean las muestras de audio, el sonido que proviene de cada cadena se calcula como en el método anterior, pero antes de que se agregue a la muestra de audio, se multiplica por el valor en el índice de la matriz de esas cadenas en la matriz de función de caída exponencial. Esto hace que el sonido se disipe suavemente con el tiempo. Cuando el índice de la matriz llega al final de la matriz de decaimiento, la cadena se detiene.

Método 4: Este último paso es lo que realmente le da a los sonidos de cuerda su sonido de cuerda realista. Antes sonaban agradables pero claramente sintetizados. Para tratar de emular mejor una cuerda de arpa del mundo real, se asigna una tasa de caída diferente a cada armónico. En las cuerdas reales, cuando la cuerda se golpea por primera vez, hay un alto contenido de armónicos de alta frecuencia que crea el tipo de sonido de punteo que esperamos de una cuerda. Estos armónicos de alta frecuencia son muy brevemente la parte principal del sonido, el sonido de la cuerda que se golpea, pero decaen muy rápidamente a medida que los armónicos más lentos se hacen cargo. Se crea una matriz de caída para cada número armónico utilizado en la síntesis de sonido, cada uno con su propia tasa de caída. Ahora cada armónico se puede multiplicar independientemente por el valor de su matriz de decaimiento correspondiente en el índice de matriz de la cuerda y agregarse al sonido.

En general, la síntesis de sonido es intuitiva pero los cálculos son pesados. Almacenar todo el sonido de la cuerda en la memoria a la vez requeriría demasiada memoria, pero calcular la onda sinusoidal y la función exponencial entre cada cuadro tomaría demasiado tiempo para mantenerse al día con la velocidad de reproducción de audio. Se utilizan varios trucos en el código para acelerar el cálculo. Todas las matemáticas, excepto en la creación inicial de las tablas de decaimiento exponencial y sinusoidal, se realizan en formato entero, lo que requiere distribuir el espacio numérico disponible en la salida de audio de 24 bits. Por ejemplo, la tabla de seno tiene una amplitud de 150, por lo que es suave pero no tan grande como para que muchas cuerdas tocadas juntas superen los 24 bits. Asimismo, los valores de la tabla exponencial se multiplican por 80 antes de redondearse a números enteros y almacenarse. Los pesos armónicos pueden tomar valores discretos entre 0 y 10. Además, todas las muestras se duplican y las ondas sinusoidales se indexan en 2, lo que reduce a la mitad la frecuencia de muestreo. Esto limita la frecuencia máxima que se puede reproducir, pero era necesario para que el número actual de cuerdas y armónicos se calculara con la suficiente rapidez.

Crear este modelo de sonido y hacer que funcionara requirió un esfuerzo considerable en el lado del procesador, y hubiera sido increíblemente difícil hacerlo funcionar en el lado de fpga desde cero en el marco de tiempo de este proyecto (imagínese tener que recrear el flujo de bits cada vez que se cambió una pieza de Verilog para probar el sonido). Sin embargo, hacerlo en el fpga probablemente podría ser una mejor manera de hacerlo, posiblemente eliminando el problema de no poder calcular muestras lo suficientemente rápido y permitir que se ejecuten más cuerdas, armónicos e incluso efectos de audio u otras tareas en el lado del procesador.

Paso 6: cableado de los sensores

Cableado de los sensores
Cableado de los sensores

Para crear las cuerdas usamos sensores de haz de ruptura de infrarrojos que detectarán cuándo se está tocando la cuerda. Pedimos nuestros sensores desde el siguiente enlace. Los sensores tienen un cable de alimentación, tierra y datos, mientras que los emisores solo tienen un cable de alimentación y tierra. Usamos los pines de tierra y 3.3 V de los encabezados PMOD para alimentar tanto los emisores como los sensores. Para alimentar todos los sensores y emisores es necesario conectar todos los sensores y emisor en paralelo. Cada uno de los cables de datos de los sensores deberá ir a su propio pin pmod.

Paso 7: construcción del esqueleto

Construyendo el esqueleto
Construyendo el esqueleto

Para crear la forma del arpa, las tres piezas se utilizan como esqueleto para colocar los sensores y emisores. En una de las dos piezas de tubo de PVC de 18 pulgadas, alinee los sensores y emisores en orden alterno a 1,5 pulgadas entre sí y luego péguelos con cinta adhesiva a la tubería. En el otro tubo de PVC de 18 pulgadas, alinee los sensores y los emisores en orden alterno, pero asegúrese de compensar el orden (es decir, si el primer tubo tenía un sensor primero, el segundo debería tener un emisor primero y viceversa). Será necesario soldar cables más largos en los cables de datos, energía y tierra para asegurarse de que puedan llegar a la placa.

Paso 8: construcción del exterior de madera

Construyendo el exterior de madera
Construyendo el exterior de madera

Este paso es opcional pero muy recomendable. El exterior de madera no solo hace que el arpa se vea bien, sino que también protege los sensores y cables de daños. El marco de madera se puede crear con un anillo rectangular sagrado de madera. El interior del rectángulo debe tener una abertura de al menos 1-1 / 2 pulgadas para adaptarse a la tubería y al esqueleto del sensor. Una vez que el marco esté construido, taladre dos agujeros que permitirán que los cables del sensor y los emisores salgan para conectarlos con la placa.

* Nota: Se recomienda agregar puntos de acceso para poder quitar e insertar el esqueleto de la tubería en caso de que sea necesario realizar reparaciones o pequeños ajustes.

Paso 9: Juntando todas las piezas

Poniendo todas las piezas juntas
Poniendo todas las piezas juntas

Una vez finalizados todos los pasos anteriores es el momento de construir el arpa. Primero coloque el esqueleto de la tubería dentro del exterior de madera. Luego, conecte los cables de los sensores y emisores en la ubicación correcta en la placa. Luego abra el SDK y haga clic en el botón de depuración para programar la placa. Una vez programada la placa, conecte unos auriculares o un altavoz. Dependiendo de qué sensor termine en qué puerto pmod, las cuerdas de su arpa probablemente estarán fuera de servicio para empezar. Debido a que puede ser difícil saber qué cable va a qué sensor cuando hay tantos cables involucrados, incluimos una forma de asignar números de cadena para interrumpir las posiciones de los bits en el software. Busque "static int sensor_map [NUM_STRINGS]" y ajuste los valores en la matriz hasta que las cadenas se reproduzcan de menor a mayor en orden.

El menú se puede utilizar abriendo un terminal en serie (por ejemplo, RealTerm) y estableciendo la velocidad en baudios en 115200 y la pantalla en ANSI. Se puede navegar por el menú usando las teclas wys para moverse hacia arriba y hacia abajo y las teclas ayd para cambiar los valores.

Paso 10: ROCK OUT

Una vez que el arpa esté completamente funcional. ¡Domina el arpa y escucha el dulce sonido de tu propia música!

Recomendado: