ESP32-S3: digitalio open-drain output doesn't work on GPIO45, so bitbangio.I2C fails there
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 48/100
- Issue 类型
- 缺陷
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 领域
- embedded-iot
调研方向
Start with the GPIO handling in ports/espressif/common-hal/digitalio/DigitalInOut.c and the open-drain release and read sequence in shared-module/bitbangio/I2C.c. Reproduce the reported difference on GPIO45 with the supplied digitalio script, then trace the lower-level ESP32-S3 GPIO behavior. Done means open-drain transactions on GPIO45 work as they do on GPIO0, without breaking the reported working cases.
由索引模型根据 Issue 内容生成。
描述
CircuitPython 10.3.1 on an ESP32-S3 (M5Stack AtomS3R: espressif_esp32s3_devkitc_1_n8r8, and a local m5stack_atoms3r build). The board's internal I2C bus is SCL GPIO0 / SDA GPIO45 with external pull-ups, a BMI270 at 0x68 and an LP5562 at 0x30.
Symptom: bitbangio.I2C(GPIO0, GPIO45) finds both devices with scan() (at 100 kHz and 10 kHz), but writeto and writeto_then_readfrom raise [Errno 5], and readfrom_into returns 0xFF. busio.I2C on the same pins works (with CIRCUITPY_I2C_ALLOW_STRAPPING_PINS), so the wiring and pull-ups are fine.
Narrowed: the same transaction bit-banged with digitalio, changing only how a line is released. "-od" = the way shared-module/bitbangio/I2C.c does it: an open-drain output set to 1, switched to input and back to read (sda_read, and scl_release's clock-stretch check).
| SCL (GPIO0) released by | SDA (GPIO45) released by | Result |
|---|---|---|
| switching to input | switching to input | works: chip ID 0x24 |
| open-drain output (with the input round trip) | switching to input | works |
| switching to input | open-drain output (with the input round trip) | address ACKed, register byte NACKed, read 0xFF |
| open-drain output | open-drain output | the same failure (bitbangio's way) |
The identical open-drain sequence works on GPIO0 and fails on GPIO45 (a strapping pin), on both builds, repeatably.
Two more observations:
DigitalInOut.drive_modereportsDriveMode.OPEN_DRAINon GPIO45 afterswitch_to_output(drive_mode=OPEN_DRAIN), before and after the input round trip (same as GPIO0, 5 and 6), so the requested mode does read back.- The address byte is ACKed in every variant; the failure begins after the first input → open-drain round trip on SDA (reading the ACK, then going back to an open-drain output set to 1).
ports/espressif/common-hal/digitalio/DigitalInOut.c is pin-agnostic (gpio_set_direction(GPIO_MODE_OUTPUT | GPIO_MODE_OUTPUT_OD)), so the cause looks to be below it; I haven't found it. Seen on one board (one AtomS3R), so I can't say whether every ESP32-S3's GPIO45 does this.
Repro (save as code.py; output above):
# Repro: digitalio open-drain output fails on ESP32-S3 GPIO45 (so bitbangio.I2C fails there). Save as code.py on an
# M5Stack AtomS3R (internal I2C: SCL GPIO0, SDA GPIO45, external pull-ups; BMI270 at 0x68, chip ID register 0x00 = 0x24)
# and read the serial console. Each run bit-bangs "read the BMI270's chip ID" with digitalio; a line in an "-od" mode is
# released the way shared-module/bitbangio/I2C.c does it (open-drain output set to 1, switched to input and back to
# read). Seen on CircuitPython 10.3.1 (espressif_esp32s3_devkitc_1_n8r8, and an m5stack_atoms3r build):
# input / scl-od-roundtrip: acks (True, True, True), chip id 24
# sda-od-roundtrip / both-od-roundtrip: acks (True, False, True), chip id ff
import digitalio, microcontroller as mc
OD = digitalio.DriveMode.OPEN_DRAIN
def run(mode):
scl = digitalio.DigitalInOut(mc.pin.GPIO0); sda = digitalio.DigitalInOut(mc.pin.GPIO45)
od = {"scl-od-roundtrip": (scl,), "sda-od-roundtrip": (sda,), "both-od-roundtrip": (scl, sda), "input": ()}[mode]
for p in od:
p.switch_to_output(value=True, drive_mode=OD)
def rel(p):
if p in od:
p.value = True
p.switch_to_input(); p.value; p.switch_to_output(value=True, drive_mode=OD) # bitbangio's round trip
else:
p.switch_to_input()
def low(p):
if p in od: p.value = False
else: p.switch_to_output(value=False)
def rd(p):
p.switch_to_input(); v = p.value
if p in od: p.switch_to_output(value=True, drive_mode=OD)
return v
def wbyte(v):
for i in range(8):
(rel if v & (0x80 >> i) else low)(sda); rel(scl); low(scl)
rel(sda); rel(scl); ack = not rd(sda); low(scl); return ack
def rbyte():
rel(sda); v = 0
for _ in range(8):
rel(scl); v = (v << 1) | rd(sda); low(scl)
rel(sda); rel(scl); low(scl); return v
rel(scl); rel(sda); low(sda); low(scl)
acks = (wbyte(0x68 << 1), wbyte(0x00))
rel(sda); rel(scl); low(sda); low(scl)
acks += (wbyte((0x68 << 1) | 1),)
v = rbyte()
low(sda); rel(scl); rel(sda)
scl.deinit(); sda.deinit()
print("%-18s acks %s chip id %02x" % (mode, acks, v))
for m in ("input", "scl-od-roundtrip", "sda-od-roundtrip", "both-od-roundtrip", "input"):
run(m)
- 主要语言
- C
- 星标
- 4.6k
- 派生
- 1.4k
- 平均合并
- 1 天 8 小时
- 30 天内合并 PR
- 158
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
adafruit/circuitpython 的其他 Issue
-
bug
难度 4/5 1-2 天 新手友好度 35/100
adafruit/circuitpython#11641 ·
维护者通常 1 天内回复
-
bug esp32 web workflow
难度 5/5 一周以上 新手友好度 12/100
adafruit/circuitpython#11616 · 9 条评论 ·
维护者通常 1 天内回复
-
nordic
难度 4/5 3-5 天 新手友好度 30/100
adafruit/circuitpython#11611 ·
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 15/100
adafruit/circuitpython#11606 ·
维护者通常 1 天内回复
-
busio zephyr
难度 5/5 一周以上 新手友好度 18/100
adafruit/circuitpython#11605 ·
维护者通常 1 天内回复
查看 adafruit/circuitpython 的全部 Issue
相似的 Issue
-
difficulty: easy
难度 2/5 1-3 小时 新手友好度 64/100
-
难度 2/5 1-3 小时 新手友好度 65/100
-
performance solver
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 76/100
ChefKissInc/Inferno#319 ·
-
llvm-tools
难度 1/5 1 小时以内 新手友好度 72/100
llvm/llvm-project#235522 ·
维护者通常 1 天内回复