Skip to content

Vídeo 29: Registros y comparadores

Juan Gonzalez-Gomez edited this page Nov 12, 2018 · 251 revisions

Vídeos

Este tutorial se ha grabado en 7 vídeos cortos separados:

Parte I: Registros

Click to see the youtube video

Parte II: Comparadores

Click to see the youtube video

Parte III: Aplicación. Juego de la Ruleta

Click to see the youtube video

Parte IV: Aplicación. Control por comandos

Click to see the youtube video

Parte V: Aplicación. Comunicación Arduino-FPGA. Puerto de Salida

Click to see the youtube video

Parte VI: Aplicación. Comunicación Arduino-FPGA. Puerto de entrada

Click to see the youtube video

Parte VII: Ejercicios propuestos

Click to see the youtube video

Descripción

Los registros nos permiten almacenar números de varios bits en nuestros circuitos, y transmitirlos de un circuito a otro en paralelo o en Serie. Veremos ejemplos de comunicación entre el Arduino y la FPGA, usando comunicaciones serie síncronas. Gracias a los comparadores, podemos tomar decisiones dependiendo de los valores almacenados en los registros.

Colección

Academia-Jedi-HW-29.zip: Colección para este tutorial. Descargar e instalar

Contenido

Registros

Los registros son los componentes mínimos que nos permiten almacenar números de varios bits en nuestros circuitos. Con ellos ya no sólo recordamos bits aislados, sino datos más complejos. Están compuestos por la unión de biestables D, tanto en paralelo como en cadena

Hay muchos tipos de registros. Los que nosotros usaremos en este tutorial son los de carga paralela y los registros de desplazamiento. Los últimos amplían a los primeros proporcionando la opción de desplazar los bits previamente cargados

Como icono usamos varias chinchetas, para indicar que hay varios biestables D en paralelo. La anchura del bus de datos de entrada y salida, de ambos registros, es igual a la cantidad de bits del registro

Registro con carga paralela

Son la extensión del biestable D a varios bits en paralelo. Nos permiten cargar y almacenar un número de N bits, que se captura cuando se recibe un tic de captura en su entrada load. Aquí se muestra un registro de 8 bits, junto a un biestable D

El parámetro nos permite establecer su valor inicial al arrancar el circuito. La salida del registro es un bus de 8 bits, por donde sale el dato almacenado. Tiene una entrada, también de 8 bits, por donde se capturan los datos cuando llega un tic por la entrada load

En la animación se mustra un registro genérico que está capturando dos números, el 25 y 17 (representados en decimal) cuando llegan los tics de captura

Ejemplo 1: Almacenamiento de un número de 3 bits

Para probar el funcionamiento de estos registros usaremos uno de 3 bits para almacenar el número introducido en binario por tres pulsadores. El número se muestra en decimal en el display de 7 segmentos al apretar la tecla de Enter (captura).

Este circuito es mucho más claro y compacto que los que hacíamos en el tutorial 28 al usar biestables D aislados. Son las ventajas del diseño jerárquico :-)

En el montaje hay tres pulsadores, un botón que hace las veces de tecla Enter y el display de 7 segmentos

Lo cargamos y lo probamos. ¡Nuestro registro almacena!

Click to see the youtube video

Registros de desplazamiento

Los registros de desplazamiento son iguales a los de carga paralela, con una entrada adicional para desplazar los bits una posición en un sentido. Nosotros usaremos registros que desplazan los bits hacia la izquierda, en el sentido de los bits más significativos (multiplicación por 2). Este es un ejemplo de registro de desplazamiento de 8 bits:

Cuando el registro recibe el tic de carga, captura el dato que entra en paralelo, igual que los registros de carga paralela. Cuando recibe un tic de desplazamiento, mueve los bits una posición hacia la izquierda, añadiendo el bit de su entrada serie como bit de menor peso, y sacando el de mayor peso por su salida serie

Ejemplo 2: Carga y desplazamiento de un dato de 3 bits

Para experimentar haremos un circuito para cargar un dato en un registro de 3 bits, y desplazarlo. El dato se introduce por 3 interruptores y se captura al apretar la Tecla ENTER. Se muestra en binario en tres LEDs y en decimal en un display de 7 segmentos. Con otro botón hacemos que se desplace. El montaje es el siguiente:

Este es el circuito. Por la entrada serie se introduce siempre un bit a 0, y la salida serie no se conecta a ningún sitio

Lo cargamos y lo probamos. En el vídeo se carga primero el dato 1, luego el 3 y finalmente el 7. Todos se desplazan 3 bits a la izquierda

Click to see the youtube video

Ejemplo 3: Disparos arcade

En el ejercicio 2 del tutorial 26 usamos temporizadores para generar el movimiento de una bala en los LEDs, emulando los juegos arcade. Crearemos el mismo efecto de una forma más fácil, usando un registro de desplazamiento de 8 bits.

