dernier circuit: un émetteur-récepteur UART prêt à l'emploi assemblé à partir d'un circuit intégré de la série 7400
Tout d'abord, voyons ce qu'est un UART... Il s'agit d'un émetteur-récepteur asynchrone universel - un protocole simple qui vous permet d'envoyer et de recevoir des données 8 bits de manière asynchrone afin qu'un processeur ou un ordinateur puisse communiquer avec le monde extérieur. Ceci est utile en soi - mon ordinateur 8 bits peut communiquer avec un ordinateur portable et utiliser un programme de surveillance de port série (comme putty ) comme interface pour l'entrée et la sortie de texte. Encore plus intéressant, je peux programmer le bootloader du système d'exploitation pour mon ordinateur 8 bits, puis le programmer via la connexion UART depuis l'ordinateur portable! Comme les modules Bluetooth de type HC-05 communiquent essentiellement avec le CPU via l'UART, je peux même utiliser le module Bluetooth pour programmer mon ordinateur 8 bits à distance! Ce serait génial.
Certains puristes considéreraient la programmation d'un ordinateur 8 bits avec un ordinateur beaucoup plus puissant comme une approche frauduleuse - mais c'est mon projet et il vit selon mes règles! Veuillez programmer la machine fabriquée à la main avec des commutateurs DIP si vous aimez la saisie de données plutôt que la programmation et souhaitez une expérience de travail authentique.
Quoi qu'il en soit de la programmation, au moins j'ai décidé de me limiter lors du développement d'un ordinateur à de simples puces TTL - pas d'Arduino, Raspberry Pi, ESP8266 et autres modules complets de Turing (sinon, qu'est-ce qui serait intéressant?).
Protocole UART et contraintes de conception
Avant de vous est la structure du signal UART. Il a un bit de début, désigné par une transition haut vers bas du signal, suivi d'un octet de données (LSB d'abord), puis d'un bit d'arrêt, qui pilote le signal haut. Parfois, il y a aussi un bit de parité, mais ce n'est pas obligatoire, donc je l'ai omis pour des raisons de simplicité. Le temps de transmission de chaque bit est déterminé par le débit en bauds (dans ce cas, bits par seconde). Par exemple, un débit de 9600 bauds signifie qu'un bit est transmis en
1/9600 = 104 μs. La forme d'onde est assez simple, nous pouvons donc l'implémenter entièrement en matériel sur des puces logiques.
J'ai dû choisir un oscillateur à cristal qui me donne accès à des vitesses de transmission standard, de préférence divisibles par des puissances de deux, afin qu'il puisse être facilement utilisé avec un compteur binaire. Après réflexion, j'ai décidé d'utiliser un oscillateur à 2,4576 MHz, car il permettait de transférer à 38400 bps (divisé par 64), soit 9600 bps (divisé par 256).
Émetteur UART
Liste des composants:
- Oscillateur à cristal 2,4576 MHz
- 3 compteurs 74LS161 4 bits
- Registre à décalage de 16 bits 74LS674
- 74LS06 ET
- 74LS74 D-trigger
- 74LS04 PAS
- Diode 1N4001
- Condensateur 470 uF (!) (Lissage de puissance)
Schème
Le circuit de l'émetteur UART est le plus simple à comprendre. Fondamentalement, il s'agit d'un registre à décalage de charge parallèle avec sortie série. Il charge l'octet de données, garde la trace des bits de début et de fin et le synchronise avec la vitesse de transmission souhaitée. Le diagramme ci-dessous montre ce processus. Dans la partie (1), l'oscillateur à quartz 2,4576 MHz est ralenti à 38 400 Hz à l'aide de deux compteurs 74LS161 à 4 bits. Dans la partie (2), le registre à décalage de 16 bits 74LS674 est utilisé pour synchroniser les données pour l'UART. J'utilise ce registre car je l'avais déjà sous la main. Je comprends que ce circuit intégré est cher et peut être difficile à trouver, mais il a définitivement simplifié tout mon schéma.
Avec seulement trois de ces circuits intégrés (deux compteurs 4 bits et un registre à décalage), vous pouvez envoyer un flux continu de caractères à l'émetteur UART à 38 400 bps (sans parité)! Oui, c'est un flux continu - je n'ai pas pris en compte le fait que le registre à décalage met à jour le tampon de charge dans un cercle - oups. Je n'avais pas besoin de ce comportement - je voulais que le processeur envoie un octet à la fois. Les choses sont compliquées par le fait que les impulsions d'horloge du processeur et de l'UART ne sont pas synchronisées, et je ne voulais pas faire d'hypothèses sur le temporisateur le plus rapide, quel signal sera pertinent à quel moment, etc. Comme j'avais besoin de gérer l'asynchronisme de manière fiable, j'ai décidé d'utiliser le schéma suivant qui fonctionne bien:
- (3) Le processeur envoie un signal de transfert d'octet non synchronisé avec le processeur et l'horloge UART.
- «». ( AND 74LS06 D- 74LS74).
- UART «» 4- 74LS161. UART.
- (4) 16 , .
Notez que je décale 16 bits au lieu de 10 bits du signal de l'émetteur UART - principalement en raison de la commodité d'utiliser le bit de report pour désactiver les circuits de transmission. Je pourrais utiliser un compteur décimal (par exemple, le 74LS162), mais je n'en avais pas sous la main lorsque j'ai assemblé le circuit sur une maquette. Peut-être que dans le schéma final, je vais y passer.
Récepteur UART
Liste des composants:
- Oscillateur à cristal 2,4576 MHz (vous pouvez utiliser le même oscillateur que le récepteur)
- 3 compteurs 4 bits 74LS161 (peut utiliser l'un des IC du récepteur)
- 74LS74 D-trigger
- 74LS04 PAS (peut utiliser le récepteur IC)
- Diode 1N4001
- Condensateur 470 uF (!) (Lissage de puissance)
- Résistances de 220 ohms et LED pour la beauté.
Il me semble que si l'émetteur UART décrit ci-dessus est facile à comprendre, le récepteur sera un peu plus compliqué. Cependant, ce qui est bien avec la logique numérique, c'est qu'elle peut être divisée en modules séparés, et alors tout ne semble plus si compliqué!
Les formes d'onde dans le coin inférieur gauche du diagramme ci-dessous montrent ce qu'il faut considérer lors de la réception d'un seul bit d'émetteur numérique. Comment savons-nous si un octet nous est envoyé? Facile - le bit de départ est indiqué par une transition de haut en bas, nous pouvons donc inverser cela et utiliser la transition de bas en haut pour régler la bascule D (74LS74) (2).
Nous devons maintenant commencer à écrire le signal en le décalant dans des registres à décalage et en échantillonnant au centre de la séquence de bits de données. Ce qu'il est important de comprendre: puisque nous ne savons pas quand nous commencerons à recevoir des données de l'UART, ce processus ne sera pas synchrone avec nos impulsions d'horloge. Par conséquent, plus nos impulsions sont rapides, plus nous nous rapprochons de la véritable origine du signal de l'émetteur. Pour plus de commodité, ma vitesse d'horloge est 16 fois la vitesse de transmission (1). Cela signifie que chaque bit transmis passe par 16 impulsions de ce générateur. Par conséquent, pour prélever un échantillon approximativement au milieu des données transmises, nous devons le faire au nombre de 8 - pour cela, nous générons le signal SAMPLING_CLK (3).
Ensuite, sur le front montant de ce nouveau signal d'horloge, nous pouvons synchroniser le signal transmis avec deux registres à décalage de sortie parallèle en série (SIPO) de 8 bits associés au milieu de chaque bit de données. Au 16e décompte, nous nous retrouvons avec un bit numérique, donc nous incrémentons un autre compteur qui garde la trace du nombre total de bits synchronisés dans (5). Lorsque ce compteur atteint 16 (il aurait pu être un compteur décimal), le circuit de réception est désactivé en effaçant la bascule D. Phew! Je donne le schéma ci-dessous, et j'espère que vous pourrez retracer la logique de son fonctionnement à l'aide de ma description.
Malheureusement, je n'ai pas d'oscilloscope et, au départ, mon circuit a donné des résultats mystérieux, acceptant un octet, puis en acceptant un autre d'une manière différente. J'ai changé l'oscillateur 2,4576 MHz pour un oscillateur 555 d'une seconde pour vérifier la logique de comptage, et j'ai trouvé un problème avec une entrée flottante sur la broche de l'un des compteurs (je déboguais à l'aide de LED). J'ai lié les deux broches de réinitialisation du compteur au signal RX_active, ce qui a fait basculer les compteurs entre marche et réinitialisation, ce qui efface leur sortie à la fin de chaque cycle d'acquisition de données. Les compteurs fonctionnent maintenant comme prévu, et lorsque j'ai remis le générateur en marche à 2,4576 MHz, tout a commencé à fonctionner correctement et de manière fiable.
Le circuit informatique final sur la maquette aura un registre de sortie pour contrôler la sortie des données vers le bus. Enfin, j'ai utilisé une bascule D supplémentaire sur le 74LS74 pour implémenter le signal RX_READY, que le processeur peut lire pour vérifier si l'octet est prêt à lire (ce n'est vrai que lorsque l'octet est entièrement reçu).
Ci-dessous, une photo de l'ordinateur assemblé et opérationnel. L'interface UART-USB est le dongle en haut à droite. La carte du milieu contient un oscillateur à quartz et des compteurs 4 bits qui génèrent diverses impulsions d'horloge. En haut, à côté de l'alimentation USB, se trouve un registre à décalage 16 bits. La carte de gauche contient la logique pour l'envoi contrôlé d'un octet (UART TX). Vous pouvez voir le bouton avec lequel j'ai simulé le signal de commande du processeur et la minuterie 555, qui agit comme une impulsion d'horloge du processeur. Le module UART RX vit sur la bonne carte. Les LED vertes indiquent la réception d'un octet à l'entrée, les LED jaunes indiquent la réception de données (signal UART RX occupé) et les LED rouges s'allument lorsque l'octet est prêt à être lu par le processeur.
À la recherche de jolies planches à pain et de compétences en câblage
Une addition
J'ai un peu optimisé le circuit (en cours de route, après avoir appris une leçon sur la différence entre le traitement d'événements asynchrones et synchrones en logique IC discrète). Je voulais réduire le nombre de puces en utilisant un compteur décimal qui compterait les bits entrants et compterait 10 bits au lieu de 16. Ensuite, je pourrais supprimer le registre à décalage.
J'ai d'abord essayé le compteur 74LS162. Pour un octet, tout fonctionnait, mais j'ai rapidement découvert qu'il avait un mécanisme de réinitialisation synchrone - c'est-à-dire qu'il faut un cycle d'horloge pour réinitialiser le signal. Puisque l'horloge s'est arrêtée après la réception du dernier bit, le compteur n'a pas été effacé. Le compteur 4 bits 74LS161 que j'ai supprimé avait une réinitialisation asynchrone, donc tout fonctionnait auparavant. C'est bien que nous ayons trouvé un compteur décimal avec réinitialisation asynchrone - 74LS160. Tout fonctionne bien avec lui - voir le diagramme mis à jour.
Vérification des erreurs dans l'octet reçu
Pour simplifier, je n'ai ajouté aucune vérification d'erreur dans l'octet résultant. Vous pouvez imaginer que nous avons ajouté un bit de parité et basculer la bascule à chaque fois qu'un "1" est reçu. Ensuite, nous saurions si nous avons reçu un nombre pair ou impair de bits et pourrions le comparer avec le bit de parité en définissant le drapeau sur la non-concordance. De plus, cela peut inclure une vérification de vérification que le bit d'arrêt était égal à "1". Pour économiser de l'espace, je n'ai pas ajouté cette fonctionnalité, mais je souhaite l'ajouter à l'avenir. La modularité du projet vous permet de le faire selon vos besoins.
Remarques
J'adore les ordinateurs 8 bits sur les breadboards et j'ai aimé faire ce mini projet. Je conçois ce circuit depuis un certain temps, et j'étais encore choqué quand je l'ai monté et que tout a fonctionné. C'est une sorte de magie! Presque.