Transceptor UART para computador de 8 bits na placa de ensaio

Recentemente, estou cada vez mais interessado em montar computadores de 8 bits a partir de chips TTL . Eu me inspirei nos vídeos maravilhosos de Ben Iter no YouTube , bem como em vários projetos do site Hackaday . No processo de projetar e montar meu próprio computador de 8 bits, pensei em como seria difícil implementar um transceptor UART baseado em um IC da série 7400.





Circuito final: um transceptor UART pronto montado a partir de um IC da série 7400



Em primeiro lugar, vamos descobrir o que é um UART... É um transceptor assíncrono universal - um protocolo simples que permite enviar e receber dados de 8 bits de forma assíncrona para que um processador ou computador possa se comunicar com o mundo exterior. Isso é útil em si mesmo - meu computador de 8 bits pode se comunicar com um laptop e usar um programa de monitoramento de porta serial (como o putty ) como uma interface para entrada e saída de texto. Ainda mais interessante, posso programar o bootloader do sistema operacional para meu computador de 8 bits e, em seguida, programá-lo por meio da conexão UART do laptop! Visto que os módulos Bluetooth do tipo HC-05 se comunicam essencialmente com a CPU via UART, posso até usar o módulo Bluetooth para programar meu computador de 8 bits à distância! Seria incrível.



Alguns puristas considerariam programar um computador de 8 bits com um computador muito mais poderoso uma abordagem fraudulenta - mas este é meu projeto e ele segue minhas regras! Programe a máquina artesanal com interruptores DIP se você gosta de inserir dados em vez de programar e deseja uma experiência autêntica de trabalho duro.



Fosse o que fosse com a programação, pelo menos eu decidi me limitar ao desenvolver um computador a chips TTL simples - sem Arduino, Raspberry Pi, ESP8266 e outros módulos de Turing-completos (caso contrário, o que seria de interesse?)



Protocolo UART e restrições de design



Antes de você está a estrutura do sinal UART. Ele tem um bit de início, denotado por uma transição de alto para baixo do sinal, seguido por um byte de dados (LSB primeiro) e, em seguida, um bit de parada, que aumenta o sinal. Às vezes, também há um bit de paridade, mas não é obrigatório, portanto, omiti-o por razões de simplicidade. O tempo de transmissão de cada bit é determinado pela taxa de transmissão (neste caso, bits por segundo). Por exemplo, uma taxa de transmissão de 9600 significa que um bit é transmitido em

1/9600 = 104 μs. A forma de onda é bastante simples, então podemos implementá-la inteiramente em hardware em chips lógicos.







Tive de escolher um oscilador de cristal que me desse acesso às taxas de transmissão padrão, de preferência divisíveis em potências de dois, de modo que fosse conveniente operar com um contador binário. Depois de pensar um pouco, decidi usar um oscilador de 2,4576 MHz, pois permitia a transferência a 38400 bps (dividido por 64), ou 9600 bps (dividido por 256).



Transmissor UART



Lista de componentes:



  • Oscilador de cristal de 2,4576 MHz
  • 3 x 74LS161 contadores de 4 bits
  • 74LS674 registrador de deslocamento de 16 bits
  • 74LS06 E
  • 74LS74 D-trigger
  • 74LS04 NÃO
  • Diodo 1N4001
  • 470 uF (!) Capacitor (suavização de potência)


Esquema



O transmissor UART é o mais fácil de entender. Basicamente, é um registro de deslocamento de carga paralelo com saída serial. Ele carrega o byte de dados, mantém o controle dos bits de início e fim e os sincroniza com a taxa de transmissão desejada. O diagrama abaixo mostra esse processo. Na parte (1), o oscilador de cristal de 2,4576 MHz é desacelerado para 38.400 Hz usando dois contadores 74LS161 de 4 bits. Na parte (2), o registrador de deslocamento de 16 bits 74LS674 é usado para sincronizar dados para o UART. Eu uso este cadastro porque já o tenho em mãos. Eu entendo que este IC é caro e pode ser difícil de encontrar, mas definitivamente simplificou todo o meu esquema.







Com apenas três desses ICs (dois contadores de 4 bits e um registrador de deslocamento), você pode enviar um fluxo contínuo de caracteres para o transmissor UART a 38.400 bps (sem paridade)! Sim, é um fluxo contínuo - não levei em consideração que o registrador de deslocamento atualiza o buffer de carga em um círculo - opa. Eu não precisava desse comportamento - queria que o processador enviasse um byte de cada vez. Tudo se complica pelo fato de que os pulsos do clock do processador e do UART não estão sincronizados, e eu não queria fazer suposições sobre qual temporizador é mais rápido, qual sinal será relevante em que momento, etc. Como eu precisava lidar com a assincronia de maneira confiável, decidi usar o seguinte esquema que funciona bem:



  • (3) O processador envia um sinal de transferência de bytes fora de sincronia com o processador e o relógio UART.
  • «». ( AND 74LS06 D- 74LS74).
  • UART «» 4- 74LS161. UART.
  • (4) 16 , .


Observe que estou trocando 16 bits em vez de 10 bits do sinal do transmissor UART - principalmente devido à conveniência de usar o bit de transporte para desabilitar o circuito de transmissão. Eu poderia usar um contador decimal (por exemplo, o 74LS162), mas não tinha um disponível quando montei o circuito em uma placa de ensaio. Talvez no esquema final eu mude para ele.



Receptor UART



Lista de componentes:



  • Oscilador de cristal de 2,4576 MHz (você pode usar o mesmo oscilador do receptor)
  • 3 x 74LS161 contadores de 4 bits (pode usar um dos ICs do receptor)
  • 74LS74 D-trigger
  • 74LS04 NÃO (pode usar IC receptor)
  • Diodo 1N4001
  • 470 uF (!) Capacitor (suavização de potência)
  • Resistores de 220 ohms e LEDs para beleza.


