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.
  1. /** 
  2.  * Demonstração do uso do PCF8574 para 
  3.  * entrada e saída 
  4.  *  
  5.  */  
  6.   
  7. #include <Wire.h>  
  8.   
  9. // Endereço do PCF8574A com pinos de endereçamento aterrados  
  10. // (para PCF7584 usar 0x20)  
  11. const byte PCF8574_Addr = 0x38;  
  12.   
  13. // Conexões do PCF8574  
  14. const byte BIT_LED = 0x20;   // pino 10 ligado ao catodo do LED  
  15.                              // anodo do LED ligado via resistor de 1K a 5V  
  16. const byte BIT_BOTAO = 0x10; // pino 9 ligado ao botão  
  17.                              // outro terminal do botão ligado a terra  
  18.   
  19. // Iniciação  
  20. void setup() {  
  21.   Wire.begin();  
  22.   // Pinos de entrada precisam ser configurados com nível alto  
  23.   // Nível alto apaga o LED  
  24.   PCF8574_Write(BIT_LED | BIT_BOTAO);  
  25. }  
  26.   
  27. // Que seja eterno enquanto dure  
  28. void loop() {  
  29.   if (PCF8574_Read() & BIT_BOTAO) {  
  30.     // nível alto indica botão solto, apagar o LED  
  31.     PCF8574_Write(BIT_LED | BIT_BOTAO);  
  32.   } else {  
  33.     // nível baixo indica botão apertado, acender o LED  
  34.     PCF8574_Write(BIT_BOTAO);      
  35.   }  
  36.   delay (100);  
  37. }  
  38.   
  39. // Escreve um byte  
  40. void PCF8574_Write(byte dado) {  
  41.   Wire.beginTransmission(PCF8574_Addr);  
  42.   Wire.write(dado);  
  43.   Wire.endTransmission();  
  44. }  
  45.   
  46. // Lê um byte  
  47. byte PCF8574_Read(){  
  48.   Wire.requestFrom(PCF8574_Addr, 1);  
  49.   return Wire.read();  
  50. }  

2 comentários:

Anônimo disse...

O clock do i2c é 100kHz

Daniel Quadros disse...

Obrigado, corrigido!