Transceptor UART para computadora de 8 bits en placa

Recientemente, estoy cada vez más interesado en ensamblar computadoras de 8 bits a partir de chips TTL . Me inspiré en los maravillosos videos de Ben Iter en YouTube , así como en varios proyectos del sitio Hackaday . En el proceso de diseñar y ensamblar mi propia computadora de 8 bits, pensé en lo difícil que sería implementar un transceptor UART basado en un IC de la serie 7400.





Circuito final: un transceptor UART listo para usar ensamblado a partir de un IC de la serie 7400 En



primer lugar, averigüemos qué es un UART... Es un transceptor asíncrono universal, un protocolo simple que le permite enviar y recibir datos de 8 bits de forma asíncrona para que un procesador o computadora pueda comunicarse con el mundo exterior. Esto es útil en sí mismo: mi computadora de 8 bits puede comunicarse con una computadora portátil y usar un programa de monitoreo de puerto serie (como putty ) como interfaz para la entrada y salida de texto. Aún más interesante, puedo programar el cargador de arranque del sistema operativo para mi computadora de 8 bits y luego programarlo a través de la conexión UART desde la computadora portátil. Dado que los módulos Bluetooth tipo HC-05 se comunican esencialmente con la CPU a través de la UART, ¡incluso puedo usar el módulo Bluetooth para programar mi computadora de 8 bits desde la distancia! Sería genial.



Algunos puristas considerarían la programación de una computadora de 8 bits con una computadora mucho más poderosa como un enfoque fraudulento, ¡pero este es mi proyecto y se rige por mis reglas! Programe la máquina hecha a mano con interruptores DIP si disfruta de la entrada de datos en lugar de programar y desea una auténtica experiencia de trabajo duro.



Sea lo que sea con la programación, al menos decidí limitarme al desarrollar una computadora a chips TTL simples, sin Arduino, Raspberry Pi, ESP8266 y otros módulos Turing-complete (de lo contrario, ¿qué sería de interés?).



Restricciones de diseño y protocolo UART



Ante ti está la estructura de la señal UART. Tiene un bit de inicio, denotado por una transición de alta a baja de la señal, seguida de un byte de datos (primero LSB) y luego un bit de parada, que impulsa la señal a alta. A veces también hay un bit de paridad, pero no es obligatorio, así que lo omití por razones de simplicidad. El tiempo de transmisión de cada bit está determinado por la velocidad en baudios (en este caso, bits por segundo). Por ejemplo, una velocidad de 9600 baudios significa que un bit se transmite en

1/9600 = 104 μs. La forma de onda es bastante simple, por lo que podemos implementarla completamente en hardware en chips lógicos.







Tuve que elegir un oscilador de cristal que me diera acceso a velocidades en baudios estándar, preferiblemente divisibles por potencias de dos, por lo que sería conveniente operar con un contador binario. Después de pensarlo un poco, decidí usar un oscilador de 2.4576 MHz, ya que permitía transferir a 38400 bps (dividido por 64), o 9600 bps (dividido por 256).



Transmisor UART



Lista de componentes:



  • Oscilador de cristal de 2,4576 MHz
  • 3 contadores 74LS161 de 4 bits
  • 74LS674 Registro de desplazamiento de 16 bits
  • 74LS06 Y
  • 74LS74 disparador D
  • 74LS04 NO
  • Diodo 1N4001
  • 470 uF (!) Condensador (suavizado de potencia)


Esquema



El transmisor UART es el más fácil de entender. Básicamente, es un registro de desplazamiento de carga paralelo con salida en serie. Carga el byte de datos, realiza un seguimiento de los bits de inicio y finalización y los sincroniza con la velocidad en baudios deseada. El siguiente diagrama muestra este proceso. En la parte (1), el oscilador de cristal de 2,4576 MHz se ralentiza a 38 400 Hz utilizando dos contadores 74LS161 de 4 bits. En la parte (2), el registro de desplazamiento de 16 bits 74LS674 se utiliza para sincronizar datos para la UART. Utilizo este registro porque ya lo tenía a mano. Entiendo que este IC es caro y puede ser difícil de encontrar, pero definitivamente simplificó todo mi esquema.







Con solo tres de estos circuitos integrados (dos contadores de 4 bits y un registro de desplazamiento), puede enviar un flujo continuo de caracteres al transmisor UART a 38.400 bps (sin paridad). Sí, es un flujo continuo, no tomé en cuenta que el registro de desplazamiento actualiza el búfer de carga en un círculo, oops. No necesitaba este comportamiento, quería que el procesador enviara un byte a la vez. Las cosas se complican por el hecho de que los pulsos de reloj del procesador y UART no están sincronizados, y no quería hacer suposiciones sobre quién es el temporizador más rápido, qué señal será relevante en qué momento, etc. Como necesitaba manejar la asincronía de manera confiable, decidí usar el siguiente esquema que funciona bien:



  • (3) El procesador envía una señal de "transferencia de bytes" fuera de sincronización con el procesador y el reloj UART.
  • «». ( AND 74LS06 D- 74LS74).
  • UART «» 4- 74LS161. UART.
  • (4) 16 , .


Tenga en cuenta que estoy cambiando 16 bits en lugar de 10 bits de la señal del transmisor UART, principalmente debido a la conveniencia de usar el bit de acarreo para deshabilitar los circuitos de transmisión. Podría usar un contador decimal (por ejemplo, el 74LS162), pero no tenía uno a mano cuando monté el circuito en una placa. Quizás en el esquema final lo cambie.



Receptor UART



Lista de componentes:



  • Oscilador de cristal de 2,4576 MHz (puede utilizar el mismo oscilador que el receptor)
  • 3 contadores 74LS161 de 4 bits (puede utilizar uno de los circuitos integrados del receptor)
  • 74LS74 disparador D
  • 74LS04 NO (puede usar el receptor IC)
  • Diodo 1N4001
  • 470 uF (!) Condensador (suavizado de potencia)
  • Resistencias de 220 ohmios y LED para mayor belleza.


Me parece que si el transmisor UART descrito anteriormente es fácil de entender, entonces el receptor será algo más complicado. Sin embargo, lo bueno de la lógica digital es que se puede dividir en módulos separados, ¡y entonces todo ya no parece tan complicado!



Las formas de onda en la esquina inferior izquierda del diagrama a continuación muestran lo que se debe considerar al recibir un solo bit de transmisor digital. ¿Cómo sabemos si se nos envía un byte? Fácil: el bit de inicio se indica mediante una transición de alto a bajo, por lo que podemos invertir eso y usar la transición de bajo a alto para configurar el D-flip-flop (74LS74) (2).



Ahora tenemos que empezar a escribir la señal cambiándola a registros de desplazamiento y muestreando en el centro de la secuencia de bits de datos. Lo que es importante entender: dado que no sabemos cuándo comenzaremos a recibir datos de la UART, este proceso no será sincrónico con los pulsos de nuestro reloj. Por lo tanto, cuanto más rápidos sean nuestros impulsos, más nos acercaremos al verdadero origen de la señal del transmisor. Por conveniencia, la velocidad de mi reloj es 16 veces la velocidad en baudios (1). Esto significa que cada bit transmitido pasa por 16 pulsos de este generador. Por lo tanto, para tomar una muestra aproximadamente en el medio de los datos transmitidos, debemos hacerlo a la cuenta de 8 - para esto generamos la señal SAMPLING_CLK (3).



Luego, en el flanco ascendente de este nuevo reloj, podemos sincronizar la señal transmitida con dos registros de desplazamiento de salida en paralelo en serie (SIPO) asociados en el medio de cada bit de datos. En el decimosexto conteo, terminamos con un bit digital, por lo que incrementamos otro contador que realiza un seguimiento del número total de bits sincronizados en (5). Cuando este contador llega a 16 (podría haber sido un contador decimal), el circuito de recepción se desactiva al borrar el flip-flop D. ¡Uf! Les doy el diagrama a continuación, y espero que puedan rastrear la lógica de su operación usando mi descripción.







Desafortunadamente, no tengo un osciloscopio, e inicialmente mi circuito dio algunos resultados misteriosos, aceptando un byte y luego aceptando otro de una manera diferente. Cambié el oscilador de 2.4576 MHz por un oscilador 555 de 1 segundo para verificar la lógica de conteo, y encontré un problema con una entrada flotante en el pin de uno de los contadores (estaba depurando usando LED). Até ambos pines de reinicio del contador a la señal RX_active, lo que provocó que los contadores cambiaran entre encendido y reinicio, lo que borra su salida al final de cada ciclo de adquisición de datos. Los contadores ahora funcionan como se esperaba, y cuando volví a encender el generador a 2.4576 MHz, todo comenzó a funcionar correctamente y de manera confiable.



El circuito final de la computadora en la placa tendrá un registro de salida para controlar la salida de datos al bus. Finalmente, utilicé un D-flip-flop adicional en el 74LS74 para implementar la señal RX_READY, que el procesador puede leer para verificar si el byte está listo para ser leído (es verdadero solo cuando el byte se recibe por completo).



A continuación se muestra una foto de la computadora ensamblada y en funcionamiento. La interfaz UART-USB es el dongle en la parte superior derecha. La placa del medio contiene un oscilador de cristal y contadores de 4 bits que generan varios pulsos de reloj. En la parte superior, junto a la alimentación USB, hay un registro de desplazamiento de 16 bits. La placa izquierda contiene la lógica para el envío controlado de un byte (UART TX). Puede ver el botón con el que simulé la señal de control del procesador y el temporizador 555, que actúa como pulso de reloj del procesador. El módulo UART RX vive en la placa derecha. Los LED verdes indican la recepción de un byte en la entrada, los LED amarillos indican la recepción de datos (señal de ocupado UART RX) y los LED rojos se encienden cuando el byte está listo para ser leído por el procesador.





Buscando protoboards y habilidades de cableado más bonitas



Adición



Optimicé un poco el circuito (en el camino, habiendo aprendido una lección sobre la diferencia entre procesar eventos asíncronos y síncronos en lógica IC discreta). Quería reducir el número de chips usando un contador decimal que contará los bits entrantes y contará en 10 bits, no en 16. Entonces podría eliminar el registro de desplazamiento.



Primero probé el contador 74LS162. Por un byte, todo funcionó, pero descubrí rápidamente que tenía un mecanismo de reinicio sincrónico, es decir, se necesita un ciclo de reloj para reiniciar la señal. Dado que el reloj se detuvo después de recibir el último bit, el contador no se borró. El contador 74LS161 de 4 bits que eliminé tenía un reinicio asincrónico, por lo que todo funcionaba antes. Es bueno que hayamos encontrado un contador decimal con reinicio asincrónico: 74LS160. Todo funciona bien con él: consulte el diagrama actualizado.







Comprobación de errores en el byte recibido



Por simplicidad, no he agregado ninguna comprobación de errores en el byte resultante. Puede imaginar que agregamos un bit de paridad y cambiamos el flip-flop cada vez que se recibe un "1". Entonces sabríamos si recibimos un número par o impar de bits, y podríamos compararlo con el bit de paridad estableciendo la bandera cuando no coincide. Además, esto puede incluir una verificación de verificación de que el bit de parada sea igual a "1". Para ahorrar espacio, no agregué esta funcionalidad, pero quiero agregarla en el futuro. La modularidad del proyecto le permite hacer esto según sea necesario.



Notas



Me encantan las computadoras de 8 bits en placas de prueba y disfruté haciendo este mini proyecto. He estado diseñando este circuito durante bastante tiempo, y todavía me sorprendió cuando lo armé y todo funcionó. ¡Esto es una especie de magia! Casi.



All Articles