El registro de desplazamiento almacena la información de visualización de las balas en vuelo. Un bit a 1 indica que hay una bala en esa posición. Si está a 0 es que no hay nada. El registro se está constantemente desplazando, con cada tic emitido por el corazón de 50ms. Cada bala permanece 50ms en un LED, y luego pasa al siguiente. Como hay 8 leds, una bala tardará 8 * 50 = 400ms en recorrerlos todos. Si queremos que vaya más rápido, sólo hay que poner un tiempo menor

Mediante el pulsador de disparo, se carga un 1 en el biestable D y cuya salida se usa como entrada serie del registro de desplazamiento. En ese momento la bala saldrá disparada con el siguiente tic del corazón (Es un biestable recámara). Adicionalmente hay conectado un bloque que emite un pitido cada vez que se dispara

Para probarlo sólo necesitamos un pulsador y un zumbador para escuchar los disparos

Lo cargamos y lo probamos. Cada vez que se aprieta el pulsador, se dispara una bala. Puede haber varias balas en vuelo a la vez

Click to see the youtube video

Comparadores

Los comparadores sirven para determinar la relación que hay entre dos números. Usaremos dos tipos de comparadores: el de igualdad y el menor que. Son muy importantes, sobre todo el de igualdad, que nos permite implementar circuitos que realicen una acción al recibir un comando

Ambos comparadores reciben dos operandos de n bits y producen un bit de salida. En el de igualdad, la salida se pone a 1 si ambos operandos son iguales y 0 si son distintos. La igualdad es una operación comutativa: da igual por donde introduzcamos cada operando

El comparador menor que pone a 1 su salida si el operando a es menor que el operando b. En caso de igualdad o que el b sea mayor, se pone a 0. Esta es una operación NO conmutativa, por lo que es MUY IMPORTANTE introducir los operandos en el orden correcto

Ejemplo 4: Comparando números de 3 bits

Para comprobar el funcionamiento de los comparadores, utilizaremos números de 3 bits. El operando a lo introduciremos en un registro de 3 bits, a través de tres pulsadores externos y un pulsador de enter. El operando b estará en un contador de 3 bits, que se incrementa con otro pulsador. En dos leds mostramos las salidas de ambos comparadores: de igualdad y menor que. El montaje es el siguiente:

En el circuito colocamos un registro para el operador a, y un contador para el operador b, y conectamos sus salidas a ambos comparadores, de igualdad y menor que

Lo cargamos y lo probamos. En este vídeo lo vemos en acción. Inicialmente los dos operandos son 0, por lo que se enciende el LED de igualdad

Click to see the youtube video

Al pulsamos el botón de incremento del operador b, tenemos que a = 0 y b = 1, por lo que a < b y se enciende el LED rojo (menor que). Luego introducimos el número 6 en a, y no se enciende ningún LED porque no se cumple ninguna condición. Incrementamos b hasta que es 6 y se cumple a = b, y volvemos a incrementar b para tener otra vez que a < b y se encienda el led rojo

Comparadores de un operando

Muchas veces hay que realizar una comparación entre un dato, que varía y una constante. Por ejemplo cuando hay que reconocer un comandos o un código. En estos caso es más fácil crear un bloque específico en el que la constante se introduce como parámetro. Estos comparadores tienen una entrada para el operando a y una para el parámetro k

Ejemplo 5: Apertura de caja fuerte con código

Reharemos el ejemplo que ya conocemos de la apertura de una caja fuerte con código, pero usando un comparador de igualdad con operando y constante. Por los interruptores externos introducimos el código y al apretar el pulsador se guardan en un registro. Este dato se compara con el código de apertura y se mueve el servo que abre la caja fuerte si es correcto. El escenario es el siguiente:

El código de apertura se configura como constante del comparador, que por defecto es 5. Para modificar el código hay que cambiar esta constante. El código introducido por el usuario se almacena en un registro de 3 bits al apretar el pulsador de ENTER, y se compara con el código de apertura

Lo cargamos y lo probamos. Primero se introducen los códigos incorrectos 1 y 3, y la caja no se abre. Luego se mete el correcto: 5 (101 en binario) y se enciende el LED verde y se mueve el servo. Por último se introduce otro incorrecto: 7. Se apaga el LED y se cierra la puerta. Se muestra en este vídeo

Click to see the youtube video

Aplicaciones

Como ejemplos de dos circuitos más avanzados que utilizan registros y comparadores haremos el juego de la ruleta, en el que ganas si al tirar el spinner el número que sale es igual al introducido previamente, y un circuito que activa otros circuitos mediante códigos

Ejemplo 6: Juego de la ruleta

