From 1e36bb37d317ed4581cea9adf68910054c6464af Mon Sep 17 00:00:00 2001 From: onexs-xsi <850559025@qq.com> Date: Tue, 24 Feb 2026 15:52:26 +0800 Subject: [PATCH] 1. Optimize i2c-related functions --- src/M5IOE1.cpp | 309 ++++++++++++++++++++++++++++++++++----- src/M5IOE1.h | 64 ++++++--- src/M5IOE1_i2c_compat.h | 312 +++++++++++++++++++++++++++++++++------- 3 files changed, 585 insertions(+), 100 deletions(-) diff --git a/src/M5IOE1.cpp b/src/M5IOE1.cpp index d20b680..4e901b3 100644 --- a/src/M5IOE1.cpp +++ b/src/M5IOE1.cpp @@ -146,17 +146,21 @@ M5IOE1::M5IOE1() _sda = 0; _scl = 0; #else - _i2cDriverType = M5IOE1_I2C_DRIVER_NONE; + _i2cDriverType = M5IOE1_I2C_DRIVER_NONE; +#if M5IOE1_HAS_I2C_MASTER _i2c_master_bus = nullptr; _i2c_master_dev = nullptr; - _i2c_bus = nullptr; - _i2c_device = nullptr; - _busExternal = false; - _sda = -1; - _scl = -1; - _port = I2C_NUM_0; - _pollTask = nullptr; - _intrQueue = nullptr; +#endif // M5IOE1_HAS_I2C_MASTER +#if M5IOE1_HAS_I2C_BUS + _i2c_bus = nullptr; + _i2c_device = nullptr; +#endif + _busExternal = false; + _sda = -1; + _scl = -1; + _port = I2C_NUM_0; + _pollTask = nullptr; + _intrQueue = nullptr; #endif memset(_callbacks, 0, sizeof(_callbacks)); @@ -179,6 +183,7 @@ M5IOE1::~M5IOE1() // 根据驱动类型进行清理 // Cleanup based on driver type switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: // 自创建:先删除设备,再删除总线 // Self-created: delete device first, then bus @@ -200,7 +205,9 @@ M5IOE1::~M5IOE1() _i2c_master_dev = nullptr; } break; +#endif // M5IOE1_HAS_I2C_MASTER +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: // 外部 i2c_bus:总是删除我们创建的设备句柄,但不删除总线 // External i2c_bus: always delete the device handle we created, but not the bus @@ -209,6 +216,17 @@ M5IOE1::~M5IOE1() _i2c_device = nullptr; } break; +#endif // M5IOE1_HAS_I2C_BUS + +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: + // Legacy I2C:如果是自创建则卸载驱动 + // Legacy I2C: uninstall driver if self-created + if (!_busExternal) { + i2c_driver_delete(_port); + } + break; +#endif // !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS default: break; @@ -274,7 +292,7 @@ m5ioe1_err_t M5IOE1::begin(TwoWire* wire, uint8_t addr, uint8_t sda, uint8_t scl // Try to wake up the device - send I2C START signal // M5IOE1 可能处于睡眠状态,需要先唤醒 // M5IOE1 may be in sleep mode, need to wake up first - M5IOE1_I2C_SEND_WAKE(_wire, _addr); + M5IOE1_I2C_ARDUINO_SEND_WAKE(_wire, _addr); M5IOE1_DELAY_MS(10); // 步骤 1: 先尝试100K通信 @@ -285,7 +303,7 @@ m5ioe1_err_t M5IOE1::begin(TwoWire* wire, uint8_t addr, uint8_t sda, uint8_t scl M5IOE1_LOG_W(TAG, "Failed at 100KHz, waiting 800ms and retrying 100KHz..."); M5IOE1_DELAY_MS(800); - M5IOE1_I2C_SEND_WAKE(_wire, _addr); + M5IOE1_I2C_ARDUINO_SEND_WAKE(_wire, _addr); M5IOE1_DELAY_MS(10); if (!_initDevice()) { @@ -302,7 +320,7 @@ m5ioe1_err_t M5IOE1::begin(TwoWire* wire, uint8_t addr, uint8_t sda, uint8_t scl } M5IOE1_DELAY_MS(100); - M5IOE1_I2C_SEND_WAKE(_wire, _addr); + M5IOE1_I2C_ARDUINO_SEND_WAKE(_wire, _addr); M5IOE1_DELAY_MS(10); if (!_initDevice()) { @@ -377,13 +395,17 @@ m5ioe1_err_t M5IOE1::begin(TwoWire* wire, uint8_t addr, uint8_t sda, uint8_t scl // ===================================================== m5ioe1_err_t M5IOE1::begin(i2c_port_t port, uint8_t addr, int sda, int scl, uint32_t speed, m5ioe1_int_mode_t intMode) { - _addr = addr; - _busExternal = false; + _addr = addr; + _busExternal = false; +#if M5IOE1_HAS_I2C_MASTER _i2cDriverType = M5IOE1_I2C_DRIVER_SELF_CREATED; - _intPin = -1; - _port = port; - _sda = sda; - _scl = scl; +#else + _i2cDriverType = M5IOE1_I2C_DRIVER_LEGACY; +#endif + _intPin = -1; + _port = port; + _sda = sda; + _scl = scl; if (intMode == M5IOE1_INT_MODE_HARDWARE) { M5IOE1_LOG_E(TAG, "Hardware interrupt mode requires interrupt pin"); @@ -401,6 +423,7 @@ m5ioe1_err_t M5IOE1::begin(i2c_port_t port, uint8_t addr, int sda, int scl, uint _requestedSpeed = speed; } +#if M5IOE1_HAS_I2C_MASTER // 始终以 100KHz 开始 - M5IOE1 在上电/复位后默认为 100KHz // Always start with 100KHz - M5IOE1 defaults to 100KHz after power-on/reset M5IOE1_LOG_I(TAG, "Initializing M5IOE1 with 100KHz (device default)"); @@ -516,6 +539,74 @@ m5ioe1_err_t M5IOE1::begin(i2c_port_t port, uint8_t addr, int sda, int scl, uint } } +#else // !M5IOE1_HAS_I2C_MASTER + // 始终以 100KHz 开始 - Legacy API (IDF < 5.3.0) + M5IOE1_LOG_I(TAG, "Initializing M5IOE1 with 100KHz (Legacy API, IDF < 5.3.0)"); + + i2c_config_t i2c_conf = {}; + i2c_conf.mode = I2C_MODE_MASTER; + i2c_conf.sda_io_num = sda; + i2c_conf.scl_io_num = scl; + i2c_conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.master.clk_speed = M5IOE1_I2C_FREQ_100K; + + esp_err_t ret = i2c_param_config(port, &i2c_conf); + if (ret != ESP_OK) { + M5IOE1_LOG_E(TAG, "i2c_param_config failed: %s", esp_err_to_name(ret)); + return M5IOE1_ERR_I2C_CONFIG; + } + + ret = i2c_driver_install(port, I2C_MODE_MASTER, 0, 0, 0); + if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) { + M5IOE1_LOG_E(TAG, "i2c_driver_install failed: %s", esp_err_to_name(ret)); + return M5IOE1_ERR_I2C_CONFIG; + } + + // 尝试唤醒设备 - 发送 I2C START 信号 + // Try to wake up the device - send I2C START signal + M5IOE1_I2C_LEGACY_SEND_WAKE(port, _addr); + M5IOE1_DELAY_MS(10); + + // 步骤 1: 先尝试100K通信 + // Step 1: Try 100K communication first + if (!_initDevice()) { + // 步骤 2: 100K失败,等待800ms后再尝试一次100K + // Step 2: 100K failed, wait 800ms and retry 100K + M5IOE1_LOG_W(TAG, "Failed at 100KHz, waiting 800ms and retrying 100KHz..."); + M5IOE1_DELAY_MS(800); + + M5IOE1_I2C_LEGACY_SEND_WAKE(port, _addr); + M5IOE1_DELAY_MS(10); + + if (!_initDevice()) { + // 步骤 3: 100K第二次失败,尝试400K + // Step 3: 100K failed again, try 400K + M5IOE1_LOG_W(TAG, "Failed at 100KHz (retry), trying 400KHz..."); + + i2c_conf.master.clk_speed = M5IOE1_I2C_FREQ_400K; + ret = i2c_param_config(port, &i2c_conf); + if (ret != ESP_OK) { + M5IOE1_LOG_E(TAG, "i2c_param_config (400K) failed: %s", esp_err_to_name(ret)); + i2c_driver_delete(port); + return M5IOE1_ERR_I2C_CONFIG; + } + + M5IOE1_I2C_LEGACY_SEND_WAKE(port, _addr); + M5IOE1_DELAY_MS(10); + + if (!_initDevice()) { + // 步骤 4: 都失败,初始化失败 + // Step 4: All attempts failed, initialization failed + M5IOE1_LOG_E(TAG, "Failed at 100KHz (twice) and 400KHz"); + i2c_driver_delete(port); + return M5IOE1_ERR_I2C_COMM; + } + } + } + +#endif // M5IOE1_HAS_I2C_MASTER + // 步骤 5: 通信成功,设置为已初始化 // Step 5: Communication succeeded, set as initialized _initialized = true; @@ -596,6 +687,7 @@ m5ioe1_err_t M5IOE1::begin(i2c_port_t port, uint8_t addr, int sda, int scl, uint return M5IOE1_OK; } +#if M5IOE1_HAS_I2C_MASTER // ===================================================== // Type 2A: Existing i2c_master_bus_handle_t, no hardware interrupt // ===================================================== @@ -785,10 +877,12 @@ m5ioe1_err_t M5IOE1::begin(i2c_master_bus_handle_t bus, uint8_t addr, uint32_t s } return M5IOE1_OK; } +#endif // M5IOE1_HAS_I2C_MASTER // ===================================================== // Type 3A: Existing i2c_bus_handle_t, no hardware interrupt // ===================================================== +#if M5IOE1_HAS_I2C_BUS m5ioe1_err_t M5IOE1::begin(i2c_bus_handle_t bus, uint8_t addr, uint32_t speed, m5ioe1_int_mode_t intMode) { _addr = addr; @@ -828,7 +922,7 @@ m5ioe1_err_t M5IOE1::begin(i2c_bus_handle_t bus, uint8_t addr, uint32_t speed, m // Try to wake up the device - send I2C START signal // M5IOE1 可能处于睡眠状态,需要先唤醒 // M5IOE1 may be in sleep mode, need to wake up first - M5IOE1_I2C_SEND_WAKE(_i2c_device, M5IOE1_REG_REV); + M5IOE1_I2C_BUS_SEND_WAKE(_i2c_device, M5IOE1_REG_REV); M5IOE1_DELAY_MS(10); // 步骤 1: 先尝试100K通信 @@ -839,7 +933,7 @@ m5ioe1_err_t M5IOE1::begin(i2c_bus_handle_t bus, uint8_t addr, uint32_t speed, m M5IOE1_LOG_W(TAG, "Failed at 100KHz, waiting 800ms and retrying 100KHz..."); M5IOE1_DELAY_MS(800); - M5IOE1_I2C_SEND_WAKE(_i2c_device, M5IOE1_REG_REV); + M5IOE1_I2C_BUS_SEND_WAKE(_i2c_device, M5IOE1_REG_REV); M5IOE1_DELAY_MS(10); if (!_initDevice()) { @@ -860,7 +954,7 @@ m5ioe1_err_t M5IOE1::begin(i2c_bus_handle_t bus, uint8_t addr, uint32_t speed, m return M5IOE1_ERR_I2C_CONFIG; } - M5IOE1_I2C_SEND_WAKE(_i2c_device, M5IOE1_REG_REV); + M5IOE1_I2C_BUS_SEND_WAKE(_i2c_device, M5IOE1_REG_REV); M5IOE1_DELAY_MS(10); if (!_initDevice()) { @@ -951,7 +1045,9 @@ m5ioe1_err_t M5IOE1::begin(i2c_bus_handle_t bus, uint8_t addr, uint32_t speed, i return M5IOE1_OK; } -#endif +#endif // M5IOE1_HAS_I2C_BUS + +#endif // !ARDUINO m5ioe1_err_t M5IOE1::setInterruptMode(m5ioe1_int_mode_t intMode, uint32_t pollingIntervalMs) { @@ -2826,6 +2922,7 @@ m5ioe1_err_t M5IOE1::setI2cConfig(uint8_t sleepTime, m5ioe1_i2c_speed_t speed, m #else esp_err_t ret; switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: if (_i2c_master_dev != nullptr) { @@ -2851,7 +2948,9 @@ m5ioe1_err_t M5IOE1::setI2cConfig(uint8_t sleepTime, m5ioe1_i2c_speed_t speed, m } } break; +#endif // M5IOE1_HAS_I2C_MASTER +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: if (_i2c_device != nullptr) { ret = i2c_bus_device_delete(&_i2c_device); @@ -2867,6 +2966,25 @@ m5ioe1_err_t M5IOE1::setI2cConfig(uint8_t sleepTime, m5ioe1_i2c_speed_t speed, m } } break; +#endif // M5IOE1_HAS_I2C_BUS + +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: { + i2c_config_t i2c_conf = {}; + i2c_conf.mode = I2C_MODE_MASTER; + i2c_conf.sda_io_num = _sda; + i2c_conf.scl_io_num = _scl; + i2c_conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.master.clk_speed = targetFreq; + ret = i2c_param_config(_port, &i2c_conf); + if (ret != ESP_OK) { + M5IOE1_LOG_E(TAG, "i2c_param_config failed: %s", esp_err_to_name(ret)); + return M5IOE1_ERR_I2C_CONFIG; + } + break; + } +#endif // !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS default: break; @@ -2890,6 +3008,7 @@ m5ioe1_err_t M5IOE1::setI2cConfig(uint8_t sleepTime, m5ioe1_i2c_speed_t speed, m } #else switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: if (_i2c_master_dev != nullptr) { @@ -2904,12 +3023,28 @@ m5ioe1_err_t M5IOE1::setI2cConfig(uint8_t sleepTime, m5ioe1_i2c_speed_t speed, m i2c_master_bus_add_device(_i2c_master_bus, &dev_config, &_i2c_master_dev); } break; +#endif // M5IOE1_HAS_I2C_MASTER +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: if (_i2c_device != nullptr) { i2c_bus_device_delete(&_i2c_device); _i2c_device = i2c_bus_device_create(_i2c_bus, _addr, originalFreq); } break; +#endif // M5IOE1_HAS_I2C_BUS +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: { + i2c_config_t i2c_conf = {}; + i2c_conf.mode = I2C_MODE_MASTER; + i2c_conf.sda_io_num = _sda; + i2c_conf.scl_io_num = _scl; + i2c_conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.master.clk_speed = originalFreq; + i2c_param_config(_port, &i2c_conf); // best effort rollback + break; + } +#endif // !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS default: break; } @@ -3253,15 +3388,23 @@ bool M5IOE1::isAutoWakeEnabled() const m5ioe1_err_t M5IOE1::sendWakeSignal() { #ifdef ARDUINO - M5IOE1_I2C_SEND_WAKE(_wire, _addr); + M5IOE1_I2C_ARDUINO_SEND_WAKE(_wire, _addr); return M5IOE1_OK; #else switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: return M5IOE1_I2C_MASTER_SEND_WAKE(_i2c_master_bus, _addr) == ESP_OK ? M5IOE1_OK : M5IOE1_ERR_I2C_COMM; +#endif +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: - return M5IOE1_I2C_SEND_WAKE(_i2c_device, M5IOE1_REG_REV) == ESP_OK ? M5IOE1_OK : M5IOE1_ERR_I2C_COMM; + return M5IOE1_I2C_BUS_SEND_WAKE(_i2c_device, M5IOE1_REG_REV) == ESP_OK ? M5IOE1_OK : M5IOE1_ERR_I2C_COMM; +#endif +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: + return M5IOE1_I2C_LEGACY_SEND_WAKE(_port, _addr) == ESP_OK ? M5IOE1_OK : M5IOE1_ERR_I2C_COMM; +#endif default: return M5IOE1_ERR_INTERNAL; } @@ -3535,19 +3678,28 @@ bool M5IOE1::_writeReg(uint8_t reg, uint8_t value) _checkAutoWake(); for (int attempt = 0; attempt < M5IOE1_I2C_RETRY_COUNT; ++attempt) { #ifdef ARDUINO - if (M5IOE1_I2C_WRITE_BYTE(_wire, _addr, reg, value)) { + if (M5IOE1_I2C_ARDUINO_WRITE_BYTE(_wire, _addr, reg, value)) { return true; } #else bool ok = false; switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: ok = (M5IOE1_I2C_MASTER_WRITE_BYTE(_i2c_master_dev, reg, value) == ESP_OK); break; +#endif +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: - ok = (M5IOE1_I2C_WRITE_BYTE(_i2c_device, reg, value) == ESP_OK); + ok = (M5IOE1_I2C_BUS_WRITE_BYTE(_i2c_device, reg, value) == ESP_OK); break; +#endif +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: + ok = (M5IOE1_I2C_LEGACY_WRITE_BYTE(_port, _addr, reg, value) == ESP_OK); + break; +#endif default: ok = false; break; @@ -3565,19 +3717,28 @@ bool M5IOE1::_writeReg16(uint8_t reg, uint16_t value) _checkAutoWake(); for (int attempt = 0; attempt < M5IOE1_I2C_RETRY_COUNT; ++attempt) { #ifdef ARDUINO - if (M5IOE1_I2C_WRITE_REG16(_wire, _addr, reg, value)) { + if (M5IOE1_I2C_ARDUINO_WRITE_REG16(_wire, _addr, reg, value)) { return true; } #else bool ok = false; switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: ok = (M5IOE1_I2C_MASTER_WRITE_REG16(_i2c_master_dev, reg, value) == ESP_OK); break; +#endif +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: - ok = (M5IOE1_I2C_WRITE_REG16(_i2c_device, reg, value) == ESP_OK); + ok = (M5IOE1_I2C_BUS_WRITE_REG16(_i2c_device, reg, value) == ESP_OK); break; +#endif +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: + ok = (M5IOE1_I2C_LEGACY_WRITE_REG16(_port, _addr, reg, value) == ESP_OK); + break; +#endif default: ok = false; break; @@ -3595,19 +3756,28 @@ bool M5IOE1::_readReg(uint8_t reg, uint8_t* value) _checkAutoWake(); for (int attempt = 0; attempt < M5IOE1_I2C_RETRY_COUNT; ++attempt) { #ifdef ARDUINO - if (M5IOE1_I2C_READ_BYTE(_wire, _addr, reg, value)) { + if (M5IOE1_I2C_ARDUINO_READ_BYTE(_wire, _addr, reg, value)) { return true; } #else bool ok = false; switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: ok = (M5IOE1_I2C_MASTER_READ_BYTE(_i2c_master_dev, reg, value) == ESP_OK); break; +#endif +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: - ok = (M5IOE1_I2C_READ_BYTE(_i2c_device, reg, value) == ESP_OK); + ok = (M5IOE1_I2C_BUS_READ_BYTE(_i2c_device, reg, value) == ESP_OK); break; +#endif +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: + ok = (M5IOE1_I2C_LEGACY_READ_BYTE(_port, _addr, reg, value) == ESP_OK); + break; +#endif default: ok = false; break; @@ -3625,19 +3795,28 @@ bool M5IOE1::_readReg16(uint8_t reg, uint16_t* value) _checkAutoWake(); for (int attempt = 0; attempt < M5IOE1_I2C_RETRY_COUNT; ++attempt) { #ifdef ARDUINO - if (M5IOE1_I2C_READ_REG16(_wire, _addr, reg, value)) { + if (M5IOE1_I2C_ARDUINO_READ_REG16(_wire, _addr, reg, value)) { return true; } #else bool ok = false; switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: ok = (M5IOE1_I2C_MASTER_READ_REG16(_i2c_master_dev, reg, value) == ESP_OK); break; +#endif +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: - ok = (M5IOE1_I2C_READ_REG16(_i2c_device, reg, value) == ESP_OK); + ok = (M5IOE1_I2C_BUS_READ_REG16(_i2c_device, reg, value) == ESP_OK); break; +#endif +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: + ok = (M5IOE1_I2C_LEGACY_READ_REG16(_port, _addr, reg, value) == ESP_OK); + break; +#endif default: ok = false; break; @@ -3655,19 +3834,28 @@ bool M5IOE1::_writeBytes(uint8_t reg, const uint8_t* data, uint8_t len) _checkAutoWake(); for (int attempt = 0; attempt < M5IOE1_I2C_RETRY_COUNT; ++attempt) { #ifdef ARDUINO - if (M5IOE1_I2C_WRITE_BYTES(_wire, _addr, reg, len, data)) { + if (M5IOE1_I2C_ARDUINO_WRITE_BYTES(_wire, _addr, reg, len, data)) { return true; } #else bool ok = false; switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: ok = (M5IOE1_I2C_MASTER_WRITE_BYTES(_i2c_master_dev, reg, len, data) == ESP_OK); break; +#endif +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: - ok = (M5IOE1_I2C_WRITE_BYTES(_i2c_device, reg, len, data) == ESP_OK); + ok = (M5IOE1_I2C_BUS_WRITE_BYTES(_i2c_device, reg, len, data) == ESP_OK); break; +#endif +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: + ok = (M5IOE1_I2C_LEGACY_WRITE_BYTES(_port, _addr, reg, len, data) == ESP_OK); + break; +#endif default: ok = false; break; @@ -3685,19 +3873,28 @@ bool M5IOE1::_readBytes(uint8_t reg, uint8_t* data, uint8_t len) _checkAutoWake(); for (int attempt = 0; attempt < M5IOE1_I2C_RETRY_COUNT; ++attempt) { #ifdef ARDUINO - if (M5IOE1_I2C_READ_BYTES(_wire, _addr, reg, len, data)) { + if (M5IOE1_I2C_ARDUINO_READ_BYTES(_wire, _addr, reg, len, data)) { return true; } #else bool ok = false; switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: ok = (M5IOE1_I2C_MASTER_READ_BYTES(_i2c_master_dev, reg, len, data) == ESP_OK); break; +#endif +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: - ok = (M5IOE1_I2C_READ_BYTES(_i2c_device, reg, len, data) == ESP_OK); + ok = (M5IOE1_I2C_BUS_READ_BYTES(_i2c_device, reg, len, data) == ESP_OK); break; +#endif +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: + ok = (M5IOE1_I2C_LEGACY_READ_BYTES(_port, _addr, reg, len, data) == ESP_OK); + break; +#endif default: ok = false; break; @@ -3957,6 +4154,7 @@ m5ioe1_err_t M5IOE1::switchI2cSpeed(m5ioe1_i2c_speed_t speed) esp_err_t ret; switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: // 对于 i2c_master 驱动:删除设备并以新速度重新添加 @@ -3996,7 +4194,9 @@ m5ioe1_err_t M5IOE1::switchI2cSpeed(m5ioe1_i2c_speed_t speed) M5IOE1_LOG_I(TAG, "I2C master device recreated at %lu Hz", targetFreq); } break; +#endif // M5IOE1_HAS_I2C_MASTER +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: // 对于 i2c_bus 驱动:删除设备并以新速度创建 // For i2c_bus driver: delete device and create with new speed @@ -4022,6 +4222,26 @@ m5ioe1_err_t M5IOE1::switchI2cSpeed(m5ioe1_i2c_speed_t speed) M5IOE1_LOG_I(TAG, "I2C bus device recreated at %lu Hz", targetFreq); } break; +#endif // M5IOE1_HAS_I2C_BUS + +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: { + i2c_config_t i2c_conf = {}; + i2c_conf.mode = I2C_MODE_MASTER; + i2c_conf.sda_io_num = _sda; + i2c_conf.scl_io_num = _scl; + i2c_conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.master.clk_speed = targetFreq; + ret = i2c_param_config(_port, &i2c_conf); + if (ret != ESP_OK) { + M5IOE1_LOG_E(TAG, "i2c_param_config failed: %s", esp_err_to_name(ret)); + return M5IOE1_ERR_I2C_CONFIG; + } + M5IOE1_LOG_I(TAG, "Legacy I2C reconfigured to %lu Hz", targetFreq); + break; + } +#endif // !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS default: M5IOE1_LOG_E(TAG, "Unknown I2C driver type"); @@ -4053,6 +4273,7 @@ m5ioe1_err_t M5IOE1::switchI2cSpeed(m5ioe1_i2c_speed_t speed) } #else switch (_i2cDriverType) { +#if M5IOE1_HAS_I2C_MASTER case M5IOE1_I2C_DRIVER_SELF_CREATED: case M5IOE1_I2C_DRIVER_MASTER: if (_i2c_master_dev != nullptr) { @@ -4067,12 +4288,28 @@ m5ioe1_err_t M5IOE1::switchI2cSpeed(m5ioe1_i2c_speed_t speed) i2c_master_bus_add_device(_i2c_master_bus, &dev_config, &_i2c_master_dev); } break; +#endif // M5IOE1_HAS_I2C_MASTER +#if M5IOE1_HAS_I2C_BUS case M5IOE1_I2C_DRIVER_BUS: if (_i2c_device != nullptr) { i2c_bus_device_delete(&_i2c_device); _i2c_device = i2c_bus_device_create(_i2c_bus, _addr, originalFreq); } break; +#endif // M5IOE1_HAS_I2C_BUS +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + case M5IOE1_I2C_DRIVER_LEGACY: { + i2c_config_t i2c_conf = {}; + i2c_conf.mode = I2C_MODE_MASTER; + i2c_conf.sda_io_num = _sda; + i2c_conf.scl_io_num = _scl; + i2c_conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + i2c_conf.master.clk_speed = originalFreq; + i2c_param_config(_port, &i2c_conf); // best effort rollback + break; + } +#endif // !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS default: break; } diff --git a/src/M5IOE1.h b/src/M5IOE1.h index 08da289..14a0639 100644 --- a/src/M5IOE1.h +++ b/src/M5IOE1.h @@ -548,10 +548,11 @@ public: m5ioe1_int_mode_t intMode = M5IOE1_INT_MODE_HARDWARE); // ===================================================== - // Type 2A: Existing i2c_master_bus_handle_t, no hardware interrupt + // Type 2A/2B: Existing i2c_master_bus_handle_t (ESP-IDF >= 5.3.0 only) // ===================================================== +#if M5IOE1_HAS_I2C_MASTER /** - * @brief Initialize with existing i2c_master_bus handle (ESP-IDF native driver) + * @brief Initialize with existing i2c_master_bus handle (ESP-IDF native driver, IDF >= 5.3.0) * @param bus Existing i2c_master_bus_handle_t * @param addr I2C address (default 0x6F) * @param speed I2C speed in Hz (for device handle creation) @@ -561,11 +562,8 @@ public: m5ioe1_err_t begin(i2c_master_bus_handle_t bus, uint8_t addr = M5IOE1_DEFAULT_ADDR, uint32_t speed = M5IOE1_I2C_FREQ_100K, m5ioe1_int_mode_t intMode = M5IOE1_INT_MODE_POLLING); - // ===================================================== - // Type 2B: Existing i2c_master_bus_handle_t, with hardware interrupt - // ===================================================== /** - * @brief Initialize with existing i2c_master_bus handle, with hardware interrupt + * @brief Initialize with existing i2c_master_bus handle, with hardware interrupt (IDF >= 5.3.0) * @param bus Existing i2c_master_bus_handle_t * @param addr I2C address * @param speed I2C speed in Hz @@ -575,10 +573,12 @@ public: */ m5ioe1_err_t begin(i2c_master_bus_handle_t bus, uint8_t addr, uint32_t speed, int intPin, m5ioe1_int_mode_t intMode = M5IOE1_INT_MODE_HARDWARE); +#endif // M5IOE1_HAS_I2C_MASTER // ===================================================== // Type 3A: Existing i2c_bus_handle_t, no hardware interrupt // ===================================================== +#if M5IOE1_HAS_I2C_BUS /** * @brief Initialize with existing i2c_bus handle (esp-idf-lib component) * @param bus Existing i2c_bus_handle_t @@ -604,7 +604,33 @@ public: */ m5ioe1_err_t begin(i2c_bus_handle_t bus, uint8_t addr, uint32_t speed, int intPin, m5ioe1_int_mode_t intMode = M5IOE1_INT_MODE_HARDWARE); -#endif +#else + /** + * @brief i2c_bus overload is intentionally kept for diagnostics when unavailable + * @note This overload exists only to provide a clear compile-time message when called. + */ + inline m5ioe1_err_t begin(i2c_bus_handle_t bus, uint8_t addr = M5IOE1_DEFAULT_ADDR, + uint32_t speed = M5IOE1_I2C_FREQ_100K, + m5ioe1_int_mode_t intMode = M5IOE1_INT_MODE_POLLING) + { + (void)bus; + (void)addr; + (void)speed; + (void)intMode; + return M5IOE1_ERR_NOT_SUPPORTED; + } + inline m5ioe1_err_t begin(i2c_bus_handle_t bus, uint8_t addr, uint32_t speed, int intPin, + m5ioe1_int_mode_t intMode = M5IOE1_INT_MODE_HARDWARE) + { + (void)bus; + (void)addr; + (void)speed; + (void)intPin; + (void)intMode; + return M5IOE1_ERR_NOT_SUPPORTED; + } +#endif // M5IOE1_HAS_I2C_BUS +#endif // !ARDUINO /** * @brief Set interrupt handling mode @@ -1517,20 +1543,26 @@ private: // I2C 句柄(根据驱动类型仅使用一对) // I2C handles (only one pair is used based on driver type) - // M5IOE1_I2C_DRIVER_SELF_CREATED: 使用 _i2c_master_bus + _i2c_master_dev + // M5IOE1_I2C_DRIVER_SELF_CREATED (IDF >= 5.3.0): 使用 _i2c_master_bus + _i2c_master_dev // uses _i2c_master_bus + _i2c_master_dev // M5IOE1_I2C_DRIVER_MASTER: 使用 _i2c_master_bus + _i2c_master_dev // uses _i2c_master_bus + _i2c_master_dev // M5IOE1_I2C_DRIVER_BUS: 使用 _i2c_bus + _i2c_device // uses _i2c_bus + _i2c_device - i2c_master_bus_handle_t _i2c_master_bus; // ESP-IDF 原生驱动/自创建 - // ESP-IDF native driver / self-created - i2c_master_dev_handle_t _i2c_master_dev; // ESP-IDF 原生驱动/自创建 - // ESP-IDF native driver / self-created - i2c_bus_handle_t _i2c_bus; // esp-idf-lib 组件 - // esp-idf-lib component - i2c_bus_device_handle_t _i2c_device; // esp-idf-lib 组件 - // esp-idf-lib component + // M5IOE1_I2C_DRIVER_LEGACY (IDF < 5.3.0): 使用 _port + _addr,无额外句柄 + // uses _port + _addr, no additional handle +#if M5IOE1_HAS_I2C_MASTER + i2c_master_bus_handle_t _i2c_master_bus; // ESP-IDF 原生驱动/自创建 (IDF >= 5.3.0) + // ESP-IDF native driver / self-created (IDF >= 5.3.0) + i2c_master_dev_handle_t _i2c_master_dev; // ESP-IDF 原生驱动/自创建 (IDF >= 5.3.0) + // ESP-IDF native driver / self-created (IDF >= 5.3.0) +#endif // M5IOE1_HAS_I2C_MASTER +#if M5IOE1_HAS_I2C_BUS + i2c_bus_handle_t _i2c_bus; // esp-idf-lib 组件 + // esp-idf-lib component + i2c_bus_device_handle_t _i2c_device; // esp-idf-lib 组件 + // esp-idf-lib component +#endif // I2C 管理标志 // I2C management flags diff --git a/src/M5IOE1_i2c_compat.h b/src/M5IOE1_i2c_compat.h index d5e1155..48350f8 100644 --- a/src/M5IOE1_i2c_compat.h +++ b/src/M5IOE1_i2c_compat.h @@ -32,8 +32,8 @@ // Arduino I2C Functions // ============================ -#ifndef M5IOE1_I2C_READ_BYTE -static inline bool M5IOE1_I2C_READ_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg, uint8_t *data) +#ifndef M5IOE1_I2C_ARDUINO_READ_BYTE +static inline bool M5IOE1_I2C_ARDUINO_READ_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg, uint8_t *data) { wire->beginTransmission(addr); wire->write(reg); @@ -49,8 +49,9 @@ static inline bool M5IOE1_I2C_READ_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg } #endif -#ifndef M5IOE1_I2C_READ_BYTES -static inline bool M5IOE1_I2C_READ_BYTES(TwoWire *wire, uint8_t addr, uint8_t start_reg, size_t len, uint8_t *data) +#ifndef M5IOE1_I2C_ARDUINO_READ_BYTES +static inline bool M5IOE1_I2C_ARDUINO_READ_BYTES(TwoWire *wire, uint8_t addr, uint8_t start_reg, size_t len, + uint8_t *data) { wire->beginTransmission(addr); wire->write(start_reg); @@ -68,11 +69,11 @@ static inline bool M5IOE1_I2C_READ_BYTES(TwoWire *wire, uint8_t addr, uint8_t st } #endif -#ifndef M5IOE1_I2C_READ_REG16 -static inline bool M5IOE1_I2C_READ_REG16(TwoWire *wire, uint8_t addr, uint8_t reg, uint16_t *data) +#ifndef M5IOE1_I2C_ARDUINO_READ_REG16 +static inline bool M5IOE1_I2C_ARDUINO_READ_REG16(TwoWire *wire, uint8_t addr, uint8_t reg, uint16_t *data) { uint8_t buf[2]; - if (!M5IOE1_I2C_READ_BYTES(wire, addr, reg, 2, buf)) { + if (!M5IOE1_I2C_ARDUINO_READ_BYTES(wire, addr, reg, 2, buf)) { return false; } // 小端模式:低字节在前 @@ -82,8 +83,8 @@ static inline bool M5IOE1_I2C_READ_REG16(TwoWire *wire, uint8_t addr, uint8_t re } #endif -#ifndef M5IOE1_I2C_WRITE_BYTE -static inline bool M5IOE1_I2C_WRITE_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg, uint8_t data) +#ifndef M5IOE1_I2C_ARDUINO_WRITE_BYTE +static inline bool M5IOE1_I2C_ARDUINO_WRITE_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg, uint8_t data) { wire->beginTransmission(addr); wire->write(reg); @@ -96,9 +97,9 @@ static inline bool M5IOE1_I2C_WRITE_BYTE(TwoWire *wire, uint8_t addr, uint8_t re } #endif -#ifndef M5IOE1_I2C_WRITE_BYTES -static inline bool M5IOE1_I2C_WRITE_BYTES(TwoWire *wire, uint8_t addr, uint8_t start_reg, size_t len, - const uint8_t *data) +#ifndef M5IOE1_I2C_ARDUINO_WRITE_BYTES +static inline bool M5IOE1_I2C_ARDUINO_WRITE_BYTES(TwoWire *wire, uint8_t addr, uint8_t start_reg, size_t len, + const uint8_t *data) { wire->beginTransmission(addr); wire->write(start_reg); @@ -113,23 +114,23 @@ static inline bool M5IOE1_I2C_WRITE_BYTES(TwoWire *wire, uint8_t addr, uint8_t s } #endif -#ifndef M5IOE1_I2C_WRITE_REG16 -static inline bool M5IOE1_I2C_WRITE_REG16(TwoWire *wire, uint8_t addr, uint8_t reg, uint16_t data) +#ifndef M5IOE1_I2C_ARDUINO_WRITE_REG16 +static inline bool M5IOE1_I2C_ARDUINO_WRITE_REG16(TwoWire *wire, uint8_t addr, uint8_t reg, uint16_t data) { uint8_t buf[2]; // 小端模式:低字节在前 // Little-endian: low byte first buf[0] = (uint8_t)(data & 0xFF); buf[1] = (uint8_t)((data >> 8) & 0xFF); - return M5IOE1_I2C_WRITE_BYTES(wire, addr, reg, 2, buf); + return M5IOE1_I2C_ARDUINO_WRITE_BYTES(wire, addr, reg, 2, buf); } #endif // 唤醒信号发送 / Wake signal send // IOE1 使用 SDA 引脚下降沿触发唤醒中断 // IOE1 uses SDA pin falling edge to trigger wake interrupt -#ifndef M5IOE1_I2C_SEND_WAKE -static inline void M5IOE1_I2C_SEND_WAKE(TwoWire *wire, uint8_t addr) +#ifndef M5IOE1_I2C_ARDUINO_SEND_WAKE +static inline void M5IOE1_I2C_ARDUINO_SEND_WAKE(TwoWire *wire, uint8_t addr) { // Send START then STOP to avoid leaving the bus in a busy state wire->beginTransmission(addr); @@ -140,8 +141,102 @@ static inline void M5IOE1_I2C_SEND_WAKE(TwoWire *wire, uint8_t addr) #else // ESP-IDF #include -#include // ESP-IDF native i2c_master driver -#include // esp-idf-lib i2c_bus component +#include + +// ============================ +// I2C 驱动检测 +// I2C Driver Detection +// ============================ + +// 检测 i2c_bus 是否可用 +// Detect if i2c_bus is available +// +// ESP-IDF < 5.3.0 +// 未启用 BACKWARD_CONFIG +// → 不支持 i2c_bus,使用传统 driver/i2c.h Legacy API +// 启用 BACKWARD_CONFIG +// → i2c_bus.h 内部回退到 driver/i2c.h,可安全使用 +// +// ESP-IDF >= 5.3.0 +// 启用 BACKWARD_CONFIG +// → i2c_bus.h 内部使用 driver/i2c.h,无冲突风险 +// 未启用 BACKWARD_CONFIG +// driver/i2c.h 已被其他组件提前包含(_DRIVER_I2C_H_ 已定义) +// → i2c_bus.h 会自定义 i2c_config_t,与已定义的版本冲突 → 禁用 +// driver/i2c.h 尚未被包含(_DRIVER_I2C_H_ 未定义) +// → 无冲突风险,按默认配置启用 i2c_bus +// +// Detection logic: +// ESP-IDF < 5.3.0: +// Without BACKWARD_CONFIG: i2c_bus not supported; use legacy driver/i2c.h API. +// With BACKWARD_CONFIG: i2c_bus.h falls back to driver/i2c.h internally, safe to use. +// ESP-IDF >= 5.3.0: +// With BACKWARD_CONFIG: i2c_bus.h uses driver/i2c.h internally, always conflict-free. +// Without BACKWARD_CONFIG: +// _DRIVER_I2C_H_ defined (driver/i2c.h already included by another component) +// → i2c_bus.h would define its own i2c_config_t, conflicting with the existing one → disabled. +// _DRIVER_I2C_H_ not defined (driver/i2c.h not yet included) +// → no conflict risk, enable i2c_bus with default config. +// +// Note: _DRIVER_I2C_H_ is the include guard of driver/i2c.h (ESP-IDF legacy I2C header). +// Checking it at preprocessor time reflects whether driver/i2c.h was included +// BEFORE this header. Inclusion after this header cannot be detected here; +// in that case the user is responsible for ensuring no conflict (or enabling BACKWARD_CONFIG). +#if __has_include() +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) +#if defined(CONFIG_I2C_BUS_BACKWARD_CONFIG) +#define M5IOE1_HAS_I2C_BUS 1 // IDF < 5.3.0 + BACKWARD_CONFIG:可用 / available +#else +#define M5IOE1_HAS_I2C_BUS 0 // IDF < 5.3.0:默认 Legacy API / legacy API by default +#endif +#else +// IDF >= 5.3.0 +#if defined(CONFIG_I2C_BUS_BACKWARD_CONFIG) +#define M5IOE1_HAS_I2C_BUS 1 // BACKWARD_CONFIG:i2c_bus.h 使用 driver/i2c.h,无冲突 / no conflict +#elif defined(_DRIVER_I2C_H_) +#define M5IOE1_HAS_I2C_BUS \ + 0 // driver/i2c.h 已提前包含 + 无 BACKWARD_CONFIG → 冲突风险,禁用 + // driver/i2c.h already included + no BACKWARD_CONFIG → conflict risk, disabled +#else +#define M5IOE1_HAS_I2C_BUS 1 // driver/i2c.h 尚未包含,无冲突风险 / driver/i2c.h not yet included, no conflict +#endif +#endif +#else +#define M5IOE1_HAS_I2C_BUS 0 +#endif + +// 选择 I2C 驱动头文件 +// Select I2C driver header +// +// ESP-IDF < 5.3.0:使用传统 Legacy I2C API(driver/i2c.h),与 i2c_bus 无关 +// ESP-IDF < 5.3.0: use legacy I2C API (driver/i2c.h), independent of i2c_bus. +// +// ESP-IDF >= 5.3.0:优先使用新版 i2c_master 驱动 +// ESP-IDF >= 5.3.0: prefer the new i2c_master driver. +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) +#include +#elif __has_include() +#include +#else +#include +#endif + +// i2c_master 原生驱动可用性检测 +// i2c_master native driver availability detection +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && __has_include() +#define M5IOE1_HAS_I2C_MASTER 1 +#else +#define M5IOE1_HAS_I2C_MASTER 0 +#endif + +#if M5IOE1_HAS_I2C_BUS +#include +#else +// i2c_bus 桩类型 +// i2c_bus stub types +typedef void *i2c_bus_handle_t; +typedef void *i2c_bus_device_handle_t; +#endif #ifdef __cplusplus extern "C" { @@ -154,12 +249,18 @@ extern "C" { typedef enum { M5IOE1_I2C_DRIVER_NONE = 0, // 未初始化 // Not initialized - M5IOE1_I2C_DRIVER_SELF_CREATED, // 使用 i2c_port_t 自创建 - // Self-created using i2c_port_t - M5IOE1_I2C_DRIVER_MASTER, // ESP-IDF 原生 i2c_master 驱动 - // ESP-IDF native i2c_master driver - M5IOE1_I2C_DRIVER_BUS // esp-idf-lib i2c_bus 组件 - // esp-idf-lib i2c_bus component + M5IOE1_I2C_DRIVER_SELF_CREATED, // 使用 i2c_port_t 自创建(IDF >= 5.3.0 用 i2c_master,否则用 Legacy) + // Self-created using i2c_port_t (i2c_master on IDF >= 5.3.0, Legacy otherwise) + M5IOE1_I2C_DRIVER_MASTER, // ESP-IDF 原生 i2c_master 驱动(仅 IDF >= 5.3.0) + // ESP-IDF native i2c_master driver (IDF >= 5.3.0 only) +#if M5IOE1_HAS_I2C_BUS + M5IOE1_I2C_DRIVER_BUS, // esp-idf-lib i2c_bus 组件 + // esp-idf-lib i2c_bus component +#endif +#if !M5IOE1_HAS_I2C_MASTER && !M5IOE1_HAS_I2C_BUS + M5IOE1_I2C_DRIVER_LEGACY, // 传统 driver/i2c.h API(IDF < 5.3.0 且无 i2c_bus) + // Legacy driver/i2c.h API (IDF < 5.3.0 without i2c_bus) +#endif } m5ioe1_i2c_driver_t; // ============================ @@ -167,22 +268,25 @@ typedef enum { // ESP-IDF I2C Functions (i2c_bus) // ============================ -#ifndef M5IOE1_I2C_READ_BYTE -static inline esp_err_t M5IOE1_I2C_READ_BYTE(i2c_bus_device_handle_t dev, uint8_t reg, uint8_t *data) +#if M5IOE1_HAS_I2C_BUS + +#ifndef M5IOE1_I2C_BUS_READ_BYTE +static inline esp_err_t M5IOE1_I2C_BUS_READ_BYTE(i2c_bus_device_handle_t dev, uint8_t reg, uint8_t *data) { return i2c_bus_read_byte(dev, reg, data); } #endif -#ifndef M5IOE1_I2C_READ_BYTES -static inline esp_err_t M5IOE1_I2C_READ_BYTES(i2c_bus_device_handle_t dev, uint8_t start_reg, size_t len, uint8_t *data) +#ifndef M5IOE1_I2C_BUS_READ_BYTES +static inline esp_err_t M5IOE1_I2C_BUS_READ_BYTES(i2c_bus_device_handle_t dev, uint8_t start_reg, size_t len, + uint8_t *data) { return i2c_bus_read_bytes(dev, start_reg, len, data); } #endif -#ifndef M5IOE1_I2C_READ_REG16 -static inline esp_err_t M5IOE1_I2C_READ_REG16(i2c_bus_device_handle_t dev, uint8_t reg, uint16_t *data) +#ifndef M5IOE1_I2C_BUS_READ_REG16 +static inline esp_err_t M5IOE1_I2C_BUS_READ_REG16(i2c_bus_device_handle_t dev, uint8_t reg, uint16_t *data) { uint8_t buf[2]; esp_err_t ret = i2c_bus_read_bytes(dev, reg, 2, buf); @@ -195,23 +299,23 @@ static inline esp_err_t M5IOE1_I2C_READ_REG16(i2c_bus_device_handle_t dev, uint8 } #endif -#ifndef M5IOE1_I2C_WRITE_BYTE -static inline esp_err_t M5IOE1_I2C_WRITE_BYTE(i2c_bus_device_handle_t dev, uint8_t reg, uint8_t data) +#ifndef M5IOE1_I2C_BUS_WRITE_BYTE +static inline esp_err_t M5IOE1_I2C_BUS_WRITE_BYTE(i2c_bus_device_handle_t dev, uint8_t reg, uint8_t data) { return i2c_bus_write_byte(dev, reg, data); } #endif -#ifndef M5IOE1_I2C_WRITE_BYTES -static inline esp_err_t M5IOE1_I2C_WRITE_BYTES(i2c_bus_device_handle_t dev, uint8_t start_reg, size_t len, - const uint8_t *data) +#ifndef M5IOE1_I2C_BUS_WRITE_BYTES +static inline esp_err_t M5IOE1_I2C_BUS_WRITE_BYTES(i2c_bus_device_handle_t dev, uint8_t start_reg, size_t len, + const uint8_t *data) { return i2c_bus_write_bytes(dev, start_reg, len, (uint8_t *)data); } #endif -#ifndef M5IOE1_I2C_WRITE_REG16 -static inline esp_err_t M5IOE1_I2C_WRITE_REG16(i2c_bus_device_handle_t dev, uint8_t reg, uint16_t data) +#ifndef M5IOE1_I2C_BUS_WRITE_REG16 +static inline esp_err_t M5IOE1_I2C_BUS_WRITE_REG16(i2c_bus_device_handle_t dev, uint8_t reg, uint16_t data) { uint8_t buf[2]; // 小端模式:低字节在前 @@ -222,11 +326,131 @@ static inline esp_err_t M5IOE1_I2C_WRITE_REG16(i2c_bus_device_handle_t dev, uint } #endif +// 唤醒信号发送 (i2c_bus) / Wake signal send (i2c_bus) +#ifndef M5IOE1_I2C_BUS_SEND_WAKE +static inline esp_err_t M5IOE1_I2C_BUS_SEND_WAKE(i2c_bus_device_handle_t dev, uint8_t reg) +{ + // Read any register to generate I2C start signal for wake + uint8_t dummy; + return i2c_bus_read_byte(dev, reg, &dummy); +} +#endif + +#else // !M5IOE1_HAS_I2C_BUS + +// ============================ +// ESP-IDF I2C 函数 (Legacy - driver/i2c.h,仅 IDF < 5.3.0 且无 i2c_bus) +// ESP-IDF I2C Functions (Legacy - driver/i2c.h, IDF < 5.3.0 without i2c_bus only) +// 使用 IDF 4.3+ 引入的简单 API:i2c_master_write_to_device / i2c_master_write_read_device +// Uses simple API introduced in IDF 4.3+: i2c_master_write_to_device / i2c_master_write_read_device +// ============================ + +#if !M5IOE1_HAS_I2C_MASTER + +#include +#include + +// 传统 I2C 超时,单位毫秒 / Legacy I2C timeout in milliseconds +#ifndef M5IOE1_I2C_LEGACY_TIMEOUT_MS +#define M5IOE1_I2C_LEGACY_TIMEOUT_MS 100 +#endif + +#ifndef M5IOE1_I2C_LEGACY_READ_BYTE +static inline esp_err_t M5IOE1_I2C_LEGACY_READ_BYTE(i2c_port_t port, uint8_t addr, uint8_t reg, uint8_t *data) +{ + return i2c_master_write_read_device(port, addr, ®, 1, data, 1, pdMS_TO_TICKS(M5IOE1_I2C_LEGACY_TIMEOUT_MS)); +} +#endif + +#ifndef M5IOE1_I2C_LEGACY_READ_BYTES +static inline esp_err_t M5IOE1_I2C_LEGACY_READ_BYTES(i2c_port_t port, uint8_t addr, uint8_t start_reg, size_t len, + uint8_t *data) +{ + return i2c_master_write_read_device(port, addr, &start_reg, 1, data, len, + pdMS_TO_TICKS(M5IOE1_I2C_LEGACY_TIMEOUT_MS)); +} +#endif + +#ifndef M5IOE1_I2C_LEGACY_READ_REG16 +static inline esp_err_t M5IOE1_I2C_LEGACY_READ_REG16(i2c_port_t port, uint8_t addr, uint8_t reg, uint16_t *data) +{ + uint8_t buf[2]; + esp_err_t ret = + i2c_master_write_read_device(port, addr, ®, 1, buf, 2, pdMS_TO_TICKS(M5IOE1_I2C_LEGACY_TIMEOUT_MS)); + if (ret == ESP_OK) { + // 小端模式:低字节在前 + // Little-endian: low byte first + *data = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8); + } + return ret; +} +#endif + +#ifndef M5IOE1_I2C_LEGACY_WRITE_BYTE +static inline esp_err_t M5IOE1_I2C_LEGACY_WRITE_BYTE(i2c_port_t port, uint8_t addr, uint8_t reg, uint8_t data) +{ + uint8_t buf[2] = {reg, data}; + return i2c_master_write_to_device(port, addr, buf, 2, pdMS_TO_TICKS(M5IOE1_I2C_LEGACY_TIMEOUT_MS)); +} +#endif + +#ifndef M5IOE1_I2C_LEGACY_WRITE_BYTES +static inline esp_err_t M5IOE1_I2C_LEGACY_WRITE_BYTES(i2c_port_t port, uint8_t addr, uint8_t start_reg, size_t len, + const uint8_t *data) +{ + // 需要在数据前添加寄存器地址 + // Need to prepend register address before data + uint8_t *buf = (uint8_t *)malloc(len + 1); + if (buf == NULL) return ESP_ERR_NO_MEM; + buf[0] = start_reg; + memcpy(buf + 1, data, len); + esp_err_t ret = i2c_master_write_to_device(port, addr, buf, len + 1, pdMS_TO_TICKS(M5IOE1_I2C_LEGACY_TIMEOUT_MS)); + free(buf); + return ret; +} +#endif + +#ifndef M5IOE1_I2C_LEGACY_WRITE_REG16 +static inline esp_err_t M5IOE1_I2C_LEGACY_WRITE_REG16(i2c_port_t port, uint8_t addr, uint8_t reg, uint16_t data) +{ + uint8_t buf[3]; + buf[0] = reg; + // 小端模式:低字节在前 + // Little-endian: low byte first + buf[1] = (uint8_t)(data & 0xFF); + buf[2] = (uint8_t)((data >> 8) & 0xFF); + return i2c_master_write_to_device(port, addr, buf, 3, pdMS_TO_TICKS(M5IOE1_I2C_LEGACY_TIMEOUT_MS)); +} +#endif + +// 唤醒信号发送 (Legacy) / Wake signal send (Legacy) +// 通过读取一个字节来产生 I2C START 信号 +// Generates I2C START signal by reading one byte +#ifndef M5IOE1_I2C_LEGACY_SEND_WAKE +static inline esp_err_t M5IOE1_I2C_LEGACY_SEND_WAKE(i2c_port_t port, uint8_t addr) +{ + uint8_t dummy = 0; + uint8_t reg = 0x00; + // 忽略错误:唤醒时设备可能处于睡眠状态,ACK 可能超时 + // Ignore error: device may be asleep during wake, ACK may timeout + i2c_master_write_read_device(port, addr, ®, 1, &dummy, 1, pdMS_TO_TICKS(10)); + return ESP_OK; +} +#endif + +#endif // !M5IOE1_HAS_I2C_MASTER + +#endif // M5IOE1_HAS_I2C_BUS + // ============================ // ESP-IDF I2C 函数 (i2c_master - 原生驱动) // ESP-IDF I2C Functions (i2c_master - native driver) +// 仅 ESP-IDF >= 5.3.0 支持(driver/i2c_master.h 引入于 5.1,API 稳定于 5.3) +// Available on ESP-IDF >= 5.3.0 only (driver/i2c_master.h introduced in 5.1, stable in 5.3) // ============================ +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + #ifndef M5IOE1_I2C_MASTER_READ_BYTE static inline esp_err_t M5IOE1_I2C_MASTER_READ_BYTE(i2c_master_dev_handle_t dev, uint8_t reg, uint8_t *data) { @@ -293,16 +517,6 @@ static inline esp_err_t M5IOE1_I2C_MASTER_WRITE_REG16(i2c_master_dev_handle_t de } #endif -// 唤醒信号发送 (i2c_bus) / Wake signal send (i2c_bus) -#ifndef M5IOE1_I2C_SEND_WAKE -static inline esp_err_t M5IOE1_I2C_SEND_WAKE(i2c_bus_device_handle_t dev, uint8_t reg) -{ - // Read any register to generate I2C start signal for wake - uint8_t dummy; - return i2c_bus_read_byte(dev, reg, &dummy); -} -#endif - // 唤醒信号发送 (i2c_master) / Wake signal send (i2c_master) #ifndef M5IOE1_I2C_MASTER_SEND_WAKE static inline esp_err_t M5IOE1_I2C_MASTER_SEND_WAKE(i2c_master_bus_handle_t bus, uint8_t addr) @@ -312,6 +526,8 @@ static inline esp_err_t M5IOE1_I2C_MASTER_SEND_WAKE(i2c_master_bus_handle_t bus, } #endif +#endif // ESP_IDF_VERSION >= 5.3.0 + #ifdef __cplusplus } #endif