Parece-me que se o transmissor UART descrito acima for fácil de entender, então o receptor será um pouco mais complicado. No entanto, o que é bom com a lógica digital é que ela pode ser dividida em módulos separados, e então tudo não parece mais tão complicado!



As formas de onda no canto esquerdo inferior do diagrama abaixo mostram o que considerar ao receber um único bit de transmissor digital. Como sabemos se um byte está sendo enviado para nós? Fácil - o bit de início é indicado por uma transição de alto para baixo, então podemos inverter isso e usar a transição de baixo para alto para definir o D-flip-flop (74LS74) (2).



Agora, precisamos começar a escrever o sinal, deslocando-o para registradores de deslocamento e amostrando no centro da sequência de bits de dados. O que é importante entender: como não sabemos quando começaremos a receber dados do UART, este processo não será sincronizado com nossos pulsos de clock. Portanto, quanto mais rápidos são nossos impulsos, mais nos aproximamos da verdadeira origem do sinal do transmissor. Por conveniência, minha velocidade de clock é 16 vezes a taxa de transmissão (1). Isso significa que cada bit transmitido passa por 16 pulsos desse gerador. Portanto, para tirar uma amostra aproximadamente no meio dos dados transmitidos, devemos fazê-lo na contagem de 8 - para isso geramos o sinal SAMPLING_CLK (3).



Então, na borda ascendente desse novo relógio, podemos sincronizar o sinal transmitido com dois registradores de deslocamento de saída paralela serial (SIPO) de 8 bits associados no meio de cada bit de dados. Na 16ª contagem, terminamos com um bit digital, então incrementamos outro contador que mantém o controle do número total de bits sincronizados em (5). Quando este contador atinge 16 (poderia ter sido um contador decimal), o circuito de recepção é desabilitado ao limpar o D flip-flop. Ufa! Apresento o diagrama abaixo e espero que você consiga rastrear a lógica de seu funcionamento usando minha descrição.







Infelizmente, não tenho um osciloscópio e, inicialmente, meu circuito deu alguns resultados misteriosos, aceitando um byte e depois aceitando outro de uma maneira diferente. Mudei o oscilador de 2,4576 MHz para um oscilador 555 de 1 segundo para verificar a lógica de contagem e encontrei um problema com uma entrada flutuante em um dos contadores (estava depurando usando LEDs). Eu liguei os dois pinos de redefinição do contador ao sinal RX_active, fazendo com que os contadores alternassem entre ligados e redefinidos, o que limpa sua saída no final de cada ciclo de aquisição de dados. Os contadores agora funcionam conforme o esperado, e quando eu coloquei o gerador de volta em 2,4576 MHz tudo começou a funcionar corretamente e de forma confiável.



O circuito do computador final na placa de ensaio terá um registro de saída para controlar a saída de dados para o barramento. Finalmente, usei um D-flip-flop extra no 74LS74 para implementar o sinal RX_READY, que o processador pode ler para verificar se o byte está pronto para ser lido (só é verdadeiro quando o byte é totalmente recebido).



Abaixo está uma foto do computador montado e funcionando. A interface UART-USB é o dongle no canto superior direito. A placa intermediária contém um oscilador de cristal e contadores de 4 bits que geram vários pulsos de clock. No topo, próximo à alimentação USB, está um registrador de deslocamento de 16 bits. A placa da esquerda contém a lógica para o envio controlado de um byte (UART TX). Você pode ver o botão com o qual simulei o sinal de controle do processador e o temporizador 555, que atua como um pulso de clock do processador. O módulo UART RX reside na placa certa. LEDs verdes indicam a chegada de um byte na entrada, LEDs amarelos indicam recepção de dados (sinal de ocupado UART RX) e LEDs vermelhos acendem quando o byte está pronto para ser lido pelo processador.





Procurando por placas de ensaio e habilidades de fiação mais bonitas



Adição



Otimizei um pouco o circuito (ao longo do caminho, tendo aprendido uma lição sobre a diferença entre o processamento de eventos assíncronos e síncronos em lógica IC discreta). Eu queria reduzir o número de chips usando um contador decimal que contaria os bits de entrada e contaria em 10 bits, não em 16. Então, eu poderia remover o registrador de deslocamento.



Tentei primeiro o contador 74LS162. Para um byte, tudo funcionou, mas eu rapidamente descobri que ele tem um mecanismo de redefinição síncrona - ou seja, leva um ciclo de clock para redefinir o sinal. Como o relógio parou após o último bit ter sido recebido, o contador não foi zerado. O contador de 4 bits 74LS161 que removi tinha uma reinicialização assíncrona, então tudo funcionou antes. É bom termos encontrado um contador decimal com reset assíncrono - 74LS160. Tudo funciona bem com ele - veja o diagrama atualizado.







Verificando erros no byte recebido



Para simplificar, não adicionei nenhuma verificação de erro no byte resultante. Você pode imaginar que adicionamos um bit de paridade e alternamos o flip-flop toda vez que um "1" é recebido. Então saberíamos se recebemos um número par ou ímpar de bits e poderíamos compará-lo com o bit de paridade configurando o sinalizador em incompatibilidade. Além disso, isso pode incluir uma verificação de que o bit de parada era igual a "1". Para economizar espaço, não adicionei essa funcionalidade, mas quero adicioná-la no futuro. A modularidade do projeto permite que você faça isso conforme necessário.



Notas



Eu amo computadores de 8 bits em placas de ensaio e gostei de fazer este miniprojeto. Eu tenho desenhado este circuito há algum tempo e ainda fiquei chocado quando o montei e tudo funcionou. Isso é algum tipo de mágica! Quase.



All Articles