← Índice del curso

Comunicación entre Dispositivos · Tema 7

UART: la trama asíncrona

Es el puerto serie de toda la vida: dos cables de datos, ningún reloj compartido y un carácter enmarcado entre un bit de inicio y uno de parada. La UART es el primer bus del curso, el que usás cada vez que escribís Serial.println, y entenderla bit a bit es la mejor base para todo lo que sigue.

01 · Punto de partida

El puerto serie de siempre

UART significa Universal Asynchronous Receiver/Transmitter: receptor y transmisor asíncrono universal. Es un periférico que prácticamente todo microcontrolador trae integrado, y su trabajo es conceptualmente simple: tomar un byte, convertirlo en una secuencia de bits con un ritmo fijo y sacarlo por un pin (TX); y a la inversa, escuchar otro pin (RX), reconocer los bits que llegan y entregar el byte armado.

En el tema 3 vimos por qué una comunicación asíncrona necesita un bit de inicio y uno de parada, y en el tema 4 calculamos cuánto tarda y cuánto rinde. Ahora estudiamos la UART completa: cómo se conecta, cómo se compone cada trama, para qué sirve la paridad, qué hay dentro del periférico y cómo se lee una trama en un osciloscopio o analizador lógico. La configuración fina (divisores de baudios y errores) sigue en el tema 8, y el manejo desde el programa (buffers e interrupciones) en el tema 9.

02 · Cableado

Cómo se conectan dos UART

La UART usa un cable por cada sentido y una referencia común. Cada dispositivo tiene un pin TX (transmite) y un pin RX (recibe), y la regla es siempre la misma: el TX de un lado va al RX del otro. Los nombres describen lo que hace el pin desde el punto de vista de su propio chip, así que si unís TX con TX, dos salidas quedan enfrentadas y nadie escucha.

Conexión cruzada de dos UART El pin TX del dispositivo A se conecta con el pin RX del dispositivo B, y el pin TX de B con el pin RX de A, de manera cruzada. Las masas de ambos dispositivos están unidas por un tercer cable. Dispositivo ADispositivo B TXRXGND TXRXGND 3 cables: TX → RX, RX ← TX y masa común Los niveles de A y B deben ser compatibles (tema 5)

La masa es tan importante como los datos: sin ella, las tensiones de TX y RX no tienen una referencia común. Y los niveles de tensión deben ser compatibles, como estudiamos en el tema 5: una UART de 5 V hacia una de 3,3 V pide un divisor o un traductor de nivel.

Niveles TTL, RS-232 y adaptadores USB

La UART del microcontrolador entrega niveles lógicos de 0 V a VCC (el «TTL serie» o «UART TTL»). No es lo mismo que el RS-232 de los puertos serie antiguos, que usa tensiones de ±3 V a ±15 V y lógica invertida; conectarlo directamente a un pin de un microcontrolador lo destruye. El RS-232 se adapta con un conversor de niveles (tema 22).

Para hablar con una computadora actual se usa un adaptador USB–serie (chips CH340, CP2102 o FT232): del lado USB aparece como un puerto COM o /dev/ttyUSB0, y del otro lado entrega TX y RX en niveles de 3,3 V o 5 V (muchos módulos tienen un puente para elegir). Las placas Arduino traen uno integrado: en el Uno, los pines 0 (RX) y 1 (TX) están compartidos con ese conversor, así que conviene no conectarles nada mientras se carga un programa ni mientras se usa el monitor serie.

Cantidad de UART por hardware en algunas plataformas habituales.
PlataformaUART de hardwareObservaciones
Arduino Uno y Nano (ATmega328P)1Compartida con el USB; para otra, SoftwareSerial (por software, más limitada)
Arduino Mega 25604Serial, Serial1, Serial2 y Serial3
ESP323UART0 para consola y programación; las otras se pueden asignar a casi cualquier pin; FIFO de hardware de 128 bytes
Raspberry Pi Pico (RP2040)2Pines configurables

