CircuitPython, Adafruit Feather RP2040 e I2C

Como provavelmente adivinhou pelo título, este artigo discute o CircuitPython, o Adafruit Feather RP2040 e o protocolo de comunicação I2C.

O RP2040 tem dois controladores I2C - bom, por exemplo, quando se pretende executar dois dispositivos I2C com o mesmo endereço I2C.

No meu equipamento de teste, tenho uma placa microcontroladora Adafruit Feather RP2040, e anexei duas das nossas Quadros de desagregação BME688 - um usando os pinos SCL + SDA e um usando A1 (para SCL) + A0 (para SDA).

Estou usando o CircuitPython na versão 7.0.0, que você pode baixar daqui.

Além disso, instalei todas as bibliotecas da Adafruit para a lib pasta no Feather RP2040. (O Feather RP2040 tem espaço suficiente em seu Flash para permitir isso)

Você pode baixar estas bibliotecas no Adafruit CircuitPython Bundle aqui. (Eu baixei adafruit-circuitpython-bundle-7.x-mpy-20211123.zip)

Nota: para instalar estas bibliotecas basta copiá-las para a pasta lib na "drive" CIRCUITPY que está montada no seu computador. Você terá que copiar apenas as bibliotecas, não os exemplos e outras coisas.

A grande vantagem em usar as coisas da Adafruit é que você recebe uma tonelada de exemplos que cobrem muitos chips populares, e você pode muito facilmente começar com isso. Há coisas como conduzir um cartão microSD usando SPI, ler um RTC e ler a partir do sensor BME680.

Teste do I2C no CircuitPython

Tenho a seguinte configuração, pois quero conduzir dois dispositivos independentemente um do outro (que, neste caso, têm os mesmos endereços):

Note que as nossas placas de quebra BME688 já incluem Pullups para SDA e SCL. (Você precisa de pullups para SDA e SCL).

Nota 2: O nosso quadro de breakout BME688 tem a opção de alterar o endereço, por isso este cenário destina-se a fins de demonstração.

Para conduzir através dos dois conjuntos de pinos sequencialmente (para descobrir os dispositivos), estou a utilizar o seguinte código:

print("Scanning SCL / SDA")
i2c = busio.I2C(board.SCL, board.SDA)
# a scan
i2c.try_lock()
print(i2c.scan())
i2c.unlock()
i2c.deinit()

print("Scanning A0 / A1")
si2c = busio.I2C(board.A1, board.A0)
# a scan
si2c.try_lock()
print(si2c.scan())
si2c.unlock()

Nota: a i2c.deinit() é a chave para este exemplo em particular funcionar! (porque SCL / SDA e A0 / A1 têm ambos o mesmo periférico de hardware I2C - veja abaixo).

Isto deve resultar no seguinte:

Scanning SCL / SDA
[119]
Scanning A0 / A1
[119]

Aqui, 119 é decimal para hex 0x77 - que é o endereço do nosso quadro de desagregação BME688 no estado padrão.

Ambas as placas são vistas, sequencialmente, nas varreduras individuais.

O problema é que nós queremos usá-los simultaneamente.

A partir de dois autocarros I2C em simultâneo no Adafruit Feather RP2040

CircuitPython suporta ambos controladores de hardware (SDA0/SCL0 e SDA1/SCL1). Você não precisa definir nenhuma configuração (qual controlador você quer usar, ou como usar mux os pinos) - isto é tratado por busio para ti.

Você precisa prestar atenção, no entanto, aos pinos que você usa, pois os pinos fornecerão apenas um desses ônibus em cada caso, e se por acaso você escolher pinos conflitantes, você terá ValueError: Periférico I2C em uso .

Se você quiser usar pinos "conflitantes", rexame SCL / SDA (que têm SCL1 e SDA1) e A0 / A1 (que também têm SCL1 e SDA1), você precisará bitbang em uma das portas:

Veja como digitalizar esta configuração de pino sem chamar deinit():

import board
import busio
import bitbangio
# https://circuitpython.readthedocs.io/en/latest/shared-bindings/bitbangio/index.html

print("Scanning SCL / SDA - main I2C")
i2c = busio.I2C(board.SCL, board.SDA)
# a scan
i2c.try_lock()
print(i2c.scan())
i2c.unlock()
# no need to call deinit here!
#i2c.deinit()

