@@ -333,17 +333,25 @@ inline bool i2cSoftware(UnitUnified& units, Component& unit, const int sda, cons
333333 @param nesso NessoN1 only: PortB (default) -> SoftwareI2C, PortA -> Wire (QWIIC)
334334 @note Dispatches via i2cPins() backend: Wire / SoftwareI2C / ExI2C.
335335*/
336- inline bool addI2C (UnitUnified& units, Component& unit, const uint32_t clock = 100000 ,
336+ inline bool addI2C (UnitUnified& units, Component& unit, const uint32_t clock = 0 ,
337337 const NessoPort nesso = NessoPort::PortB)
338338{
339- const auto p = i2cPins (nesso);
340- M5_LIB_LOGI (" wiring: addI2C board=0x%02x nesso=%d backend=%d sda=%d scl=%d" , (int )M5 .getBoard (), (int )nesso,
341- (int )p.backend , (int )p.sda , (int )p.scl );
339+ // clock == 0: use the unit's own configured clock; clock > 0: override it. Either way the unit's
340+ // component_config().clock is the single source of truth applied per transaction by every backend.
341+ if (clock != 0 ) {
342+ auto cfg = unit.component_config ();
343+ cfg.clock = clock;
344+ unit.component_config (cfg);
345+ }
346+ const uint32_t eff_clock = unit.component_config ().clock ;
347+ const auto p = i2cPins (nesso);
348+ M5_LIB_LOGI (" wiring: addI2C board=0x%02x nesso=%d backend=%d sda=%d scl=%d clock=%lu" , (int )M5 .getBoard (),
349+ (int )nesso, (int )p.backend , (int )p.sda , (int )p.scl , (unsigned long )eff_clock);
342350 switch (p.backend ) {
343351 case I2CPins::Backend::SoftwareI2C:
344352 return i2cSoftware (units, unit, p.sda , p.scl );
345353 case I2CPins::Backend::Wire:
346- return i2cWire (units, unit, Wire, p.sda , p.scl , clock );
354+ return i2cWire (units, unit, Wire, p.sda , p.scl , eff_clock );
347355 case I2CPins::Backend::ExI2C:
348356 return i2cClass (units, unit, M5 .Ex_I2C );
349357 }
@@ -468,13 +476,20 @@ inline bool addSPI(UnitUnified& units, Component& unit, const uint32_t clock_hz,
468476 @note Some Hats need extra pin pre-setup (e.g. HatHEART does pinMode(scl, OUTPUT)); do that in the
469477 caller before this call, or build the connection with the low-level i2cWire.
470478*/
471- inline bool addHatI2C (UnitUnified& units, Component& unit, const uint32_t clock = 400000 )
479+ inline bool addHatI2C (UnitUnified& units, Component& unit, const uint32_t clock = 0 )
472480{
473481 const auto p = hatI2CPins ();
474482 if (p.sda < 0 || p.scl < 0 ) {
475483 M5_LIB_LOGE (" wiring: Hat I2C unsupported board=0x%02x" , (int )M5 .getBoard ());
476484 return false ;
477485 }
486+ // clock == 0: use the unit's own configured clock; clock > 0: override it.
487+ if (clock != 0 ) {
488+ auto cfg = unit.component_config ();
489+ cfg.clock = clock;
490+ unit.component_config (cfg);
491+ }
492+ const uint32_t eff_clock = unit.component_config ().clock ;
478493#if SOC_I2C_NUM > 1
479494 TwoWire& wire = p.useWire1 ? Wire1 : Wire;
480495#else
@@ -486,8 +501,8 @@ inline bool addHatI2C(UnitUnified& units, Component& unit, const uint32_t clock
486501 TwoWire& wire = Wire;
487502#endif
488503 M5_LIB_LOGI (" wiring: addHatI2C board=0x%02x sda=%d scl=%d clock=%lu wire=%s" , (int )M5 .getBoard (), (int )p.sda ,
489- (int )p.scl , (unsigned long )clock , p.useWire1 ? " Wire1" : " Wire" );
490- return i2cWire (units, unit, wire, p.sda , p.scl , clock );
504+ (int )p.scl , (unsigned long )eff_clock , p.useWire1 ? " Wire1" : " Wire" );
505+ return i2cWire (units, unit, wire, p.sda , p.scl , eff_clock );
491506}
492507
493508/* !
@@ -663,15 +678,42 @@ inline bool ensureI2CLegacyDriver(const i2c_port_t port, const gpio_num_t sda, c
663678 M5_LIB_LOGE (" wiring: I2C bus cache full (max %zu)" , kI2CBusCacheSize );
664679 return false ;
665680 }
666- i2c_config_t conf{};
667- conf.mode = I2C_MODE_MASTER ;
668- conf.sda_io_num = sda;
669- conf.scl_io_num = scl;
670- conf.sda_pullup_en = true ;
671- conf.scl_pullup_en = true ;
672- conf.master .clk_speed = clock;
673- if (i2c_param_config (port, &conf) != ESP_OK || i2c_driver_install (port, I2C_MODE_MASTER , 0 , 0 , 0 ) != ESP_OK ) {
674- M5_LIB_LOGE (" wiring: legacy i2c driver install failed port=%d sda=%d scl=%d" , (int )port, (int )sda, (int )scl);
681+ // i2c_driver_install returns ESP_FAIL for several distinct reasons in IDF 5.0/5.1 (already
682+ // installed by another owner / OOM / slave-setup failure). Probe i2c_get_period to disambiguate:
683+ // it writes high/low only when the driver object exists (p_i2c_obj != NULL), so high>0 && low>0
684+ // means the port is genuinely installed and can be borrowed.
685+ const esp_err_t install_err = i2c_driver_install (port, I2C_MODE_MASTER , 0 , 0 , 0 );
686+ if (install_err == ESP_OK ) {
687+ // Freshly installed by us: configure pins and timing.
688+ i2c_config_t conf{};
689+ conf.mode = I2C_MODE_MASTER ;
690+ conf.sda_io_num = sda;
691+ conf.scl_io_num = scl;
692+ conf.sda_pullup_en = true ;
693+ conf.scl_pullup_en = true ;
694+ conf.master .clk_speed = clock;
695+ if (i2c_param_config (port, &conf) != ESP_OK ) {
696+ M5_LIB_LOGE (" wiring: legacy i2c param_config failed port=%d sda=%d scl=%d" , (int )port, (int )sda, (int )scl);
697+ i2c_driver_delete (port);
698+ return false ;
699+ }
700+ M5_LIB_LOGI (" wiring: legacy i2c INSTALLED(new) port=%d sda=%d scl=%d clock=%u" , (int )port, (int )sda, (int )scl,
701+ (unsigned )clock);
702+ } else if (install_err == ESP_FAIL ) {
703+ // Already installed by another owner -> borrow it (keep the owner's pins/timing; do NOT
704+ // re-run i2c_param_config and override them). Confirm the driver really exists first.
705+ int high = 0 , low = 0 ;
706+ i2c_get_period (port, &high, &low);
707+ if (!(high > 0 && low > 0 )) {
708+ M5_LIB_LOGE (" wiring: legacy i2c driver install failed port=%d sda=%d scl=%d" , (int )port, (int )sda,
709+ (int )scl);
710+ return false ;
711+ }
712+ M5_LIB_LOGI (" wiring: legacy i2c BORROW(already) port=%d sda=%d scl=%d high=%d low=%d" , (int )port, (int )sda,
713+ (int )scl, high, low);
714+ } else {
715+ M5_LIB_LOGE (" wiring: legacy i2c driver install error=0x%x port=%d sda=%d scl=%d" , (int )install_err, (int )port,
716+ (int )sda, (int )scl);
675717 return false ;
676718 }
677719 cache[count++] = key;
@@ -824,13 +866,21 @@ inline spi_device_handle_t spiDeviceHandle(const spi_host_device_t host, const g
824866// ! (M5Unified does NOT call Wire.begin() on these boards, so I2C_NUM_0 is free)
825867// ! - ExI2C (Core, NanoC6/NanoH2) -> borrow M5.Ex_I2C
826868// ! (Core: In_I2C/Ex_I2C share I2C_NUM_0, already installed by M5.begin(); borrow it)
827- inline bool addI2C (UnitUnified& units, Component& unit, const uint32_t clock = 100000 ,
869+ inline bool addI2C (UnitUnified& units, Component& unit, const uint32_t clock = 0 ,
828870 const NessoPort nesso = NessoPort::PortB)
829871{
830- const auto pins = i2cPins (nesso);
831- const auto board = M5 .getBoard ();
872+ // clock == 0: use the unit's own configured clock; clock > 0: override it. component_config().clock
873+ // is the single source of truth applied per transaction (Wire / I2C_Class / master device / legacy).
874+ if (clock != 0 ) {
875+ auto cfg = unit.component_config ();
876+ cfg.clock = clock;
877+ unit.component_config (cfg);
878+ }
879+ const uint32_t eff_clock = unit.component_config ().clock ;
880+ const auto pins = i2cPins (nesso);
881+ const auto board = M5 .getBoard ();
832882 M5_LIB_LOGI (" wiring(ESP-IDF): addI2C board=0x%02x nesso=%d sda=%d scl=%d clock=%u backend=%d" , (int )board,
833- (int )nesso, (int )pins.sda , (int )pins.scl , (unsigned )clock , (int )pins.backend );
883+ (int )nesso, (int )pins.sda , (int )pins.scl , (unsigned )eff_clock , (int )pins.backend );
834884 switch (pins.backend ) {
835885 case I2CPins::Backend::SoftwareI2C:
836886#if defined(M5_HAL_HPP)
@@ -848,11 +898,11 @@ inline bool addI2C(UnitUnified& units, Component& unit, const uint32_t clock = 1
848898 // Stick / S3 etc: M5Unified does not call Wire.begin(), so install ourselves
849899 // (Core is routed to ExI2C by i2cPins() and never reaches this branch)
850900#if __has_include(<driver/i2c_master.h>)
851- auto bus = i2cBusHandle (I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl , clock );
901+ auto bus = i2cBusHandle (I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl , eff_clock );
852902 if (!bus) return false ;
853903 return units.add (unit, bus);
854904#else // legacy driver (IDF 5.0 / 5.1): install legacy driver, then add by port
855- if (!detail::ensureI2CLegacyDriver (I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl , clock )) {
905+ if (!detail::ensureI2CLegacyDriver (I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl , eff_clock )) {
856906 return false ;
857907 }
858908 return units.add (unit, I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl );
@@ -902,13 +952,20 @@ inline bool addSPI(UnitUnified& units, Component& unit, const uint32_t clock_hz,
902952}
903953
904954// ! @brief Add a unit on the board's Hat I2C header (NessoN1 uses Wire1, others Wire) (ESP-IDF native)
905- inline bool addHatI2C (UnitUnified& units, Component& unit, const uint32_t clock = 400000 )
955+ inline bool addHatI2C (UnitUnified& units, Component& unit, const uint32_t clock = 0 )
906956{
907957 const auto p = hatI2CPins ();
908958 if (p.sda < 0 || p.scl < 0 ) {
909959 M5_LIB_LOGE (" wiring: Hat I2C unsupported board=0x%02x" , (int )M5 .getBoard ());
910960 return false ;
911961 }
962+ // clock == 0: use the unit's own configured clock; clock > 0: override it.
963+ if (clock != 0 ) {
964+ auto cfg = unit.component_config ();
965+ cfg.clock = clock;
966+ unit.component_config (cfg);
967+ }
968+ const uint32_t eff_clock = unit.component_config ().clock ;
912969 i2c_port_t port;
913970 if (p.useWire1 ) {
914971#if SOC_HP_I2C_NUM >= 2
@@ -923,13 +980,13 @@ inline bool addHatI2C(UnitUnified& units, Component& unit, const uint32_t clock
923980 port = I2C_NUM_0 ;
924981 }
925982 M5_LIB_LOGI (" wiring(ESP-IDF): addHatI2C port=%d sda=%d scl=%d clock=%u" , (int )port, (int )p.sda , (int )p.scl ,
926- (unsigned )clock );
983+ (unsigned )eff_clock );
927984#if __has_include(<driver/i2c_master.h>)
928- auto bus = i2cBusHandle (port, (gpio_num_t )p.sda , (gpio_num_t )p.scl , clock );
985+ auto bus = i2cBusHandle (port, (gpio_num_t )p.sda , (gpio_num_t )p.scl , eff_clock );
929986 if (!bus) return false ;
930987 return units.add (unit, bus);
931988#else // legacy driver (IDF 5.0 / 5.1)
932- if (!detail::ensureI2CLegacyDriver (port, (gpio_num_t )p.sda , (gpio_num_t )p.scl , clock )) return false ;
989+ if (!detail::ensureI2CLegacyDriver (port, (gpio_num_t )p.sda , (gpio_num_t )p.scl , eff_clock )) return false ;
933990 return units.add (unit, port, (gpio_num_t )p.sda , (gpio_num_t )p.scl );
934991#endif
935992}
0 commit comments