03 · La trama

La trama paso a paso

Cada carácter viaja en una trama que sigue siempre el mismo orden. Con el reposo en nivel alto, la secuencia es:

  1. Bit de inicio (start): un 0 que dura un bit. Su flanco de bajada avisa al receptor que empieza un carácter y le permite alinear su reloj.
  2. Bits de datos: entre 5 y 9, habitualmente 8, enviados con el bit menos significativo (LSB) primero.
  3. Bit de paridad (opcional): un bit de verificación calculado a partir de los datos.
  4. Bit(s) de parada (stop): uno o dos bits en nivel alto (1). Devuelven la línea al estado de reposo y garantizan que el siguiente bit de inicio produzca un flanco. Con 5 bits de datos se usaba históricamente 1,5 bits de parada.
Trama UART 8E1 del carácter C La línea está en reposo en nivel alto, baja un bit para el inicio, transmite los ocho bits de datos del carácter C (0x43) con el menos significativo primero: 1, 1, 0, 0, 0, 0, 1, 0; sigue el bit de paridad par con valor 1, el bit de parada en nivel alto y vuelve al reposo. reposoinicio D0D1D2D3D4D5D6D7 paridadparadareposo 0 11000010 11 8 bits de datos: 0x43 = 0100 0011, enviados del bit 0 al bit 7 control Carácter «C» en formato 8E1 (11 bits en la línea) 3 bits en 1 en los datos + paridad 1 = 4 (cantidad par)

El ejemplo muestra la letra C (código ASCII 0x43 = 01000011). Como se envía el bit 0 primero, en la línea aparece al revés de como se escribe: 1 1 0 0 0 0 1 0. Este es el error de lectura más habitual al decodificar una captura: la secuencia hay que invertirla para leer el valor en la notación habitual.

Cada bit dura el mismo tiempo, 1/velocidad. A 9600 baudios, 104,2 µs. La trama completa de la figura ocupa 11 bits (inicio, 8 datos, paridad y parada), y el reposo entre caracteres puede durar cualquier tiempo: es la parte «asíncrona».

04 · Un chequeo barato

El bit de paridad

El bit de paridad es un sistema de detección de errores mínimo: se agrega un bit a los datos para que la cantidad total de unos tenga siempre una paridad conocida. El receptor cuenta los unos de lo que llegó, y si la cantidad no tiene la paridad esperada, sabe que algún bit cambió en el camino.

  • En paridad par (even, E) el bit se elige de modo que el total de unos (datos más paridad) sea par.
  • En paridad impar (odd, O) el total debe ser impar.
  • Hay variantes de «marca» (siempre 1) y «espacio» (siempre 0), que no verifican nada y se usan en protocolos especiales.

El cálculo es un XOR de todos los bits de datos: da 1 si la cantidad de unos es impar. Ese resultado es el bit de paridad par; su negado es el de paridad impar.

Bit de paridad de algunos bytes.
ByteBinarioCantidad de unosParidad parParidad impar
0x43 («C»)0100 0011310
0x41 («A»)0100 0001201
0x7E0111 1110601
0x7F0111 1111710
0xFF1111 1111801
0x000000 0000001

La paridad tiene un alcance limitado: detecta cualquier cantidad impar de bits invertidos (1, 3, 5…), pero deja pasar una cantidad par (2, 4…), porque la paridad total no cambia. Un ruido que alterara dos bits del mismo carácter pasaría inadvertido. Además cuesta un bit por carácter: a 9600 baudios, una trama 8E1 rinde unos 873 bytes por segundo en lugar de 960. Por eso, en comunicaciones modernas se confía más en un CRC por mensaje (tema 44) que en la paridad por carácter, y la mayoría de los enlaces UART de aficionados trabajan sin paridad (8N1).

05 · Notación

Formatos: 8N1, 8E1, 7E1…

