Cómo crear un sistema de hardware/software para lograr los objetivos de diseño

La partición de hardware/software es el problema de dividir los cálculos de una aplicación en una parte que se ejecuta como instrucciones secuenciales en un microprocesador (el "software") y una parte que se ejecuta como circuitos paralelos en alguna estructura de circuito integrado, como un ASIC o FPGA ( el “hardware”), con el fin de lograr objetivos de diseño definidos para métricas como rendimiento, potencia, tamaño y costo.

Introducción:

El objetivo del proyecto es implementar un sistema basado en Co-Diseño Hardware/Software sobre una placa FPGA Spartan 3E y un microcontrolador utilizando la herramienta Xilinx IDE. Los alfabetos y/o números se ingresan a través del teclado (botones en la FPGA) y a su vez se muestran en la pantalla LCD (en el Microcontrolador), al mismo tiempo que se reproducirá en un altavoz el audio correspondiente a la tecla presionada. El sistema está implementado tanto en hardware como en software. Se utiliza VHDL como lenguaje de descripción de hardware y C como software. Aunque este proyecto se puede implementar simplemente usando FPGA o microcontrolador, ese no es el objetivo aquí. Queremos introducir este concepto para que pueda aplicarse más adelante a problemas en los que la partición realmente puede marcar una diferencia en el rendimiento.

Arquitectura del sistema:

Visão geral do co-design de hardware e software baseado em FPGA

Fig. 1: Descripción general del codiseño de software de hardware basado en FPGA

Componentes necesarios:

1. Placa FPGA Nexys 2 Spartan 3E

dos. Microcontrolador – Atmega16 + LCD

3. altavoz de 8 ohmios

4. Conectores

5. CI 74LS245

Construcción:

A. FPGA

· La FPGA debe configurarse para recibir entradas del usuario a través de sus interruptores de entrada (o interruptores de botón).

· Entonces, el primer paso es configurar algunos conmutadores como entrada a la FPGA.

· A continuación, debemos asegurarnos de que cada clave corresponda a una entrada diferente. Por lo tanto, proporcionaremos un código (4 bits) para identificar de forma única cada entrada.

· Como se trata de un prototipo, solo definiremos 4 entradas. Y por lo tanto codificarlos como:

Uno => “0001”

B => “0010”

C => “0011”

E => “0100”

· Es decir, en cuanto se pulsa un interruptor, lo identificamos mediante el código de 4 bits exclusivo del mismo.

· Lo siguiente que debe hacer la FPGA es enviar estos 4 bits al microcontrolador.

· Comunicación:

La comunicación puede ser síncrona o asíncrona.

Si prefiere el modo asíncrono, la transmisión paralela es el modo más rápido a elegir.

Si prefiere el modo síncrono, le recomiendo configurar SPI entre el FPGA (maestro) y el microcontrolador (esclavo).

Aquí usaré la transmisión paralela asíncrona.

· Para la transmisión utilizaremos Conectores de Periféricos (Conectores PMod) en la placa Spartan 3E. La siguiente figura explica esto en detalle.

Diagrama de circuito do conector Nexys2 Pmod

Fig. 2: Diagrama del circuito del conector Pmod de Nexys2

· Podemos configurar PMods como Entrada y Salida. Aquí se definirán como salida.

B. Microcontrolador

· Esta unidad tiene principalmente dos tareas. Primero, mostrar el carácter en la pantalla LCD. En segundo lugar, reproduzca el sonido correspondiente al carácter en el altavoz.

· Tarea 1: Mostrar

Dado que utilizamos comunicación paralela, cada una de estas líneas de transmisión está conectada a los pines de entrada correspondientes en el microcontrolador. Ahora bien, puede haber dos formas de garantizar que los datos se lean correctamente;

1. Los pines se sondean continuamente para comprobar si hay alguna actividad. Una vez allí, el valor se lee y se almacena en una variable.

dos. Configuramos una interrupción. Esta interrupción se dispara desde la FPGA cuando las líneas de transmisión van a enviar los bits. Una vez recibida la interrupción, el valor se lee y luego se almacena en la variable.