Es un juego de azar, en el que se gana si el número que se obtiene al tirar la ruleta es igual al seleccionado inicialmente. Como usaremos números de 3 bits, se puede elegir cualquier número entre 0 y 7. El jugador debe seguir los siguientes pasos:

  1. Inicialmente se enciende el LED verde para indicar que el jugador debe introducir por los 3 interruptores el número elegido. Estamos en el modo selección. Al Apretar el botón de enter el número aparecerá en el display de 7 segmentos. Esta elección todavía no es definitiva. Se puede volver a seleccionar otro número y apretar enter tantas veces como se quiera

  2. Una vez satisfecho con el número, se aprieta el botón de tirar. Se apaga el led verde y se enciende el amarillo. Estamos en modo ruleta. Ahora en el display aparecerá el número actual de la ruleta, que inicialmente es el 0.

  3. El jugador tira la ruleta (el spiner). En el display aparecerán los números consecutivamente. Cada vez que un brazo del spiner pasa por sensor de IR suena un pitido y se incrementa el número del display

  4. Cuando se detiene el spiner, bien se emite un pitido grave si el número del display es distinto al seleccionado (el jugador pierde), o bien se activa una sirena acústica y luminosa para indicar que ha ganado

  5. Al cabo de unos segundos, el juego vuelve al estado inicial, y el circuito entra en el modo selección: Se enciende el led verde y se apaga el resto. Volvemos al punto 1

El escenario se muestra a continuación. Dispone de 3 interruptores para introducir el número elegido, dos pulsadores (enter y tirar), 4 LEDs, un zumbador, un display de 7 segmentos, el sensor IR y el spiner que hace de ruleta

En este vídeo se muestra un ejemplo del funcionamiento. Se introduce el número 5 y se pulsa enter. Luego se da al botón de tirar y se dan vueltas a la ruleta. Se falla. Luego se repite el proceso con el número 1. Esta segunda vez se hace trampa y se detiene la ruleta para ganar y ver lo que ocurre

Click to see the youtube video

Visto el funcionamiento y sus partes... ¿Cómo se hace este circuito?

Lo resolveremos por partes, que luego juntaremos. Necesitamos un biestables RS para determinar en qué modo está el circuito: modo selección o modo ruleta. La salida la conectamos a los LEDs indicadores: Verde y Amarillo. Inicialmente se empieza en modo selección y al apretar el botón de tirar se cambia a modo ruleta

El botón de Enter, para elegir número, sólo funciona en el modo selección, por lo que colocamos una AND de habilitación (y una NOT) para que sólo se propage su tic si estamos en este modo. Y lo mismo con el tic que viene del IR de la ruleta: con la AND hacemos que sus tics se habiliten sólo en modo ruleta

De esta forma, si estamos tirando la ruleta en modo selección, no ocurre nada. Y si pulsamos el botón de enter en el modo ruleta, tampoco ocurrirá nada. No podemos elegir otro número mientras esté en marcha

Por los tres interruptores se introduce el número elegido y se captura en un registro de 3 bits al apretar la tecla Enter, si es que estamos en el modo de selección. Si no, se ignora la pulsación.

Los tics que llegan del infrarrojo al tirar la ruleta, si estamos en modo ruleta, llegan a un temporizador de 10ms para generar el pitido del movimiento de la ruleta, y de este llegan a la entrada cnt de un contador de 3 bits, que contiene el número actual de la ruleta. Se incrementa con cada paso del brazo del spiner sobre el sensor

El número elegido y el número actual de la ruleta se llevan a un comparador para saber si son iguales o no. Estos números también se muestran por el display de 7 segmentos, según el modo en el que estemos: selección o ruleta

Una parte muy importante es saber cuándo la ruleta está parada. Usaremos un segundo biestable RS para almacenar este estado. Sabemos que está parada si han pasado al menos 2 segundos desde la recepción del último tic del spiner. Cualquier tic que llegue antes de ese tiempo hace que el temporizador se reinicie y vuelva a empezar la cuenta. Por ello, sólo cuando el temporizador emite el tic de 2 segundos es porque se ha parado. Mientras esté en movimiento el spiner, no emitirá ningún tic de fin

Ya podemos determinar cuando se ha ganado: si el numero elegido es igual al de la ruleta, y la ruleta está parada, entonces se ha ganado. Si los números son diferentes, con la ruleta parada, se ha perdido. Con un par de puertas AND y una NOT obtenemos la señal de acierto y la de fallo

La señal de acierto activa la sirena luminosa y la acústica. El zumbador puede emitir 3 sonidos diferentes, según la situación: el sonido del movimiento de la ruleta, la sirena de acierto, y el tono de fallo. Usando la señal de acierto y el estado de la ruleta configuramos los multiplexores para que se escuche el sonido que toque

Para volver al estado inicial se genera una señal de reset, que inicializa los dos biestables del circuito. Al pararse la ruleta se emite un tic que activa otro temporizador, durante 3 segundos, transcurridos los cuales se emite la señal de reset. Adicionalmente se puede hacer un reset manual, en cualquier momento, apretando el pulsador SW1

Juntando todos estos circuitos parciales, y reodenando los componentes, obtenemos el circuito final que implementa el juego de la ruleta

Ejemplo 7: Control por comandos

Haremos una consola de control, muy básica, para manejar tres dispositivos diferentes: una alarma luminosa, que podemos encender o apagar, una puerta accionada por un servo, que también abrimos o cerramos, y un motor, que además de encenderlo y apagarlo podemos cambiar su sentido de la marcha.

El control de los dispositivos lo hacemos introduciendo un código, o comando, a través de la consola. Este código de 3 bits lo metemos por 3 interruptores y pulsamos la tecla ENTER. En el display se muestra el último comando introducido. El montaje es el siguiente:

El listado de todos los comandos posibles, junto a sus códigos se muestra en la siguiente tabla. En total tenemos 8 comandos

Comando Código Descripción
RESET 0 Apagar todos los dispositivos (volver al estado inicial)
AlarmON 1 Activar Alarma luminosa
AlarmOFF 2 Desactivar Alarma luminosa
OPEN 3 Abrir la puerta (Se mueve el servo)
CLOSE 4 Cerrar la puerta (Se mueve el servo)
MotorON 5 Encender el motor
MotorOFF 6 Apagar motor
MotorDIR 7 Cambiar el sentido de la marcha del motor

El funcionamiento es simple. Se introduce el código en binario y se pulsa Enter. En este vídeo se muestra un ejemplo en acción. Primero ejecuta el comando 1 para activar la alarma, luego el 3 para abrir la puerta y el 5 para encender el motor. Cada vez que se ejecuta el 7, el sentido de giro del motor cambia. Con el 4 se cierra la puerta y finalmente se ejecuta el 0 para apagarlo todo y volver al estado inicial

Click to see the youtube video

¿Cómo se hace este circuito?

Lo haremos por partes y luego lo unimos todo. Empezamos por la parte que ya conocemos y que hemos usado en los circuitos anteriores: la consola. El valor de los 3 interruptores externos se captura en el registro de comando al apretar la tecla ENTER y se muestra su valor en el display de 7 segmentos

Ahora añadimos los comandos. Comenzamos con la alarma. Usamos un biestable RS para almacenar su estado: 0-apagada, 1-activada. Su salida está conectada directamente al bloque de la alarma. Mediante un comparador de un operando determinamos si el comando actual tiene el código 1 (AlarmaON). Su salida se conecta directamente a la entrada set del biestable RS. Con cualquier otro código no se activa el comparador y la alarma permanece en el mismo estado

El comando de apagado de la alarma se implementa con el mismo esquema. Se usa un comparador para saber si el comando introducido es el de código 2 (AlarmOFF). Su salida se conecta a la entrada de reset del biestable de estado

Este mismo esquema lo usamos con el dispositivo 2: La puerta. Un biestable de estado para almacenar si está abierta o cerrada y dos comparadores para los comandos de abrir y cerrar, cuyos códigos son 3 y 4

Y otra vez lo mismo para el Dispositivo 3: el Motor. Los códigos de activación y desactivación son 5 y 6 respectivamente

El comando 7 es diferente. Cada vez que se introduce, el motor cambia de sentido. Por ello, usamos un biestable de cambio, en vez de uno RS, que se conecta a la entrada de dirección del bloque del motor. Además necesitamos un tic para hacer el cambio. Usamos el propio botón de enter. Así, el cambio de sentido se producirá cuando se haya introducido el código 7 y llegue un tic de enter.

El tic del pulsador no se conecta directamente al biestable de cambio, sino que se pasa por un biestable D del sistema para que llegue a la vez que el código del comando. Si no se hiciera, llegaría antes el tic que el omando, y no funcionaría

El último comando es el de RESET, que pone todos los biestables a 0. Lo implementamos con un comparador para el código 0 y la salida la llevamos a la entrada reset de todos los biestables, a través de una puerta OR. Cada dispositivo se pone a 0 bien porque recibe su comando de OFF particular o el RESET

Ponemos todas las partes juntas y tenemos el circuito final

Comunicando la FPGA con un Arduino

Una de las aplicaciones de las FPGAs es crear periféricos o controladores para usarlos desde un microprocesador, como por ejemplo Arduino. En la FPGA hacemos la parte física, con pensamiento hardware, mientras que en el Arduino la parte de programación, con pensamiento computacional, mezclando de esta forma el diseño hardware con el software

Una forma sencilla de empezar es usando comunicaciones serie síncronas entre el Arduino y la FPGA, donde los bits de transmiten uno detrás de otro el cable de datos (un cable por sentido), en los instantes indicados por una señal de reloj. Usaremos además un cable de control para indicar el fin de la transferencia

El Arduino es el Máster: es el que genera la señal de reloj y el que toma la iniciativa en la comunicación, tanto para enviar como para recibir. La FPGA es la esclava. Este tipo de comunicación se llama maestro-esclavo, y es el esquema usado en los buses como el SPI o i2c

Aunque el Arduino incorpora hardware específico para realizar esta comunicación, en los ejemplos de este tutorial lo haremos por todo por software, para comprender mejor el funcionamiento. Primero aprenderemos a enviar datos desde el Arduino a la FPGA (escritura), y luego de la FPGA al arduino (lectura)

Usaremos números de 8 bits (bytes) como unidades de comunicación, aunque podríamos usarlos de cualquier otra longitud. También estableceremos el convenio de enviar primero el bit más significativo y el último el de menor peso, en ambos sentidos de comunicación

Comunicación Arduino -> FPGA (Escritura)