La configuración de una UART se resume en tres símbolos: bits de datos, paridad (N = ninguna, E = par, O = impar) y bits de parada. «8N1» significa 8 bits de datos, sin paridad y 1 bit de parada: es el formato por defecto de casi todo. Ambos extremos deben usar el mismo.

Bits por carácter y eficiencia (datos / bits en la línea) de algunos formatos.
FormatoBits en la líneaEficienciaCuándo se usa
8N11080,0 %El estándar de facto: módulos, consola, GPS, Bluetooth serie
8E11172,7 %Equipos industriales y protocolos que piden detección de errores
8N21172,7 %Receptores lentos que necesitan más tiempo entre caracteres
8E21266,7 %Algunas variantes de Modbus y equipos antiguos
7E11070,0 %Texto ASCII de 7 bits con paridad, en equipos heredados
9N11181,8 %Direccionamiento en redes multiprocesador

Los 9 bits de datos tienen un uso particular: en un bus con varios receptores, el noveno bit puede marcar si el byte es una dirección (9.º bit en 1) o un dato (9.º bit en 0). Cada receptor ignora los datos hasta que ve una dirección que es la suya; muchos microcontroladores traen un «modo multiprocesador» que lo hace por hardware. Es una forma económica de agregar direccionamiento a una UART sin cambiar el cableado.

06 · Hardware

Dentro del periférico

Una UART de hardware se encarga de todo el trabajo bit a bit, para que el procesador solo maneje bytes. Está compuesta por un camino de transmisión, uno de recepción y un reloj común:

Bloques internos de una UART En el camino de transmisión, la CPU escribe en un registro de datos; el contenido pasa a un registro de desplazamiento con lógica de trama que agrega inicio, paridad y parada, y sale por el pin TX. En el camino de recepción, el pin RX alimenta un detector de inicio y muestreo, luego un registro de desplazamiento con verificación de paridad y parada, y por último un registro de datos que la CPU lee. Un generador de baudios alimenta ambos caminos y se indican las banderas de estado. CPUbus dedatos Registro dedatos TX Desplazamiento + tramainicio · paridad · parada Registro dedatos RX Desplazamiento + verificaciónparidad · parada Generador de baudiosreloj ÷ divisor (muestreo 16×) pin TXpin RX Banderas de estado: TX libre · RX completo · error de trama · error de paridad · desbordamiento

En la transmisión, la CPU escribe un byte en el registro de datos y sigue con lo suyo; la UART lo pasa al registro de desplazamiento, le agrega el bit de inicio, la paridad y la parada, y lo saca bit a bit al ritmo del generador de baudios. En cuanto el registro de datos queda libre, activa la bandera «TX libre» para que el programa pueda cargar el siguiente byte mientras el anterior todavía se está enviando.

En la recepción, la UART observa el pin RX con un contador que corre a 16 veces la velocidad de bits (sobremuestreo 16×). Al detectar el flanco de bajada del inicio, cuenta 8 pulsos para ubicarse en el centro del bit; en los AVR toma tres muestras seguidas (pulsos 8, 9 y 10) y se queda con el valor mayoritario, lo que descarta pulsos de ruido. A partir de ahí muestrea cada 16 pulsos, es decir, en el centro de cada bit. Cuando completa el carácter lo verifica (paridad y parada), lo copia al registro de datos RX y activa la bandera «RX completo».

Las banderas de error indican qué falló: error de trama (el bit de parada no llegó en 1), error de paridad y desbordamiento (llegó un carácter nuevo antes de que el programa leyera el anterior). Las analizamos con detalle en los temas 8 y 9. Muchos microcontroladores agregan una FIFO de hardware (el ESP32 tiene 128 bytes) para soportar a la CPU ocupada sin perder caracteres.

07 · La línea en reposo

Reposo, BREAK y línea desconectada

