quinta-feira, julho 09, 2020

Expansores de I/O PCF8574 e PCF8574A

Os integrados PCF8574 e PCF8574A são expansores de I/O com interface I2C bastante populares. Entretanto eles tem algumas peculiaridades quando usados de modo bidirecional que não costumam ser muito comentadas.


A base para o que vou falar aqui é este datasheet da NXP. Uma primeira informação importante é que a única diferença entre os dois modelos é o endereço I2C; daqui para frente vou falar apenas PCF8574 ao tratar do que é comum.

Vamos começar do básico: o objetivo deste componente é fornecer 8 entradas/saídas digitais através de uma conexão I2C (dois fios, compartilhados com outros dispositivos I2C). É um recurso útil quando precisamos de mais I/O digitais e não queremos usar um microcontrolador mais sofisticado.

A interface I2C é simples:
  • Suporta clock de até 100KHz
  • O endereço é determinado por três pinos do integrado (tabela adiante). Estes pinos não tem pullup ou pulldown interno, precisam ser sempre ligados externamente,
  • Existe um único registrador de leitura e um único registrador de escrita, dispensando endereçamento interno.

A complicação é a relação entre o registrador interno de escrita e os pinos de entrada e saída. O datasheet se refere a estes pinos como "quasi-bidirectional". Nos circuitos bidirecionais, temos normalmente um sinal de direção: quando a direção é saída o nível dos pinos é controlado pelo integrado, quando a direção é entrada o nível dos pinos é controlado pelo que estiver ligado externamente (costuma-se dizer que os pinos estão em "tristate", um estado diferente de alto ou baixo).

O PCF8574 não tem a opção de "tristate", está sempre acionando as saídas. O truque é que o acionamento para o nível alto é "fraco".  Quando uma saída está no nível 1 ela pode ser forçada externamente para o nível 0 (e ser usada como entrada). Porém quando uma saída está no nível 0 ela não pode ser forçada para o nível 1.

Se você quiser usar um pino do PCF8574 como saída, basta escrever o valor desejado no bit correspondente do registrador interno. Resta lembrar que ele não é capaz de fornecer correntes acima de 0,3mA quando está em nível alto (mas consegue absorver 10mA quando está em nível baixo).

Para usar um pino como entrada, é preciso escrever 1 no bit correspondente do registrador de saída. Se você deixar este pino aberto e medir a tensão ele estará em nível alto. Conectado a um circuito externo, este pino poderá ser colocado tanto em nível alto como baixo e isto será refletido no registrador de leitura.

Vejamos um pequeno exemplo, ligando o PCF8274A a um Arduino e usando com saída um LED e como entrada um botão:


Reparar que o LED foi ligado para ser aceso quando a saída estiver em nível baixo, não é possível operar de outra forma.

O programa abaixo acende o LED quando o botão é apertado e o apaga quando o LED é solto.
/**
 * Demonstração do uso do PCF8574 para
 * entrada e saída
 * 
 */

#include <Wire.h>

// Endereço do PCF8574A com pinos de endereçamento aterrados
// (para PCF7584 usar 0x20)
const byte PCF8574_Addr = 0x38;

// Conexões do PCF8574
const byte BIT_LED = 0x20;   // pino 10 ligado ao catodo do LED
                             // anodo do LED ligado via resistor de 1K a 5V
const byte BIT_BOTAO = 0x10; // pino 9 ligado ao botão
                             // outro terminal do botão ligado a terra

// Iniciação
void setup() {
  Wire.begin();
  // Pinos de entrada precisam ser configurados com nível alto
  // Nível alto apaga o LED
  PCF8574_Write(BIT_LED | BIT_BOTAO);
}

// Que seja eterno enquanto dure
void loop() {
  if (PCF8574_Read() & BIT_BOTAO) {
    // nível alto indica botão solto, apagar o LED
    PCF8574_Write(BIT_LED | BIT_BOTAO);
  } else {
    // nível baixo indica botão apertado, acender o LED
    PCF8574_Write(BIT_BOTAO);    
  }
  delay (100);
}

// Escreve um byte
void PCF8574_Write(byte dado) {
  Wire.beginTransmission(PCF8574_Addr);
  Wire.write(dado);
  Wire.endTransmission();
}

// Lê um byte
byte PCF8574_Read(){
  Wire.requestFrom(PCF8574_Addr, 1);
  return Wire.read();
}

2 comentários:

Anônimo disse...

O clock do i2c é 100kHz

Daniel Quadros disse...

Obrigado, corrigido!