Tutorial · Iniciante · 28 min
GPIO e portas do 8051: absorve corrente, não fornece
Um pino do 8051 absorve dez miliampères e fornece quase nenhum. Veja por que os LEDs são invertidos, por que se escreve 1 antes de ler e o que uma porta aciona.
Introdução
O primeiro programa de 8051 que todo mundo escreve faz um LED piscar, e uma quantidade surpreendente deles não pisca. A ligação parece certa, o código parece certo e o pino mede um convincente 5 V sem fazer nada útil.
A causa quase sempre é a mesma, e é arquitetônica, não um erro: um pino do 8051 é bom para absorver corrente e ruim para fornecê-la. Todo o restante deste tutorial decorre dessa assimetria — por que os LEDs são ligados ao contrário de um Arduino, por que ler uma chave exige antes uma escrita e por que uma porta não consegue acionar oito coisas.
Uma porta é apenas um byte
Não existe API de GPIO. As quatro portas são registradores de funções especiais em endereços fixos, e escrever em uma delas altera os pinos:
#include <reg52.h>
void main(void) {
P1 = 0x00; // all eight P1 pins low
P1 = 0xFF; // all eight high
P1 = 0b10101010; // alternating
}
Bits individuais recebem nomes com sbit, que é a forma que você verá em quase todo o material de curso:
sbit LED = P1^0; // Keil C51 syntax
sbit BUTTON = P1^1;
LED = 0; // one instruction — the 8051 has real bit operations
Esse ^ não significa OU exclusivo (XOR). É a notação da Keil para “bit 0 de P1”. No SDCC, o equivalente vem de <8051.h> como P1_0, e o restante do código não muda.
Do que é feito um pino
Cada pino de porta tem um pull-up interno fraco para a fonte e um transistor de pull-down forte para a massa, com o latch da porta controlando o gate do transistor. As duas ligações têm forças muito diferentes.
Escreva 0 e o transistor liga, levando o pino quase à massa e absorvendo corrente de um circuito externo. Escreva 1 e o transistor desliga, deixando apenas aquele pull-up fraco — dezenas de microampere — para levar a linha ao nível alto. É suficiente para apresentar nível lógico 1 a outra entrada CMOS, mas não chega perto de acender qualquer coisa.
Essa disposição é chamada de quase bidirecional: o mesmo pino é uma saída e uma entrada, sem registrador de direção para configurar, porque um pino que mantém 1 já está suficientemente liberado para ser controlado por outro dispositivo.
Por isso os LEDs vão ao contrário
Em um Arduino, você liga um LED do pino, passando por um resistor, à massa e mantém o pino alto. Faça isso em um 8051 e você terá no máximo um brilho fraco, porque 60 µA não é uma fonte de luz.
A ligação do 8051 é invertida: ânodo a +5 V pelo resistor e cátodo ao pino. O pino absorve a corrente, e um 0 liga o LED.
sbit LED = P1^0;
LED = 0; // ON — the pin sinks current from the supply
LED = 1; // OFF — the pin releases the line
Dimensione o resistor para a corrente desejada, lembrando que o próprio pino apresenta uma queda de cerca de meio volt ao absorver corrente:
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 Ω
O orçamento de corrente da porta que ninguém menciona
Os limites por pino são só metade da história. A folha de dados do AT89S52 especifica os dois limites, e o segundo é muito mais restritivo:
| Limite | Valor |
|---|---|
| Corrente máxima que um pino pode absorver | 10 mA |
| Corrente total máxima em qualquer uma das portas 1, 2 ou 3 | 15 mA |
| Corrente total máxima em toda a porta 0 | 26 mA |
| Corrente total máxima em todo o chip | 71 mA |
Leia a linha do meio novamente. Um único pino pode absorver 10 mA, mas todos os oito pinos da porta 1, juntos, podem absorver no máximo 15 mA. O exercício clássico de ligar oito LEDs a uma porta e executar um padrão de sequência funciona — um LED por vez. Acender os oito a 10 mA cada exige 80 mA de uma porta dimensionada para 15 mA.
Peças fora das especificações normalmente não explodem; elas sofrem queda de tensão. Seus níveis lógicos derivam, o chip esquenta e o comportamento se torna intermitente de maneiras que parecem falhas de software. Se quiser oito LEDs brilhantes e simultâneos, a solução é um driver de LED: uma matriz Darlington ULN2803 absorve meio ampere por canal, e o 8051 apenas diz a ele o que fazer.
Com a porta em seu limite, cada um dos oito LEDs simultâneos recebe cerca de 1,9 mA. Um LED moderno de alta eficiência fica perfeitamente visível com essa corrente, então uma sequência ou uma barra de LEDs funciona — apenas faça a conta antes de presumir o brilho.
Para ler uma entrada: escreva 1 primeiro
Como o mesmo transistor atende às duas direções, um pino do 8051 só pode ser lido como entrada se seu latch mantiver um 1. Com um 0 no latch, o transistor está levando o pino à massa e qualquer leitura retorna 0, independentemente do que esteja conectado.
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
}
}
}
Omitir essa primeira linha é a falha de entrada mais comum no 8051. Não é um erro possível em um chip com registrador de direção, e é exatamente por isso que ele afeta quem chega vindo de AVR ou PIC.
Chaves são ligadas à massa, e não à fonte, pelo mesmo motivo pelo qual os LEDs são invertidos: o pull-up fraco é o estado ocioso, e o trabalho da chave é levar o pino à massa ao ser pressionada.
A porta 0 é a exceção
A porta 0 não tem nenhum pull-up interno. Ela usa uma saída em dreno aberto (open-drain), porque também funciona como barramento multiplexado de endereço e dados para memória externa. Quando usada como E/S geral, pode puxar um pino para baixo e, fora disso, só pode deixá-lo flutuar.
Corrija isso com um resistor de pull-up externo em cada pino P0 usado — 10 kΩ para +5 V, ou uma rede de resistores se precisar dos oito. Sem eles, as saídas P0 nunca chegam a um nível alto e as entradas P0 leem ruído. As portas 1, 2 e 3 não precisam disso.
Debounce, porque uma chave não é uma chave
Um contato mecânico sofre bounce por alguns milissegundos, e um laço de consulta do 8051 é rápido o bastante para ver cada transição como um acionamento separado. A solução simples é confirmar a leitura após um atraso:
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
}
Vinte milissegundos superam confortavelmente o bounce de uma chave tátil e ficam bem abaixo do limiar de percepção humana. Um atraso bloqueante serve enquanto você aprende; quando uma interrupção de temporizador estiver rodando, verifique a chave a partir dela para que o atraso não paralise todo o restante.
Quando algo dá errado
- O LED brilha muito fracamente. Está ligado como no Arduino, do pino à massa. Inverta: ânodo a +5 V pelo resistor, cátodo ao pino, e mantenha o pino baixo.
- Uma entrada sempre lê 0. O latch do pino está mantendo 0. Escreva um 1 nele antes de ler.
- A porta 0 não faz nada. Não há resistores de pull-up externos. Adicione 10 kΩ para +5 V em cada pino P0 em uso.
- Um acionamento aparece como três. Bounce de contato. Confirme a leitura após cerca de 20 ms.
- Tudo funciona até várias saídas ficarem ligadas ao mesmo tempo. Você excedeu o orçamento de 15 mA da porta. Mova a carga para um ULN2803 ou reduza a corrente por LED.
- Os pinos se comportam de forma estranha somente quando a porta serial está rodando. Você está usando P3.0 ou P3.1, que são RXD e TXD. As funções alternativas da porta 3 têm prioridade sobre seus planos para eles.
Explore o grafo
Faz parte destas montagens
Projetos e caminhos de aprendizagem que incluem este tutorial.
Leitura complementar