Cuando nadie transmite, la línea permanece en nivel alto: es el estado de reposo (idle), que equivale a un 1 continuo. Esto tiene varias consecuencias prácticas:

  • El bit de inicio es una transición de 1 a 0: la información de «empieza un carácter» está en el flanco, no en el nivel.
  • La línea desconectada también debe verse como reposo. Una entrada RX flotante capta ruido y la UART lo interpreta como flancos de inicio: aparecen caracteres «fantasma». Por eso se usa una resistencia de pull-up en RX (los módulos serie y los adaptadores ya la traen), y por eso conviene poner una pull-up de unos 10 kΩ en el TX de un microcontrolador que arranca: durante el reset los pines suelen quedar en alta impedancia y, sin pull-up, el receptor del otro lado podría detectar un inicio falso.
  • El BREAK es una condición especial: la línea se mantiene en nivel bajo durante más tiempo que una trama completa (más de 10 bits). Ningún carácter válido lo produce, así que el receptor lo identifica como un evento de señalización. LIN y DMX512 lo usan para marcar el comienzo de un mensaje, y algunos bootloaders, para pedir un reinicio.

08 · Leer una captura

Decodificar una trama a mano

Cuando una comunicación no funciona, el recurso definitivo es mirar la línea con un osciloscopio o un analizador lógico y decodificar la trama a mano. El procedimiento es siempre el mismo:

  1. Medí el ancho del bit más corto (entre dos flancos consecutivos). Su inversa es la velocidad: 104 µs equivale a 9600 baudios; 8,7 µs a 115 200.
  2. Ubicá el flanco de bajada del inicio y dividí la captura en intervalos de un bit a partir de ahí.
  3. Leé el nivel en el centro de cada intervalo. El primero es el inicio (debe ser 0); los siguientes son los datos, del bit 0 al 7.
  4. Armá el byte invirtiendo el orden: el primer bit leído es el bit 0 (el de menor peso).
  5. Verificá la paridad (si la hay) y el bit de parada (debe ser 1).
  6. Pasá el valor a hexadecimal y buscalo en la tabla ASCII.

Un ejemplo. Una captura a 9600 baudios muestra, tras el flanco de bajada, los niveles 0 | 1 0 0 0 0 0 1 0 | 1. El primer 0 es el inicio, el último 1 es la parada y los ocho del medio son los datos, del bit 0 al bit 7. Invertidos, 0 1 0 0 0 0 0 1 es 0100 0001 = 0x41 = «A».

Algunos caracteres ASCII y su trama 8N1 (inicio, bits 0 a 7 y parada).
CarácterCódigoBinarioEn la línea (8N1)
A0x410100 00010 | 1 0 0 0 0 0 1 0 | 1
a0x610110 00010 | 1 0 0 0 0 1 1 0 | 1
00x300011 00000 | 0 0 0 0 1 1 0 0 | 1
U0x550101 01010 | 1 0 1 0 1 0 1 0 | 1
CR (retorno de carro)0x0D0000 11010 | 1 0 1 1 0 0 0 0 | 1
LF (nueva línea)0x0A0000 10100 | 0 1 0 1 0 0 0 0 | 1

El carácter «U» (0x55) alterna ceros y unos en toda la trama, y por eso se usa para calibrar la velocidad: el ancho de cada pulso es exactamente un bit. Si el análisis da un byte distinto del esperado pero con los bits «espejados», casi seguro estás leyendo en el orden equivocado.

09 · Con el microcontrolador

Serial.begin, print y write

En Arduino, el objeto Serial maneja la UART principal. La velocidad se indica en begin y, de manera opcional, el formato. Este programa devuelve cada carácter que recibe, con su código, y muestra la diferencia entre print y write:

void setup() {
    Serial.begin(9600);                 // 9600 baudios, formato 8N1 por defecto
    // Serial.begin(9600, SERIAL_8E1);  // 8 datos, paridad par, 1 parada
    // Serial.begin(9600, SERIAL_7E1);  // 7 datos, paridad par, 1 parada
}