La operación de enviar datos desde el Arduino a la FPGA lo llamaremos escritura. Utilizaremos tres cables: el que lleva los datos del arduino a la FPGA (verde), el que lleva la señal de reloj (gris) y la señal de control (amarillo). Además hay que conectar las masas (GND) de ambas placas (negro)

En esta tabla se muestran los pines usados para las conexiones de los cables de los ejemplos que usaremos

Pin Arduino Pin Alhambra Descripción
D12 D12 Reloj
D11 D11 Datos
D10 D10 Control
GND GND Masa

La recepción de los datos serie se hace usando un registro de desplazamiento de 8 bits. El cable de datos se conecta a la entrada sin y la señal de reloj (clk) a la de desplazamiento

Durante la recepción del dato, el registro se va desplazando y en su salida aparecen valores temporales que NO son el dato final. Por ello, colocamos un registro adicional que capture el dato final, cuando se haya recibido completamente. La orden de captura es la que llega por el cable de control

Circuito sincronizador

Todas las señales que llegan desde el exterior de la FPGA tiene que pasar por un circuito sincronizador, que tiene este aspecto en Icestudio, y que se encuentra en el menú Varios/Entradas/Sync de la colección 29

El sincronizador está formado por dos biestables D en cascada cuya misión es minimizar los problemas de metaestabilidad que suceden cuando la señal exterior cambia en el mismo instante que el flanco de reloj del sistema de la FPGA. En esos casos, el dato capturado no es ni uno ni cero. Se puede encontrar más información sobre este problema en esta entrada de la wikipedia: Metaestabilidad en electrónica digital

Receptor serie síncrono

Nuestro receptor serie síncrono genérico, está formado por las tres señales de entrada: datos, reloj y control, que pasan por su respectivos sincronizadores. Además tenemos el registro de desplazamiento para convertir el dato seria a paralelo y el registro de datos que contiene el dato final recibido

Las señales de reloj y control las convertimos en tics, mediante un conversor de flancos de subida en tics, para poder introducirlas en las entradas de shift y load de los registros de desplazamiento y datos respectivamente

El arduino cada vez que realiza una operación de escritura sobre la FPGA está almacenando un dato de 8 bits en este registro de datos. Según el circuito que se esté implementando, este dato recibido se usará con un fin u otro

Para realizar esta escritura, el Arduino (software) tiene que realizar estas operaciones:

  • Inicialmente las señales de reloj y control deben estar a 0
  • Enviar el dato en seríe, empezando por el bit más significativo. Por cada bit enviado debe generar un pulso en la señal de reloj (escritura de un 1, seguida de un 0)
  • Una vez enviado el octavo bit, se debe generar otro pulso en la señal de control (un 1 y luego un 0) para que el dato se capture en el registro de datos

Software para escritura

Para realizar la escritura desde el Arduino a la FPGA implementamos la función fpga_write(), que envía el dato pasado como parámetro. Se implementa de forma muy fácil usando la función shiftOut de la biblioteca de Arduino

void fpga_write(int value) {
  shiftOut(DAT, CLK, MSBFIRST, value);
  digitalWrite(CTRL, HIGH);
  digitalWrite(CTRL, LOW);
}

La función shiftOut() envía datos de 8 bits de una vez. Con la constante MSBFIRST se indica que envíe primero el bit de mayor peso (que es como lo tenemos configurado en el hardware). Las constantes CLK, DAT y CTRL son los pines de arduino por donde sacar las señales de reloj, datos y control respectivamente

Las señales de reloj y datos las controla la propia función shiftOut. Una vez que se ha enviado el dato, se emite el pulso en la señal de control para que se captura en el registro de datos. Este pulso lo emitimos nosotros, desde la función fpga_write()

Ejemplo 8: Implementación de un puerto de salida adicional para el Arduino

Como ejemplo de escritura de un dato desde el Arduino a la FPGA, vamos a aumentar las salidas de nuestro Arduino usando un puerto de 8 bits en la FPGA. Es el mismo ejemplo usado en este tutorial de Programarfacil.com: Aumentar salidas digitales de Arduino con el shift register, pero usando la FPGA en vez de un registro de deplazamiento discreto

El montaje es el siguiente. Tenemos un Arduino Uno conectado a una Icezum Alhambra, mediante 4 cables: reloj, datos, control y GND. Ambas placas están conectas ordenador a través del USB, para cargar el software en el Arduino y el hardware en la Alhambra

El conexionado entre el Arduino Uno y la Icezum Alhambra se resume en esta tabla

Pin Arduino Pin Alhambra Descripción
D12 D12 Reloj
D11 D11 Datos
D10 D10 Control
GND GND Masa

La implementación del circuito usa el receptor serie síncrono genérico, al que se le ha conectando la salida del registro de datos a los LEDs de la Alhambra, para ver el dato recibido

En el Arduino cargamos un programa de pruebas, que por ejemplo escriba los datos 0x55 y 0xAA cada medio segundo. Primero se configuran los pines a usar. El pin de control se pone a 0. En el bucle principal se envían los dos valores. El programa se puede descargar de aquí: Puerto-secuencia.ino

