diff --git a/README.md b/README.md index 78e63b7..3f4f6e4 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,28 @@ M5PM1 is a dual-platform (ESP-IDF & Arduino) driver library for M5Stack PM1 Powe - examples/neopixel/neopixel.ino - examples/usb_interrupt_sleep/usb_interrupt_sleep.ino +## Notes + +### Include Order (ESP-IDF) + +If used alongside `M5Unified`, include it **before** `M5PM1`: + +```cpp +#include // ✓ must come first +#include +``` + +```cpp +#include // ✗ causes i2c_config_t conflict +#include +``` + +> **Why:** On ESP-IDF ≥ 5.3.0 without `CONFIG_I2C_BUS_BACKWARD_CONFIG`, `i2c_bus.h` defines +> its own `i2c_config_t`. Including it before `M5Unified` (which pulls in `driver/i2c.h`) +> creates a conflicting declaration error. Reversing the order avoids this. +> +> Alternatively, enable `CONFIG_I2C_BUS_BACKWARD_CONFIG` in menuconfig to remove the restriction. + ## License - [M5PM1 - MIT](LICENSE) diff --git a/idf_component.yml b/idf_component.yml index e0124b8..472b765 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -12,4 +12,4 @@ dependencies: espressif/i2c_bus: version: "^1.0.0" public: true -version: "1.0.4" \ No newline at end of file +version: "1.0.5" \ No newline at end of file diff --git a/library.json b/library.json index 31316a4..ab3c7fe 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "M5PM1", - "version": "1.0.4", + "version": "1.0.5", "description": "M5Stack PM1 Power Management IC Driver Library", "keywords": [ "m5stack", diff --git a/library.properties b/library.properties index 3ed3b68..b5e8db1 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=M5PM1 -version=1.0.4 +version=1.0.5 author=M5Stack maintainer=M5Stack sentence=M5Stack PM1 Power Management IC Driver Library diff --git a/src/M5PM1.cpp b/src/M5PM1.cpp index ea33e1a..a31ef19 100644 --- a/src/M5PM1.cpp +++ b/src/M5PM1.cpp @@ -162,8 +162,10 @@ M5PM1::M5PM1() _i2cDriverType = M5PM1_I2C_DRIVER_NONE; _i2c_master_bus = nullptr; _i2c_master_dev = nullptr; +#if M5PM1_HAS_I2C_BUS _i2c_bus = nullptr; _i2c_device = nullptr; +#endif _busExternal = false; _sda = -1; _scl = -1; @@ -194,12 +196,14 @@ M5PM1::~M5PM1() } break; +#if M5PM1_HAS_I2C_BUS case M5PM1_I2C_DRIVER_BUS: if (_i2c_device) { i2c_bus_device_delete(&_i2c_device); _i2c_device = nullptr; } break; +#endif default: break; @@ -582,6 +586,7 @@ m5pm1_err_t M5PM1::begin(i2c_master_bus_handle_t bus, uint8_t addr, uint32_t spe return M5PM1_OK; } +#if M5PM1_HAS_I2C_BUS m5pm1_err_t M5PM1::begin(i2c_bus_handle_t bus, uint8_t addr, uint32_t speed) { _addr = addr; @@ -685,6 +690,7 @@ m5pm1_err_t M5PM1::begin(i2c_bus_handle_t bus, uint8_t addr, uint32_t speed) M5PM1_LOG_I(TAG, "M5PM1 initialized at address 0x%02X (I2C: %lu Hz)", _addr, (unsigned long)_requestedSpeed); return M5PM1_OK; } +#endif // M5PM1_HAS_I2C_BUS #endif // ARDUINO @@ -1244,9 +1250,11 @@ bool M5PM1::_writeReg(uint8_t reg, uint8_t value) case M5PM1_I2C_DRIVER_MASTER: success = M5PM1_I2C_MASTER_WRITE_BYTE(_i2c_master_dev, reg, value) == ESP_OK; break; +#if M5PM1_HAS_I2C_BUS case M5PM1_I2C_DRIVER_BUS: success = M5PM1_I2C_WRITE_BYTE(_i2c_device, reg, value) == ESP_OK; break; +#endif default: success = false; break; @@ -1276,9 +1284,11 @@ bool M5PM1::_writeReg16(uint8_t reg, uint16_t value) case M5PM1_I2C_DRIVER_MASTER: success = M5PM1_I2C_MASTER_WRITE_REG16(_i2c_master_dev, reg, value) == ESP_OK; break; +#if M5PM1_HAS_I2C_BUS case M5PM1_I2C_DRIVER_BUS: success = M5PM1_I2C_WRITE_REG16(_i2c_device, reg, value) == ESP_OK; break; +#endif default: success = false; break; @@ -1308,9 +1318,11 @@ bool M5PM1::_readReg(uint8_t reg, uint8_t* value) case M5PM1_I2C_DRIVER_MASTER: success = M5PM1_I2C_MASTER_READ_BYTE(_i2c_master_dev, reg, value) == ESP_OK; break; +#if M5PM1_HAS_I2C_BUS case M5PM1_I2C_DRIVER_BUS: success = M5PM1_I2C_READ_BYTE(_i2c_device, reg, value) == ESP_OK; break; +#endif default: success = false; break; @@ -1340,9 +1352,11 @@ bool M5PM1::_readReg16(uint8_t reg, uint16_t* value) case M5PM1_I2C_DRIVER_MASTER: success = M5PM1_I2C_MASTER_READ_REG16(_i2c_master_dev, reg, value) == ESP_OK; break; +#if M5PM1_HAS_I2C_BUS case M5PM1_I2C_DRIVER_BUS: success = M5PM1_I2C_READ_REG16(_i2c_device, reg, value) == ESP_OK; break; +#endif default: success = false; break; @@ -1372,9 +1386,11 @@ bool M5PM1::_writeBytes(uint8_t reg, const uint8_t* data, uint8_t len) case M5PM1_I2C_DRIVER_MASTER: success = M5PM1_I2C_MASTER_WRITE_BYTES(_i2c_master_dev, reg, len, data) == ESP_OK; break; +#if M5PM1_HAS_I2C_BUS case M5PM1_I2C_DRIVER_BUS: success = M5PM1_I2C_WRITE_BYTES(_i2c_device, reg, len, data) == ESP_OK; break; +#endif default: success = false; break; @@ -1404,9 +1420,11 @@ bool M5PM1::_readBytes(uint8_t reg, uint8_t* data, uint8_t len) case M5PM1_I2C_DRIVER_MASTER: success = M5PM1_I2C_MASTER_READ_BYTES(_i2c_master_dev, reg, len, data) == ESP_OK; break; +#if M5PM1_HAS_I2C_BUS case M5PM1_I2C_DRIVER_BUS: success = M5PM1_I2C_READ_BYTES(_i2c_device, reg, len, data) == ESP_OK; break; +#endif default: success = false; break; @@ -4387,6 +4405,7 @@ m5pm1_err_t M5PM1::switchI2cSpeed(m5pm1_i2c_speed_t speed) } break; } +#if M5PM1_HAS_I2C_BUS case M5PM1_I2C_DRIVER_BUS: if (_i2c_device != nullptr) { ret = i2c_bus_device_delete(&_i2c_device); @@ -4403,6 +4422,7 @@ m5pm1_err_t M5PM1::switchI2cSpeed(m5pm1_i2c_speed_t speed) } } break; +#endif default: M5PM1_LOG_E(TAG, "Unknown I2C driver type"); return M5PM1_ERR_INTERNAL; @@ -4435,12 +4455,14 @@ m5pm1_err_t M5PM1::switchI2cSpeed(m5pm1_i2c_speed_t speed) } break; } +#if M5PM1_HAS_I2C_BUS case M5PM1_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 default: break; } @@ -4487,8 +4509,10 @@ m5pm1_err_t M5PM1::sendWakeSignal() case M5PM1_I2C_DRIVER_SELF_CREATED: case M5PM1_I2C_DRIVER_MASTER: return M5PM1_I2C_MASTER_SEND_WAKE(_i2c_master_bus, _addr) == ESP_OK ? M5PM1_OK : M5PM1_ERR_I2C_COMM; +#if M5PM1_HAS_I2C_BUS case M5PM1_I2C_DRIVER_BUS: return M5PM1_I2C_SEND_WAKE(_i2c_device, M5PM1_REG_HW_REV) == ESP_OK ? M5PM1_OK : M5PM1_ERR_I2C_COMM; +#endif default: return M5PM1_ERR_INTERNAL; } diff --git a/src/M5PM1.h b/src/M5PM1.h index dc65622..c0d2aed 100644 --- a/src/M5PM1.h +++ b/src/M5PM1.h @@ -1061,6 +1061,7 @@ public: m5pm1_err_t begin(i2c_master_bus_handle_t bus, uint8_t addr = M5PM1_DEFAULT_ADDR, uint32_t speed = M5PM1_I2C_FREQ_100K); +#if M5PM1_HAS_I2C_BUS /** * @brief Initialize with existing i2c_bus handle (esp-idf-lib) * @param bus Existing i2c_bus_handle_t @@ -1070,6 +1071,23 @@ public: * Return M5PM1_OK on success, error code otherwise */ m5pm1_err_t begin(i2c_bus_handle_t bus, uint8_t addr = M5PM1_DEFAULT_ADDR, uint32_t speed = M5PM1_I2C_FREQ_100K); +#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 m5pm1_err_t begin(i2c_bus_handle_t bus, uint8_t addr = M5PM1_DEFAULT_ADDR, + uint32_t speed = M5PM1_I2C_FREQ_100K) + { + (void)bus; + (void)addr; + (void)speed; +#if defined(__GNUC__) || defined(__clang__) + _m5pm1_i2c_bus_api_unavailable(); +#endif + return M5PM1_ERR_NOT_SUPPORTED; + } +#endif #endif /** @@ -2870,8 +2888,10 @@ private: // I2C handles i2c_master_bus_handle_t _i2c_master_bus; i2c_master_dev_handle_t _i2c_master_dev; +#if M5PM1_HAS_I2C_BUS i2c_bus_handle_t _i2c_bus; i2c_bus_device_handle_t _i2c_device; +#endif // I2C 管理标志 // I2C management flags diff --git a/src/M5PM1_i2c_compat.h b/src/M5PM1_i2c_compat.h index 62857be..7e006f2 100644 --- a/src/M5PM1_i2c_compat.h +++ b/src/M5PM1_i2c_compat.h @@ -124,8 +124,93 @@ static inline void M5PM1_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 M5PM1_HAS_I2C_BUS 1 // IDF < 5.3.0 + BACKWARD_CONFIG:可用 / available + #else + #define M5PM1_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 M5PM1_HAS_I2C_BUS 1 // BACKWARD_CONFIG:i2c_bus.h 使用 driver/i2c.h,无冲突 / no conflict + #elif defined(_DRIVER_I2C_H_) + #define M5PM1_HAS_I2C_BUS 0 // driver/i2c.h 已提前包含 + 无 BACKWARD_CONFIG → 冲突风险,禁用 + // driver/i2c.h already included + no BACKWARD_CONFIG → conflict risk, disabled + #else + #define M5PM1_HAS_I2C_BUS 1 // driver/i2c.h 尚未包含,无冲突风险 / driver/i2c.h not yet included, no conflict + #endif + #endif +#else + #define M5PM1_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 + +#if M5PM1_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" { @@ -139,7 +224,9 @@ typedef enum { M5PM1_I2C_DRIVER_NONE = 0, // 未初始化 / Not initialized M5PM1_I2C_DRIVER_SELF_CREATED, // 使用 i2c_port_t 自创建 / Self-created using i2c_port_t M5PM1_I2C_DRIVER_MASTER, // ESP-IDF 原生 i2c_master 驱动 / ESP-IDF native i2c_master driver +#if M5PM1_HAS_I2C_BUS M5PM1_I2C_DRIVER_BUS // esp-idf-lib i2c_bus 组件 / esp-idf-lib i2c_bus component +#endif } m5pm1_i2c_driver_t; // ============================ @@ -147,6 +234,8 @@ typedef enum { // ESP-IDF I2C Functions (i2c_bus) // ============================ +#if M5PM1_HAS_I2C_BUS + #ifndef M5PM1_I2C_READ_BYTE static inline esp_err_t M5PM1_I2C_READ_BYTE(i2c_bus_device_handle_t dev, uint8_t reg, uint8_t *data) { @@ -202,11 +291,53 @@ static inline esp_err_t M5PM1_I2C_WRITE_REG16(i2c_bus_device_handle_t dev, uint8 } #endif +// PM1睡眠模式唤醒信号 (i2c_bus) +// Wake signal for PM1 sleep mode (i2c_bus) +#ifndef M5PM1_I2C_SEND_WAKE +static inline esp_err_t M5PM1_I2C_SEND_WAKE(i2c_bus_device_handle_t dev, uint8_t reg) +{ + uint8_t dummy; + return i2c_bus_read_byte(dev, reg, &dummy); +} +#endif + +#else // !M5PM1_HAS_I2C_BUS + +// i2c_bus API 不可用时的编译期报错,可能原因: +// A) ESP-IDF < 5.3.0 且未启用 CONFIG_I2C_BUS_BACKWARD_CONFIG +// B) ESP-IDF >= 5.3.0 且未启用 CONFIG_I2C_BUS_BACKWARD_CONFIG, +// 且 driver/i2c.h 已被其他组件在本头文件之前包含(检测到 _DRIVER_I2C_H_ 已定义), +// i2c_bus.h 自定义的 i2c_config_t 与其冲突。 +// +// Compile-time error when i2c_bus API is unavailable. Possible reasons: +// A) ESP-IDF < 5.3.0 and CONFIG_I2C_BUS_BACKWARD_CONFIG is not enabled. +// B) ESP-IDF >= 5.3.0, CONFIG_I2C_BUS_BACKWARD_CONFIG is not enabled, and +// driver/i2c.h was already included by another component before this header +// (_DRIVER_I2C_H_ was defined), causing i2c_config_t conflict with i2c_bus.h. +#if defined(__GNUC__) || defined(__clang__) +void __attribute__((error( + "M5PM1: i2c_bus API is unavailable. " + "Reason A (ESP-IDF < 5.3.0): i2c_bus requires CONFIG_I2C_BUS_BACKWARD_CONFIG on this IDF version. " + "Reason B (ESP-IDF >= 5.3.0): driver/i2c.h was included before this header and " + "CONFIG_I2C_BUS_BACKWARD_CONFIG is not enabled, causing i2c_config_t conflict. " + "Fix 1: enable CONFIG_I2C_BUS_BACKWARD_CONFIG in menuconfig " + "(Component config -> i2c_bus -> Enable backward compatible config). " + "Fix 2: ensure no other component includes driver/i2c.h before M5PM1 headers (ESP-IDF >= 5.3.0 only). " + "Fix 3: use M5PM1_I2C_DRIVER_MASTER or M5PM1_I2C_DRIVER_SELF_CREATED instead." +))) _m5pm1_i2c_bus_api_unavailable(void); +#endif + +#endif // M5PM1_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 M5PM1_I2C_MASTER_READ_BYTE static inline esp_err_t M5PM1_I2C_MASTER_READ_BYTE(i2c_master_dev_handle_t dev, uint8_t reg, uint8_t *data) { @@ -273,27 +404,17 @@ static inline esp_err_t M5PM1_I2C_MASTER_WRITE_REG16(i2c_master_dev_handle_t dev } #endif -// 使用i2c_bus的PM1睡眠模式唤醒信号 -// Wake signal for PM1 sleep mode using i2c_bus -#ifndef M5PM1_I2C_SEND_WAKE -static inline esp_err_t M5PM1_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的PM1睡眠模式唤醒信号 -// Wake signal for PM1 sleep mode using i2c_master +// PM1睡眠模式唤醒信号 (i2c_master) +// Wake signal for PM1 sleep mode (i2c_master) #ifndef M5PM1_I2C_MASTER_SEND_WAKE static inline esp_err_t M5PM1_I2C_MASTER_SEND_WAKE(i2c_master_bus_handle_t bus, uint8_t addr) { - // Use i2c_master_probe to generate START signal for wake return i2c_master_probe(bus, addr, 10); } #endif +#endif // ESP_IDF_VERSION >= 5.3.0 + #ifdef __cplusplus } #endif