quinta-feira, junho 04, 2015

Display I2C no Raspberry Pi

Continuando as minhas experiências com o display alfanumérico com interface I2C, vamos ver aqui como conectá-lo a um Raspberry Pi.


Hardware

A conexão do display é bastante simples. Uma preocupação foi quanto ao fato do processador do Rasp Pi trabalhar com 3.3V. Inicialmente eu estava pensando em alimentar o display também com 3.3V (o que requer dois capacitores), mas eu lembrei que no I2C os dispositivos nunca colocam uma tensão alta na via; eles conectam o sinal ao terra ou o deixam desconectado. A tensão alta é gerada por resistores de pull-up. Portanto, basta alimentar o display com 5V e ligar os resistores de pull-up ao 3.3V. Melhor ainda, o Raspberry já tem os resistores internamente, o que reduz o circuito a umas poucas conexões:



Software - SO

É aqui onde as coisas se complicam um pouco. Por default, o Raspbian (que é o OS que usei) não habilita o I2C, liberando os pinos correspondentes para GPIO. Existe muita documentação a respeito da habilitação do I2C na web, mas esta parte sofreu várias mudanças desde o lançamento do Rasp Pi e muita coisa está desatualizada.

Para começar, ocorreu uma mudança de hardware. O Raspberry Pi original (com 256M de Ram) usava o port I2C0 do processador, as versões seguintes usam o I2C1. Vou usar aqui sempre I2C1, lembre-se de mudar se estiver usando um Rasp Pi antigo.

Para o I2C funcionar nas versões mais recentes do Raspbian é necessário que o Linux carregue dois módulos (i2c-bcm2708 e i2c-dev). Isto é comandado listando estes módulos em /etc/modules e habilitando os dispositivos i2c no Device Tree através do arquivo /boot/config.txt.

Em teoria, o raspi-config faz isto para nós. Basta executá-lo (com sudo), escolher Advanced Options, I2C, Yes e Yes. Para mim faltaram o i2c-dev em /etc/modules e o dtparam=i2c1=on no config.txt. A Adafruit tem um bom tutorial a respeito.

Para facilitar o uso do I2C, instale dois pacotes:

sudo apt-get install python-smbus i2c-tools

Agora é só rebootar

sudo reboot

Após o boot podemos fazer um teste:

sudo i2cdetect -y 1

Isto irá listar os endereços dos dispositivos ligados ao I2C; no caso do display será apresentado o endereço 0x3e.

pi@raspberrypi ~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Como isto funciona? Sempre que um dispositivo é endereçado ele deve confirmar através de um NAK (transmitir um 0). O i2cdetect testa um a um os 127 endereços possíveis e verifica quais foram respondidos.

Neste ponto é necessário usar o sudo para ter acesso ao i2c. Podemos simplificar isto colocando usuário pi no grupo i2c e dando permissão de acesso a este grupo:

sudo adduser pi i2c
sudo echo "SUBSYSTEM==\"i2c-dev\", GROUP=\"i2c\", MODE=\"0666\"" >> /etc/udev/rules.d/99-i2c.rules
sudo reboot 

Software - Aplicação

Para um exemplo rápido vamos usar Python e a biblioteca SMBus (SMBus é uma variante do I2C). O que nos interessa são dois métodos:

write_byte_data(int addr, char cmd, char val)
Envia para o dispositivo de endereço addr cmd seguido de val.

write_i2c_block_data(int addr, char cmd, long vals[])
Envia para o dispositivo de endereço addr cmd seguido dos valores na lista val.

No nosso caso o "cmd" será a seleção entre registrador ou dado.

O código abaixo faz apenas a iniciação do LCD e a apresentação de um texto curto.

#!/usr/bin/python

from time import sleep
import smbus

# Endereço do display e os comandos para selecionar registrador ou dado
DEVICE_ADDRESS = 0x3E
SEL_REG = 0x00
SEL_DADO = 0x40

# Objeto para acesso ao I2C1
bus = smbus.SMBus(1)

# Iniciação do display
# O último comando é um Clear que demora um pouco mais de 1ms
bus.write_byte_data(DEVICE_ADDRESS, SEL_REG, 0x38)
bus.write_byte_data(DEVICE_ADDRESS, SEL_REG, 0x39)
bus.write_byte_data(DEVICE_ADDRESS, SEL_REG, 0x14)
bus.write_byte_data(DEVICE_ADDRESS, SEL_REG, 0x79)
bus.write_byte_data(DEVICE_ADDRESS, SEL_REG, 0x50)
bus.write_byte_data(DEVICE_ADDRESS, SEL_REG, 0x6C)
bus.write_byte_data(DEVICE_ADDRESS, SEL_REG, 0x0C)
bus.write_byte_data(DEVICE_ADDRESS, SEL_REG, 0x01)
sleep (0.002)

# Escreve a mensagem
bus.write_i2c_block_data(DEVICE_ADDRESS, SEL_DADO, list(bytearray("DQSoft")))

Links

Os links abaixo são as origens das informações que coloquei acima.

Habilitação do i2c, de forma sucinta.
O tutorial da Adafruit
Descrição dos métodos do SMBus
Problemas causados com a introdução do Device Tree

Um comentário:

EverPi disse...

Nice! Geralmente quando preciso utilizar o i2c em algo para testes eu costumo carregar utilizando a tool da wiring pi "gpio load i2c"