void loop() {
    if (Serial.available() > 0) {
        int c = Serial.read();                 // un byte (0 a 255)
        Serial.print("Recibí: ");
        Serial.write(c);                       // envía UN byte, el carácter tal cual
        Serial.print("  código ");
        Serial.print(c);                       // envía el número en texto: "65" son 2 bytes
        Serial.print("  hex ");
        Serial.println(c, HEX);                // "41" y fin de línea (CR + LF)
    }
}

Si le escribís una «A», responde Recibí: A código 65 hex 41. Fijate en las tres formas de enviar el mismo valor:

  • Serial.write(65) envía un byte, 0x41, que el receptor interpreta como «A».
  • Serial.print(65) envía dos bytes: los caracteres «6» (0x36) y «5» (0x35).
  • Serial.print(65, HEX) envía los caracteres «4» y «1» (0x34 y 0x31).

Esta distinción es esencial: print trabaja en texto, cómodo de leer pero que ocupa más bytes; write trabaja en binario, compacto pero ilegible en un monitor serie. Los protocolos de texto (comandos AT, NMEA) usan print; los binarios (Modbus, protocolos propios), write. Además, println agrega dos bytes al final (CR y LF), y el receptor debe estar preparado para ellos.

Para usar una segunda UART en un ESP32 se indican el formato y los pines: Serial2.begin(115200, SERIAL_8N1, 16, 17); asigna RX = 16 y TX = 17. En un AVR sin segunda UART, SoftwareSerial emula una por software, aunque a velocidades altas pierde caracteres porque no puede atender a la vez el envío y la recepción.

10 · Analogía con programación

Armar y leer tramas

Las operaciones de la UART se pueden reproducir en pocas líneas de Python: calcular el bit de paridad, armar la trama de un carácter y leerla del lado del receptor. Con ellas comprobamos qué detecta la paridad y qué no: invertimos uno y dos bits de una trama 8E1 del carácter «C» y vemos qué entrega el receptor.

Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.

def paridad(dato, tipo):
    unos = bin(dato).count("1")
    if tipo == "E":
        return unos % 2              # par: el total de unos debe quedar par
    if tipo == "O":
        return 1 - unos % 2          # impar: el total debe quedar impar
    return None


def armar_trama(dato, datos=8, tipo="N", paradas=1):
    bits = [0] + [(dato >> i) & 1 for i in range(datos)]       # inicio + datos (LSB primero)
    p = paridad(dato & ((1 << datos) - 1), tipo)
    if p is not None:
        bits.append(p)
    return bits + [1] * paradas                                 # parada(s)


def leer_trama(bits, datos=8, tipo="N", paradas=1):
    if bits[0] != 0:
        return "falso inicio: no hay carácter"
    dato = sum(b << i for i, b in enumerate(bits[1:1 + datos]))
    pos = 1 + datos
    problemas = []
    if tipo != "N":
        if bits[pos] != paridad(dato, tipo):
            problemas.append("error de paridad")
        pos += 1
    if any(b != 1 for b in bits[pos:pos + paradas]):
        problemas.append("error de trama")
    return f"0x{dato:02X} {chr(dato)!r}  " + (", ".join(problemas) or "sin errores")


def alterar(bits, posiciones):
    copia = bits[:]
    for p in posiciones:
        copia[p] ^= 1
    return copia


trama = armar_trama(0x43, tipo="E")
casos = {
    "sin errores": [],
    "1 bit alterado (D3)": [4],
    "2 bits alterados (D3 y D5)": [4, 6],
    "bit de parada alterado": [10],
    "bit de inicio alterado": [0],
}
for nombre, posiciones in casos.items():
    bits = alterar(trama, posiciones)
    print(f"{nombre:28s} {''.join(map(str, bits))}  {leer_trama(bits, tipo='E')}")