const int LED = 13;  //-- LED de Arduino
const int CLK = 12;  //-- Pin de reloj
const int DAT = 11;  //-- Pin de Datos
const int CTRL = 10; //-- Pin de control

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(DAT, OUTPUT);
  pinMode(CTRL, OUTPUT);
  digitalWrite(CTRL, LOW);
  digitalWrite(LED, LOW);
}

void fpga_write(int value) {
 shiftOut(DAT, CLK, MSBFIRST, value);
 digitalWrite(CTRL, HIGH);
 digitalWrite(CTRL, LOW);
}

void loop() {
   fpga_write(0x55);
   delay(500);
   fpga_write(0xAA);
   delay(500);
}

Para probarlo, cargamos el hardware en la Alhambra, desde Icestudio, y el software en Arduino, desde el IDE de Arduino. En este vídeo lo vemos en acción. Los valores escritos por el Arduino aparecen en los LEDs. La secuencia se detiene en cualquiera de los siguientes casos: reset de la FPGA, de Arduino o si desconectamos uno de los cables

Click to see the youtube video

¡Bienvenidos al mundo del codiseño Hardware/Software! Ahora podemos tocar ambos mundos fácilmente. En la pantalla de la izquierda tengo Icestudio, para hacer actualizaciones del hardware. En la derecha el Arduino IDE, para modificar el software. Ambos los actualizamos en segundos, y en el orden que queramos

Comunicación FPGA -> Arduino (Lectura)

La operación de recibir datos en el Arduino desde la FPGA lo llamaremos lectura. Igual que para la escritura, Utilizaremos tres cables: reloj, control y datos. La diferencia está en que el cable de datos transmite la información en el sentido FPGA -> Arduino. Para diferenciarlo del otro sentido (Arduino -> FPGA) usaremos el color azúl

En esta tabla se muestran los pines usados para las conexiones de los cables de los ejemplos que veremos

Pin Arduino Pin Alhambra Descripción
D12 D12 Reloj
D10 D10 Control
D9 D9 Datos
GND GND Masa

La transmisión de los datos desde la FPGA se hace usando un registro de desplazamiento de 8 bits. El cable de datos se conecta a la salida serie so, la señal de reloj (clk) a la de desplazamiento (shift) y la señal de control a la de carga paralela. El dato de 8 bits se introduce por la entrada paralela d del registro

Transmisor serie síncrono

Nuestro transmisor serie síncrono genérico está formado por dos señales de entrada: reloj y control, que pasan por su respectivos sincronizadores y llegan al registro de desplazamiento, que convierte el dato a enviar de paralelo a serie. El dato se envía por la salida seríe so, y pasa por un biestable D del sistema para eliminar los espúreos (glitches) y cumplir con las reglas del diseño síncrono

El dato a enviar normalmente estará almacenado en un registro, que llamaremos registro de datos, pero podría venir de cualquier otro lugar: una memoria, un contador, etc, por eso no se ha incluido en el esquema genérico

Para realizar la lectura, el Arduino (software) tiene que realizar estas operaciones:

  • Inicialmente las señales de reloj y control deben estar a 0
  • Envíar un pulso por la señal de control, para que la FPGA capture el dato a enviar
  • Recibir el dato en serie, empezando por el bit más significativo. Cada vez que lee un bit debe segenera un pulso en la señal de reloj
  • Al cabo de 8 pulsos de reloj ya tiene el dato completo

Software para lectura

Para realizar la lectura de la FPGA desde el Arduino implementamos la función fpga_read(), que devuelve el dato recibido. Se implementa de forma muy fácil usando la función shiftIn de la biblioteca de Arduino

byte fpga_read() {
  byte c;

  //-- Indicar a la FPGA que capture el dato
  digitalWrite(CTRL, HIGH);
  digitalWrite(CTRL, LOW);

  //-- Recibir el dato
  c =  shiftIn(DAT, CLK, MSBFIRST);
  return c;
}

La función shiftIn() recibe un dato de 8 bits de una vez. Con la constante MSBFIRST se indica que recibe primero el bit de mayor peso (que es como lo tenemos configurado en el hardware). Las constantes CLK, CTRL y DAT son los pines de arduino por donde sacar las señales de reloj y control, y el pin por donde se reciben los datos respectivamente

La señal de reloj la controla la propia función shiftIn. Antes de invocarla es necesario generar un pulso en la señal de control para que la FPGA capture el dato. Lo emitimos nosotros al comienzo de la función fpga_read()

Ejemplo 9: Implementación de un puerto de entrada adicional para el Arduino

Como ejemplo de lectura de un dato de la FPGA desde el Arduino, vamos a implementar un puerto de entrada adicional de 8 bits. En los bits menos significativos de este puerto conectaremos tres interruptores, y desde el Arduino leeremos sus valores

El montaje es el siguiente. Tenemos un Arduino Uno conectado a una Icezum Alhambra, mediante 4 cables: reloj, datos, control y GND. Ambas placas están conectas ordenador a través del USB, para cargar el software en el Arduino y el hardware en la Alhambra

Por los 3 pulsadores se introduce un valor y se pulsa el botón de ENTER para capturarlo. Su valor se muestra en el display de 7 segmentos. Ese será el valor que lea el arduino, que mostrará por la consola serie

