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.
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.
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:
Postar um comentário