· El segundo método es más eficaz que el primero . Así que sigamos con ello.

· Por lo tanto, el valor se lee usando la variable PIN. y almacenado en alguna variable de nuestra elección.

· Tarea 2: Sonido

Tenemos que reproducir un sonido en el altavoz de 8”. Para ello utilizaremos el flash interno del microcontrolador. Almacenaremos un sonido de muestra (digamos un pitido) en la memoria. Y lo reproduciremos según la entrada. Es decir, 'A' significa reproducir el pitido una vez. 'B' significa tocarlo dos veces, y así sucesivamente.

El microcontrolador que estamos usando aquí es el Atmega16 que tiene sólo 16K Bytes de memoria flash interna. Por tanto, no podemos almacenar muchos sonidos diferentes en esta memoria. Para lograr esto, necesitamos usar un flash externo e interconectarlo con el microcontrolador. Este no es el enfoque aquí, así que no iremos allí.

Y para reproducir audio PCM en el microcontrolador AVR, usaremos el PWM (modo PWM rápido) de alta velocidad del AVR. Casi se ve bien y puede usarse para proyectos simples que requieren efectos de sonido.

Paso 1: crea un archivo .wav. Si no tiene ninguno, utilice el siguiente enlace y convierta texto a voz. Guárdelo como un archivo .wav.

http://www2.research.att.com/~ttsweb/tts/demo.php

Paso 2: Convierta el archivo wav creado en un archivo wav de 8 bits sin firmar de 8000 muestras por segundo utilizando la opción de software de NCH. Se puede encontrar aquí:

Paso 3: convierta el archivo wav de 8 bits en un archivo c de muestra utilizando el siguiente software.

Paso 4: copie las muestras creadas en el archivo de encabezado pcm_sample y guárdelo.

En esencia, lo que estamos haciendo aquí es tomar una muestra de sonido y luego muestrearla para crear muestras finitas (8000) de 8 bits cada una. Estas muestras se almacenan en flash usando el comando PROG_MEM (debido al uso de la biblioteca avr/pgmspace). Estas muestras actuarán como ciclo de trabajo para el PWM que se generará. Establecemos el ciclo de trabajo inicial (valor en el registro OCR1A) en 0. Después de eso, las muestras se leerán continuamente desde la memoria flash y se escribirán en el OCR1A. Finalmente, el altavoz se conectará a la salida PWM (OC1A) y podremos escuchar los sonidos.

Para utilizar el flash interno, utilizamos las siguientes funciones:

1. constante larga pcm_length=6800;

const unsigned char pcm_samples PROGMEM ={insertar muestras aquí};

Esta función se utiliza para crear el archivo de encabezado "pcm_sample" que se utilizará más adelante.

dos. pgm_read_byte(&pcm_samples(muestra++));

Esta función se utiliza para leer byte tras byte del archivo de encabezado.

Conexiones:

Para la FPGA:

1. Como entrada se utilizarán los 4 interruptores deslizantes SW0, SW1, SW2 y SW3.

Tabela listando conectores PMod

Fig. 3: Tabla que enumera los conectores PMod

dos. A continuación, para la salida FPGA, usaremos PMod JA. Las conexiones son:

La interrupción (O) está conectada a JA1 “L15”

Línea de Transmisión “Z0” conectada a JA2 “K12”

Línea de Transmisión “Z1” conectada a JA3 “L17”

Línea de Transmisión “Z2” conectada a JA4 “M15”

Línea de Transmisión “Z3” conectada a JA7 “K13”

Nota 1: No podemos usar JA5, JA6 ya que están predefinidos para usarse como GND, VCC.

Nota 2: Dado que Spartan 3E funciona con lógica de 3,3 V y Atmega16 con lógica de 5 V, necesitamos conectar un IC Logic Shifter como 74LS245 para nivelar las líneas de transmisión antes de conectarlas al PUERTO del microcontrolador.

