classe I2C – um protocolo série de dois fios

I2C é um protocolo de dois fios para comunicação entre dispositivos. Ao nível físico é composto por 2 fios: SCL e SDA, as linhas de relógio e de dados, respetivamente.

Os objetos I2C são criados associados a um barramento específico. Podem ser inicializados aquando da criação ou inicializados posteriormente.

Imprimir o objeto I2C fornece informações sobre a sua configuração.

Existem implementações I2C por hardware e por software através das classes I2C e SoftI2C. O I2C por hardware utiliza o suporte de hardware subjacente do sistema para realizar as leituras/escritas e é geralmente eficiente e rápido, mas pode ter restrições quanto aos pinos que podem ser usados. O I2C por software é implementado por bit-banging e pode ser usado em qualquer pino, mas não é tão eficiente. Estas classes têm os mesmos métodos disponíveis e diferem principalmente na forma como são construídas.

Nota

O barramento I2C requer circuitos de pull-up em SDA e SCL para o seu funcionamento. Normalmente são resistências na gama de 1 a 10 kOhm, ligadas de cada SDA/SCL à Vcc. Sem estas, o comportamento é indefinido e pode variar entre bloqueio, reinicialização inesperada do watchdog ou simplesmente valores errados. Frequentemente, estes circuitos de pull-up já estão integrados na placa MCU ou nas placas de expansão do sensor, mas não existe uma regra geral. Por isso, verifique em caso de problemas. Consulte também este excelente guia de aprendizagem da Adafruit sobre cablagem I2C.

Exemplo de utilização:

from machine import I2C

i2c = I2C(freq=400000)          # create I2C peripheral at frequency of 400kHz
                                # depending on the port, extra parameters may be required
                                # to select the peripheral and/or pins to use

i2c.scan()                      # scan for peripherals, returning a list of 7-bit addresses

i2c.writeto(42, b'123')         # write 3 bytes to peripheral with 7-bit address 42
i2c.readfrom(42, 4)             # read 4 bytes from peripheral with 7-bit address 42

i2c.readfrom_mem(42, 8, 3)      # read 3 bytes from memory of peripheral 42,
                                #   starting at memory-address 8 in the peripheral
i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of peripheral 42
                                #   starting at address 2 in the peripheral

Construtores

class machine.I2C(id: int, *, scl: Pin | None = None, sda: Pin | None = None, freq: int = 400000, timeout: int = 50000)

Constrói e devolve um novo objeto I2C usando os seguintes parâmetros:

  • id identifica um periférico I2C específico. Os valores permitidos dependem da porta/placa em particular.

  • scl deve ser um objeto pino que especifica o pino a usar para SCL.

  • sda deve ser um objeto pino que especifica o pino a usar para SDA.

  • freq deve ser um inteiro que define a frequência máxima para SCL.

  • timeout é o tempo máximo em microssegundos permitido para transações I2C. Este parâmetro não é permitido nalgumas portas.

Note que algumas portas/placas terão valores predefinidos de scl e sda que podem ser alterados neste construtor. Outras terão valores fixos de scl e sda que não podem ser alterados.

Métodos Gerais

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

Inicializa o barramento I2C com os argumentos fornecidos:

  • scl é um objeto pino para a linha SCL

  • sda é um objeto pino para a linha SDA

  • freq é a taxa de relógio SCL

No caso de I2C por hardware, a frequência de relógio real pode ser inferior à frequência solicitada. Isto depende do hardware da plataforma. A taxa real pode ser determinada imprimindo o objeto I2C.

scan() List[int]

Examina todos os endereços I2C entre 0x08 e 0x77 inclusive e devolve uma lista dos que respondem. Um dispositivo responde se baixar a linha SDA após o seu endereço (incluindo um bit de escrita) ser enviado no barramento.

Operações I2C primitivas

Os métodos seguintes implementam as operações primitivas do barramento I2C controlador e podem ser combinados para realizar qualquer transação I2C. São fornecidos caso necessite de maior controlo sobre o barramento; caso contrário, podem ser usados os métodos standard (ver abaixo).

