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
- O Adafruit Feather RP2040 tem um LED NeoPixel. NeoOs pixéis usam um protocolo proprietárionão usam a I2C.
- O Adafruit CircuitPython Community Bundle tem um par de condutores adicionais
- Há um DebugI2C Ajudante
- O matriz de suporte irá mostrar-lhe que módulos são suportados na sua placa (por exemplo, o Adafruit Feather RP2040)
Referências / Recursos / Links / Leitura adicional
- Pacote Adafruit CircuitPython (bibliotecas para o CircuitPython)
- CircuitPython Busio documentação
- Documentação da placa CircuitPython
- CircuitPython sobre a documentação do RP2040
- CircuitPython I2C Essentials
Quer saber mais sobre I2C? Consulte o nosso artigo sobre o assunto aqui.
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
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
Obrigado por este artigo.
Estou a migrar do Raspberry Pi Pico e tenho andado à procura de algo assim!
Espetacular!