Para el microcontrolador:

1. Primero conecte la interrupción a PD2 (ya que estamos usando INT0, puede ser cualquier cosa dependiendo de su elección). Luego conecte las líneas de transmisión a los pines 0-3 de cualquier puerto. Aquí se utiliza el puerto B.

dos. Conecte el altavoz a través de un filtro de paso bajo (resistencia (1k) + condensador (470uf) conectado a GND) a PD5 (salida OC1A-PWM).

Laboral:

Se puede ver que tan pronto como presionamos un botón en la FPGA, podemos ver la letra correspondiente en la pantalla LCD y escuchar el número de pulsaciones en el altavoz.

Conclusión:

Este fue un experimento simple para mostrar el concepto de codiseño Hardware-Software. Aunque este proyecto por sí solo no explora todo el potencial de este concepto, es un buen comienzo para los principiantes que pueden aprender de él y utilizarlo en sus propios proyectos.

Diagramas de pines:

Imagem de um interruptor de 4 slides em uma placa FPGA

Fig. 4: Imagen del interruptor de 4 deslizadores en la placa FPGA

74LS245

Código fuente del proyecto

###


 //Programa para utilizar interrupciones externas (hardware) del microcontrolador AVR (ATmega16)
#incluir "lcd.h"
#incluir #incluir #incluir #incluir "pcm_sample.h" #incluir #incluir #definir SAMPLE_RATE 8000; muestra volátil uint16_t; int muestra_count; /* inicializa PWM */ vacío pwm_init (vacío) { lcd_init; /* usa el pin OC1A como salida */ DDRD = _BV(PD5); /* * borrar OC1A en la comparación de partidos * configure OC1A en ABAJO, modo no inversor *PWM rápido, 8 bits */ TCCR1A = _BV(COM1A1) _BV(WGM10); /* *PWM rápido, 8 bits * Preescalador: clk/1 = 8MHz * Frecuencia PWM = 8 MHz / (255 + 1) = 31,25 kHz */ TCCR1B = _BV(WGM12) _BV(CS10); /* establece el ciclo de trabajo inicial en cero */ OCR1A = 0; /* Configurar temporizador0 */ interno; a = PIBB; //como los pines están conectados a PB0, PB1, PB2, PB3, los códigos formarán números enteros como 1,2,3,4 //lcd_print(a,10); si(a==1) { _delay_ms(100); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); //leyendo el flash serial completamente una vez if(muestra>pcm_length)muestra=0; //luego de leer haciendo el contador=0 lcd_send_string("Uno"); } si(a==2) { lcd_send_string("B"); _delay_ms(100); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); if(muestra>pcm_length)muestra=0; _delay_ms(200); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); if(muestra>pcm_length)muestra=0; } si(a==3) { lcd_send_string("C"); _delay_ms(100); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); if(muestra>pcm_length)muestra=0; _delay_ms(200); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); if(muestra>pcm_length)muestra=0; _delay_ms(300); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); if(muestra>pcm_length)muestra=0; } si(a==4) { lcd_send_string("D"); _delay_ms(100); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); if(muestra>pcm_length)muestra=0; _delay_ms(200); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); if(muestra>pcm_length)muestra=0; _delay_ms(300); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); if(muestra>pcm_length)muestra=0; _delay_ms(400); OCR1A = pgm_read_byte(&pcm_samples(muestra++)); if(muestra>pcm_length)muestra=0; } } ISR (INT0_vector) { pwm_init; } int principal (vacío) { lcd_init; cli; GICR =(1< //Establece el Bit6 del GICR para desenmascarar la interrupción INT0. MCUCR =(3<

saber ; mientras(1) { } }

¿Preguntas relacionadas con este artículo?
👉 Pregunte y discuta en los foros Electro-Tech-Online.com y EDAboard.com.

¡¡Dinos qué piensas!! Cancelar respuesta

contenido relacionado

Regresar al blog

Deja un comentario

Ten en cuenta que los comentarios deben aprobarse antes de que se publiquen.