Estes métodos estão disponíveis apenas na classe SoftI2C.

start() None

Gera uma condição START no barramento (SDA transita para baixo enquanto SCL está alto).

stop() None

Gera uma condição STOP no barramento (SDA transita para alto enquanto SCL está alto).

readinto(buf: bytearray, nack: bool = True, /) None

Lê bytes do barramento e armazena-os em buf. O número de bytes lidos é o comprimento de buf. Um ACK será enviado no barramento após receber todos os bytes exceto o último. Após receber o último byte, se nack for verdadeiro é enviado um NACK; caso contrário é enviado um ACK (e neste caso o periférico assume que mais bytes serão lidos numa chamada posterior).

write(buf: bytes) int

Escreve os bytes de buf no barramento. Verifica se é recebido um ACK após cada byte e interrompe a transmissão dos bytes restantes se for recebido um NACK. A função devolve o número de ACKs recebidos.

Operações standard do barramento

Os métodos seguintes implementam as operações standard de leitura e escrita do controlador I2C que visam um determinado dispositivo periférico.

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

nbytes do periférico especificado por addr. Se stop for verdadeiro, é gerada uma condição STOP no final da transferência. Devolve um objeto bytes com os dados lidos.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

Lê para buf a partir do periférico especificado por addr. O número de bytes lidos será o comprimento de buf. Se stop for verdadeiro, é gerada uma condição STOP no final da transferência.

O método devolve None.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

Escreve os bytes de buf no periférico especificado por addr. Se for recebido um NACK após a escrita de um byte de buf, os bytes restantes não são enviados. Se stop for verdadeiro, é gerada uma condição STOP no final da transferência, mesmo que seja recebido um NACK. A função devolve o número de ACKs recebidos.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

Escreve os bytes contidos em vector no periférico especificado por addr. vector deve ser uma tupla ou lista de objetos com o protocolo buffer. O addr é enviado uma vez e depois os bytes de cada objeto em vector são escritos sequencialmente. Os objetos em vector podem ter comprimento zero, caso em que não contribuem para a saída.

Se for recebido um NACK após a escrita de um byte de um dos objetos em vector, os bytes restantes, e quaisquer objetos restantes, não são enviados. Se stop for verdadeiro, é gerada uma condição STOP no final da transferência, mesmo que seja recebido um NACK. A função devolve o número de ACKs recebidos.

Operações de memória

Alguns dispositivos I2C funcionam como dispositivos de memória (ou conjuntos de registos) que podem ser lidos e escritos. Neste caso existem dois endereços associados a uma transação I2C: o endereço do periférico e o endereço de memória. Os métodos seguintes são funções de conveniência para comunicar com tais dispositivos.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

nbytes do periférico especificado por addr a partir do endereço de memória especificado por memaddr. O argumento addrsize especifica o tamanho do endereço em bits. Devolve um objeto bytes com os dados lidos.

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

Lê para buf a partir do periférico especificado por addr a partir do endereço de memória especificado por memaddr. O número de bytes lidos é o comprimento de buf. O argumento addrsize especifica o tamanho do endereço em bits.

O método devolve None.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

Escreve buf no periférico especificado por addr a partir do endereço de memória especificado por memaddr. O argumento addrsize especifica o tamanho do endereço em bits.

O método devolve None.

classe SoftI2C – barramento I2C emulado por software

A classe SoftI2C implementa I2C por bit-banging em pinos GPIO arbitrários. Expõe a mesma interface de métodos que I2C, mais as operações primitivas de barramento de baixo nível (start(), stop(), readinto(), write()) para chamadores que precisam de montar transações não standard. Use-a quando os pinos necessários não estão ligados a um bloco I2C por hardware, quando necessita de mais barramentos do que o hardware fornece, ou para comunicar com dispositivos que requerem sequências invulgares (relógios extra, starts repetidos após escritas, etc.).

