terça-feira, junho 02, 2015

I2C Master no ATtiny25 (USI)

Este post vem da minha experiência de conectar um display 16x2 com interface I2C a um ATtiny25; acredito que estas informações sejam úteis para quem quiser ligar dispositivos I2C a um microcontrolador ATtiny que tenha a USI (Universal Serial Interface).


Comunicação I2C

O I2C é um protocolo que permite interligar vários dispositivos mestres e escravos com apenas dois fios (mais o terra), em um esquema do tipo "varal".

Do ponto de vista elétrico, temos dois sinais: clock (SCL) e dados (SDA).  O sinal SCL é sempre comandado por um mestre, o SDA é um sinal bidirecional. Como temos vários dispositivos conectados simultaneamente, o acionamento deve ser do tipo "open collector", onde o dispositivo pode "puxar"o sinal para terra (nível baixo) ou deixá-lo livre.


Resistores de "pull up" garantem nível alto quando nenhum dispositivo está forçando o nível baixo. O valor destes resistores é determinado por duas considerações (como descrito, por exemplo, aqui):
  • Se o resistor for muito baixo, a corrente ao "puxá-lo" para baixo será muito grande; ao atingir o limite de corrente do dispositivo, ele poderá pifar ou não conseguir forçar o nível baixo.
  • Se o resistor for muito alto, a combinação com a capacitância do barramento acarretará um tempo de subida do sinal incompatível com a taxa de comunicação utilizada.
O diagrama abaixo mostra a dinâmica básica da comunicação.


Normalmente o sinal SDA só é alterado quando SCL está no nível baixo. Violações desta regra são usadas pelo mestre para sinalizar o começo e o fim da comunicação (start e stop). Os dados são organizados em bytes (oito bits enviados serialmente), após a transferência de cada byte o receptor deve indicar a aceitação enviando um bit (ACK ou NAK). O primeiro byte enviado pelo mestre após o start contém o endereço do escravo (7 bits) e a indicação da direção da comunicação: escrita (o mestre vai enviar bytes) ou leitura (o escravo vai enviar bytes). O significado dos bytes recebidos e enviados varia conforme o escravo e não é definido pelo padrão I2C.


A USI

Enquanto os ATmegas costumam ter uma interface específica para I2C (chamada de TWI pois I2C é marca registrada), a maioria dos ATtinys possuem a USI, que implementa parcialmente I2C e SPI. Vamos ver aqui apenas o seu uso com I2C, mais especificamente como mestre. A Atmel publicou uma Application Note sobre isto (pdf aqui), mas achei-a confusa.

Diagrama de Blocos da USI


O coração da USI é um shift register, o USIDR. A cada pulso de um clock, os dados são deslocados para a esquerda. O bit 7 está ligado à saída e o bit 0 à entrada. Um contador de 4 bits, com clock independente, pode ser usado para contar os deslocamentos.

O controle da USI é feito através de dois registradores, USICR e USISR:



O primeiro passo para usar a USI como interface I2C é programar o "Wire Mode" (USIWM)  no registrador USICR com "10". Isto faz com que o pino SCL passe a operar no modo "open collector", quando programado como saída. O funcionamento do pino SDA depende da direção programada no respectivo registrador DDR:
  • Entrada: o pino SDA é uma entrada, conectado ao bit 0 do USIDR.
  • Saída: o pino SDA é uma saída "open-collector", controlada pelo bit 7 do USIDR ou pelo respectivo pino do registrador PORT.
Reparar, portanto, que a comunicação vai exigir acessar o registrador DDR além dos registradores da USI. A USI não possui facilidades para gerar start e stop; os sinais SCL e SDA terão que ser posicionados manualmente.

Os bits USICS e USICLK do USICR selecionam os clocks para o USIDR e para o contador em USISR.


Basicamente temos três fontes: o sinal SCL (usado nos escravos),  o Timer0  e diretamente pelo software. O uso do timer0 deixa as coisas mais automáticas, mas consume um recurso importante. O exemplo da ATmel usa o clock gerado pelo software, o que é feito através dos bits USICLK e USICT do USICR. USICLK gera um pulso no clock do USIDR, USICT muda o sinal SCL (é preciso mudar duas vezes para gerar um pulso.

O contador fica nos bits USICNT do USISR. Quando este contador dá a volta (passa de 1111 para 0000) o bit USIOIF é ligado e, opcionalmente, uma interrupção é gerada. No caso de clock gerado manualmente pelo USICT, cada pulso corresponderá a dois incrementos na contagem (pois USICT foi ativado duas vezes). O overflow do contador causa também a cópia do USIDR para o USIBR, fornecendo um buffer de 1 byte na recepção.

Resumindo, a USI fornece recursos bem limitados, exigindo bastante manipulação direta dos sinais. Se o clock for gerado pelo Timer0 é possível fazer a comunicação através da interrupção do contador, em paralelo com outros processamentos. Gerando o clock manualmente, a USI oferece somente um shift register comandando uma saída open collector.

No lado escravo, que não explorei ainda, a USI possui a detecção do start, que pode gerar uma interrupção. O tratamento do endereçamento fica por conta do software.

Exemplo

Coloquei nos arquivos do blog o LCD25.zip que ilustra o comando do display por um ATtiny25. Neste caso o ATtiny apenas envia bytes para o display; a recepção de dados fica restrita aos ACK/NAK.

Nenhum comentário: