-
Notifications
You must be signed in to change notification settings - Fork 2
S6: Comunicaciones
- Tiempo: 2h
- Fecha: Lunes, 21 de Noviembre de 2022
-
Objetivos de la sesión:
- Conocer las formas de comunicación de datos entre circuitos
- Aprender a comunicar el Arduino con la FPGA
- Comunicar la FPGA con el PC por puerto serie
- Introducción
- Comunicaciones en paralelo
- Comunicaciones serie
- Comunicación Arduino-FPGA
- Comunicación FPGA-PC: Puerto Serie
- Componentes virtuales en la FPGA
- Autor
- Licencia
- Créditos
- Enlaces
Nuestros circuitos tiene que comunicarse con otros circuitos, para enviarles datos o recibir información de ellos. Estos circuitos pueden estar dentro de la propia FPGA, o bien en chip diferentes. En esta sesión veremos cómo se realiza el intercambio de información y veremos ejemplos de dos mecanismos muy usados: El SPI y el puerto serie. Aprenderemos a comunicar nuestro Arduino con la FPGA, y la FPGA con nuestro PC
Este es nuestro esquema general, que iremos refinando. Queremos que dos circuitos digitales intercambien información. ¿Cómo lo hacemos?
En cualquier sistema de comunicación tenemos dos aspectos:
- El dato a transmitir. Tiene un tamaño. Y una forma de transmitirse
- Evento de transmisión: Cuándo transmitir el dato
- Sentido de la transmisión: Los datos van de un origen a un destino
Empezaremos por lo que ocurre en el interior de los chips. En nuestro caso, en el interior de la FPGA. Los datos se transmiten en paralelo, usándose un cable para cada bit
En esta figura se muestra un ejemplo de transmisión de un dato de 8 bits desde un circuito origen (el transmisor) a otro destino (el receptor). Todos los bits se transmite a la vez, por 8 cables independientes
En los esquemas de estos circuitos NO se dibujan todos los cables, para que sean legibles. En su lugar se dibuja un cable con una barra cruzada que indica el número de bits que se transmiten en paralelo. Además, se pueden añadir etiquetas para no tener que dibujar el bus completo desde el extremo inicial al final
En este esquema de ejemplo se han conectado dos circuitos a través de un cable de bus de 64 bits. Aunque no se dibujen todos los cables... ¡En realidad hay 64 cables que transportan los bits!
Para indicar cuando se transmite un dato se utiliza una señal que solemos denominar señal de reloj. Cada flanco de subida de esta señal indica que hay datos disponibles que se deben capturar. Estos datos están sincronizados con esta señal de reloj, por lo que se dice que trata de una comunicación síncrona
Las transmisiones más rápidas son las que se realizan en paralelo y a la velocidad del reloj sistema: Comunicaciones síncronas en paralelo. Cada transferencia dura 1 ciclo de reloj
En esta figura se muestra un ejemplo del funcionamiento. Hay 3 registros de 16 bits conectados en cadena, inicializados a 0000
. En cada ciclo de reloj los datos pasan del registro de la izquierda al de la derecha. En el estado inicial hay un DATO
disponible a la entrada del registro 1
En el momento inicial, los tres registros están a 0. Cuando llega el prier flanco, el DATO
se captura en el registro 1. En el siguiente ciclo pasa al registro 2. Y en el tercer ciclo al registro 3. Así, el DATO
tarda 1 ciclo en pasar de un registro a otro. Ha tardado 3 ciclos en llegar al registro 3
La transmisión no tiene por qué hacerse de manera continua, en cada ciclo de reloj, sino que nos puede interesar controlar cuándo queremos que haya transferencia y cuando no. Para ello utilizamos la señal de validación (Strobe). Es una señal de control que indica que hay un dato disponible para que el receptor capture
En este ejemplo el circuito 1 envía un dato de 8 bits al registro, que se captura cuando se activa la señal de validación (strobe)
Este es el cronograma. Inicialmente la señal de validación está desactivada. Por la salida del circuito 1 sale un valor cualquiera. Al llegar el primer ciclo el circuito 1 envía el DATO
. Pero todavía no lo captura el registro. En el siguiente ciclo el circuito 1 activa la señal de validación, para indicar que el registro ya sí que puede capturar el dato. En el siguiente flanco se captura, por lo que en el tercer ciclo ya está el dato en el registro
Ahora el flujo de información se controla con la señal de validación. Hasta que no se activa no hay transferencia de información
En los circuitos receptores que hemos usado en los ejemplos anteriores (un registro) el dato se captura en un ciclo, pero podría ocurrir que el circuito tarde tiempo en realizar esta escritura, de manera que no se le puede enviar información en cualquier momento, sino sólo cuando está disponible
En estos casos el circuito emisor tiene que tener en cuenta el estado del receptor: si está ocupado (busy) o disponible (ready). Para eso se utiliza la señal de busy (o su negada, ready)
En este cronograma se muestra el funcionamiento. El dato sólo se transfiere cuando el receptor está diponible (busy = 1). Al realizar la escritura, el receptor pone busy a 1. Cuando ha finalizado la operación se vuelve a poner a 0 y se puede realizar otra transferencia
Las comunicaciones en paralelo son muy rápidas, pero consumen muchos cables. Esto cobra especial importancia fuera del chip, cuando estamos conectando dos elementos. Si uno le tiene que enviar a otro un valor de 32 bits, ¡habría que tirar 32 cables externos!
Para reducir el número de cables entre chips, usamos las comunciaciones serie. Para transmitir un dato del emisor al receptor ahora necesitamos sólo 1 cable. Los bits se envían secuencialmente uno detrás de otro por ese cable
En este ejemplo el chip 1 le ha transitido el valor 0x55
al chip 2 a través de un único cable, mediante comunicación serie. Por el cable se han enviado secuencialmente los 8 bits del dato: 01010101
Ahorramos muchos cables, y los diseños son más limpios, PERO las comunicaciones serie son MUCHO MÁS LENTAS. En el ejemplo anterior, necesitamos al menos 8 ciclos de reloj para transmitir todos los bits, mientras que en el caso paralelo lo hacíamos sólo en 1 cicloj
Para que la comunicación serie funcione, los dos extremos tienen que seguir las mismas reglas. El diseñador debe decir el orden de envío de los bits: empezar por el de menor peso o por el mayor. También se debe determinar qué evento usar para indicar que ha llegado un bit. Se puede usar una señal de reloj adicional para validar cada bit, o bien cada chip usa su propio reloj interno. Esto nos divide las comunicaciones serie en dos tipos: Síncronas y Asíncronas
En las comunicaciones serie síncronas se utilizan dos cables: uno para el envío de los bits en serie y otro con la señal de reloj que indica cuándo se puede capturar cada bit
La señal de reloj se suele denominar SCK y define el tiempo que transcurre entre cada bit. En este cronograma se muestra la transferencia de un dato de 8 bits. Se envía primero el bit más significativo
Algunos ejemplos de comunicaciones serie síncronas son los buses SPI e I2C
En el Bus SPI hay un circuito, el maestro que lleva la voz cantante de la comunicación. Es el que genera la señal de reloj SCLK. Se puede comunicar con uno o varios circuitos, que se llaman esclavos
Este es el esquema de comunicación punto-punto, entre un maestro y un esclavo
Se utilizan 4 cables en total:
- MOSI (Master-Out, Slave-IN): Datos en el sentido maestro-esclavo
- MISO (Master-In, Slave-Out): Datos en el sentido esclavo-maestro
- SCLK: Señal de reloj para cada bit, generada por el maestro
- SS: (Slave Select): Selección del esclavo
Este es el esquema de comunicación multipunto, en el que el maestro se comunica con varios esclavos. Sólo el esclavo seleccionado mediante la correspondiente señal ss es el que recibe los datos de MOSI y envía las respuestas por MISO
El Bus I2C también es de tipo Maestro-esclavo. Está pensando para conectar fácilmente muchos esclavos, usando sólo 2 cables, uno de datos y otro de reloj, que se denominan SDA y SCL respectivamente
Este es el esquema de conexión punto-punto entre el maestro y un esclavo. El cable de datos SDA es bidireccional
En esta figura se muestra la conexión de un maestro con dos esclavos. Cada uno de los esclavos tiene asignada una dirección.
En las comunicaciones serie asíncronas cada uno de los circuitos tiene su propio reloj para detectar la llegada de los bits, por eso NO hay cable con la señal de reloj. En esta figura se muestra la conexión entre dos circuitos, utilizando comunicaciones asíncronas. Sólo se necesitan 2 cables de datos, cada uno para un sentido de la transmisión
La comunicación es punto-punto entre dos iguales, y full-duplex. No hay maestros ni esclavos. La señal del emisor al receptor se llama TX. La del receptor al emisor se llama RX
En este cronograma se muestra cómo se realiza la transmisión del dato 0x53
. En total se transmite los 8 bits más un bit de comienzo (start) a 0 y un bit de stop a 1
La velocidad de la transmisión debe ser conocida a priori tanto por el emisor como por el receptor. Se utiliza en baudios (que son bits por segundo). Dos velocidades muy típicas son 9600 y 115200 baudios
Cuando utilizamos este tipo de comunicación desde el PC lo denominamos comunicación por puerto serie
En la FPGA hemos aprendido a diseñar controladores hardware para nuestros robots: unidades de PWM, procesamiento de sensores, control de servos... Desde Arduino hacemos programas (software) para implementar comportamientos en el robot. Para comunicarnos con los controladores hardware en la FPGA utilizaremos el bus SPI
Este es el esquema de conexionado entre Arduino y la FPGA
Estos son los pines que vamos a utilizar:
Pin Arduino | Pin Alhambra | Descripción |
---|---|---|
D13 | D13 | SCLK. Señal de reloj |
D12 | D12 | MISO. Datos desde la FPGA al Arduino |
D11 | D11 | MOSI. Datos desde el Arduino a la FPGA |
D10 | D10 | SS. Selección de la FPGA |
Veremos dos ejemplos, uno para escribir información de Arduino a la FPGA y para leer datos de la FPGA en Arduino
El circuito en la FPGA está formado por un registro de desplazamiento hacia la izquierda, que captura los 8 bits que llegan en serie. Se captura un bit por cada flanco de subida del reloj SCLK. Cuando termina de enviarse el dato la señal SS se pone a 1, capturándose el dato en un registro de 8 bits, cuya salida está conectada a los LEDs
Este mismo ejemplo se puede implementar utilizando el bloque spi esclavo, que encapsula todo lo necesario. Así es más facil integrar las comunicaciones en nuestros circuitos
Este es el programa que se carga en Arduino. Desde el bucle principal se envían los valores 0x55
y 0xAA
a la FPGA cada medio segundo
#include <SPI.h>
//-- Pin usado para la seleccion del esclavo
#define SS 10
void setup() {
//-- Inicializar SPI
SPI.begin();
SPI.beginTransaction (SPISettings (4000000, MSBFIRST, SPI_MODE0));
}
//-- Enviar un valor por el SPI para
//-- sacarlo por los LEDs de la FPGA
void write_LEDs(uint8_t value)
{
digitalWrite(SS, LOW);
SPI.transfer(value);
digitalWrite(SS, HIGH);
}
void loop() {
//-- Sacar valor 0xAA por los LEDs
write_LEDs(0xAA);
delay(500);
//-- Sacar valor 0x55 por los LEDs
write_LEDs(0x55);
delay(500);
}
Bienvenidos al mundo del codiseño Hardware-Software!!. Una parte se hace en software y la otra en Hardware
Para leer información de la FPGA se utiliza también un registro de desplazamiento hacia la izquierda. Se usa la señal SS para realizar la carga del dato a enviar al maestro en el registro. La señal del reloj (SCLK) realiza el desplazamiento de los bits, que se van enviando por la salida del esclavo (MOSI)
En este ejemplo se envían al maestro (Arduino) el estado de los dos pulsadores SW1 y SW2
Este es el mismo ejemplo, pero usando el bloque SPI esclavo
Este es el programa que se carga en el arduino. Realiza una lectura por el SPI cada 300ms. El valor recibido se muestra en dos leds conectados a arduino (LED1 y LED2) y se envía por el puerto serie al PC para verlo en el monitor serie
#include <SPI.h>
//-- Pin usado para la seleccion del esclavo
#define SS 10
//-- Pin de los LEDs
#define LED1 7
#define LED2 6
void setup() {
//-- Configurar los LEDs
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
//-- Inicializar SPI
SPI.begin();
SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0));
Serial.begin(9600);
}
//-- Leer los pulsadores de la FPGA,
//-- a través del SPI
uint8_t leer_pulsadores()
{
//-- Activar el esclavo
digitalWrite(SS, LOW);
//-- Leer el valor. A la vez hay que enviar
//-- otro dato. Mandamos un 0 (basura)
uint8_t value = SPI.transfer(0x00);
//-- Desactivar el esclavo
digitalWrite(SS, HIGH);
return value;
}
void loop()
{
//-- Leer los pulsadores
uint8_t estado = leer_pulsadores();
//-- Encender el LED1 si el pulsador SW1 está apretado
digitalWrite(LED1, estado & 0x01);
//-- Encender el LED2 si el puslador SW2 está apretado
digitalWrite(LED2, estado & 0x02);
//-- Mostrar el valor leido por puerto serie
Serial.write(estado+'0');
Serial.write("\n");
delay(300);
}
Desde la FPGA podemos enviar información al PC, y recibirla de él, a través del puerto Serie. Para ello necesitamos utilizar los bloques serial-tx y serial-rx disponibles en la colección stdio
Este circuito envía el carácter 'A' cada vez que se aprieta el pulsador SW1
A través del Monitor serie de Icestudio podemos ver los caracteres recibidos:
En este ejemplo se utiliza el receptor serie para mostrar por los LEDs los caracteres enviados desde el PC a través del terminal serie
La comunicación por puerto serie nos permite implementar interfaces gráficos en el PC con componentes virtuales. Los componentes virtuales más básicos son los pulsadores y los LEDs. Al apretar un pulsador en la interfaz gráfica se envía el evento por el puerto serie a la FPGA y un componente obtiene una señal de estado, que se puede utilizar igual que si viniese de un componente real
Este es un ejemplo de un panel con 4 interruptores, 4 pulsadores y 8 LEDs virtuales
Está accesible desde esta página web: PANEL DE PRUEBA. Es necesario utilizar un navegador que soporte WebSerial, como Chrome o Chromium
En este ejemplo se llevan las 8 entradas virtuales (4 pulsadores y 4 interruptores) a los LEDs reales, para verlos. Y los dos pulsadores reales se muestran por los LEDs etiquetados como a
y b
- Juan González-Gómez (Obijuan)
- Cuaderno Técnico FPGAs Libres. CT8: Entrada y salidas de bits con componentes virtuales
- Tutorial FPGAs Libres para makers: Puerto serie
- Cuaderno Técnico FPGAs Libres. CT5: SPI Esclavo
- Cuaderno Técnico FPGAs Libres. CT6: SPI Maestro
- Tutorial FPGAs Libres para makers: Registros y comparadores. Comunicación FPGA-Arduino
- Universidad Rey Juan Carlos de Madrid
- Escuela Técnica Superior de Ingeniería de Telecomunicaciones (URJC)
- L15: FPGAs Libres. Icestudio
- L16: FPGAs (II). Domadores de bits
- L17: FPGAs (III). Señales y tiempo
- L18: Control digital de motores
- S7: Procesadores en FPGA: RISC-V
- S1: Robots
- S2: Estructuras mecánicas
- S3: Estructuras mecánicas (II)
- S4: Estructuras mecánicas (III)
- S5: Sensores binarios
- S6: Comunicaciones