Lorsque j’ai commencé l’étude de mon Minitel 1 Couleur pour faire sa remise en état (en cours au moment d’écrire ces lignes), j’ai fait un passage sur les différents composants qu’ils avait et j’ai remarqué des puces marquées MAB8048.
Ce sont des versions Philips du microcontrôleur Intel 8048 de la série MCS-48.
Les microcontrôleurs de cette série sont les premiers microcontrôleurs fabriqués par Intel en 1976.
Ils intègrent un CPU, de la mémoire RAM et une mémoire ROM (parfois externe) contenant le programme à faire exécuter.
Du fait que ces composants étaient peu coûteux et que la plus part des instructions programme en ROM ne nécessitait qu’un seul octet, cette série est resté assez populaire dans le monde de l’électronique jusque dans les années 2000.
La série MCS-51 sortie 1980 est l’évolution de la série MCS-48.
Dans un but de préservation et de pouvoir remplacer les microcontrôleurs de mon Minitel lorsqu’ils tomberont en panne, j’ai voulu sauvegarder le contenu de leur ROM.
Problème : cette série MCS-48 n’est plus produite depuis longtemps et mon programmateur Wellon VP-290 ne sait pas les lire et encore moins les programmer.
Vu la rareté des Minitel 1 Couleur, il fallait vraiment pouvoir sauvegarder le programme se trouvant dans ces microcontrôleurs.
J’ai pensé un temps acheter un autre programmateur du commerce mais même d’occasion, ces appareils coûtent assez cher.
Je me suis donc mis en tête d’essayer de faire mon propre programmateur pour microcontrôleur MCS-48 !
La première étape a été de chercher de la documentation technique sur comment lire et programmer ces microcontrôleurs anciens.
J’ai pu trouver en ligne le manuel utilisateur d’Intel pour les microcontrôleurs de la famille MCS-48 :
Fichier PDF : MCS-48_Family_Users_Manual_Jul78.pdf
Ce tableau résume brièvement tout cela :
Modèle |
Format |
ROM |
RAM |
I/O |
Effaçable |
Programmable |
Tension prog. |
8021 |
DIP-28 |
1 Ko |
64 octets |
21 |
Non |
Non (mask ROM) |
12 V |
8022 |
DIP-40 |
2 Ko |
64 octets |
21 |
Non |
Non (mask ROM) |
12 V |
8035 |
DIP-40 |
Externe |
64 octets |
27 |
- |
- |
- |
8039 |
DIP-40 |
Externe |
128 octets |
27 |
- |
- |
- |
8040 |
DIP-40 |
Externe |
256 octets |
27 |
- |
- |
- |
8048 |
DIP-40 |
1 Ko |
64 octets |
27 |
Non |
Non (mask ROM) |
12 V |
8049 |
DIP-40 |
2 Ko |
128 octets |
27 |
Non |
Non (mask ROM) |
12 V |
8050 |
DIP-40 |
4 Ko |
256 octets |
27 |
Non |
Non (mask ROM) |
12 V |
8648 |
DIP-40 |
1 Ko |
64 octets |
27 |
Non |
Oui (one-time) |
12 V |
8748 |
DIP-40 |
1 Ko |
64 octets |
27 |
Oui (UV) |
Oui |
25 V ou 21 V |
8749 |
DIP-40 |
2 Ko |
128 octets |
27 |
Oui (UV) |
Oui |
25 V ou 21 V |
87P50 |
DIP-40 |
Externe |
256 octets |
27 |
- |
- |
- |
Je ne m’attarderai pas ici aux autres composants de la série considérés comme des périphériques.
Des tensions de programmation sont indiqués même lorsque la ROM n’est pas programmable car cette tension sert également juste à la lecture par le programmateur.
Certains modèles ont des boîtiers DIP de tailles différentes et donc des configurations de câblage différentes :
Les microcontrôleurs 8048 que je souhaite sauvegarder contiennent donc 1 Ko de ROM.
Vu que je ne souhaite sauvegarder que des modèles 8048 au format DIP-40 et qu’il y a des modèles qui utilisent un format DIP et un câblage différents, j’ai décidé dans un premier temps que mon programmateur maison ne gérera que les microcontrôleurs avec le même format DIP et le même câblage, c’est à dire les modèles suivants qui restent les plus courants :
- 8048
- 8049
- 8050
- 8648
- 8748
C’est pour la ROM externe qu’il faut en utiliser un et c’est un autre problème puisqu’il faut en avoir un qui est peut lire/programmer le modèle de ROM en question.
Je n’exclue pas que, dans une évolution future de mon programmateur, les modèles 8021 et 8022 puissent être gérés.
Maintenant qu’on sait quels composants sont à manipuler, il faut trouver comment les manipuler.
Plusieurs connexions (pins) au microcontrôleur sont utilisées et sont résumées dans ce tableau :
Numéro pin |
Nom pin |
Description |
1 |
T0 |
Test 0 : Sélection du mode « program » (écriture) ou « verify » (lecture). |
2 |
XTAL 1 |
Entrée horloge (1 à 6 Mhz) pour un quartz ou un oscillateur externe. |
3 |
XTAL 2 |
Utilisé pour la 2e connexion du quartz. Non utilisé si utilisation d’un oscillateur externe. |
4 |
/RESET |
Initialisation et application adresse lors la lecture/écriture. |
7 |
EA |
External Access. Activation du mode « program » (écriture) ou « verify » (lecture). |
12-19 |
DB0-DB7 |
Data Bus 0 à 7 : Bus de données bits 0 à 7 lors des écritures et lecture de données. Bus d’adresse bits 0 à 7 lors de la sélection d’une adresse. |
20 |
VSS |
Masse. |
21-22 |
P20-P21 |
Bus d’adresse bits 8 à 9 lors de la sélection d’une adresse. |
23 |
P22 |
Bus d’adresse bit 10 lors de la sélection d’une adresse. Utilisé uniquement chez 8049/8050/8749. Non connecté chez 8048/8648/8748. |
24 |
P23 |
Bus d’adresse bit 11 lors de la sélection d’une adresse. Utilisé uniquement chez 8050. Non connecté chez 8048/8049/8648/8748/8749. |
25 |
PROG |
Entrée impulsion de programmation d’un emplacement mémoire. |
26 |
VDD |
Alimentation pour la programmation (+12/+21/+25 V suivant le modèle). |
40 |
VCC |
Alimentation principale (+5V). |
En parcourant la documentation technique de la série MCS-48, on trouve :
- un diagramme expliquant les séquences à suivre pour lire la ROM ainsi que les tensions à appliquer en fonction du modèle 80xx ou 87xx :
Ils ne mentionnent donc pas toutes les connexions pour les adresses mémoire A0 à A11 mais on trouve un peu plus loin ce diagramme :
Dans un tableau, on apprend que le temps de programmation de chaque octet est de minimum 50 ms.
Version PDF : Sequences_programmation_MCS-48.pdf
On peut remarquer qu’il y a une relecture au moment de l’écriture. Il faut toujours relire ce qui vient d’être écrit car si une zone mémoire a déjà été écrite et que la nouvelle information n’a pas été enregistrée ou que le microcontrôleur est défectueux, il faut le détecter tout de suite.
J’ai donc toutes les informations dont j’ai besoin, maintenant je peux commencer les expérimentations !
Je ne vais bien sûr pas utiliser les 8048 de mon Minitel pour mes expérimentations au risque de les détruire lors d’une mauvaise manipulation ce qui entraînerait la disparition totale de leur programme en ROM.
Et vu la rareté des Minitel 1 Couleur, il y a peu de chances de trouver d’autres 8048 provenant du même modèle de Minitel avec le même programme à l’intérieur.
J’ai commandé des puces 8748 et 8749 pour tenter d’écrire dessus :
J’avais déjà noté que la tension de programmation nécessaire uniquement pour la lecture des 80xx était de +12V.
Cette tension de programmation doit donc être ajustable sur le programmateur.
J’ai choisi d’utiliser un Arduino pour me faciliter la vie dans mes expérimentations de réalisation d’un programmateur.
C’est une plateforme à bas coût que beaucoup de gens techniques connaissent ou ont peut-être déjà.
Il y a jusqu’à 17 signaux à manipuler sur microcontrôleur MCS-48. Avec ses 18 entrées/sorties GPIO (sans compter les deux qui servent pour la liaison série), il y a assez de connexions possibles sur un Arduino Uno pour cela. C’est donc ce modèle d’Arduino que je vais utiliser.
Cela serait possible théoriquement également avec un Arduino Leonardo mais je n’en ai pas pour le vérifier.
Pour la source de tension de programmation, qui supérieure au +5V que peut fournir l’Arduino, j’ai utilisé un module convertisseur DC vers DC step-up que j’avais en stock :
Vu que je ne souhaite pas à avoir à souder une puce 87xx pour la connecter au programmeur, j’ai commandé un support ZIF pour puces au format DIP-40 :
Mon programmateur utilise un système de lignes de commandes via la console Arduino pour effectuer les actions.
Les données lues sont affichées au format hexadécimal et le circuit prend une ligne des valeurs hexadécimales comme données à écrire.
C’est simple et ça fonctionne assez bien déjà !
Comme en témoigne cette capture d’écran, j’ai fait une découverte lors de mes expérimentations :
Lorsqu’un emplacement mémoire a été écrit, on ne peut pas écrire autre autre chose à place.
La donnée présente est verrouillée jusqu’à ce qu’on procède à l’effacement complet du microcontrôleur.
Par contre, le reste des emplacements mémoire sont toujours (heureusement) inscriptibles !
Par la suite, j’ai intégré le support des instructions au format Intel HEX qui était communément utilisé à l’époque des années 70 et après pour les fichiers dumps de ROM.
Plus d’informations là : https://fr.wikipedia.org/wiki/HEX_(Intel)
Truc intéressant : En enregistrant les lignes Intel HEX dans un fichier texte ouvert avec le logiciel Notepad++, j’ai remarqué que celui-ci gère ce format et marque en rouge les checksums quand ils ne sont pas bons !
Cela m'a permit de découvrir un bug dans le calcul du checksum et de le corriger.
J’ai fait ensuite quelques améliorations électroniques sur le prototype et j’ai désormais un circuit prêt à faire sur plaque d’essai époxy !
Une difficulté non prévue au départ est apparue :
J’ai remarqué que deux ensembles de pins d’un coté de l’Arduino n’étaient pas séparés d’un espace de 2,54mm mais de moins.
De l’autre coté, c’est bon.
C’est dommage pour ceux qui voudraient se faire des shields maison avec des plaques d’essai classiques au pas de 2,54mm ordinaires et pas spéciales Arduino.
Obligé de tordre les pattes … :/
A la base, je pensais que cet espacement réduit était une sorte de détrompeur pour éviter de mettre son shield à l'envers.
Mais l'idée qui a été évoquée sur Mastodon que ce soit volontaire d'Arduino pour empêcher l'utilisation de plaques d'essais classiques et vendre plus de shields et de plaques d'essais spécifiques ne me parait pas mauvaise non plus.
Et voici la version finale sur plaque d’essai époxy !
Puis est venu le moment de vérité …
Ce programmateur va-t-il réussir à sauvegarder le contenu de la ROM d’un des microcontrôleurs 8048 d’un rare Minitel 1 Couleur sans le détruire ?
J’ai dessoudé le microcontrôleur 8048 du clavier du Minitel couleur puis je l’ai mis dans mon programmateur.
Des données ont été lues !
Ensuite j’ai programmé une puce 8748 vierge avec les données récupérées et je l’ai mise dans le clavier.
Et la copie a fonctionné !!!
Mon programmateur MCS-48 donc a bien fonctionné et la ROM de la puce originale a pu être sauvegardée !
J’ai bien sûr vérifié que la puce originale fonctionnait toujours après ces manipulations. ;)
Voici le schéma électronique :
Lors de certains moments brefs lors de la lecture, il arrivait qu’il y ait une trop forte consommation de courant par le microcontrôleur MCS-48 sur les sorties de l’Arduino provoquant un reset de ce dernier et donc empêchant la lecture de manière aléatoire.
Les résistances de 1 kΩ en entrée des portes logiques TTL peuvent paraître inutiles mais elles sont là pour éviter qu’une entrée passe à l’état haut s’il y a un faux contact entre le circuit et l’Arduino ce qui pourrait enclencher des comportements non souhaités voir destructifs sur le microcontrôleur.
C’est peut-être exagéré mais le droit à l’erreur n’est pas autorisé avec les microcontrôleurs uniques que je souhaite sauvegarder.
La LEDs sont purement accessoires pour visualiser ce qu’il se passe. Si on le souhaite, on peut s’en passer.
J’ai utilisé un quartz de 4 MHz dans mon circuit parce que c’est que j’avais le plus en stock mais on peut mettre n’importe quelle valeur entre 1 et 6 MHz d’après la documentation technique.
Les valeurs des résistances qui appliquent un certain courant dans les collecteurs des transistors pour les signaux de programmation EA, VDD et PROG ont été définies en fonction du gain (hFE) des transistors BC337-25 et du courant nécessaire sur les entrées correspondantes sur le microcontrôleur.
Voici le code source Arduino de ce programmateur : Programmateur MCS-48 experimental pour Arduino - Code source.ino
Et voici comment on se sert de ce programmateur :
b) Connecter l’Arduino à un PC.
c) Programmer l’Arduino avec le code source.
La sortie console de l’onglet « Serial Monitor » doit afficher « OK Ready ».
Si ce n’est pas le cas, vérifier que le « Serial Monitor » est configuré en 9600 baud.
Par ailleurs, l’appuie de la touche Entrée doit être configurée en mode « New Line »
Sur le circuit, la signal EA du programmateur devrait être activé.
Pour avoir une brève documentation sur la liste des commandes, il est possible de taper « HELP » suivi de la touche Entrée :
Cette interface est anglais car je me suis dit que ce petit circuit pourrait être utile à d’autres et pas qu’à des francophones et qu’avec juste le code source, le schéma et la commande « HELP », ils pouvaient déjà très bien se débrouiller sans toute l’histoire détaillée dans cet article de blog.
Tous les traitements qui se sont bien passés ont un message final qui commence par « OK » suivi d’un texte bref.
Toutes les erreurs qui se produisent ont un message qui commence par « ERR » suivi par un texte bref.
Avec ce système de ligne de commandes et des messages « OK » ou « ERR », il y a éventuellement moyen de faire une plus joli interface qui communique en série avec l’Arduino ou faire des scripts automatisés.
2) Configuration du programmateur en fonction du microcontrôleur
b) Connecter un multimètre aux sorties du convertisseur DC-DC.
c) Régler le convertisseur DC-DC pour que sa tension en sortie soit égale à la tension de programmation VPROG du microcontrôleur en ajoutant 0,5V supplémentaire.
Pour les microcontrôleurs 80xx, la tension de programmation VPROG est généralement de +12V.
Pour les microcontrôleurs 87xx et 86xx, la tension de programmation VPROG est généralement de +25V. Vérifier sur le microcontrôleur s’il y a une autre tension indiquée. Parfois, elle est de +21V.
Exemple 2 : Un microcontrôleur 8749 sans inscription supplémentaire. Sa tension de programmation est donc celle standard de +25V.
Exemple 3 : Ici +12,5V pour une tension de programmation à +12V d’un microcontrôleur 8048 qui va être présenté dans la suite de cette procédure.
d) Vérifier la tension du signal EA sur le support ZIF. Elle doit être à +5V.
Si ce n’est pas le cas, il faut ajuster son potentiomètre de réglage.
e) Vérifier la tension du signal VDD sur le support ZIF. Elle doit être à +5V.
Si ce n’est pas le cas, il faut ajuster son potentiomètre de réglage.
Pour quitter le mode calibration, il suffit juste de redémarrer l’Arduino en appuyant sur le bouton RESET ou de faire une des actions des paragraphes suivants.
Le programmateur est désormais prêt à accueillir un microcontrôleur à lire ou écrire.
a) Après avoir vérifié les réglages de tension du point précédent, mettre le microcontrôleur sur le support ZIF avec le levier vers le haut et baisser le levier pour verrouiller le microcontrôleur en place.
b) Via l’interface de ligne de commande, il faut ensuite sélectionner type de microcontrôleur qu’on souhaite utiliser.
Pour cela, il y a la commande « MODE <CODE> ».
Le paramètre CODE à la valeur 80 est pour l’utilisation de microcontrôleur 80xx.
Le paramètre CODE à la valeur 87 est pour l’utilisation de microcontrôleur 87xx et 87xx.
Par défaut, le programmateur est en mode 80 pour microcontrôleurs 80xx.
Donc dans le cas du 8048 présenté en place sur le support ZIF plus haut, il n’y a rien à faire.
Se tromper de mode n’est pas destructeur. Il est possible de passer d’un mode à l’autre à n’importe quel moment.
c) On peut désormais lire le contenu de la ROM et pour cela il y a la commande « READ <ROMSIZE> <FORMAT> » avec les paramètres suivants :
- ROMSIZE : Indique une longueur de données à lire depuis l’adresse mémoire 0. En général c’est la taille de la ROM dans le cas de lecture complète ou juste les premiers éléments en mémoire. Cette valeur peut aller de 1 à 4096.
- FORMAT : Indique le format des données qui vont être retournées dans la sortie console. Il y a le format HEXA pour des données en hexadécimal ou le format IHEX qui permet d’avoir directement un fichier au format Intel HEX.
La lecture se termine toujours par « OK Read done » si tout s’est bien passé.
Il n’y a qu’à copier le contenu de la sortie console vers un éditeur de texte pour avoir une fichier de sauvegarde de la ROM.
4) Écrire dans la ROM d’un microcontrôleur
a) Après avoir vérifié les réglages de tension, mettre un microcontrôleur vierge sur le support ZIF avec le levier vers le haut et baisser le levier pour verrouiller le microcontrôleur en place.
b) Changer le mode si besoin avec la commande « MODE <CODE> » avec le paramètre CODE à 87 pour les microcontrôleurs 87xx et 86xx.
Les microcontrôleurs 80xx ne sont pas programmables. Leur ROM est directement programmé en usine lors de leur fabrication.
c) Il est possible de vérifier si un microcontrôleur est bien vierge avec la commande « ISCLEAR <ROMSIZE> » avec le paramètre ROMSIZE qui est la taille de la ROM (« 1024 » pour les 1 Ko de ROM des 8748 par exemple).
Pour l’expérimental, il est possible de mettre une valeur inférieure à la taille de la ROM qui va uniquement vérifier du début de la ROM jusqu’à la taille indiquée.
Attention : Si la ROM contient uniquement des 0 véritablement écrits, cette fonction ne fera pas la différence entre une mémoire vierge et une mémoire avec que des 0 écrits.
d) Pour écrire ensuite en ROM, il y a la commande « WRITE <FORMAT> <DATA> ».
Il y a deux formats de données qui peuvent être utilisés : le format HEXA pour des données bruites en hexadécimal et le format IHEX pour des données issues de lignes au format Intel HEX.
La procédure d’écrire est différente pour les deux formats.
Pour écrire en format HEXA :
- Puis on peut commencer à envoyer des données à enregistrer avec la commande « WRITE HEXA <DONNESHEXACIMALES> ».
Le commande retourne « ERR Bad value at adress XXXX » si la donnée relue après écriture n’est pas correcte et que l’écriture a donc échouée.
Pour écrire en format IHEX (Intel HEX) :
Il suffit uniquement envoyer des commandes « WRITE IHEX <DONNEESIHEX> » avec dans le paramètre IHEX une ligne du fichier au format Intel HEX.
Chaque ligne contient l’emplacement mémoire où écrire les données et les données en question.
Le commande retourne « ERR Bad value at adress XXXX » si la donnée relue après écriture n’est pas correcte et que l’écriture a donc échouée.
Et voilà ! :)
Ce petit programmateur expérimental est assez rudimentaire et possède quelques réglages manuels à faire comparé à un programmateur du commerce mais j’en suis très content.
Il y aura peut-être des évolutions qui seront faites. :)