Resultado: la trama de «C» en 8E1 es 01100001011. Con un bit alterado en D3, el receptor entrega 0x4B («K») y avisa de error de paridad. Con dos bits alterados (D3 y D5) entrega 0x6B («k») sin ningún aviso: la paridad total no cambió. Si se altera el bit de parada, el dato es correcto pero hay error de trama; si se altera el de inicio, el receptor no detecta ningún carácter. Probá con paridad impar (tipo="O") y con 7 bits de datos.

La función leer_trama supone que el receptor ya sincronizó con el flanco de inicio. Un receptor real, además, muestrea cada bit en su centro, tolera cierto desvío de reloj y puede perder sincronismo con el ruido, tal como vimos en el tema 3.

11 · Exploración interactiva

Constructor de tramas UART

Este laboratorio arma la trama de un carácter con el formato que elijas y la hace pasar por una línea donde podés invertir uno o dos bits. Verás la trama enviada, la recibida (con los bits alterados resaltados), el tamaño de la trama, su eficiencia, cuánto dura y qué concluye el receptor: dato correcto, error de paridad, error de trama, falso inicio o —lo más interesante— un dato alterado que pasa sin avisar.

Emisor, línea y receptor UART

Posición—
Línea (emisor)—
Línea (receptor)—
Carácter enviado—
Bit de paridad calculado—
Bits en la línea · eficiencia—
Duración de un carácter—
Caracteres por segundo—
Lo que entiende el receptor—
Veredicto—

Elegí el formato: la trama se actualiza al instante.

En la fila «Posición», I es el bit de inicio, 0 a 7 los bits de datos, P la paridad y S la parada. Si el valor no cabe en los bits de datos elegidos, se descartan los bits altos, como haría el periférico.

Recorrido guiado

  1. Con los valores iniciales (67 = «C», 8 bits, paridad par, 1 parada, 9600 baudios) la trama es 01100001011: inicio 0, datos 11000010, paridad 1 y parada 1. Son 11 bits, con eficiencia de 72,7 % y 1,15 ms por carácter (unos 873 caracteres por segundo).
  2. Cambiá la paridad a Ninguna: la trama pasa a 10 bits, la eficiencia sube a 80 % y la velocidad a 960 caracteres por segundo. La paridad cuesta un bit.
  3. Volvé a paridad par e invertí el bit de la posición 4 (D3): el receptor entiende «K» (0x4B) y el veredicto es Error de paridad. Un solo bit alterado se detecta siempre.
  4. Invertí además el bit de la posición 6 (D5): el receptor entiende «k» (0x6B) y el veredicto es Dato alterado sin detectar. Con dos bits alterados la paridad no cambia.
  5. Dejá un solo bit invertido, pero ahora el último (la parada): el dato es correcto pero hay Error de trama, porque el bit de parada no llegó en 1.
  6. Invertí el bit 0 (inicio): el receptor no detecta ningún carácter (falso inicio). En una línea real, ese flanco que falta haría que el receptor se desalineara con la trama siguiente.
  7. Elegí paridad «Ninguna» y 2 bits de parada (formato 8N2). La trama es de 11 bits y la eficiencia, de 72,7 %.
  8. Poné 7 bits de datos, paridad par y el valor 233 (que ocupa 8 bits): el periférico descarta el bit alto y entrega 105 («i»). Una UART de 7 bits no puede enviar bytes binarios arbitrarios.
Desafío: encontrar un dato alterado que pase inadvertido

Con paridad par y 8 bits, buscá dos posiciones de datos que, invertidas juntas, dejen pasar el error. Cualquier par de bits de datos cumple (la cantidad de unos cambia en −2, 0 o +2, siempre un número par de diferencia). ¿Y un bit de datos más el bit de paridad? Tampoco se detecta si cambian los dos a la vez, porque el total de unos mantiene su paridad. Con tres bits alterados, en cambio, el error vuelve a detectarse. Verificalo en el laboratorio.

12 · Método de trabajo

