[i2c] Fix RP2040 I2C bus selection based on pin assignment (#14745)

This commit is contained in:
J. Nick Koston
2026-03-12 14:53:46 -10:00
committed by Jesse Hills
parent b0447dc521
commit 039efdb02a
2 changed files with 43 additions and 4 deletions

View File

@@ -93,11 +93,31 @@ def _bus_declare_type(value):
raise NotImplementedError
def _rp2040_i2c_controller(pin):
"""Return the I2C controller number (0 or 1) for a given RP2040/RP2350 GPIO pin.
See RP2040 datasheet Table 2 (section 1.4.3, "GPIO Functions"):
https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
See RP2350 datasheet Table 7 (section 9.4, "Function Select"):
https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf
"""
return (pin // 2) % 2
def validate_config(config):
if CORE.is_esp32:
return cv.require_framework_version(
esp_idf=cv.Version(5, 4, 2), esp32_arduino=cv.Version(3, 2, 1)
)(config)
if CORE.is_rp2040:
sda_controller = _rp2040_i2c_controller(config[CONF_SDA])
scl_controller = _rp2040_i2c_controller(config[CONF_SCL])
if sda_controller != scl_controller:
raise cv.Invalid(
f"SDA pin GPIO{config[CONF_SDA]} is on I2C{sda_controller} but "
f"SCL pin GPIO{config[CONF_SCL]} is on I2C{scl_controller}. "
f"Both pins must be on the same I2C controller."
)
return config
@@ -146,6 +166,23 @@ def _final_validate(config):
full_config = fv.full_config.get()[CONF_I2C]
if CORE.using_zephyr and len(full_config) > 1:
raise cv.Invalid("Second i2c is not implemented on Zephyr yet")
if CORE.is_rp2040:
if len(full_config) > 2:
raise cv.Invalid(
"The maximum number of I2C interfaces for RP2040/RP2350 is 2"
)
if len(full_config) > 1:
controllers = [
_rp2040_i2c_controller(conf[CONF_SDA]) for conf in full_config
]
if len(set(controllers)) != len(controllers):
raise cv.Invalid(
"Multiple I2C buses are configured to use the same I2C controller. "
"Each bus must use pins on a different controller. "
"The I2C controller is determined by (gpio / 2) % 2: "
"even pin pairs (0-1, 4-5, 8-9, ...) use I2C0, "
"odd pin pairs (2-3, 6-7, 10-11, ...) use I2C1."
)
if CORE.is_esp32 and get_esp32_variant() in ESP32_I2C_CAPABILITIES:
variant = get_esp32_variant()
max_num = ESP32_I2C_CAPABILITIES[variant]["NUM"]

View File

@@ -20,12 +20,14 @@ void ArduinoI2CBus::setup() {
#if defined(USE_ESP8266)
wire_ = new TwoWire(); // NOLINT(cppcoreguidelines-owning-memory)
#elif defined(USE_RP2040)
static bool first = true;
if (first) {
// Select Wire instance based on pin assignment, not definition order.
// I2C controller = (gpio / 2) % 2: even pairs (0-1,4-5,...) → I2C0, odd pairs (2-3,6-7,...) → I2C1
// RP2040 datasheet Table 2 (section 1.4.3): https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
// RP2350 datasheet Table 7 (section 9.4): https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf
if ((this->sda_pin_ / 2) % 2 == 0) {
wire_ = &Wire;
first = false;
} else {
wire_ = &Wire1; // NOLINT(cppcoreguidelines-owning-memory)
wire_ = &Wire1;
}
#endif