Tutoriel · Débutant · 28 min de lecture

GPIO 8051 et interfaçage des ports : puisez, ne sourcez pas

Broche 8051 : elle puise dix milliampères et n'en source presque aucun. D'où le câblage de LED inversé, le 1 écrit avant de lire, et les limites d'un port.

Introduction

Le premier programme 8051 que tous écrivent fait clignoter une LED, et un nombre surprenant d’entre eux ne clignotent pas. Le câblage semble bon, le code semble bon, et la broche mesure un 5 V assuré qui ne fait rien d’utile.

La cause est presque toujours la même, et elle est architecturale plutôt qu’une erreur : une broche 8051 tire bien vers le bas et mal vers le haut. Tout le reste de ce tutoriel en découle — pourquoi les LED se câblent à l’opposé d’un Arduino, pourquoi lire un interrupteur exige d’abord une écriture, et pourquoi un port ne peut pas piloter huit quoi que ce soit.

Un port est juste un octet

Il n’y a pas d’API GPIO. Les quatre ports sont des registres spéciaux à adresses fixes, et en écrire un change les broches :

#include <reg52.h>

void main(void) {
    P1 = 0x00;        // all eight P1 pins low
    P1 = 0xFF;        // all eight high
    P1 = 0b10101010;  // alternating
}

Les bits individuels se nomment avec sbit, la forme vue dans presque tout le matériel de cours :

sbit LED    = P1^0;   // Keil C51 syntax
sbit BUTTON = P1^1;

LED = 0;              // one instruction — the 8051 has real bit operations

Ce ^ n’est pas un ou-exclusif. C’est la notation Keil pour « bit 0 de P1 ». Sous SDCC, l’équivalent vient de <8051.h> comme P1_0, et le reste du code est inchangé.

De quoi est faite une broche

Chaque broche de port possède un faible rappel interne vers l’alimentation et un fort transistor de tirage vers la masse, avec le verrou de port pilotant la grille du transistor. Les deux n’ont rien à voir en force.

Structure interne d'une broche de port 8051. Dans une frontière de puce en pointillés, un faible rappel marqué environ soixante microampères relie le rail cinq volts au nœud de broche, et un transistor à effet de champ de tirage marqué comme puisant jusqu'à dix milliampères relie le nœud de broche à la masse, sa grille pilotée depuis le verrou de port. La broche sort de la puce et se connecte à une LED externe dont la cathode fait face à la broche et l'anode va via une résistance 330 ohms au rail cinq volts. Deux lignes en dessous indiquent qu'écrire 0 allume le transistor pour tirer la broche à la masse et allumer la LED, et qu'écrire 1 l'éteint en ne laissant que soixante microampères donc la LED reste éteinte. Une note finale dit que le port 0 n'a aucun rappel et exige une résistance externe 10 kiloohms.
L'asymétrie qui définit le câblage 8051 : un transistor qui puise des milliampères contre un rappel qui source des microampères. Télécharger le SVG

Écrivez 0 et le transistor s’allume, bloquant la broche près de la masse et absorbant volontiers le courant venu de l’extérieur. Écrivez 1 et le transistor s’éteint, ne laissant que ce faible rappel — des dizaines de microampères — pour tenir la ligne haute. Assez pour présenter un 1 logique à une autre entrée CMOS, et loin d’assez pour allumer quoi que ce soit.

Cet arrangement s’appelle quasi bidirectionnel : la même broche est sortie et entrée, sans registre de direction à configurer, car une broche tenant un 1 est déjà assez relâchée pour être pilotée par autre chose.

Les LED vont donc dans l’autre sens

Sur Arduino, on câble une LED de la broche via une résistance à la masse et on pilote la broche haute. Faites cela sur 8051 et vous obtenez au mieux une faible lueur, car 60 µA n’est pas une source de lumière.

Le câblage 8051 est inversé : anode vers +5 V via la résistance, cathode vers la broche. La broche puise le courant, et un 0 allume la LED.

sbit LED = P1^0;

LED = 0;    // ON  — the pin sinks current from the supply
LED = 1;    // OFF — the pin releases the line

Dimensionnez la résistance pour le courant voulu, en rappelant que la broche elle-même chute d’environ un demi-volt en puisant :

R = (Vsupply − Vf(LED) − Vpin) / I

red LED at 5 mA :  (5 − 2.0 − 0.5) / 0.005 = 500 Ω   → use 470 Ω
red LED at 10 mA:  (5 − 2.0 − 0.5) / 0.010 = 250 Ω   → use 270 Ω

Le budget de courant de port que personne ne mentionne

Les limites par broche ne sont que la moitié de l’histoire. La fiche technique de l’AT89S52 spécifie les deux, et la seconde est bien plus restrictive :

Limite Valeur
Courant puisé maximal par broche 10 mA
Total maximal pour tout le port 1, 2 ou 3 15 mA
Total maximal pour tout le port 0 26 mA
Total maximal sur toute la puce 71 mA

Relisez cette ligne du milieu. Une seule broche peut puiser 10 mA, mais tout le port 1 ne peut puiser que 15 mA entre ses huit broches. L’exercice classique de câbler huit LED à un port et d’exécuter un chenillard convient — une LED à la fois. Allumez les huit à 10 mA chacune et vous demandez 80 mA à un port calibré pour 15.

Les pièces hors spéc n’explosent généralement pas ; elles s’affaissent. Vos niveaux logiques dérivent, la puce chauffe, et le comportement devient intermittent de façons qui ressemblent à des bogues logiciels. Pour huit LED brillantes et simultanées, le correctif est un pilote : un réseau Darlington ULN2803 puise un demi-ampère par canal, et le 8051 se contente de commander.

À la limite propre du port, huit LED simultanées reçoivent environ 1,9 mA chacune. Une LED moderne à haut rendement est parfaitement visible à cela, donc un chenillard ou un bargraphe convient — faites juste l’arithmétique avant de supposer la brillance.

Lire une entrée : écrire 1 d’abord

Comme le même transistor sert les deux sens, une broche 8051 ne peut être lue en entrée que si son verrou contient un 1. Avec un 0 verrouillé, le transistor bloque la broche à la masse et chaque lecture rend 0 quoi que soit connecté.

sbit BUTTON = P1^1;

void main(void) {
    BUTTON = 1;               // release the pin — REQUIRED before reading
    while (1) {
        if (BUTTON == 0) {    // switch wired to ground: pressed reads 0
            // handle press
        }
    }
}

Sauter cette première ligne est le bogue d’entrée 8051 le plus courant. Ce n’est pas une erreur possible sur une puce à registre de direction, d’où elle piège les arrivants d’AVR ou PIC.

Les interrupteurs se câblent vers la masse, pas vers l’alimentation, pour la même raison que les LED étaient inversées : le faible rappel est l’état de repos, et le travail de l’interrupteur est de le dominer en court-circuitant la broche vers le bas.

Le port 0 est l’exception

Le port 0 n’a aucun rappel interne. Il est à drain ouvert, car il double le bus multiplexé d’adresse et données pour la mémoire externe. Utilisé en E/S généralistes, il peut tirer une broche vers le bas et sinon seulement flotter.

Corrigez avec un rappel externe sur chaque broche P0 utilisée — 10 kΩ vers +5 V, ou un réseau de résistances pour les huit. Sans eux, les sorties P0 n’atteignent jamais le haut et les entrées P0 lisent du bruit. Les ports 1, 2 et 3 n’ont besoin de rien.

Anti-rebond, car un interrupteur n’est pas un interrupteur

Un contact mécanique rebondit quelques millisecondes, et une boucle de scrutation 8051 est assez rapide pour voir chaque rebond comme un appui séparé. Le correctif bon marché est de confirmer la lecture après un délai :

sbit BUTTON = P1^1;

bit pressed(void) {
    if (BUTTON != 0) return 0;    // not pressed
    delay_ms(20);                 // let the contact settle
    return (BUTTON == 0);         // still down? real press
}

Vingt millisecondes survivent largement au rebond d’un bouton tactile et restent loin du perceptible humain. Un délai bloquant convient en apprentissage ; une fois une interruption de minuterie en route, échantillonnez l’interrupteur depuis elle pour que le délai cesse de tout caler.

Quand ça tourne mal

  • La LED luit très faiblement. Elle est câblée façon Arduino, de broche à masse. Retournez-la : anode vers +5 V via la résistance, cathode vers la broche, et pilotez la broche basse.
  • Une entrée lit toujours 0. Le verrou de la broche tient 0. Écrivez-y 1 avant de lire.
  • Le port 0 ne fait rien. Pas de rappels externes. Ajoutez 10 kΩ vers +5 V sur chaque broche P0 utilisée.
  • Un appui s’enregistre triple. Rebond de contact. Confirmez la lecture après ~20 ms.
  • Tout marche jusqu’à plusieurs sorties actives à la fois. Vous dépassez le budget 15 mA du port. Déplacez la charge vers un ULN2803 ou réduisez le courant par LED.
  • Les broches se comportent bizarrement seulement quand le port série tourne. Vous utilisez P3.0 ou P3.1, qui sont RXD et TXD. Les fonctions alternées du port 3 priment sur vos projets pour elles.

Explorer le graphe

Fait partie de ces réalisations

Les projets et parcours d'apprentissage qui incluent ce tutoriel.

Pour aller plus loin

Références