Cómo poner a andar un enlace serie

  1. Cableá: TX de un lado al RX del otro, RX al TX, y masa común. Verificá con un multímetro que no haya TX con TX.
  2. Revisá los niveles. ¿Ambos extremos trabajan a 3,3 V o a 5 V? ¿O es RS-232 (±12 V)? Adaptá donde haga falta.
  3. Configurá el mismo formato en los dos lados: velocidad, bits de datos, paridad y bits de parada. Lo más simple es 8N1.
  4. Probá en un solo sentido primero. Hacé que un lado envíe un carácter conocido («U» sirve) y mirá la llegada en el otro, o directamente la línea con un analizador.
  5. Si llega basura, sospechá de la velocidad o del formato; si no llega nada, del cableado cruzado, de la masa o de los niveles.
  6. Definí el fin de mensaje: un salto de línea, un byte de control o una longitud, como veremos en el tema 41.
  7. Agregá detección de errores si el entorno es ruidoso: paridad para lo mínimo, CRC por mensaje para lo serio.

Un truco de depuración: unir momentáneamente TX con RX del mismo dispositivo (loopback) permite comprobar que el programa y el periférico funcionan: todo lo que se envíe debería volver. Si el loopback anda y la conexión con el otro equipo no, el problema está afuera de tu placa.

13 · Comprensión

Confusiones frecuentes

«El pin TX de un lado se conecta al TX del otro»

El nombre indica la función desde el punto de vista del propio chip. Hay que cruzar los cables: TX con RX y RX con TX. Unir TX con TX deja dos salidas enfrentadas y nadie escuchando; con niveles opuestos, además, circula corriente de cortocircuito.

«La UART envía el bit más significativo primero, como se escribe el número»

Envía el menos significativo primero. Al leer una captura hay que invertir el orden de los bits para obtener el valor, y por eso 0x41 aparece en la línea como 1 0 0 0 0 0 1 0.

«Con paridad no pueden pasar errores»

La paridad detecta una cantidad impar de bits invertidos, pero no una cantidad par: dos bits alterados dejan la paridad intacta. Es un chequeo muy básico, y por eso los protocolos serios agregan un CRC.

«Serial.print(65) envía la letra A»

Envía los caracteres «6» y «5». Para enviar el byte 65, es decir la letra «A», hay que usar Serial.write(65). Mezclar ambos es una fuente clásica de datos incomprensibles.

«Un puerto serie TTL y uno RS-232 son compatibles porque ambos son UART»

La trama es la misma, pero los niveles eléctricos no: RS-232 usa ±3 a ±15 V con lógica invertida y puede destruir un pin TTL. Hace falta un conversor de niveles (tema 22).

«Si nadie transmite, la línea puede estar en cualquier nivel»

El reposo es un 1. Una línea sin definir (flotante o a nivel bajo) provoca caracteres falsos o un BREAK continuo. Por eso se usan pull-ups, y por eso un dispositivo apagado puede arrastrar la línea y producir errores de trama.

14 · Práctica guiada

Predecí antes de comprobar

1. Dibujar la trama

Escribí la secuencia de bits en la línea para enviar la letra «A» (0x41) en formato 8N1, desde el reposo hasta la parada. ¿Cuánto dura a 9600 baudios?

Ver solución

0x41 = 0100 0001; el bit 0 va primero: 1 0 0 0 0 0 1 0. La trama es 0 | 1 0 0 0 0 0 1 0 | 1 (inicio, datos, parada). Son 10 bits: 10 / 9600 ≈ 1,04 ms.

2. Bit de paridad

Calculá el bit de paridad par y el de paridad impar del byte 0x7E.

Ver solución

0x7E = 0111 1110, que tiene 6 unos. Con paridad par, el total ya es par, así que el bit vale 0. Con paridad impar hay que sumar un 1 para que el total sea 7: el bit vale 1.

3. Decodificar una captura