La implementación del circuito usa el transmisor serie síncrono genérico, al que se le ha conectado por su entrada paralela el registro de datos que contiene el número introducido por los interruptores. Este registro es el que se muestra por el display de 7 segmentos

El registro de desplazamiento es de 8 bits, mientras que el registro de datos es de 3 bits. Por eso se usa un elemento acoplador para conectarlos, que simplemente añade ceros en los bits de mayor peso del registro de datos. Este elemento se encuentra en el menú Varios/Bus/3-bits/Acoplador-3-8

En el Arduino cargamos un programa de pruebas que lee constantemente el registro de datos, y si detecta algún cambio, muestra el número por la consola serie. El programa completo se puede descargar de aquí: Puerto-entrada.ino y se muestra a continuación:

const int LED = 13;
const int CLK = 12;  //-- Pin de reloj
const int DAT = 9;   //-- Pin de Datos
const int CTRL = 10; //-- Pin de control

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(DAT, INPUT);
  pinMode(CTRL, OUTPUT);
  digitalWrite(CTRL, LOW);
  digitalWrite(CLK, LOW);
  digitalWrite(LED,LOW);
  Serial.begin(9600);
}

byte fpga_read() {
  byte c;

  //-- Indicar a la FPGA que capture el dato
  digitalWrite(CTRL, HIGH);
  digitalWrite(CTRL, LOW);

  //-- Recibir el dato
  c =  shiftIn(DAT, CLK, MSBFIRST);
  return c;
}

void loop() {
  byte dat;
  byte old = 0xff;

  //-- Leer el puerto constantemente
  while(1) {

    //-- Leer dato de la FPGA
    dat = fpga_read();

    //-- Si ha habido un cambio desde la última
    //-- lectura, mostrarlo en la consola
    if (dat != old) {
      Serial.write(dat+'0');
      Serial.write("\n");
    }  

    //-- Almacenar dato leido para saber
    //-- si hay cambios
    old = dat;  
  }
}

En el bucle principal se lee el dato en la variable dat, y en old se encuentra el valor leido anteriormente. Si son diferentes, se que ha habido un cambio con respecto a la lectura anterior, por lo que se imprime el número. Para pasar de binario a ASCII sólo hay que sumar el carácter '0': Serial.write(dat+'0');

Para probarlo, cargamos el hardware en la Alhambra, desde Icestudio, y el software en Arduino, desde el IDE de Arduino. En este vídeo lo vemos en acción. Los números introducidos en los switches se muestra en la consola serie del Arduino al apretar el botón de Enter

Click to see the youtube video

Ejercicios propuestos (20 BitPoints)

Ver los detalles de los ejercicios y las entregas en el menú Archivos/Ejemplos/2-Ejercicios de la colección de este tutorial

Resumen:

  • Ejercicio 29.1 (Total 5 Bitpoints): Cinta trasportadora manual de LEDs

Simulación de un cinta transportadora en los 8 LEDs de la placa. El avance de la cinta se hace manualmente, mediante un spiner. Cada tic generado por el spiner se usa para que los LEDs avancen una posición. El LED que sale por el más significativo vueve a entrar por el menos significativo, en un bucle espacial. Cada vez que hay un tic de avance se genera un sonido de 10ms

La cinta transportadora se carga apretando un pulsador externo. Se enciende un LED para indicar que hay un objeto nuevo que se introducirá en la cinta en el siguiente tic de avance. Al entrar, el LED de carga se apaga. El objeto que ha entrado se queda dentro de la cinta todo el rato. Se pueden introducir varios objetos

Si la cinta se para durante más de 2 segundos, se emitirá un tono diferente, de 1 segundo de duración, para avisar de que está detenida

En este Vídeo se muestra un ejemplo de funcionamiento:

Click to see the youtube video

  • Ejercicio 29.2 (Total 5 Bitpoints): Servidor hardware para Arduino

Implementar un circuito que dote a Arduino de 3 puertos de salida de 4 bits. El puerto 0 muestra los bits en los LED del 3 al 0. El puerto 1 en los LEDs 7 -4, y el puerto 2 en un display de 7 Segmentos. Estos puertos se implementarán mediante comunicaciones serie síncronas entre el Arduino y la FPGA.
Desde el Arduino se envían comandos de 8 bits, que tienen el siguiente formato:

  • Los 4 bits menos significativos contienen el valor a sacar por el puerto especificado
  • Los 4 bits más significativos indican el puerto por el que sacar la información
Bits 7-4 Comando Descripción
0000 0 Acceso al puerto 0
0001 1 Acceso al puerto 1
0010 2 Acceso al puerto 2
resto x Comando inválido

Se usará un LED externo para indicar si el comando recibido es válido o no. En caso de recibirse uno erróneo, se enciende. Cuando se recibe uno correcto se apaga. Se usa sólo como indicador y no se tomará ninguna acción cuando
el comando es erróneo.

