diff --git a/idf_component.yml b/idf_component.yml index 18d25068..a05d1261 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -2,7 +2,7 @@ description: Unified library for M5Stack series issues: https://github.com/m5stack/M5Unified/issues repository: https://github.com/m5stack/M5Unified.git url: https://github.com/m5stack/M5Unified.git -version: 0.2.24 +version: 0.2.25 dependencies: m5stack/m5gfx: version: ">=0.2.31" diff --git a/library.json b/library.json index ee3cdf51..1d5fba4a 100644 --- a/library.json +++ b/library.json @@ -16,7 +16,7 @@ "version": ">=0.2.31" } ], - "version": "0.2.24", + "version": "0.2.25", "frameworks": ["arduino", "espidf", "*"], "platforms": ["espressif32", "native"], "headers": "M5Unified.h" diff --git a/library.properties b/library.properties index 41ad1346..59a8a635 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=M5Unified -version=0.2.24 +version=0.2.25 author=M5Stack maintainer=M5Stack sentence=Unified library for M5Stack series diff --git a/src/M5Unified.hpp b/src/M5Unified.hpp index 60831cab..325aa699 100644 --- a/src/M5Unified.hpp +++ b/src/M5Unified.hpp @@ -356,9 +356,16 @@ namespace m5 // Boards where GPIO46 is exposed to the application (camera VSYNC on CoreS3, camera data on // AtomS3R Cam, header pin on StampS3 / AtomS3R Ext) get the pad restored once the board is // known, see below. + // Skipped when the application already ran Display.init(): GPIO46 may then carry the display + // bus (StopWatch QSPI io2), which this output would overwrite. The hold is applied below, + // once the pin map is known, only on boards that use GPIO46 as their power hold. + const bool gpio46_hold = (Display.getBoard() == m5gfx::board_t::board_unknown); m5gfx::gpio::pin_backup_t gpio46_backup(GPIO_NUM_46); - m5gfx::gpio_hi(GPIO_NUM_46); - m5gfx::pinMode(GPIO_NUM_46, m5gfx::pin_mode_t::output); + if (gpio46_hold) + { + m5gfx::gpio_hi(GPIO_NUM_46); + m5gfx::pinMode(GPIO_NUM_46, m5gfx::pin_mode_t::output); + } #endif auto brightness = Display.getBrightness(); @@ -376,10 +383,15 @@ namespace m5 _board = board; _setup_pinmap(board); #if defined ( CONFIG_IDF_TARGET_ESP32S3 ) + if (!gpio46_hold && getPin(pin_name_t::power_hold) == GPIO_NUM_46) + { + m5gfx::gpio_hi(GPIO_NUM_46); + m5gfx::pinMode(GPIO_NUM_46, m5gfx::pin_mode_t::output); + } // Restore only on boards positively identified as exposing GPIO46 to the application // (camera VSYNC / data, header pin); every other case (power hold, display bus pins // configured by Display.init(), a fallback board) keeps the previous behaviour. - switch (board_detected ? board : board_t::board_unknown) + switch (board_detected && gpio46_hold ? board : board_t::board_unknown) { case board_t::board_M5StackCoreS3: case board_t::board_M5StackCoreS3SE: diff --git a/src/M5Unified.inl b/src/M5Unified.inl index 14281fd1..491f0d9f 100644 --- a/src/M5Unified.inl +++ b/src/M5Unified.inl @@ -145,9 +145,7 @@ static constexpr const uint8_t _pin_table_i2c_ex_in[][5] = { { board_t::board_M5StampC5 , 255 ,255 , 255 ,255 }, { board_t::board_M5ToughC5 , GPIO_NUM_3 ,GPIO_NUM_2 , GPIO_NUM_3 ,GPIO_NUM_2 }, // PortA は内部バスと同一 (レベルシフタ経由の物理分配) { board_t::board_unknown , 255 ,255 , 255 ,255 }, -#elif defined (CONFIG_IDF_TARGET_ESP32S2) -{ board_t::board_unknown , 255 ,255 , 255 ,255 }, -#else +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) { board_t::board_M5Stack , GPIO_NUM_22,GPIO_NUM_21 , GPIO_NUM_22,GPIO_NUM_21 }, { board_t::board_M5Paper , GPIO_NUM_22,GPIO_NUM_21 , GPIO_NUM_32,GPIO_NUM_25 }, { board_t::board_M5TimerCam , GPIO_NUM_14,GPIO_NUM_12 , GPIO_NUM_13,GPIO_NUM_4 }, @@ -157,6 +155,10 @@ static constexpr const uint8_t _pin_table_i2c_ex_in[][5] = { { board_t::board_M5AtomU , GPIO_NUM_21,GPIO_NUM_25 , GPIO_NUM_32,GPIO_NUM_26 }, { board_t::board_M5AtomPsram , GPIO_NUM_21,GPIO_NUM_25 , GPIO_NUM_32,GPIO_NUM_26 }, { board_t::board_unknown , GPIO_NUM_22,GPIO_NUM_21 , GPIO_NUM_33,GPIO_NUM_32 }, // Core2,Tough,StickC,CoreInk,Station,StampPico +#else +// A chip without boards of its own: nothing to probe. Kept explicit so that it never +// inherits the ESP32 table above. +{ board_t::board_unknown , 255 ,255 , 255 ,255 }, #endif }; @@ -180,8 +182,7 @@ static constexpr const uint8_t _pin_table_port_bc[][5] = { { board_t::board_M5Tab5 , GPIO_NUM_17,GPIO_NUM_52, GPIO_NUM_7 ,GPIO_NUM_6 }, // Tab5 #elif defined (CONFIG_IDF_TARGET_ESP32C5) { board_t::board_M5ToughC5 , GPIO_NUM_1 ,GPIO_NUM_6 , GPIO_NUM_12,GPIO_NUM_11 }, -#elif defined (CONFIG_IDF_TARGET_ESP32S2) -#else +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) { board_t::board_M5Stack , GPIO_NUM_36,GPIO_NUM_26 , GPIO_NUM_16,GPIO_NUM_17 }, { board_t::board_M5StackCore2 , GPIO_NUM_36,GPIO_NUM_26 , GPIO_NUM_13,GPIO_NUM_14 }, { board_t::board_M5Tough , GPIO_NUM_36,GPIO_NUM_26 , GPIO_NUM_13,GPIO_NUM_14 }, @@ -202,8 +203,7 @@ static constexpr const uint8_t _pin_table_port_de[][5] = { #elif defined (CONFIG_IDF_TARGET_ESP32C61) #elif defined (CONFIG_IDF_TARGET_ESP32H2) #elif defined (CONFIG_IDF_TARGET_ESP32C5) -#elif defined (CONFIG_IDF_TARGET_ESP32S2) -#else +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) { board_t::board_M5Stack , GPIO_NUM_34,GPIO_NUM_35 , GPIO_NUM_5 ,GPIO_NUM_13 }, { board_t::board_M5StackCore2 , GPIO_NUM_34,GPIO_NUM_35 , GPIO_NUM_27,GPIO_NUM_19 }, { board_t::board_M5Station , GPIO_NUM_36,GPIO_NUM_26 , GPIO_NUM_16,GPIO_NUM_17 }, // B2 / C2 @@ -235,8 +235,7 @@ static constexpr const uint8_t _pin_table_sd[][7] = { { board_t::board_M5Tab5 , GPIO_NUM_43, GPIO_NUM_44, GPIO_NUM_39, GPIO_NUM_40, GPIO_NUM_41, GPIO_NUM_42 }, #elif defined (CONFIG_IDF_TARGET_ESP32C5) { board_t::board_M5ToughC5 , GPIO_NUM_9 , GPIO_NUM_7 , GPIO_NUM_8 , 255 , 255 , GPIO_NUM_10 }, -#elif defined (CONFIG_IDF_TARGET_ESP32S2) -#else +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) { board_t::board_M5Stack , GPIO_NUM_18, GPIO_NUM_23, GPIO_NUM_19, 255 , 255 , GPIO_NUM_4 }, { board_t::board_M5StackCore2 , GPIO_NUM_18, GPIO_NUM_23, GPIO_NUM_38, 255 , 255 , GPIO_NUM_4 }, { board_t::board_M5Tough , GPIO_NUM_18, GPIO_NUM_23, GPIO_NUM_38, 255 , 255 , GPIO_NUM_4 }, @@ -269,8 +268,7 @@ static constexpr const uint8_t _pin_table_other0[][2] = { #elif defined (CONFIG_IDF_TARGET_ESP32H2) { board_t::board_M5NanoH2 , GPIO_NUM_11 }, #elif defined (CONFIG_IDF_TARGET_ESP32C5) -#elif defined (CONFIG_IDF_TARGET_ESP32S2) -#else +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) { board_t::board_M5Stack , GPIO_NUM_15 }, { board_t::board_M5StackCore2 , GPIO_NUM_25 }, { board_t::board_M5Station , GPIO_NUM_4 }, @@ -298,8 +296,7 @@ static constexpr const uint8_t _pin_table_other1[][2] = { #elif defined (CONFIG_IDF_TARGET_ESP32C61) #elif defined (CONFIG_IDF_TARGET_ESP32H2) #elif defined (CONFIG_IDF_TARGET_ESP32C5) -#elif defined (CONFIG_IDF_TARGET_ESP32S2) -#else +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) { board_t::board_M5StickCPlus2 , GPIO_NUM_4 }, { board_t::board_M5Paper , GPIO_NUM_2 }, @@ -420,8 +417,7 @@ static constexpr const uint8_t _pin_table_mbus[][31] = { }, #elif defined (CONFIG_IDF_TARGET_ESP32H2) #elif defined (CONFIG_IDF_TARGET_ESP32C5) -#elif defined (CONFIG_IDF_TARGET_ESP32S2) -#else +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) { board_t::board_M5Stack , 255 , GPIO_NUM_35, 255 , GPIO_NUM_36, diff --git a/src/gitTagVersion.h b/src/gitTagVersion.h index 0ecd0c5b..4a82349b 100644 --- a/src/gitTagVersion.h +++ b/src/gitTagVersion.h @@ -1,4 +1,4 @@ #define M5UNIFIED_VERSION_MAJOR 0 #define M5UNIFIED_VERSION_MINOR 2 -#define M5UNIFIED_VERSION_PATCH 24 +#define M5UNIFIED_VERSION_PATCH 25 #define M5UNIFIED_VERSION F( M5UNIFIED_VERSION_MAJOR "." M5UNIFIED_VERSION_MINOR "." M5UNIFIED_VERSION_PATCH ) diff --git a/src/utility/Mic_Class.inl b/src/utility/Mic_Class.inl index 869c8c8d..daa2cecf 100644 --- a/src/utility/Mic_Class.inl +++ b/src/utility/Mic_Class.inl @@ -317,13 +317,18 @@ namespace m5 if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { i2s_pdm_rx_config_t i2s_config; memset(&i2s_config, 0, sizeof(i2s_pdm_rx_config_t)); -#if defined ( CONFIG_IDF_TARGET_ESP32P4 ) || defined (CONFIG_IDF_TARGET_ESP32H2) i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_DEFAULT; -#else - i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_PLL_160M; -#endif +#if defined ( M5UNIFIED_I2S_PLL_D2_HZ ) i2s_config.clk_cfg.sample_rate_hz = 48000; // dummy setting i2s_config.clk_cfg.mclk_multiple = i2s_mclk_multiple_t::I2S_MCLK_MULTIPLE_128; // dummy setting +#else + // No verified raw clock recipe on this chip: the driver keeps the clock, so the real + // port rate (over_sampling included, as the capture task expects) goes in here. + // DSR 16 matches what the raw path selects. + i2s_config.clk_cfg.sample_rate_hz = _calc_rec_rate(); + i2s_config.clk_cfg.mclk_multiple = i2s_mclk_multiple_t::I2S_MCLK_MULTIPLE_256; + i2s_config.clk_cfg.dn_sample_mode = i2s_pdm_dsr_t::I2S_PDM_DSR_16S; +#endif i2s_config.slot_cfg.data_bit_width = i2s_data_bit_width_t::I2S_DATA_BIT_WIDTH_16BIT; i2s_config.slot_cfg.slot_bit_width = i2s_slot_bit_width_t::I2S_SLOT_BIT_WIDTH_16BIT; i2s_config.slot_cfg.slot_mode = (_cfg.stereo) ? i2s_slot_mode_t::I2S_SLOT_MODE_STEREO : i2s_slot_mode_t::I2S_SLOT_MODE_MONO; @@ -336,14 +341,9 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { { i2s_std_config_t i2s_config; memset(&i2s_config, 0, sizeof(i2s_std_config_t)); -#if defined ( CONFIG_IDF_TARGET_ESP32P4 ) || defined (CONFIG_IDF_TARGET_ESP32H2) i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_DEFAULT; -#else - i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_PLL_160M; -#endif -#if defined ( CONFIG_IDF_TARGET_ESP32P4 ) - { // ESP32-P4 はクロックをドライバ管理で最終値に確定させる (mic_task での raw 分周 - // 上書きを行わない)。クロック源既定 (minimum supported revision < 3 のビルドは +#if M5UNIFIED_I2S_DRIVER_MANAGED_CLK + { // クロックをドライバ管理で最終値に確定させる (mic_task での raw 分周上書きを行わない)。クロック源既定 (minimum supported revision < 3 のビルドは // XTAL 40MHz / rev >= 3 ビルドは PLL_F160M) もドライバに委ねる。 int os = _cfg.over_sampling; if (os < 1) { os = 1; } else if (os > 8) { os = 8; } @@ -458,6 +458,7 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { bool use_pdm = (self->_cfg.pin_bck < 0 && !self->_cfg.use_adc); +#if defined ( M5UNIFIED_I2S_PLL_D2_HZ ) static constexpr uint32_t PLL_D2_CLK = M5UNIFIED_I2S_PLL_D2_HZ; uint32_t bits = (self->_cfg.use_adc) ? 1 : 16; /// 1サンプリング当たりの出力ビット数; @@ -473,6 +474,7 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { // read here: a queued record() with a new rate may overwrite it while // this request is still being captured. calcClockDiv(&div_a, &div_b, &div_n, PLL_D2_CLK / (bits * div_m), self->_rec_sample_rate); +#endif // false when the raw clock latch below times out: the channel must not // report itself active with an unlatched clock configuration. @@ -489,7 +491,10 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { #endif #endif -#if defined (M5UNIFIED_I2S_HW_V2) +#if !defined ( M5UNIFIED_I2S_PLL_D2_HZ ) + // No verified raw recipe for this chip: the driver keeps the mode and clock it configured. + (void)dev; (void)use_pdm; +#elif defined (M5UNIFIED_I2S_HW_V2) dev->rx_conf.rx_pdm_en = use_pdm; dev->rx_conf.rx_tdm_en = !use_pdm; @@ -500,9 +505,9 @@ if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) { dev->rx_conf.rx_pdm2pcm_en = use_pdm; dev->rx_conf.rx_pdm_sinc_dsr_16_en = 1; #endif -#if defined (CONFIG_IDF_TARGET_ESP32P4) - // ESP32-P4 の std 経路はクロックを _setup_i2s でドライバ管理により最終値に - // 設定済みのため、raw 分周の上書きを行わない (PDM 経路は従来どおり)。 +#if M5UNIFIED_I2S_DRIVER_MANAGED_CLK + // std 経路はクロックを _setup_i2s でドライバ管理により最終値に設定済みのため、 + // raw 分周の上書きを行わない (PDM 経路は従来どおり)。 const bool skip_raw_clk = !use_pdm; #else const bool skip_raw_clk = false; diff --git a/src/utility/Power_Class.hpp b/src/utility/Power_Class.hpp index 708e7f8d..eb66396e 100644 --- a/src/utility/Power_Class.hpp +++ b/src/utility/Power_Class.hpp @@ -132,9 +132,12 @@ namespace m5 void setLed(uint8_t brightness = 255); /// all power off. + /// @note PowerHub enters light sleep after requesting power-off and restarts if it wakes. void powerOff(void); /// sleep and timer boot. The boot condition can be specified by the argument. + /// @note PowerHub paces alarm writes by 50 ms, settles for 500 ms before requesting + /// power-off, then enters light sleep and restarts if it wakes. /// @param seconds Number of seconds to boot. void timerSleep(int seconds); diff --git a/src/utility/Power_Class.inl b/src/utility/Power_Class.inl index 2ab16458..f201de0f 100644 --- a/src/utility/Power_Class.inl +++ b/src/utility/Power_Class.inl @@ -1693,11 +1693,18 @@ namespace m5 #if defined (CONFIG_IDF_TARGET_ESP32S3) case board_t::board_M5PowerHub: - uint8_t buf[6]={}; - M5.In_I2C.writeRegister(powerhub_i2c_addr, 0x00, buf, sizeof(buf), i2c_freq); - M5.In_I2C.writeRegister8(powerhub_i2c_addr, 0xE0, 1, i2c_freq); - use_deepsleep = false; - break; + { + // The STM32 arms the RTC alarm asynchronously after acknowledging the writes. + // Let it finish before the power-off request can overtake the alarm update. + static constexpr uint32_t powerhub_alarm_settle_ms = 500; + if (withTimer) { m5gfx::delay(powerhub_alarm_settle_ms); } + uint8_t buf[6]={}; + M5.In_I2C.writeRegister(powerhub_i2c_addr, 0x00, buf, sizeof(buf), i2c_freq); + M5.In_I2C.writeRegister8(powerhub_i2c_addr, 0xE0, 1, i2c_freq); + // Do not resume the sketch with outputs off and the alarm still armed. + use_deepsleep = false; + break; + } #endif } diff --git a/src/utility/Speaker_Class.inl b/src/utility/Speaker_Class.inl index 9c3899fb..cc8e0ef8 100644 --- a/src/utility/Speaker_Class.inl +++ b/src/utility/Speaker_Class.inl @@ -202,14 +202,12 @@ namespace m5 i2s_std_config_t i2s_config; memset(&i2s_config, 0, sizeof(i2s_std_config_t)); -#if defined ( CONFIG_IDF_TARGET_ESP32H2 ) || defined ( CONFIG_IDF_TARGET_ESP32P4 ) + // On every chip whose raw divider path runs (M5UNIFIED_I2S_PLL_D2_HZ), I2S_CLK_SRC_DEFAULT + // is the PLL_160M source that path expects, so no chip list is needed here. (The ESP32-P4 + // default is chosen at run time, which is why its clock stays driver managed.) i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_DEFAULT; -#else - i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_PLL_160M; -#endif -#if defined ( CONFIG_IDF_TARGET_ESP32P4 ) - // ESP32-P4 はクロックをドライバ管理で最終値に確定させる (spk_task での raw 分周 - // 上書きを行わない)。ここで実レートを渡すことで、begin 中のクロック遷移が +#if M5UNIFIED_I2S_DRIVER_MANAGED_CLK + // クロックをドライバ管理で最終値に確定させる (spk_task での raw 分周上書きを行わない)。ここで実レートを渡すことで、begin 中のクロック遷移が // 一度きりになり、外付け codec (Tab5=ES8388) のロック失敗を防ぐ。 // クロック源既定 (minimum supported revision < 3 のビルドは XTAL 40MHz / // rev >= 3 ビルドは PLL_F160M) もドライバに委ねる。 @@ -400,7 +398,7 @@ namespace m5 #else const i2s_port_t i2s_port = self->_cfg.i2s_port; -#if defined (CONFIG_IDF_TARGET_ESP32P4) +#if M5UNIFIED_I2S_DRIVER_MANAGED_CLK // クロックは _setup_i2s でドライバ管理により最終値に設定済み (実レート + // 256fs、40MHz source ビルドの高レートのみ 128fs)。 // ドライバの分数分周 (a/b ≤ 511) は十分高精度のため、レート換算は公称値でよい。 @@ -537,7 +535,7 @@ namespace m5 dev->conf.tx_fifo_reset = 0; #endif -#endif // !CONFIG_IDF_TARGET_ESP32P4 (raw クロック設定ブロック全体) +#endif // !M5UNIFIED_I2S_DRIVER_MANAGED_CLK (raw クロック設定ブロック全体) // i2s_zero_dma_buffer(i2s_port); enum spk_i2s_state diff --git a/src/utility/m5unified_i2s.h b/src/utility/m5unified_i2s.h index 0be333f0..f56c6008 100644 --- a/src/utility/m5unified_i2s.h +++ b/src/utility/m5unified_i2s.h @@ -65,19 +65,25 @@ /// Source clock frequency assumed by the raw clock divider setup in the speaker/mic /// tasks (the frequency selected by tx/rx_clk_sel = 1 on HW v2, PLL_160M on HW v1). /// This is a per-chip physical property that cannot be derived from a capability -/// macro, so every known target is enumerated explicitly. +/// macro, so it is only defined for the targets on which it has been verified; on +/// any other chip the raw divider is not written and the driver keeps the clock it +/// configured (M5UNIFIED_I2S_DRIVER_MANAGED_CLK). #if defined ( CONFIG_IDF_TARGET_ESP32C3 ) || defined ( CONFIG_IDF_TARGET_ESP32C6 ) || defined ( CONFIG_IDF_TARGET_ESP32C5 ) || defined ( CONFIG_IDF_TARGET_ESP32C61 ) || defined ( CONFIG_IDF_TARGET_ESP32S3 ) #define M5UNIFIED_I2S_PLL_D2_HZ (120*1000*1000) // 240 MHz/2 #elif defined ( CONFIG_IDF_TARGET_ESP32P4 ) - #define M5UNIFIED_I2S_PLL_D2_HZ (20*1000*1000) // 20 MHz -#elif defined ( CONFIG_IDF_TARGET_ESP32H2 ) || defined ( CONFIG_IDF_TARGET_ESP32H4 ) + #define M5UNIFIED_I2S_PLL_D2_HZ (20*1000*1000) // 20 MHz (PDM capture only; the std path is driver managed) +#elif defined ( CONFIG_IDF_TARGET_ESP32H2 ) #define M5UNIFIED_I2S_PLL_D2_HZ (96*1000*1000) // PLL_F96M -#else - /// Unknown I2S-capable targets fall back to the HW v1 value. The message below is - /// intentionally not #warning (which fails -Werror builds); it flags that the - /// frequency must be verified and added to the table above. - #if defined (M5UNIFIED_I2S_PORT_COUNT) && defined (CONFIG_IDF_TARGET) && !defined (CONFIG_IDF_TARGET_ESP32) && !defined (CONFIG_IDF_TARGET_ESP32S2) - #pragma message ("M5Unified: unknown target, assuming a 80 MHz I2S source clock. Verify it and extend the table in m5unified_i2s.h") - #endif +#elif !defined ( CONFIG_IDF_TARGET ) || defined ( CONFIG_IDF_TARGET_ESP32 ) || defined ( CONFIG_IDF_TARGET_ESP32S2 ) #define M5UNIFIED_I2S_PLL_D2_HZ (80*1000*1000) // 160 MHz/2 #endif + +/// The speaker/mic std paths leave the sample clock to the driver (real rate passed at +/// setup, no raw divider override) on chips whose clock tree the raw path has not been +/// verified against, and on ESP32-P4 where the one-shot clock transition is required +/// by the external codec. +#if defined ( CONFIG_IDF_TARGET_ESP32P4 ) || !defined ( M5UNIFIED_I2S_PLL_D2_HZ ) + #define M5UNIFIED_I2S_DRIVER_MANAGED_CLK 1 +#else + #define M5UNIFIED_I2S_DRIVER_MANAGED_CLK 0 +#endif diff --git a/src/utility/rtc/RTC_PowerHub_Class.inl b/src/utility/rtc/RTC_PowerHub_Class.inl index 2731f870..9d3801e1 100644 --- a/src/utility/rtc/RTC_PowerHub_Class.inl +++ b/src/utility/rtc/RTC_PowerHub_Class.inl @@ -10,6 +10,10 @@ namespace m5 { + // The STM32 applies D0..D3 from its main loop after acknowledging I2C writes. + // Pace the writes so an older update cannot clear the pending flag for a newer one. + static constexpr std::uint32_t powerhub_alarm_apply_wait_ms = 50; + static std::uint8_t weekdayToPowerHub(std::int8_t weekDay) { static constexpr std::uint8_t weekDayTable[] = { 1, 2, 4, 8, 10, 20, 40 }; @@ -113,21 +117,24 @@ namespace m5 } } - if (date->date >= 0) + // A time-only alarm has no date; zero in D2 means every day. + if (date && date->date >= 0) { irq_enable = true; buf[2] = date->date & 0x1f; } writeRegister(0xD0, buf, 3); + m5gfx::delay(powerhub_alarm_apply_wait_ms); if (irq_enable) { bitOn(0xB0, irq_enable); bitOn(0xD3, irq_enable); } else { bitOff(0xB0, irq_enable); - bitOff(0xD3, irq_enable); + writeRegister8(0xD3, 0); // A zero bit mask would leave the alarm enabled. } + m5gfx::delay(powerhub_alarm_apply_wait_ms); return irq_enable; } @@ -142,11 +149,13 @@ namespace m5 if (!_init) { return; } static constexpr const std::uint8_t buf[4] = {}; writeRegister(0xD0, buf, sizeof(buf)); + m5gfx::delay(powerhub_alarm_apply_wait_ms); } void RTC_PowerHub_Class::disableIRQ(void) { if (!_init) { return; } - bitOff(0xD3, 0); // disable alarm + writeRegister8(0xD3, 0); // Clear the enable register; bitOff with a zero mask is a no-op. + m5gfx::delay(powerhub_alarm_apply_wait_ms); } }