Una captura en formato 8O1 (8 datos, paridad impar, 1 parada) muestra: 0 | 1 0 0 1 0 0 1 0 | 0 | 1. ¿Qué carácter es y la paridad es correcta?

Ver solución

Los datos, del bit 0 al 7, son 1 0 0 1 0 0 1 0: los bits 0, 3 y 6 valen 1, es decir 1 + 8 + 64 = 73 = 0x49 = «I». Hay 3 unos (cantidad impar); con paridad impar el bit debe ser 0 para que el total siga siendo impar. Como llegó un 0, la paridad es correcta. El bit de parada es 1: la trama es válida.

4. Eficiencia y velocidad

Un enlace usa el formato 8E2 a 19 200 baudios. ¿Cuántos bits ocupa cada carácter, cuánto dura y cuántos caracteres por segundo se pueden enviar? ¿Cuál es la eficiencia?

Ver solución

Bits por carácter: 1 + 8 + 1 + 2 = 12. Duración: 12 / 19 200 = 625 µs. Caracteres por segundo: 19 200 / 12 = 1600. Eficiencia: 8 / 12 ≈ 66,7 %.

5. print contra write

El programa ejecuta Serial.print(65) y luego Serial.write(65). ¿Cuántos bytes salen en total y cuáles son? ¿Cuánto tarda a 9600 baudios en 8N1?

Ver solución

print(65) envía dos bytes: 0x36 («6») y 0x35 («5»). write(65) envía uno: 0x41 («A»). En total 3 bytes, es decir 30 bits: 30 / 9600 ≈ 3,1 ms. El receptor ve el texto «65A».

6. Un error que no se ve

Se envía «C» (0x43) en formato 8E1 y el ruido invierte los bits D1 y D4. ¿Qué carácter cree recibir el receptor y detecta el error?

Ver solución

0x43 = 0100 0011. Invertir D1 (el 1 pasa a 0) y D4 (el 0 pasa a 1) da 0101 0001 = 0x51 = «Q». Cambiaron dos bits, así que la cantidad de unos sigue siendo 3 y la paridad par sigue coincidiendo: no se detecta el error. El receptor entrega «Q» como si fuera correcto.

15 · Síntesis

Ideas para recordar

  • La UART convierte bytes en una secuencia de bits asíncrona: dos cables de datos (TX y RX), cruzados entre los dispositivos, y una masa común.
  • Cada trama lleva un bit de inicio (0), de 5 a 9 bits de datos con el LSB primero, un bit de paridad opcional y uno o dos bits de parada (1).
  • El reposo es un 1; el inicio se detecta por su flanco de bajada. Una línea flotante produce caracteres falsos y un nivel bajo prolongado es un BREAK.
  • La paridad par o impar detecta un número impar de bits alterados, pero no un número par; cuesta un bit por carácter.
  • El formato se resume como 8N1, 8E1, 7E1…; ambos extremos deben coincidir. 8N1 rinde el 80 % y es el estándar de facto.
  • El periférico incluye registros de datos, registros de desplazamiento, un generador de baudios y un receptor que sobremuestrea 16× y lee en el centro de cada bit; informa errores de trama, de paridad y de desbordamiento.
  • Para decodificar una captura se mide el ancho de bit, se lee el centro de cada intervalo y se invierte el orden de los bits para obtener el valor.
  • En Arduino, write envía un byte y print envía su representación en texto; los niveles TTL de la UART no son los del RS-232.

¿Listo para el tema 8?

Antes de avanzar, dibujá de memoria la trama 8E1 del carácter «k» (0x6B) con su bit de paridad, explicá por qué dos bits alterados pueden pasar inadvertidos y decí qué verías en el receptor si conectás TX con TX. Si podés justificar las tres respuestas, tenés clara la trama de la UART.

En el próximo tema estudiamos la configuración de una UART: cómo se calcula el divisor de baudios, cuánto error de reloj admite cada velocidad y qué significan realmente los errores de muestreo y de trama.