Construtores

class machine.SoftI2C(scl: Pin, sda: Pin, *, freq: int = 400000, timeout: int = 50000)

Constrói um barramento I2C por software controlado por scl / sda.

freq é a taxa de relógio SCL pretendida em Hz (a taxa real é tipicamente inferior devido à sobrecarga do ciclo de bit-banging).

timeout é o tempo máximo em microssegundos para aguardar pelo clock stretching (SCL mantido baixo por outro dispositivo no barramento); ao expirar é lançado um OSError(ETIMEDOUT).

Métodos Gerais

init(scl: Pin, sda: Pin, *, freq: int = 400000) None

Reinicializa o barramento I2C por software com os pinos e frequência fornecidos. Equivalente a construir um novo SoftI2C no mesmo objeto.

scan() List[int]

Examina todos os endereços I2C entre 0x08 e 0x77 inclusive e devolve uma lista dos que responderam.

Operações I2C primitivas

Os métodos seguintes implementam as operações primitivas do barramento I2C controlador e podem ser combinados para realizar qualquer transação I2C. São exclusivos do SoftI2C – a classe I2C por hardware não os expõe.

start() None

Gera uma condição START no barramento (SDA transita para baixo enquanto SCL está alto).

stop() None

Gera uma condição STOP no barramento (SDA transita para alto enquanto SCL está alto).

readinto(buf: bytearray, nack: bool = True, /) None

Lê bytes do barramento para buf. São lidos len(buf) bytes; um ACK é enviado após cada byte exceto o último. Após o último byte, nack=True (o predefinido) envia um NACK para terminar a transferência; nack=False envia um ACK para que o dispositivo permaneça selecionado para um readinto() subsequente.

write(buf: bytes) int

Escreve buf no barramento, verificando o ACK após cada byte. A transmissão para no primeiro NACK. Devolve o número de ACKs recebidos.

Operações standard do barramento

Os métodos seguintes implementam as operações standard de leitura e escrita do controlador I2C que visam um determinado dispositivo periférico.

readfrom(addr: int, nbytes: int, stop: bool = True, /) bytes

nbytes do dispositivo no endereço de 7 bits addr. Se stop for verdadeiro, é gerada uma condição STOP no final da transferência.

readfrom_into(addr: int, buf: bytearray, stop: bool = True, /) None

len(buf) bytes do dispositivo em addr para buf. Se stop for verdadeiro, é gerada uma condição STOP no final da transferência.

writeto(addr: int, buf: bytes, stop: bool = True, /) int

Escreve buf no dispositivo em addr. A transmissão para no primeiro NACK. Se stop for verdadeiro, é sempre gerada uma condição STOP no final da transferência (mesmo com NACK antecipado). Devolve o número de ACKs recebidos.

writevto(addr: int, vector: tuple | list, stop: bool = True, /) int

Escreve a concatenação dos buffers em vector no dispositivo em addr como uma única transação. Os buffers vazios são ignorados. Comporta-se como writeto() para a semântica de stop e valor de retorno.

Operações de memória

Alguns dispositivos I2C funcionam como dispositivos de memória (ou conjuntos de registos) que podem ser lidos e escritos. Neste caso existem dois endereços associados a uma transação I2C: o endereço do periférico e o endereço de memória. Os métodos seguintes são auxiliares de conveniência para comunicar com tais dispositivos.

readfrom_mem(addr: int, memaddr: int, nbytes: int, *, addrsize: int = 8) bytes

nbytes do dispositivo em addr a partir do registo memaddr. addrsize é a largura do endereço de registo em bits (tipicamente 8 ou 16).

readfrom_mem_into(addr: int, memaddr: int, buf: bytearray, *, addrsize: int = 8) None

Lê para buf a partir do dispositivo em addr a partir do registo memaddr.

writeto_mem(addr: int, memaddr: int, buf: bytes, *, addrsize: int = 8) None

Escreve buf no dispositivo em addr a partir do registo memaddr.