print("Scanning A0 / A1 - secondary I2C [bitbang!]")
si2c = bitbangio.I2C(board.A1, board.A0)
# a scan
si2c.try_lock()
print(si2c.scan())
si2c.unlock()

# we also do not need to call deinit here
#i2c.deinit()

Nós estamos usando bitbangio para conduzir um I2C secundário. No meu caso o I2C secundário é usado para um propósito interno (para um expansor de portas), e muito provavelmente pode fazer com uma velocidade de interface mais baixa.

Nota: não é possível escolher o periférico I2C que é encaminhado para os pinos no software - se você precisa de um periférico diferente, você precisa usar pinos diferentes.

Resolução de problemas de erros I2C

ValueError: Periférico I2C em uso

Se você está usando busio.I2C para ambos os portos: Verifique se você já está usando o mesmo hardware do periférico I2C - e se você precisa reatribuir seus pinos.

Por exemplo, tanto SCL como SDA , e A0 e A1 partilham o mesmo hardware I2C periférico (SCL1 / SDA1 - ver a imagem do pinout Adafruit Feather RP2040 neste artigo).

Caso você queira usar a mesma configuração de pinos, você pode usar o bitbangio para "criar" uma porta I2C adicional controlada por software. O lado negativo disto é uma velocidade mais baixa para esta porta I2C do software, e uma maior carga de CPU.

RuntimeError: Não foi encontrado nenhum puxador no SDA ou SCL; verifique a sua fiação

Se você obtiver o seguinte erro

RuntimeError: Não foi encontrado nenhum puxador no SDA ou SCL; verifique a sua fiação

então você deve colocar resistências de pullup entre 3V3 na placa (o pino de 3.3V) e respectivamente seus pinos SDA e SCL. Estes são necessários para o funcionamento normal do I2C (os dispositivos puxam os pinos I2C para baixo para comunicar, o estado padrão / ocioso no ônibus é alto) - e não estão incluídos no Adafruit Feather RP2040. Eles estão incluídos em muitos periféricos Adafruit, e em periféricos de outras empresas (como, mais uma vez, o nosso próprio Quadro de quebra BME688).

Se você não sabe o que é um pullup: isto é essencialmente uma resistência entre o pino em questão (por exemplo, SDA) e o pino de alimentação 3,3 V. Não precisa de ser terrivelmente preciso. Você deve começar com resistores de 10 kOhm, se isso não funcionar, possivelmente tente um resistor de 1 kOhm para um pullup "mais rígido".

TimeoutError: O relógio estica muito tempo

Verifique se o chip com o qual quer falar está devidamente alimentado.

Notas diversas

Referências / Recursos / Links / Leitura adicional

Quer saber mais sobre I2C? Consulte o nosso artigo sobre o assunto aqui.

3 comentários

  1. Rob em Janeiro 23, 2023 às 4:45 pm

    Obrigado por um artigo útil! Tenho duas sugestões:

    1) Uma causa para o erro "no pull up found" (que foi o que me trouxe aqui) também pode ser causada por um erro estúpido: ter um fio solto! Isto acontece-me com demasiada frequência porque estou a usar conectores Stemma que são um pouco finos.

    2) Penso que o seu código de exemplo é um pouco confuso, na medida em que penso que poderia/deveria ter começado com o caso mais simples em que utiliza os dois controladores I2C disponíveis, tais como

    i2c = busio.I2C(board.SCL, board.SDA)

    si2c = busio.I2C(board.A9, board.A6)

    Os métodos que utiliza depois disso ainda são, naturalmente, válidos e úteis!

    /rob

    • raspi berry em Fevereiro 4, 2023 às 11:47 am

      Obrigado pelo seu comentário atencioso, Rob! Os fios soltos são como o "é alimentado?" do mundo do suporte informático 🙂 É melhor verificar do que passar horas a depurar

  2. Edward M Johnstone em Junho 27, 2023 às 2:43 pm

    Obrigado por este artigo.
    Estou a migrar do Raspberry Pi Pico e tenho andado à procura de algo assim!
    Espetacular!

Deixe um comentário