El software del Arduino implementará un contador de segundos, mostrando en el display de 7 segmentos (puerto 2) los dígitos 0 - 9, uno cada segundok. Al pasar medio segundo, se encienden todos los LEDs del puerto 1, y se
apagan los del puerto 2. Al trancurrir un segundo completo, se apagan los del puerto 1, se encienden los del 0 y se actualiza el dígito en el puerto 2

Antes de comenzar la cuenta, se enviará un comando erróneo, para comprobar que el LED indicador se encience. Y se esperará 2 segundos antes de ejecutar el bucle principal anterior

En este Vídeo se muestra un ejemplo de funcionamiento:

Click to see the youtube video

  • Ejercicio 29.3 (Total 5 Bitpoints): Puerto de entrada y salida en Arduino

Implementar un puerto de entrada y de salida adicionales para Arduino, mediante comunicaciones serie síncronas. El puerto de entrada debe estar conectado a un contador de 8 bits que cuenta las pasadas que da un spiner (las veces que se interrumpe el haz de infrarrojos). Este spiner está simulando un encoder. Cada vez que el Arduino realice una lectura de este puerto, se le devolverá el valor de este contador. El puerto de salida está conectado al display de 7 segmentos. Sólo se usarán los 4 bits de menor peso. El programa principal de Arduino deberá leer el valor del encoder y mostrar su valor en la consola serie. Además, deberá sacar por el display de 7 segmentos el dígito decimal (0-9) correspoindinete a la lecturadel enconder. Así, si la cuenta del encoder va por 157, en el display se muestra el dígito 7 (Esto hay que hacerlo mediante código en Arduino. Es fácil de hacer con la instrucción sprintf y formato %03d (que almacena un número rellenando con 0s los digitos no usados de mayor peso. De esta forma, el dígito de menor peso está siempre en la posicion 2 del array: cad[2])

Click to see the youtube video

  • Ejercicio 29.4 (5 Bitpoints). Ejercicio Libre. Premiar la creatividad. Entregar por redes sociales o github: Pantallazos, enlaces, vídeos, etc...

Ejercicios entregados

Josep Montoliu (Klarojms)

Ejercicio 29.1

  • Vídeo en Youtube:

Click to see the youtube video

Ejercicio 29.2

  • Vídeo en Youtube:

Click to see the youtube video

Ejercicio 29.3

  • Vídeo en Youtube:

Click to see the youtube video

Ejercicio 29.4

  • Vídeos en Youtube:

Click to see the youtube video

Click to see the youtube video

Andrés (@Avarez_)

Ejercicio 29.1

Ejercicio 29.2

Ejercicio 29.3

Manuel Andújar (Matalobo)

Ejercicio 29.1

Ejercicio 29.2

Ejercicio 29.3

Autor

Licencia

Créditos y agradecimientos

Enlaces

Preguntas frecuentes

  • ¿Dónde puedo conseguir la placa Icezum Alhambra?

Pueden conseguir una desde Alhambrabits

  • ¿Dónde puedo comprar material electrónico?. Hay muchos sitios. Uno muy bueno es Bricogeek

  • ¿Cómo aprendo a manejar github?

Hay mucha información en internet. En su momento hice este Tutorial: Github y FreeCAD para enseñar a manejarlo. Los ejemplos están hechos con ficheros de FreeCAD, sin embargo, lo que se enseña es genérico. También vale para las entregas de los ejercicios del tutorial de Electrónica digital para makers

  • Los pulsadores de la Icezum Alhambra no me funcionan

Eso es debido a que se han metido restos de flux y no hacen buen contacto. En el apartado ¡No me funcionan los pulsadores! del Tutorial 9 se indica cómo solucionarlo fácilmente

  • ¿Dónde puedo encontrar más información sobre las señales PWM?

Echa un vistazo a este post de Rincón Ingenieril sobre el tema

  • He conectado un pulsador externo pero no me funciona. He hecho un circuito para conectar el botón con un led, y al apretar se enciende el LED, pero luego no se apaga. NO funciona bien

Los pulsadores externos que se conecten a los pines de 5v de la Alhambra (D0 - D13) tiene que llevar una resistencia de pull-up o pull-down con valores entre 460 ohm y 2K. Típicamente usamos 1K. Esto hace que los conversores de nivel se configuren como entradas y que el pulsador funcione correctamente. Puedes encontrar más información En este enlace

Es un servo muy usado y muy estándar. Si buscar por internet encontrarás muchos sitios donde los vendes, a diferentes precios. Aquí en España se puede conseguir muy fácilmente a través de BricoGeek: Servo SM-4303R Bricogeek y también en Iberobotics: Servo SM-4303R Iberobotics

  • Parece ser que los servos Futaba 3003 se pueden trucar para convertirlos en rotación continua. ¿Conoces algún tutorial sobre como hacerlo?

El Futaba 3003 es uno de los servos que típicamente se han trucado para construir robots móviles con ellos. Robots como Tritt, El Skybot o el Miniskybot los utilizan. Existen muchísimos tutoriales para hacerlo. En esta página puedes encontrar todas las formas de trucarlos. El que recomendamos es el caso 2

Clone this wiki locally