CircuitPython、Adafruit Feather RP2040 和 I2C

正如您可能从标题猜到的那样,本文将讨论 CircuitPython、Adafruit Feather RP2040 和 I2C 通信协议。

RP2040有两个I2C控制器--例如,当你想用相同的I2C地址运行两个I2C设备时,就很好。

在我的测试设置中,我有一个Adafruit Feather RP2040微控制器板,并连接了两个我们的 BME688分线板 - 一个使用引脚SCL+SDA,一个使用A1(用于SCL)+A0(用于SDA)。

我使用的是CircuitPython的7.0.0版本。 你可以从这里下载.

此外,我把Adafruit的所有库都安装到了 枰子 在Feather RP2040上的文件夹。(Feather RP2040的Flash有足够的空间来实现这一点)

你可以下载这些库,在 Adafruit CircuitPython捆绑包在此.(我下载了 adafruit-circuitpython-bundle-7.x-mpy-20211123.zip)

注意事项:要安装这些库,只需将它们复制到安装在您电脑上的CIRCUITPY "驱动器 "的lib文件夹中。当然,你必须只复制库,而不是例子和其他东西。

使用Adafruit的东西的最大好处是,你可以得到大量的例子,涵盖了许多流行的芯片,你可以非常容易地开始使用它。比如使用SPI驱动microSD卡,读取RTC,以及从BME680传感器读取数据。

在CircuitPython中测试I2C

我有如下设置,因为我想独立驱动两个设备(在这种情况下,它们恰好有相同的地址)。

羽绒服RP2040和两个 BME688分线板

请注意,我们的BME688分线板已经包括SDA和SCL的上拉电阻。(你需要在SDA和SCL上设置上拉器)。

注2:我们的BME688分线板有改变地址的选项,所以这个方案是为了演示的目的。

为了依次驱动这两个引脚组(以发现设备),我使用了以下代码。

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()

注释: i2c.deinit() 是这个特定例子工作的关键!(因为SCL / SDA和A0 / A1都有相同的硬件I2C外围--见下文)。

这应该输出以下内容。

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

这里,119是 十进制为十六进制0x77 - 这就是我们的BME688分线板在默认状态下的地址。

两块板子都看到了,在个别扫描中依次出现。

问题是,我们想同时使用它们。

在Adafruit Feather RP2040上同时运行两条I2C总线

CircuitPython支持 两个 硬件控制器(SDA0/SCL0和SDA1/SCL1)。你不需要设置任何配置(你想使用哪个控制器,或如何混用引脚)--这由 公交车 为你。

但你需要注意的是,你使用的是哪种针脚,因为针脚将只提供 在每一种情况下,如果你碰巧选择了冲突的引脚,你将得到 ValueError:I2C外围设备正在使用中 .

如果你想使用 "冲突 "的引脚,例如SCL/SDA(有SCL1和SDA1)和A0/A1(也有SCL1和SDA1),你将需要对其中一个端口进行咬接。

下面是如何在不调用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()

我们正在使用 咬牙切齿 来驱动一个二级I2C。在我的例子中,次级I2C用于内部用途(用于端口扩展器),而且很可能可以使用较低的接口速度。

注意:你不能在软件中选择路由到引脚的I2C外设--如果你需要一个不同的外设,你需要使用不同的引脚。

排除I2C错误的故障

ValueError:I2C外围设备正在使用中

如果你在两个端口都使用busio.I2C。检查你是否已经在使用相同的硬件I2C外设--以及你是否需要重新分配你的引脚。

例如,SCL和SDA ,以及A0和A1共享相同的硬件I2C外设(SCL1 / SDA1 - 参见本文的Adafruit Feather RP2040引脚图)。

如果你确实想使用相同的引脚设置,你可以使用bitbangio来 "创建 "一个额外的软件控制的I2C端口。这样做的缺点是这个软件I2C端口的速度较低,而且CPU负载较高。

RuntimeError:在SDA或SCL上没有发现上拉装置;检查你的接线方式

如果你得到以下错误

RuntimeError:在SDA或SCL上没有发现上拉装置;检查你的接线方式

那么你应该在电路板上的3V3(3.3V引脚)和你的SDA和SCL引脚之间分别安装上拉电阻。这些是正常I2C操作所需要的(设备拉低I2C引脚进行通信,总线上的默认/空闲状态是高电平)--Adafruit Feather RP2040上没有。许多Adafruit的外设和其他公司的外设都有这些功能(比如,我们自己的 BME688分线板).

如果你不知道什么是上拉:这基本上是在有关引脚(如SDA)和3.3V电源引脚之间的一个电阻。它不需要非常精确。你应该从10 kOhm的电阻开始,如果这不起作用,可以尝试用1 kOhm的电阻来做一个 "更硬 "的上拉。

TimeoutError。时钟拉伸时间过长

检查你想对话的芯片是否正常供电。

杂项说明

参考资料/资源/链接/进一步阅读

想进一步了解 I2C?查看 点击此处查看我们的文章.

3评论

  1. Rob 在1 月 23, 2023在4:45 下午

    谢谢你的文章,很有帮助!我有两个建议。

    1) "没有找到拉升 "错误的一个原因(这就是我来到这里的原因)也可能是由一个愚蠢的错误造成的:有一个松动的电线这种情况经常发生在我身上,因为我使用的是Stemma连接器,它有点细小。

    2) 我认为你的示例代码有点混乱,我认为它可以/应该从最简单的情况开始,即你使用两个可用的I2C控制器,比如说

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

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

    之后你所使用的方法当然仍然是有效和有用的!

    /rob

    • raspi berry 在2 月 4, 2023在11:47 上午

      谢谢你的深思熟虑的评论,Rob!松动的电线就像计算机支持世界中的 "它有电吗?"🙂检查比花几个小时调试要好。

  2. Edward M Johnstone 在6 月 27, 2023在2:43 下午

    谢谢你的这篇文章。
    我是从 Raspberry Pi Pico 移植过来的,一直在寻找这样的东西!
    棒极了!

发表评论