terça-feira, novembro 06, 2018

Conexão de Raspberry Pi a Arduino através de I2C

Embora o Raspberry Pi tenha uma grande capacidade de processamento e uma quantidade respeitável de recursos de interface, às vezes ele é não é suficiente para aplicações de controle devido a:
  • Falta de ADC (Conversor Analógico para Digital)
  • Limitações na latência (tempo de resposta a um evento externo)
  • Limitações em gerar sinais com temporizações precisas
Por estes motivos, alguns projetos com Raspberry utilizam um Arduino para interagir com componentes externos. Nestes casos, o mais comum é o uso da comunicação serial assíncrona, por ser um tipo de interface mais conhecido. Entretanto, o Raspberry é limitado quanto à porta serial, particularmente os modelos com Bluetooth.

Vamos examinar aqui uma outra forma de interconectar um Raspberry Pi a um Arduino: a comunicação serial I2C.




Revisão rápida do padrão I2C

Já falei várias vezes no blog sobre I2C. Resumindo,  esta comunicação utiliza dois sinais: SCL (clock) e SDA (dados) e permite a ligação de vários dispositivos slave (escravo) a um master (mestre) através de um barramento (ou "varal").


O sinal SCL é sempre gerado pelo mestre. Já o sinal SDA é bi-direcional, podendo ser controlado pelo mestre ou pelo escravo. Do ponto de vista elétrico são usadas saídas open-colector, que podem ser colocadas em "zero" (terra) pelos dispositivos. Resistores de pull-up são necessários para manter os sinais em "um" quando nenhuma saída está ativada. Normalmente o sinal SDA só muda quando o sinal SCL está em zero. Duas violações desta regra caracterizam o início (start) e o fim (stop) de uma comunicação.


O primeiro byte enviado pelo mestre contém o endereço do escravo (7 bits) mais a seleção entre leitura e escrita (1 bit). O escravo confirma o endereçamento forçando um bit zero (ACK).

No caso de escrita, o mestre envia em seguida os bytes de dados, o escravo confirma cada um com ACK; ao final o mestre sinaliza o stop. Na leitura, os bits de dados são enviados pelo escravo e o mestre confirma com um NOACK (não aciona SDA).

A figura abaixo mostra uma leitura de EEProm I2C, onde uma escrita (endereço) é seguida de uma leitura (dados); neste caso o Stop da primeira transação pode ser omitido.


Cuidados devem ser tomados ao conectar dispositivos de 3.3 e 5V na mesma rede I2C, o que comentei aqui. Resumindo, se você ligar direto provavelmente vai funcionar mas você está se arriscando.

I2C no Raspberry Pi, em modo Mestre

Já falei sobre isto no passado. O primeiro passo é habilitar o I2C, o que deve ser feito através do raspi-config. Em seguida garanta que os pacotes i2c-tools e python-smbus estão instalados:

sudo apt-get install python-smbus i2c-tools

Feito isto, podemos criar um objeto no nosso programa python para ler e escrever via I2C:

import smbus
bus = smbus.SMBus(1)

Se você estiver usando um Pi original com 256M de Ram, troque o 1 por 0 na segunda linha. Os métodos disponíveis podem ser vistos aqui.

I2C no Arduino, em modo Escravo

A maioria dos exemplos de uso de I2C no Arduino são com ele operando em modo mestre. A biblioteca padrão (Wire) também permite a operação em modo escravo, de uma forma bem simples:
  • Para iniciar o modo escravo, use Wire.begin(endereço)
  • Use Wire.onReceive para registrar uma rotina que será chamada sempre que dados forem recebidos pelo I2C para o endereço informado. Os dados são lidos usando Wire.read
  • Use Wire.onRequest para registrar uma rotina que será chamada quando o mestre requisitar dados. User Wire.Write para enviar os dados.

Exemplo Prático

Neste exemplo vou usar um Raspberry Pi Zero W ligado a um Arduino Pro Mini (um modelo bem em conta, porém sem conexão USB para a Serial. Ao Arduino serão ligados um potenciômetro (que requer ADC) e um anel de LEDs RGB (que requer temporização precisa).

O primeiro byte enviado pelo Raspberry Pi determinará a operação:
  • 0 indica leitura do potenciômetro. Esta escrita será seguida de uma operação de leitura I2C que  retornará dois bytes, correspondente à leitura do potenciômetro (0 a 1023).
  • 1 indica atualização dos LEDs RGB. Deve ser seguido de 21 bytes, três para cada LED. O primeiro byte é a intensidade do verde, o segundo a intensidade do vermelho e o terceiro a intensidade do azul.

A montagem que usei está abaixo, reparem que usei MOSFETs 2N7000 separar as vias I2C de 3,3 e 5 Volts e não coloquei resistores de pullup externos (os pullups internos são suficientes).



O software para os dois lados está no github. Para uma demonstração simples, o Raspberry lê periodicamente o potenciômetro e atualiza os LEDs RGB.


Nenhum comentário: