From f4fc6f87111a71035b8089d1c8636ed256c2258e Mon Sep 17 00:00:00 2001 From: tzyt Date: Tue, 26 May 2026 21:03:47 -0500 Subject: [PATCH 01/10] initial commit for SDM driver --- esp-hal/README.md | 3 +- esp-hal/src/lib.rs | 2 + .../src/_build_script_utils.rs | 67 +++++++++++++++ .../src/_generated_esp32.rs | 34 ++++++++ .../src/_generated_esp32c3.rs | 35 ++++++++ .../src/_generated_esp32c5.rs | 35 ++++++++ .../src/_generated_esp32c6.rs | 35 ++++++++ .../src/_generated_esp32h2.rs | 35 ++++++++ .../src/_generated_esp32p4.rs | 83 ++++++++++++++----- .../src/_generated_esp32s2.rs | 39 +++++++++ .../src/_generated_esp32s3.rs | 39 +++++++++ esp-metadata/devices/esp32.toml | 6 +- esp-metadata/devices/esp32c3.toml | 6 +- esp-metadata/devices/esp32c5.toml | 6 +- esp-metadata/devices/esp32c6.toml | 6 +- esp-metadata/devices/esp32h2.toml | 6 +- esp-metadata/devices/esp32p4.toml | 7 +- esp-metadata/devices/esp32s2.toml | 6 +- esp-metadata/devices/esp32s3.toml | 6 +- esp-metadata/src/cfg.rs | 12 ++- 20 files changed, 437 insertions(+), 31 deletions(-) diff --git a/esp-hal/README.md b/esp-hal/README.md index ee66f4763bd..6eec34ae65a 100644 --- a/esp-hal/README.md +++ b/esp-hal/README.md @@ -101,7 +101,7 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a | SDIO host | ❌ | | | | | | | ❌ | | ❌ | | SDIO slave | ❌ | | | [❌][5169] [^1] | ❌ | [❌][5417] [^1] | | ❌ | | | | SHA | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ❌ | ⚒️ | ⚒️ | -| SDM | [❌][2370] [^1] | | [❌][2370] [^1] | [❌][2370] [^1] | [❌][2370] [^1] | | [❌][2370] [^1] | [❌][2370] [^1] | [❌][2370] [^1] | [❌][2370] [^1] | +| SDM | ✔️ | | ✔️ | ✔️ | ✔️ | | ✔️ | ✔️ | ✔️ | ✔️ | | Light/deep sleep | ⚒️ | ⚒️ | ⚒️ | [❌][5165] [^1] | ⚒️ | [❌][5424] [^1] | ⚒️ | ❌ | ⚒️ | ⚒️ | | SPI master | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ⚒️ | ✔️ | ✔️ | | SPI slave | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ❌ | ⚒️ | ⚒️ | @@ -128,7 +128,6 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a [884]: https://github.com/esp-rs/esp-hal/issues/884 [1905]: https://github.com/esp-rs/esp-hal/issues/1905 [1909]: https://github.com/esp-rs/esp-hal/issues/1909 -[2370]: https://github.com/esp-rs/esp-hal/issues/2370 [5153]: https://github.com/esp-rs/esp-hal/issues/5153 [5154]: https://github.com/esp-rs/esp-hal/issues/5154 [5155]: https://github.com/esp-rs/esp-hal/issues/5155 diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 56fb7b7d939..79867386157 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -389,6 +389,8 @@ unstable_driver! { pub mod rsa; #[cfg(sha_driver_supported)] pub mod sha; + #[cfg(sdm_driver_supported)] + pub mod sdm; #[cfg(touch)] pub mod touch; #[cfg(soc_has_trace0)] diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index 061a8d76342..4b20df73400 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -294,6 +294,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -357,6 +358,8 @@ impl Chip { "rsa_version=\"1\"", "rsa_size_increment=\"512\"", "rsa_memory_size_bytes=\"512\"", + "sdm_clock_sources_apb", + "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_multi_core_enabled", @@ -505,6 +508,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -568,6 +572,8 @@ impl Chip { "cargo:rustc-cfg=rsa_version=\"1\"", "cargo:rustc-cfg=rsa_size_increment=\"512\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"512\"", + "cargo:rustc-cfg=sdm_clock_sources_apb", + "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_multi_core_enabled", @@ -1297,6 +1303,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -1379,6 +1386,8 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_apb", + "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_cpu_has_csr_pc", @@ -1512,6 +1521,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -1594,6 +1604,8 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_apb", + "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_cpu_has_csr_pc", @@ -1868,6 +1880,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "soc_driver_supported", "spi_master_driver_supported", "spi_slave_driver_supported", @@ -1961,6 +1974,9 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_xtal", + "sdm_clock_sources_pll_f80m", + "sdm_default_clock_source=\"pll_f80m\"", "soc_cpu_has_branch_predictor", "soc_cpu_csr_prv_mode=\"2064\"", "soc_cpu_csr_prv_mode_is_set", @@ -2131,6 +2147,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", "cargo:rustc-cfg=spi_slave_driver_supported", @@ -2224,6 +2241,9 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_xtal", + "cargo:rustc-cfg=sdm_clock_sources_pll_f80m", + "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f80m\"", "cargo:rustc-cfg=soc_cpu_has_branch_predictor", "cargo:rustc-cfg=soc_cpu_csr_prv_mode=\"2064\"", "cargo:rustc-cfg=soc_cpu_csr_prv_mode_is_set", @@ -2550,6 +2570,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -2642,6 +2663,9 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_xtal", + "sdm_clock_sources_pll_f80m", + "sdm_default_clock_source=\"pll_f80m\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_cpu_has_csr_pc", @@ -2835,6 +2859,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -2927,6 +2952,9 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_xtal", + "cargo:rustc-cfg=sdm_clock_sources_pll_f80m", + "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f80m\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_cpu_has_csr_pc", @@ -3799,6 +3827,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -3891,6 +3920,9 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_xtal", + "sdm_clock_sources_pll_f48m", + "sdm_default_clock_source=\"pll_f48m\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_cpu_has_csr_pc", @@ -4051,6 +4083,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -4143,6 +4176,9 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_xtal", + "cargo:rustc-cfg=sdm_clock_sources_pll_f48m", + "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f48m\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_cpu_has_csr_pc", @@ -4304,6 +4340,7 @@ impl Chip { "multi_core", "soc_has_efuse", "soc_has_gpio", + "soc_has_gpio_sd", "soc_has_system", "soc_has_hp_sys", "soc_has_hp_sys_clkrst", @@ -4372,6 +4409,7 @@ impl Chip { "interrupts_driver_supported", "psram_driver_supported", "rng_driver_supported", + "sdm_driver_supported", "soc_driver_supported", "spi_master_driver_supported", "systimer_driver_supported", @@ -4432,6 +4470,9 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_xtal", + "sdm_clock_sources_pll_f80m", + "sdm_default_clock_source=\"pll_f80m\"", "soc_cpu_has_branch_predictor", "soc_multi_core_enabled", "soc_rc_fast_clk_default=\"20000000\"", @@ -4481,6 +4522,7 @@ impl Chip { "cargo:rustc-cfg=multi_core", "cargo:rustc-cfg=soc_has_efuse", "cargo:rustc-cfg=soc_has_gpio", + "cargo:rustc-cfg=soc_has_gpio_sd", "cargo:rustc-cfg=soc_has_system", "cargo:rustc-cfg=soc_has_hp_sys", "cargo:rustc-cfg=soc_has_hp_sys_clkrst", @@ -4549,6 +4591,7 @@ impl Chip { "cargo:rustc-cfg=interrupts_driver_supported", "cargo:rustc-cfg=psram_driver_supported", "cargo:rustc-cfg=rng_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", "cargo:rustc-cfg=systimer_driver_supported", @@ -4609,6 +4652,9 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_xtal", + "cargo:rustc-cfg=sdm_clock_sources_pll_f80m", + "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f80m\"", "cargo:rustc-cfg=soc_cpu_has_branch_predictor", "cargo:rustc-cfg=soc_multi_core_enabled", "cargo:rustc-cfg=soc_rc_fast_clk_default=\"20000000\"", @@ -4989,6 +5035,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -5071,6 +5118,8 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"512\"", "sha_dma", + "sdm_clock_sources_apb", + "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_rc_fast_clk_default=\"8500000\"", @@ -5215,6 +5264,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -5297,6 +5347,8 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"512\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_apb", + "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_rc_fast_clk_default=\"8500000\"", @@ -5651,6 +5703,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -5747,6 +5800,8 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"512\"", "sha_dma", + "sdm_clock_sources_apb", + "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_multi_core_enabled", @@ -5910,6 +5965,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -6006,6 +6062,8 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"512\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_apb", + "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_multi_core_enabled", @@ -6413,6 +6471,7 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(rsa_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(lp_timer_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(sha_driver_supported)"); + println!("cargo:rustc-check-cfg=cfg(sdm_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(sleep_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(soc_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(spi_master_driver_supported)"); @@ -6450,6 +6509,7 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(phy_combo_module)"); println!("cargo:rustc-check-cfg=cfg(rmt_has_per_channel_clock)"); println!("cargo:rustc-check-cfg=cfg(rng_trng_supported)"); + println!("cargo:rustc-check-cfg=cfg(sdm_clock_sources_apb)"); println!("cargo:rustc-check-cfg=cfg(sleep_light_sleep)"); println!("cargo:rustc-check-cfg=cfg(sleep_deep_sleep)"); println!("cargo:rustc-check-cfg=cfg(soc_multi_core_enabled)"); @@ -6640,6 +6700,8 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(i2c_master_has_reliable_fsm_reset)"); println!("cargo:rustc-check-cfg=cfg(i2s_clock_configured_by_pcr)"); println!("cargo:rustc-check-cfg=cfg(rmt_has_tx_loop_auto_stop)"); + println!("cargo:rustc-check-cfg=cfg(sdm_clock_sources_xtal)"); + println!("cargo:rustc-check-cfg=cfg(sdm_clock_sources_pll_f80m)"); println!("cargo:rustc-check-cfg=cfg(soc_cpu_has_branch_predictor)"); println!("cargo:rustc-check-cfg=cfg(soc_cpu_csr_prv_mode_is_set)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f12m)"); @@ -6693,6 +6755,7 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(soc_has_mem2mem10)"); println!("cargo:rustc-check-cfg=cfg(soc_has_mem2mem11)"); println!("cargo:rustc-check-cfg=cfg(esp32h2)"); + println!("cargo:rustc-check-cfg=cfg(sdm_clock_sources_pll_f48m)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f96m_clk)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f64m_clk)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f48m_clk)"); @@ -6805,6 +6868,10 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(rsa_version, values(\"1\",\"3\",\"2\"))"); println!("cargo:rustc-check-cfg=cfg(rsa_size_increment, values(\"512\",\"32\"))"); println!("cargo:rustc-check-cfg=cfg(rsa_memory_size_bytes, values(\"512\",\"384\"))"); + println!( + "cargo:rustc-check-cfg=cfg(sdm_default_clock_source, \ + values(\"apb\",\"pll_f80m\",\"pll_f48m\"))" + ); println!( "cargo:rustc-check-cfg=cfg(soc_rc_fast_clk_default, \ values(\"8500000\",\"17500000\",\"20000000\"))" diff --git a/esp-metadata-generated/src/_generated_esp32.rs b/esp-metadata-generated/src/_generated_esp32.rs index 821b93f6c20..3e94fa0ffe6 100644 --- a/esp-metadata-generated/src/_generated_esp32.rs +++ b/esp-metadata-generated/src/_generated_esp32.rs @@ -301,6 +301,15 @@ macro_rules! property { ("sha.dma") => { false }; + ("sdm.channel_count") => { + 8 + }; + ("sdm.channel_count", str) => { + stringify!(8) + }; + ("sdm.default_clock_source") => { + "apb" + }; ("sleep.light_sleep") => { true }; @@ -552,6 +561,22 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((4, GPIO_SD4)); _for_each_inner_sdm_channel!((5, + GPIO_SD5)); _for_each_inner_sdm_channel!((6, GPIO_SD6)); + _for_each_inner_sdm_channel!((7, GPIO_SD7)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3), (4, GPIO_SD4), (5, GPIO_SD5), (6, GPIO_SD6), (7, + GPIO_SD7))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3085,6 +3110,8 @@ macro_rules! implement_peripheral_clocks { Aes, /// EMAC peripheral clock signal Emac, + /// GPIO_SD peripheral clock signal + GpioSd, /// I2C_EXT0 peripheral clock signal I2cExt0, /// I2C_EXT1 peripheral clock signal @@ -3138,6 +3165,7 @@ macro_rules! implement_peripheral_clocks { const ALL: &[Self] = &[ Self::Aes, Self::Emac, + Self::GpioSd, Self::I2cExt0, Self::I2cExt1, Self::I2s0, @@ -3175,6 +3203,9 @@ macro_rules! implement_peripheral_clocks { .wifi_clk_en() .modify(|_, w| w.emac_clk_en().bit(enable)); } + Peripheral::GpioSd => { + let _ = enable; + } Peripheral::I2cExt0 => { crate::peripherals::SYSTEM::regs() .perip_clk_en() @@ -3304,6 +3335,9 @@ macro_rules! implement_peripheral_clocks { .wifi_rst_en() .modify(|_, w| w.emac_rst().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::I2cExt0 => { crate::peripherals::SYSTEM::regs() .perip_rst_en() diff --git a/esp-metadata-generated/src/_generated_esp32c3.rs b/esp-metadata-generated/src/_generated_esp32c3.rs index 7a0abffaad8..8ec1bc2ac10 100644 --- a/esp-metadata-generated/src/_generated_esp32c3.rs +++ b/esp-metadata-generated/src/_generated_esp32c3.rs @@ -319,6 +319,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 4 + }; + ("sdm.channel_count", str) => { + stringify!(4) + }; + ("sdm.default_clock_source") => { + "apb" + }; ("sleep.light_sleep") => { true }; @@ -846,6 +855,18 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3077,6 +3098,8 @@ macro_rules! implement_peripheral_clocks { Dma, /// DS peripheral clock signal Ds, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -3128,6 +3151,7 @@ macro_rules! implement_peripheral_clocks { Self::ApbSarAdc, Self::Dma, Self::Ds, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2s0, @@ -3170,6 +3194,14 @@ macro_rules! implement_peripheral_clocks { .perip_clk_en1() .modify(|_, w| w.crypto_ds_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_clk_en1() @@ -3284,6 +3316,9 @@ macro_rules! implement_peripheral_clocks { .perip_rst_en1() .modify(|_, w| w.crypto_ds_rst().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_rst_en1() diff --git a/esp-metadata-generated/src/_generated_esp32c5.rs b/esp-metadata-generated/src/_generated_esp32c5.rs index dd96b298df0..4ddf4075894 100644 --- a/esp-metadata-generated/src/_generated_esp32c5.rs +++ b/esp-metadata-generated/src/_generated_esp32c5.rs @@ -355,6 +355,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 4 + }; + ("sdm.channel_count", str) => { + stringify!(4) + }; + ("sdm.default_clock_source") => { + "pll_f80m" + }; ("sleep.light_sleep") => { false }; @@ -981,6 +990,18 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3250,6 +3271,8 @@ macro_rules! implement_peripheral_clocks { Dma, /// ECC peripheral clock signal Ecc, + /// GPIO_SD peripheral clock signal + GpioSd, /// I2C_EXT0 peripheral clock signal I2cExt0, /// I2S0 peripheral clock signal @@ -3295,6 +3318,7 @@ macro_rules! implement_peripheral_clocks { Self::ApbSarAdc, Self::Dma, Self::Ecc, + Self::GpioSd, Self::I2cExt0, Self::I2s0, Self::ParlIo, @@ -3334,6 +3358,14 @@ macro_rules! implement_peripheral_clocks { .ecc_conf() .modify(|_, w| w.ecc_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.sigmadelta_clk_en().bit(enable)); + } Peripheral::I2cExt0 => { crate::peripherals::SYSTEM::regs() .i2c0_conf() @@ -3437,6 +3469,9 @@ macro_rules! implement_peripheral_clocks { .ecc_conf() .modify(|_, w| w.ecc_rst_en().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::I2cExt0 => { crate::peripherals::SYSTEM::regs() .i2c0_conf() diff --git a/esp-metadata-generated/src/_generated_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index 47deb8a42fc..b1369617b4c 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -346,6 +346,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 4 + }; + ("sdm.channel_count", str) => { + stringify!(4) + }; + ("sdm.default_clock_source") => { + "pll_f80m" + }; ("sleep.light_sleep") => { true }; @@ -918,6 +927,18 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3952,6 +3973,8 @@ macro_rules! implement_peripheral_clocks { Ecc, /// ETM peripheral clock signal Etm, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -4015,6 +4038,7 @@ macro_rules! implement_peripheral_clocks { Self::Ds, Self::Ecc, Self::Etm, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2s0, @@ -4072,6 +4096,14 @@ macro_rules! implement_peripheral_clocks { .etm_conf() .modify(|_, w| w.etm_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .hmac_conf() @@ -4231,6 +4263,9 @@ macro_rules! implement_peripheral_clocks { .etm_conf() .modify(|_, w| w.etm_rst_en().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .hmac_conf() diff --git a/esp-metadata-generated/src/_generated_esp32h2.rs b/esp-metadata-generated/src/_generated_esp32h2.rs index 0f74b18f8d3..d1d4b75ffcb 100644 --- a/esp-metadata-generated/src/_generated_esp32h2.rs +++ b/esp-metadata-generated/src/_generated_esp32h2.rs @@ -334,6 +334,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 4 + }; + ("sdm.channel_count", str) => { + stringify!(4) + }; + ("sdm.default_clock_source") => { + "pll_f48m" + }; ("sleep.light_sleep") => { true }; @@ -898,6 +907,18 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -2959,6 +2980,8 @@ macro_rules! implement_peripheral_clocks { Ecdsa, /// ETM peripheral clock signal Etm, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -3016,6 +3039,7 @@ macro_rules! implement_peripheral_clocks { Self::Ecc, Self::Ecdsa, Self::Etm, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2cExt1, @@ -3077,6 +3101,14 @@ macro_rules! implement_peripheral_clocks { .etm_conf() .modify(|_, w| w.etm_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .hmac_conf() @@ -3233,6 +3265,9 @@ macro_rules! implement_peripheral_clocks { .etm_conf() .modify(|_, w| w.etm_rst_en().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .hmac_conf() diff --git a/esp-metadata-generated/src/_generated_esp32p4.rs b/esp-metadata-generated/src/_generated_esp32p4.rs index 0e7c23b004b..1980c22d5d0 100644 --- a/esp-metadata-generated/src/_generated_esp32p4.rs +++ b/esp-metadata-generated/src/_generated_esp32p4.rs @@ -274,6 +274,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 8 + }; + ("sdm.channel_count", str) => { + stringify!(8) + }; + ("sdm.default_clock_source") => { + "pll_f80m" + }; ("sleep.light_sleep") => { false }; @@ -830,6 +839,22 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((4, GPIO_SD4)); _for_each_inner_sdm_channel!((5, + GPIO_SD5)); _for_each_inner_sdm_channel!((6, GPIO_SD6)); + _for_each_inner_sdm_channel!((7, GPIO_SD7)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3), (4, GPIO_SD4), (5, GPIO_SD5), (6, GPIO_SD6), (7, + GPIO_SD7))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -2689,6 +2714,8 @@ macro_rules! implement_peripheral_clocks { Emac, /// GDMA peripheral clock signal Gdma, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C0 peripheral clock signal @@ -2778,6 +2805,7 @@ macro_rules! implement_peripheral_clocks { Self::Ecdsa, Self::Emac, Self::Gdma, + Self::GpioSd, Self::Hmac, Self::I2c0, Self::I2c1, @@ -2866,6 +2894,14 @@ macro_rules! implement_peripheral_clocks { .soc_clk_ctrl1() .modify(|_, w| w.gdma_sys_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::HP_SYS_CLKRST::regs() .soc_clk_ctrl1() @@ -3114,6 +3150,9 @@ macro_rules! implement_peripheral_clocks { .hp_rst_en0() .modify(|_, w| w.rst_en_gdma().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::HP_SYS_CLKRST::regs() .hp_rst_en2() @@ -3616,10 +3655,11 @@ macro_rules! for_each_peripheral { _for_each_inner_peripheral!((@ peri_type #[doc = "EFUSE peripheral singleton"] EFUSE <= EFUSE() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "GPIO peripheral singleton"] GPIO <= GPIO() (unstable))); - _for_each_inner_peripheral!((@ peri_type #[doc = "SYSTEM peripheral singleton"] - SYSTEM <= HP_SYS() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = - "HP_SYS peripheral singleton"] HP_SYS <= HP_SYS() (unstable))); - _for_each_inner_peripheral!((@ peri_type #[doc = + _for_each_inner_peripheral!((@ peri_type #[doc = "GPIO_SD peripheral singleton"] + GPIO_SD <= GPIO_SD() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc + = "SYSTEM peripheral singleton"] SYSTEM <= HP_SYS() (unstable))); + _for_each_inner_peripheral!((@ peri_type #[doc = "HP_SYS peripheral singleton"] + HP_SYS <= HP_SYS() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "HP_SYS_CLKRST peripheral singleton"] HP_SYS_CLKRST <= HP_SYS_CLKRST() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "RNG peripheral singleton"] RNG <= LP_SYS() (unstable))); @@ -3758,6 +3798,7 @@ macro_rules! for_each_peripheral { _for_each_inner_peripheral!((GPIO51)); _for_each_inner_peripheral!((GPIO52)); _for_each_inner_peripheral!((GPIO53)); _for_each_inner_peripheral!((GPIO54)); _for_each_inner_peripheral!((GPIO(unstable))); + _for_each_inner_peripheral!((GPIO_SD(unstable))); _for_each_inner_peripheral!((SYSTEM(unstable))); _for_each_inner_peripheral!((HP_SYS(unstable))); _for_each_inner_peripheral!((HP_SYS_CLKRST(unstable))); @@ -3934,19 +3975,21 @@ macro_rules! for_each_peripheral { "GPIO54 peripheral singleton"] GPIO54 <= virtual()), (@ peri_type #[doc = "EFUSE peripheral singleton"] EFUSE <= EFUSE() (unstable)), (@ peri_type #[doc = "GPIO peripheral singleton"] GPIO <= GPIO() (unstable)), (@ peri_type #[doc = - "SYSTEM peripheral singleton"] SYSTEM <= HP_SYS() (unstable)), (@ peri_type #[doc - = "HP_SYS peripheral singleton"] HP_SYS <= HP_SYS() (unstable)), (@ peri_type - #[doc = "HP_SYS_CLKRST peripheral singleton"] HP_SYS_CLKRST <= HP_SYS_CLKRST() - (unstable)), (@ peri_type #[doc = "RNG peripheral singleton"] RNG <= LP_SYS() - (unstable)), (@ peri_type #[doc = "INTERRUPT_CORE0 peripheral singleton"] - INTERRUPT_CORE0 <= INTERRUPT_CORE0() (unstable)), (@ peri_type #[doc = - "INTERRUPT_CORE1 peripheral singleton"] INTERRUPT_CORE1 <= INTERRUPT_CORE1() - (unstable)), (@ peri_type #[doc = "LP_I2C_ANA_MST peripheral singleton"] - LP_I2C_ANA_MST <= LP_I2C_ANA_MST() (unstable)), (@ peri_type #[doc = - "CLIC peripheral singleton"] CLIC <= CLIC() (unstable)), (@ peri_type #[doc = - "IO_MUX peripheral singleton"] IO_MUX <= IO_MUX() (unstable)), (@ peri_type #[doc - = "LP_AON peripheral singleton"] LP_AON <= LP_SYS() (unstable)), (@ peri_type - #[doc = "LP_AON_CLKRST peripheral singleton"] LP_AON_CLKRST <= LP_AON_CLKRST() + "GPIO_SD peripheral singleton"] GPIO_SD <= GPIO_SD() (unstable)), (@ peri_type + #[doc = "SYSTEM peripheral singleton"] SYSTEM <= HP_SYS() (unstable)), (@ + peri_type #[doc = "HP_SYS peripheral singleton"] HP_SYS <= HP_SYS() (unstable)), + (@ peri_type #[doc = "HP_SYS_CLKRST peripheral singleton"] HP_SYS_CLKRST <= + HP_SYS_CLKRST() (unstable)), (@ peri_type #[doc = "RNG peripheral singleton"] RNG + <= LP_SYS() (unstable)), (@ peri_type #[doc = + "INTERRUPT_CORE0 peripheral singleton"] INTERRUPT_CORE0 <= INTERRUPT_CORE0() + (unstable)), (@ peri_type #[doc = "INTERRUPT_CORE1 peripheral singleton"] + INTERRUPT_CORE1 <= INTERRUPT_CORE1() (unstable)), (@ peri_type #[doc = + "LP_I2C_ANA_MST peripheral singleton"] LP_I2C_ANA_MST <= LP_I2C_ANA_MST() + (unstable)), (@ peri_type #[doc = "CLIC peripheral singleton"] CLIC <= CLIC() + (unstable)), (@ peri_type #[doc = "IO_MUX peripheral singleton"] IO_MUX <= + IO_MUX() (unstable)), (@ peri_type #[doc = "LP_AON peripheral singleton"] LP_AON + <= LP_SYS() (unstable)), (@ peri_type #[doc = + "LP_AON_CLKRST peripheral singleton"] LP_AON_CLKRST <= LP_AON_CLKRST() (unstable)), (@ peri_type #[doc = "LP_SYS peripheral singleton"] LP_SYS <= LP_SYS() (unstable)), (@ peri_type #[doc = "LP_WDT peripheral singleton"] LP_WDT <= LP_WDT() (unstable)), (@ peri_type #[doc = "LPWR peripheral singleton"] LPWR @@ -4025,9 +4068,9 @@ macro_rules! for_each_peripheral { (GPIO27), (GPIO28), (GPIO29), (GPIO30), (GPIO31), (GPIO32), (GPIO33), (GPIO34), (GPIO35), (GPIO36), (GPIO37), (GPIO38), (GPIO39), (GPIO40), (GPIO41), (GPIO42), (GPIO43), (GPIO44), (GPIO45), (GPIO46), (GPIO47), (GPIO48), (GPIO49), (GPIO50), - (GPIO51), (GPIO52), (GPIO53), (GPIO54), (GPIO(unstable)), (SYSTEM(unstable)), - (HP_SYS(unstable)), (HP_SYS_CLKRST(unstable)), (RNG(unstable)), - (INTERRUPT_CORE0(unstable)), (INTERRUPT_CORE1(unstable)), + (GPIO51), (GPIO52), (GPIO53), (GPIO54), (GPIO(unstable)), (GPIO_SD(unstable)), + (SYSTEM(unstable)), (HP_SYS(unstable)), (HP_SYS_CLKRST(unstable)), + (RNG(unstable)), (INTERRUPT_CORE0(unstable)), (INTERRUPT_CORE1(unstable)), (LP_I2C_ANA_MST(unstable)), (CLIC(unstable)), (IO_MUX(unstable)), (LP_AON(unstable)), (LP_AON_CLKRST(unstable)), (LP_SYS(unstable)), (LP_WDT(unstable)), (LPWR(unstable)), (PMU(unstable)), (SYSTIMER(unstable)), diff --git a/esp-metadata-generated/src/_generated_esp32s2.rs b/esp-metadata-generated/src/_generated_esp32s2.rs index f1958452fdf..9ef2c62edf8 100644 --- a/esp-metadata-generated/src/_generated_esp32s2.rs +++ b/esp-metadata-generated/src/_generated_esp32s2.rs @@ -304,6 +304,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 8 + }; + ("sdm.channel_count", str) => { + stringify!(8) + }; + ("sdm.default_clock_source") => { + "apb" + }; ("sleep.light_sleep") => { true }; @@ -777,6 +786,22 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((4, GPIO_SD4)); _for_each_inner_sdm_channel!((5, + GPIO_SD5)); _for_each_inner_sdm_channel!((6, GPIO_SD6)); + _for_each_inner_sdm_channel!((7, GPIO_SD7)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3), (4, GPIO_SD4), (5, GPIO_SD5), (6, GPIO_SD6), (7, + GPIO_SD7))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3093,6 +3118,8 @@ macro_rules! implement_peripheral_clocks { DedicatedGpio, /// DS peripheral clock signal Ds, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -3157,6 +3184,7 @@ macro_rules! implement_peripheral_clocks { Self::CryptoDma, Self::DedicatedGpio, Self::Ds, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2cExt1, @@ -3217,6 +3245,14 @@ macro_rules! implement_peripheral_clocks { .perip_clk_en1() .modify(|_, w| w.crypto_ds_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_clk_en1() @@ -3388,6 +3424,9 @@ macro_rules! implement_peripheral_clocks { .perip_rst_en1() .modify(|_, w| w.crypto_ds_rst().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_rst_en1() diff --git a/esp-metadata-generated/src/_generated_esp32s3.rs b/esp-metadata-generated/src/_generated_esp32s3.rs index c95c7cf5083..201ec6065ac 100644 --- a/esp-metadata-generated/src/_generated_esp32s3.rs +++ b/esp-metadata-generated/src/_generated_esp32s3.rs @@ -325,6 +325,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 8 + }; + ("sdm.channel_count", str) => { + stringify!(8) + }; + ("sdm.default_clock_source") => { + "apb" + }; ("sleep.light_sleep") => { true }; @@ -808,6 +817,22 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((4, GPIO_SD4)); _for_each_inner_sdm_channel!((5, + GPIO_SD5)); _for_each_inner_sdm_channel!((6, GPIO_SD6)); + _for_each_inner_sdm_channel!((7, GPIO_SD7)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3), (4, GPIO_SD4), (5, GPIO_SD5), (6, GPIO_SD6), (7, + GPIO_SD7))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3112,6 +3137,8 @@ macro_rules! implement_peripheral_clocks { Dma, /// DS peripheral clock signal Ds, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -3184,6 +3211,7 @@ macro_rules! implement_peripheral_clocks { Self::DedicatedGpio, Self::Dma, Self::Ds, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2cExt1, @@ -3241,6 +3269,14 @@ macro_rules! implement_peripheral_clocks { .perip_clk_en1() .modify(|_, w| w.crypto_ds_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_clk_en1() @@ -3410,6 +3446,9 @@ macro_rules! implement_peripheral_clocks { .perip_rst_en1() .modify(|_, w| w.crypto_ds_rst().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_rst_en1() diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index 5cdb46883b1..b8f9a75f669 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -302,6 +302,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Ledc" }, { name = "Pcnt" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "let _ = enable;", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, @@ -829,7 +830,10 @@ support_status = "partial" algo = { sha1 = 0, sha256 = 0, sha384 = 0, sha512 = 0 } # fake mode bits, ESP32 has separate register sets [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 8 +clock_sources = ["apb"] +default_clock_source = "apb" [device.spi_master] version = 1 diff --git a/esp-metadata/devices/esp32c3.toml b/esp-metadata/devices/esp32c3.toml index 176418b1fce..342d41ffa49 100644 --- a/esp-metadata/devices/esp32c3.toml +++ b/esp-metadata/devices/esp32c3.toml @@ -228,6 +228,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Timg0", template_params = { peripheral = "timergroup" }, keep_enabled = true }, { name = "Ledc" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, @@ -484,7 +485,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 4 +clock_sources = ["apb"] +default_clock_source = "apb" [device.spi_master] version = 3 diff --git a/esp-metadata/devices/esp32c5.toml b/esp-metadata/devices/esp32c5.toml index 4b1bcfae326..50bcad40198 100644 --- a/esp-metadata/devices/esp32c5.toml +++ b/esp-metadata/devices/esp32c5.toml @@ -268,6 +268,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "I2s0", template_params = { peripheral = "i2s" } }, { name = "Pcnt" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.sigmadelta_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Systimer", keep_enabled = true }, # can be clocked from XTAL_CLK or RC_FAST_CLK, the latter has no divider? { name = "ApbSarAdc", template_params = { peripheral = "saradc", clk_en_field = "saradc_reg_clk_en", rst_field = "saradc_reg_rst_en" }, keep_enabled = true }, { name = "ParlIo", template_params = { clk_en_field = "parl_clk_en", rst_field = "parl_rst_en" } }, @@ -655,7 +656,10 @@ support_status = { status = "not_supported", issue = 5164 } support_status = { status = "not_supported", issue = 884 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 4 +clock_sources = ["xtal", "pll_f80m"] +default_clock_source = "pll_f80m" [device.lp_uart] support_status = { status = "not_supported", issue = 5155 } diff --git a/esp-metadata/devices/esp32c6.toml b/esp-metadata/devices/esp32c6.toml index 91b71e73bb7..5ea54fc79d0 100644 --- a/esp-metadata/devices/esp32c6.toml +++ b/esp-metadata/devices/esp32c6.toml @@ -303,6 +303,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Hmac" }, #{ name = "Iomux" }, #{ name = "MemMonitor" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Trace0", template_params = { peripheral = "trace" } }, #{ name = "Assist" }, #{ name = "Cache" }, @@ -690,7 +691,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 4 +clock_sources = ["xtal", "pll_f80m"] +default_clock_source = "pll_f80m" [device.spi_master] version = 3 diff --git a/esp-metadata/devices/esp32h2.toml b/esp-metadata/devices/esp32h2.toml index 5425a14f696..dd5c2b9151b 100644 --- a/esp-metadata/devices/esp32h2.toml +++ b/esp-metadata/devices/esp32h2.toml @@ -256,6 +256,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Ecdsa" }, #{ name = "Iomux" }, #{ name = "MemMonitor" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Trace0", template_params = { peripheral = "trace" } }, #{ name = "Assist" }, #{ name = "Cache" }, @@ -588,7 +589,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 4 +clock_sources = ["xtal", "pll_f48m"] +default_clock_source = "pll_f48m" [device.ecdsa] support_status = { status = "not_supported", issue = 5444 } diff --git a/esp-metadata/devices/esp32p4.toml b/esp-metadata/devices/esp32p4.toml index fd7aecea33d..bb85650b382 100644 --- a/esp-metadata/devices/esp32p4.toml +++ b/esp-metadata/devices/esp32p4.toml @@ -196,6 +196,7 @@ clocks = { system_clocks = { clock_tree = [ # PWM/Counter/RMT -- LCD/CAM clock gate lives in SOC_CLK_CTRL3 (not modelled in current PAC), keep stub. { name = "Ledc", template_params = { default_rst_template = "{{rst_en1_template}}", rst_field = "rst_en_ledc" } }, { name = "Pcnt", template_params = { default_clk_en_template = "{{apb_clk_template}}", apb_field = "pcnt_apb_clk_en", default_rst_template = "{{rst_en1_template}}", rst_field = "rst_en_pcnt" } }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Mcpwm0", template_params = { default_clk_en_template = "{{apb_clk_template}}", apb_field = "mcpwm0_apb_clk_en", default_rst_template = "{{rst_en1_template}}", rst_field = "rst_en_pwm0" } }, { name = "Mcpwm1", template_params = { default_clk_en_template = "{{apb_clk_template}}", apb_field = "mcpwm1_apb_clk_en", default_rst_template = "{{rst_en1_template}}", rst_field = "rst_en_pwm1" } }, # NOTE: `rmt_sys_clk_en` despite the name lives on SOC_CLK_CTRL2, not CTRL1. @@ -247,6 +248,7 @@ peripherals = [ # Minimal set for esp-hal compilation { name = "EFUSE", hidden = true }, { name = "GPIO" }, + { name = "GPIO_SD" }, { name = "SYSTEM", pac = "HP_SYS" }, { name = "HP_SYS" }, { name = "HP_SYS_CLKRST" }, @@ -454,7 +456,10 @@ support_status = "not_supported" # SDIO host [device.sd_slave] support_status = "not_supported" [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 8 +clock_sources = ["xtal", "pll_f80m"] +default_clock_source = "pll_f80m" [device.sleep] support_status = "not_supported" # PMU eco5 sleep -- placeholder TODO in esp-hal [device.spi_slave] diff --git a/esp-metadata/devices/esp32s2.toml b/esp-metadata/devices/esp32s2.toml index 041a16bfbe8..28c5fa1dd5b 100644 --- a/esp-metadata/devices/esp32s2.toml +++ b/esp-metadata/devices/esp32s2.toml @@ -247,6 +247,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Ledc" }, { name = "Pcnt" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, @@ -613,7 +614,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = 5, sha512_256 = 6, sha512_t = 7 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 8 +clock_sources = ["apb"] +default_clock_source = "apb" [device.spi_master] version = 2 diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index fde81fd91ec..eb7ad502634 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -262,6 +262,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Ledc" }, { name = "Pcnt" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, @@ -805,7 +806,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = 5, sha512_256 = 6, sha512_t = 7 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 8 +clock_sources = ["apb"] +default_clock_source = "apb" [device.spi_master] version = 3 diff --git a/esp-metadata/src/cfg.rs b/esp-metadata/src/cfg.rs index 5c63d0b5015..983ccc5183b 100644 --- a/esp-metadata/src/cfg.rs +++ b/esp-metadata/src/cfg.rs @@ -5,6 +5,7 @@ pub(crate) mod i2c_master; pub(crate) mod interrupt; pub(crate) mod rmt; pub(crate) mod rsa; +pub(crate) mod sdm; pub(crate) mod sha; pub(crate) mod soc; pub(crate) mod spi_master; @@ -18,6 +19,7 @@ pub(crate) use gpio::*; pub(crate) use i2c_master::*; pub(crate) use interrupt::*; pub(crate) use rmt::*; +pub(crate) use sdm::*; pub(crate) use sha::*; pub(crate) use soc::*; pub(crate) use spi_master::*; @@ -635,7 +637,15 @@ driver_configs![ SdmProperties { driver: sdm, name: "SDM", - properties: {} + properties: { + /// Number of sigma-delta channels. + channel_count: SdmChannels, + /// Supported source clocks for the shared SDM/IO_MUX clock. + #[serde(default)] + clock_sources: Vec, + /// Default source clock for the shared SDM/IO_MUX clock. + default_clock_source: String, + } }, SleepProperties { driver: sleep, From 50db7519090cc7ae7023dd3dca32e4a4a9bde51d Mon Sep 17 00:00:00 2001 From: tzyt Date: Tue, 26 May 2026 21:05:09 -0500 Subject: [PATCH 02/10] add new file for SDM --- esp-hal/src/sdm.rs | 580 +++++++++++++++++++++ esp-metadata/src/cfg/sdm.rs | 50 ++ examples/peripheral/sdm/.cargo/config.toml | 24 + examples/peripheral/sdm/Cargo.toml | 62 +++ examples/peripheral/sdm/src/main.rs | 46 ++ 5 files changed, 762 insertions(+) create mode 100644 esp-hal/src/sdm.rs create mode 100644 esp-metadata/src/cfg/sdm.rs create mode 100644 examples/peripheral/sdm/.cargo/config.toml create mode 100644 examples/peripheral/sdm/Cargo.toml create mode 100644 examples/peripheral/sdm/src/main.rs diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs new file mode 100644 index 00000000000..e7c76da0a5b --- /dev/null +++ b/esp-hal/src/sdm.rs @@ -0,0 +1,580 @@ +//! Sigma-delta modulation peripheral. +//! +//! The sigma-delta modulator produces a pulse-density modulated output on a +//! GPIO matrix signal. Each channel can be configured with a carrier frequency +//! and pulse density, then routed to one output pin. + +use core::fmt; + +use esp_sync::NonReentrantMutex; + +use crate::{ + gpio::{ + OutputConfig, OutputSignal, PinGuard, + interconnect::{OutputSignal as GpioOutputSignal, PeripheralOutput}, + }, + peripherals::GPIO_SD, + system::{GenericPeripheralGuard, Peripheral}, + time::Rate, +}; + +static CLOCK_STATE: NonReentrantMutex = NonReentrantMutex::new(SdmClockState::new()); + +struct SdmClockState { + users: usize, + source: Option, +} + +impl SdmClockState { + const fn new() -> Self { + Self { + users: 0, + source: None, + } + } +} + +for_each_sdm_channel!( + (channels $(($ch:literal, $signal:ident)),*) => { + paste::paste! { + /// Sigma-delta peripheral. + /// + /// This type only owns the SDM peripheral token and exposes the hardware + /// channels. Moving individual channels out of this collection is supported. + #[derive(Debug)] + #[non_exhaustive] + pub struct Sdm<'d> { + _instance: GPIO_SD<'d>, + $( + #[doc = concat!("Channel ", stringify!($ch), ".")] + pub []: Channel<$ch>, + )* + } + + impl<'d> Sdm<'d> { + /// Creates a new sigma-delta peripheral driver. + pub fn new(instance: GPIO_SD<'d>) -> Self { + Self::new_with_config(instance, SdmConfig::default()) + } + + /// Creates a new sigma-delta peripheral driver with the given + /// peripheral-wide configuration. + /// + /// The SDM clock source is shared by all SDM channels through the IO_MUX + /// clock, so it is selected here instead of being configurable per channel. + pub fn new_with_config(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { + Self { + _instance: instance, + $( + []: Channel::new(config.clock_source), + )* + } + } + } + } + + fn output_signal(channel: usize) -> OutputSignal { + match channel { + $( + $ch => OutputSignal::$signal, + )* + _ => unreachable!("SDM channel index out of range"), + } + } + }; +); + +/// Sigma-delta peripheral configuration. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct SdmConfig { + /// Clock source used by the shared SDM/IO_MUX clock. + pub clock_source: ClockSource, +} + +impl SdmConfig { + /// Selects the source clock used by all SDM channels. + pub const fn with_clock_source(mut self, clock_source: ClockSource) -> Self { + self.clock_source = clock_source; + self + } +} + +impl Default for SdmConfig { + fn default() -> Self { + Self { + clock_source: ClockSource::default(), + } + } +} + +/// Source clock for the shared SDM/IO_MUX clock. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum ClockSource { + /// APB clock. + #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + Apb, + /// XTAL clock. + #[cfg(any(esp32c5, esp32c6, esp32h2, esp32p4))] + Xtal, + /// Fixed 80 MHz PLL clock. + #[cfg(any(esp32c5, esp32c6, esp32p4))] + PllF80m, + /// Fixed 48 MHz PLL clock. + #[cfg(esp32h2)] + PllF48m, +} + +impl Default for ClockSource { + fn default() -> Self { + cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] { + Self::Apb + } else if #[cfg(any(esp32c5, esp32c6, esp32p4))] { + Self::PllF80m + } else if #[cfg(esp32h2)] { + Self::PllF48m + } + } + } +} + +impl ClockSource { + fn frequency(self) -> Rate { + match self { + #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + Self::Apb => Rate::from_hz(crate::soc::clocks::apb_clk_frequency()), + #[cfg(any(esp32c5, esp32c6, esp32h2, esp32p4))] + Self::Xtal => crate::clock::xtal_clock(), + #[cfg(any(esp32c5, esp32c6, esp32p4))] + Self::PllF80m => Rate::from_mhz(80), + #[cfg(esp32h2)] + Self::PllF48m => Rate::from_mhz(48), + } + } +} + +/// Sigma-delta channel timing configuration. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Timing { + /// Derive the hardware prescaler from a requested output frequency. + Frequency(Rate), + /// Use a raw hardware prescaler. + /// + /// The hardware divider range is `1..=256`. + Prescaler(u16), +} + +impl Timing { + fn prescaler(self, clock_source: ClockSource) -> Result { + match self { + Self::Frequency(frequency) => prescaler_from_frequency(frequency, clock_source), + Self::Prescaler(prescaler) => { + check_prescaler(prescaler)?; + Ok(prescaler) + } + } + } +} + +/// Sigma-delta channel configuration. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ChannelConfig { + /// Channel timing. + /// + /// Requested frequencies are converted to a raw hardware prescaler using + /// the SDM clock source selected in [`SdmConfig`]. + pub timing: Timing, + /// Pulse density. + /// + /// The value ranges from `-128` to `127`. + pub pulse_density: i8, +} + +impl ChannelConfig { + /// Sets the requested output frequency. + pub const fn with_frequency(mut self, frequency: Rate) -> Self { + self.timing = Timing::Frequency(frequency); + self + } + + /// Sets the raw hardware prescaler. + pub const fn with_prescaler(mut self, prescaler: u16) -> Self { + self.timing = Timing::Prescaler(prescaler); + self + } + + /// Sets the pulse density. + pub const fn with_pulse_density(mut self, density: i8) -> Self { + self.pulse_density = density; + self + } + + /// Sets the duty cycle. + /// + /// This is a convenience mapping where `0` maps to the minimum density and + /// `255` maps to the maximum density. + pub const fn with_duty(mut self, duty: u8) -> Self { + self.pulse_density = duty_to_density(duty); + self + } +} + +impl Default for ChannelConfig { + fn default() -> Self { + Self { + timing: Timing::Prescaler(1), + pulse_density: 0, + } + } +} + +/// Sigma-delta configuration or runtime error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// The requested frequency cannot be represented by the hardware prescaler. + UnreachableTargetFrequency, + /// Another SDM channel is already using a different clock source. + ClockSourceConflict, + /// The raw prescaler is outside the hardware range. + PrescalerOutOfRange, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::UnreachableTargetFrequency => f.write_str("unreachable target frequency"), + Self::ClockSourceConflict => f.write_str("SDM clock source conflict"), + Self::PrescalerOutOfRange => f.write_str("prescaler out of range"), + } + } +} + +impl core::error::Error for Error {} + +/// A sigma-delta channel. +/// +/// Channels are exposed by [`Sdm`]. A channel enables the SDM peripheral clock +/// when it is connected. +#[derive(Debug)] +pub struct Channel { + // This is copied from the Sdm-level configuration so partially moved + // channels still use the shared clock source selected at construction time. + // It is intentionally not configurable per channel. + clock_source: ClockSource, + pin_guard: Option, + clock_guard: Option, +} + +impl Channel { + const fn new(clock_source: ClockSource) -> Self { + Self { + clock_source, + pin_guard: None, + clock_guard: None, + } + } + + /// Configures this channel and connects it to an output pin. + /// + /// Reconnecting a channel replaces the previous pin connection. + pub fn connect<'d>( + &mut self, + pin: impl PeripheralOutput<'d>, + config: ChannelConfig, + ) -> Result<(), Error> { + let prescaler = config.timing.prescaler(self.clock_source)?; + self.clock_guard = Some(SdmClockGuard::new(self.clock_source)?); + set_prescaler_raw(CHANNEL, prescaler); + set_pulse_density_raw(CHANNEL, config.pulse_density); + self.reconnect(pin); + Ok(()) + } + + /// Connects this channel to a new output pin without changing its channel + /// configuration. + /// + /// This only changes the GPIO matrix route and replaces the previous pin + /// connection. + pub fn reconnect<'d>(&mut self, pin: impl PeripheralOutput<'d>) { + self.pin_guard = None; + let pin: GpioOutputSignal<'d> = pin.into(); + pin.apply_output_config(&OutputConfig::default()); + pin.set_output_enable(true); + self.pin_guard = Some(pin.connect_with_guard(output_signal(CHANNEL))); + } + + /// Returns whether the channel is currently routed to an output pin. + pub fn is_connected(&self) -> bool { + self.pin_guard.is_some() + } + + /// Applies channel timing and pulse density configuration. + /// + /// This is equivalent to setting the prescaler/frequency and pulse density + /// separately. + /// + /// The channel must have been successfully connected first. + pub fn apply_config(&mut self, config: ChannelConfig) -> Result<(), Error> { + let prescaler = config.timing.prescaler(self.clock_source)?; + set_prescaler_raw(CHANNEL, prescaler); + set_pulse_density_raw(CHANNEL, config.pulse_density); + Ok(()) + } + + /// Sets raw pulse density. + /// + /// The value ranges from `-128` to `127`. + /// + /// The channel must have been successfully connected first. + pub fn set_pulse_density(&mut self, density: i8) { + set_pulse_density_raw(CHANNEL, density); + } + + /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the + /// maximum density. + /// + /// The channel must have been successfully connected first. + pub fn set_duty(&mut self, duty: u8) { + self.set_pulse_density(duty_to_density(duty)) + } + + /// Sets raw prescaler. + /// + /// The hardware divider range is `1..=256`. + /// + /// The channel must have been successfully connected first. + pub fn set_prescaler(&mut self, prescaler: u16) -> Result<(), Error> { + check_prescaler(prescaler)?; + set_prescaler_raw(CHANNEL, prescaler); + Ok(()) + } + + /// Sets the output frequency. + /// + /// The channel must have been successfully connected first. + pub fn set_frequency(&mut self, frequency: Rate) -> Result<(), Error> { + let prescaler = prescaler_from_frequency(frequency, self.clock_source)?; + self.set_prescaler(prescaler) + } + + /// Reads the raw hardware prescaler. + /// + /// The returned value is in the hardware divider range `1..=256`. + /// + /// The channel must have been successfully connected first. + pub fn prescaler(&mut self) -> Result { + Ok(prescaler_raw(CHANNEL)) + } + + /// Reads the raw pulse density. + /// + /// The returned value is in the hardware range `-128..=127`. + /// + /// The channel must have been successfully connected first. + pub fn pulse_density(&mut self) -> Result { + Ok(pulse_density_raw(CHANNEL)) + } +} + +impl Drop for Channel { + fn drop(&mut self) { + self.pin_guard = None; + if self.clock_guard.is_some() { + set_pulse_density_raw(CHANNEL, 0); + } + self.clock_guard = None; + } +} + +#[derive(Debug)] +struct SdmClockGuard { + _peripheral: GenericPeripheralGuard<{ Peripheral::GpioSd as u8 }>, + clock_source: ClockSource, +} + +impl SdmClockGuard { + fn new(clock_source: ClockSource) -> Result { + CLOCK_STATE.with(|state| { + if state.users == usize::MAX { + panic!("SDM clock reference counter overflowed"); + } + + if state.users == 0 { + configure_clock_source(clock_source); + state.source = Some(clock_source); + } else if state.source != Some(clock_source) { + return Err(Error::ClockSourceConflict); + } + + state.users += 1; + Ok(()) + })?; + + Ok(Self { + _peripheral: GenericPeripheralGuard::new(), + clock_source, + }) + } +} + +impl Drop for SdmClockGuard { + fn drop(&mut self) { + CLOCK_STATE.with(|state| match state.users { + 0 => panic!("SDM clock reference counter underflowed"), + 1 => { + debug_assert_eq!(state.source, Some(self.clock_source)); + state.users = 0; + state.source = None; + } + _ => { + debug_assert_eq!(state.source, Some(self.clock_source)); + state.users -= 1; + } + }); + } +} + +fn prescaler_from_frequency(frequency: Rate, clock_source: ClockSource) -> Result { + let source_frequency = clock_source.frequency().as_hz(); + let requested_frequency = frequency.as_hz(); + + if requested_frequency == 0 || requested_frequency > source_frequency { + return Err(Error::UnreachableTargetFrequency); + } + + let prescaler = + (source_frequency as u64 + requested_frequency as u64 / 2) / requested_frequency as u64; + if prescaler == 0 || prescaler > 256 { + return Err(Error::UnreachableTargetFrequency); + } + + Ok(prescaler as u16) +} + +fn check_prescaler(prescaler: u16) -> Result<(), Error> { + if (1..=256).contains(&prescaler) { + Ok(()) + } else { + Err(Error::PrescalerOutOfRange) + } +} + +const fn duty_to_density(duty: u8) -> i8 { + (duty as i16 - 128) as i8 +} + +fn configure_clock_source(clock_source: ClockSource) { + // Newer chips expose this selector in different clock-control blocks: + // - C5/C6/H2 use PCR.iomux_clk_conf.iomux_func_clk_sel, but the selector + // encodings differ by chip. + // - P4 uses HP_SYS_CLKRST.peri_clk_ctrl26.iomux_clk_src_sel, a one-bit + // XTAL/PLL_F80M selector. + // - ESP32/C3/S2/S3 use APB for this driver path and do not need a source + // selector write here. + cfg_if::cfg_if! { + if #[cfg(esp32c5)] { + crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { + w.iomux_func_clk_sel() + .bits(match clock_source { + ClockSource::Xtal => 0, + ClockSource::PllF80m => 2, + }) + .iomux_func_clk_en() + .set_bit() + }); + } else if #[cfg(esp32c6)] { + crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { + w.iomux_func_clk_sel() + .bits(match clock_source { + ClockSource::PllF80m => 1, + ClockSource::Xtal => 3, + }) + .iomux_func_clk_en() + .set_bit() + }); + } else if #[cfg(esp32h2)] { + crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { + w.iomux_func_clk_sel() + .bits(match clock_source { + ClockSource::Xtal => 0, + ClockSource::PllF48m => 2, + }) + .iomux_func_clk_en() + .set_bit() + }); + } else if #[cfg(esp32p4)] { + crate::peripherals::HP_SYS_CLKRST::regs() + .peri_clk_ctrl26() + .modify(|_, w| { + match clock_source { + ClockSource::Xtal => w.iomux_clk_src_sel().clear_bit(), + ClockSource::PllF80m => w.iomux_clk_src_sel().set_bit(), + } + .iomux_clk_en() + .set_bit() + }); + } else { + let _ = clock_source; + } + } +} + +fn set_pulse_density_raw(channel: usize, density: i8) { + // The register layout is the same conceptually, but ESP32-C5's PAC names + // the field `sd_in`; the other supported PACs name it `in`. + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.sd_in().bits(density as _) }); + + #[cfg(not(esp32c5))] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.in_().bits(density as _) }); +} + +fn set_prescaler_raw(channel: usize, prescaler: u16) { + // Hardware stores prescaler - 1. ESP32-C5's PAC names the field + // `sd_prescale`; the other supported PACs name it `prescale`. + let bits = (prescaler - 1) as _; + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.sd_prescale().bits(bits) }); + + #[cfg(not(esp32c5))] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.prescale().bits(bits) }); +} + +fn pulse_density_raw(channel: usize) -> i8 { + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + return sd.sigmadelta(channel).read().sd_in().bits() as i8; + + #[cfg(not(esp32c5))] + return sd.sigmadelta(channel).read().in_().bits() as i8; +} + +fn prescaler_raw(channel: usize) -> u16 { + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + let bits = sd.sigmadelta(channel).read().sd_prescale().bits(); + + #[cfg(not(esp32c5))] + let bits = sd.sigmadelta(channel).read().prescale().bits(); + + bits as u16 + 1 +} diff --git a/esp-metadata/src/cfg/sdm.rs b/esp-metadata/src/cfg/sdm.rs new file mode 100644 index 00000000000..fcde4d1ea50 --- /dev/null +++ b/esp-metadata/src/cfg/sdm.rs @@ -0,0 +1,50 @@ +use proc_macro2::TokenStream; +use quote::format_ident; + +use crate::{cfg::GenericProperty, generate_for_each_macro, number}; + +#[derive(Clone, Debug)] +pub struct SdmChannels { + count: u32, +} + +impl<'de> serde::Deserialize<'de> for SdmChannels { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(Self { + count: u32::deserialize(deserializer)?, + }) + } +} + +impl GenericProperty for SdmChannels { + fn macros(&self) -> Option { + let channels = (0..self.count) + .map(|channel| { + let channel = number(channel); + let signal = format_ident!("GPIO_SD{channel}"); + quote::quote! { #channel, #signal } + }) + .collect::>(); + + Some(generate_for_each_macro( + "sdm_channel", + &[("channels", &channels)], + )) + } + + fn property_macro_branches(&self) -> TokenStream { + let count = number(self.count); + + quote::quote! { + ("sdm.channel_count") => { + #count + }; + ("sdm.channel_count", str) => { + stringify!(#count) + }; + } + } +} diff --git a/examples/peripheral/sdm/.cargo/config.toml b/examples/peripheral/sdm/.cargo/config.toml new file mode 100644 index 00000000000..44dacc4278b --- /dev/null +++ b/examples/peripheral/sdm/.cargo/config.toml @@ -0,0 +1,24 @@ +[target.'cfg(target_arch = "riscv32")'] +runner = "espflash flash --monitor" +rustflags = [ + "-C", "link-arg=-Tlinkall.x", + "-C", "force-frame-pointers", +] + +[target.'cfg(target_arch = "xtensa")'] +runner = "espflash flash --monitor" +rustflags = [ + # GNU LD + "-C", "link-arg=-Wl,-Tlinkall.x", + "-C", "link-arg=-nostartfiles", + + # LLD + # "-C", "link-arg=-Tlinkall.x", + # "-C", "linker=rust-lld", +] + +[env] +ESP_LOG = "info" + +[unstable] +build-std = ["core", "alloc"] diff --git a/examples/peripheral/sdm/Cargo.toml b/examples/peripheral/sdm/Cargo.toml new file mode 100644 index 00000000000..ff94441bf35 --- /dev/null +++ b/examples/peripheral/sdm/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "sdm" +version = "0.0.0" +edition = "2024" +publish = false + +[dependencies] +esp-backtrace = { path = "../../../esp-backtrace", features = [ + "panic-handler", + "println", +] } +esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" } +esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] } +esp-println = { path = "../../../esp-println", features = ["log-04"] } + +[features] +esp32 = [ + "esp-backtrace/esp32", + "esp-bootloader-esp-idf/esp32", + "esp-hal/esp32", +] +esp32c3 = [ + "esp-backtrace/esp32c3", + "esp-bootloader-esp-idf/esp32c3", + "esp-hal/esp32c3", +] +esp32c5 = [ + "esp-backtrace/esp32c5", + "esp-bootloader-esp-idf/esp32c5", + "esp-hal/esp32c5", +] +esp32c6 = [ + "esp-backtrace/esp32c6", + "esp-bootloader-esp-idf/esp32c6", + "esp-hal/esp32c6", +] +esp32h2 = [ + "esp-backtrace/esp32h2", + "esp-bootloader-esp-idf/esp32h2", + "esp-hal/esp32h2", +] +esp32p4 = [ + "esp-backtrace/esp32p4", + "esp-bootloader-esp-idf/esp32p4", + "esp-hal/esp32p4", +] +esp32s2 = [ + "esp-backtrace/esp32s2", + "esp-bootloader-esp-idf/esp32s2", + "esp-hal/esp32s2", +] +esp32s3 = [ + "esp-backtrace/esp32s3", + "esp-bootloader-esp-idf/esp32s3", + "esp-hal/esp32s3", +] + +[profile.release] +debug = true +debug-assertions = true +lto = "fat" +codegen-units = 1 diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs new file mode 100644 index 00000000000..2600942aa13 --- /dev/null +++ b/examples/peripheral/sdm/src/main.rs @@ -0,0 +1,46 @@ +//! This example shows how to generate a sigma-delta output signal. +//! +//! The following wiring is assumed: +//! - SDM output => GPIO2 + +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + delay::Delay, + main, + sdm::{ChannelConfig, Sdm}, + time::Rate, +}; +use esp_println::print; + +esp_bootloader_esp_idf::esp_app_desc!(); + +#[main] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let sdm = Sdm::new(peripherals.GPIO_SD); + let mut channel = sdm.channel0; + channel + .connect( + peripherals.GPIO2, + ChannelConfig::default() + .with_frequency(Rate::from_khz(500)) + .with_pulse_density(0), + ) + .unwrap(); + + let delay = Delay::new(); + let mut duty = 0; + + loop { + channel.set_duty(duty); + print!("SDM duty: {duty:3}\r"); + + duty = duty.wrapping_add(1); + delay.delay_millis(100); + } +} From 6d8338dbb06d99ac217198ee92bc70e034034376 Mon Sep 17 00:00:00 2001 From: tzyt Date: Wed, 27 May 2026 12:11:13 -0500 Subject: [PATCH 03/10] changing supported to partial in support matrix --- esp-hal/README.md | 2 +- esp-metadata/devices/esp32.toml | 2 +- esp-metadata/devices/esp32c3.toml | 2 +- esp-metadata/devices/esp32c5.toml | 2 +- esp-metadata/devices/esp32c6.toml | 2 +- esp-metadata/devices/esp32h2.toml | 2 +- esp-metadata/devices/esp32p4.toml | 2 +- esp-metadata/devices/esp32s2.toml | 2 +- esp-metadata/devices/esp32s3.toml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/esp-hal/README.md b/esp-hal/README.md index 6eec34ae65a..d6709fdc1e6 100644 --- a/esp-hal/README.md +++ b/esp-hal/README.md @@ -101,7 +101,7 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a | SDIO host | ❌ | | | | | | | ❌ | | ❌ | | SDIO slave | ❌ | | | [❌][5169] [^1] | ❌ | [❌][5417] [^1] | | ❌ | | | | SHA | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ❌ | ⚒️ | ⚒️ | -| SDM | ✔️ | | ✔️ | ✔️ | ✔️ | | ✔️ | ✔️ | ✔️ | ✔️ | +| SDM | ⚒️ | | ⚒️ | ⚒️ | ⚒️ | | ⚒️ | ⚒️ | ⚒️ | ⚒️ | | Light/deep sleep | ⚒️ | ⚒️ | ⚒️ | [❌][5165] [^1] | ⚒️ | [❌][5424] [^1] | ⚒️ | ❌ | ⚒️ | ⚒️ | | SPI master | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ⚒️ | ✔️ | ✔️ | | SPI slave | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ❌ | ⚒️ | ⚒️ | diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index c366c767cb6..79366364fb4 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -835,7 +835,7 @@ support_status = "partial" algo = { sha1 = 0, sha256 = 0, sha384 = 0, sha512 = 0 } # fake mode bits, ESP32 has separate register sets [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 8 clock_sources = ["apb"] default_clock_source = "apb" diff --git a/esp-metadata/devices/esp32c3.toml b/esp-metadata/devices/esp32c3.toml index 28548000a84..e046529d03c 100644 --- a/esp-metadata/devices/esp32c3.toml +++ b/esp-metadata/devices/esp32c3.toml @@ -488,7 +488,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 4 clock_sources = ["apb"] default_clock_source = "apb" diff --git a/esp-metadata/devices/esp32c5.toml b/esp-metadata/devices/esp32c5.toml index e886525162d..87f490f2774 100644 --- a/esp-metadata/devices/esp32c5.toml +++ b/esp-metadata/devices/esp32c5.toml @@ -659,7 +659,7 @@ support_status = { status = "not_supported", issue = 5164 } support_status = { status = "not_supported", issue = 884 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 4 clock_sources = ["xtal", "pll_f80m"] default_clock_source = "pll_f80m" diff --git a/esp-metadata/devices/esp32c6.toml b/esp-metadata/devices/esp32c6.toml index d4b955bf0d2..df4400d8ac7 100644 --- a/esp-metadata/devices/esp32c6.toml +++ b/esp-metadata/devices/esp32c6.toml @@ -694,7 +694,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 4 clock_sources = ["xtal", "pll_f80m"] default_clock_source = "pll_f80m" diff --git a/esp-metadata/devices/esp32h2.toml b/esp-metadata/devices/esp32h2.toml index 91b4a215b0b..36a526f704c 100644 --- a/esp-metadata/devices/esp32h2.toml +++ b/esp-metadata/devices/esp32h2.toml @@ -592,7 +592,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 4 clock_sources = ["xtal", "pll_f48m"] default_clock_source = "pll_f48m" diff --git a/esp-metadata/devices/esp32p4.toml b/esp-metadata/devices/esp32p4.toml index 847f8d4acd7..4a8024cfa3e 100644 --- a/esp-metadata/devices/esp32p4.toml +++ b/esp-metadata/devices/esp32p4.toml @@ -462,7 +462,7 @@ support_status = "not_supported" # SDIO host [device.sd_slave] support_status = "not_supported" [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 8 clock_sources = ["xtal", "pll_f80m"] default_clock_source = "pll_f80m" diff --git a/esp-metadata/devices/esp32s2.toml b/esp-metadata/devices/esp32s2.toml index c93a7ab75cd..75c0df069eb 100644 --- a/esp-metadata/devices/esp32s2.toml +++ b/esp-metadata/devices/esp32s2.toml @@ -617,7 +617,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = 5, sha512_256 = 6, sha512_t = 7 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 8 clock_sources = ["apb"] default_clock_source = "apb" diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index 4ccbc9be630..178630ef2d0 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -809,7 +809,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = 5, sha512_256 = 6, sha512_t = 7 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 8 clock_sources = ["apb"] default_clock_source = "apb" From fa129640feeadfacbbf70aea667890fe327ddb2c Mon Sep 17 00:00:00 2001 From: tzyt Date: Wed, 27 May 2026 20:53:01 -0500 Subject: [PATCH 04/10] change SDM API to typestate style --- esp-hal/src/sdm.rs | 241 +++++++++++----------------- examples/peripheral/sdm/src/main.rs | 21 +-- 2 files changed, 102 insertions(+), 160 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index e7c76da0a5b..c962082ca1c 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -40,14 +40,15 @@ for_each_sdm_channel!( /// Sigma-delta peripheral. /// /// This type only owns the SDM peripheral token and exposes the hardware - /// channels. Moving individual channels out of this collection is supported. + /// channel creators. Moving individual channel creators out of this + /// collection is supported. #[derive(Debug)] #[non_exhaustive] pub struct Sdm<'d> { _instance: GPIO_SD<'d>, $( - #[doc = concat!("Channel ", stringify!($ch), ".")] - pub []: Channel<$ch>, + #[doc = concat!("Channel ", stringify!($ch), " creator.")] + pub []: ChannelCreator<$ch>, )* } @@ -66,13 +67,17 @@ for_each_sdm_channel!( Self { _instance: instance, $( - []: Channel::new(config.clock_source), + []: ChannelCreator::new(config.clock_source), )* } } } } + }; +); +for_each_sdm_channel!( + (channels $(($ch:literal, $signal:ident)),*) => { fn output_signal(channel: usize) -> OutputSignal { match channel { $( @@ -157,84 +162,6 @@ impl ClockSource { } } -/// Sigma-delta channel timing configuration. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Timing { - /// Derive the hardware prescaler from a requested output frequency. - Frequency(Rate), - /// Use a raw hardware prescaler. - /// - /// The hardware divider range is `1..=256`. - Prescaler(u16), -} - -impl Timing { - fn prescaler(self, clock_source: ClockSource) -> Result { - match self { - Self::Frequency(frequency) => prescaler_from_frequency(frequency, clock_source), - Self::Prescaler(prescaler) => { - check_prescaler(prescaler)?; - Ok(prescaler) - } - } - } -} - -/// Sigma-delta channel configuration. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ChannelConfig { - /// Channel timing. - /// - /// Requested frequencies are converted to a raw hardware prescaler using - /// the SDM clock source selected in [`SdmConfig`]. - pub timing: Timing, - /// Pulse density. - /// - /// The value ranges from `-128` to `127`. - pub pulse_density: i8, -} - -impl ChannelConfig { - /// Sets the requested output frequency. - pub const fn with_frequency(mut self, frequency: Rate) -> Self { - self.timing = Timing::Frequency(frequency); - self - } - - /// Sets the raw hardware prescaler. - pub const fn with_prescaler(mut self, prescaler: u16) -> Self { - self.timing = Timing::Prescaler(prescaler); - self - } - - /// Sets the pulse density. - pub const fn with_pulse_density(mut self, density: i8) -> Self { - self.pulse_density = density; - self - } - - /// Sets the duty cycle. - /// - /// This is a convenience mapping where `0` maps to the minimum density and - /// `255` maps to the maximum density. - pub const fn with_duty(mut self, duty: u8) -> Self { - self.pulse_density = duty_to_density(duty); - self - } -} - -impl Default for ChannelConfig { - fn default() -> Self { - Self { - timing: Timing::Prescaler(1), - pulse_density: 0, - } - } -} - /// Sigma-delta configuration or runtime error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -260,89 +187,107 @@ impl fmt::Display for Error { impl core::error::Error for Error {} -/// A sigma-delta channel. +/// Creates a connected sigma-delta channel. /// -/// Channels are exposed by [`Sdm`]. A channel enables the SDM peripheral clock -/// when it is connected. +/// Channel creators are exposed by [`Sdm`]. Calling [`connect`](Self::connect) +/// consumes the creator and returns an active [`Channel`], which prevents +/// accidentally connecting the same channel twice. #[derive(Debug)] -pub struct Channel { +pub struct ChannelCreator { // This is copied from the Sdm-level configuration so partially moved - // channels still use the shared clock source selected at construction time. + // channel creators still use the shared clock source selected at construction time. // It is intentionally not configurable per channel. clock_source: ClockSource, - pin_guard: Option, - clock_guard: Option, + prescaler: u16, + pulse_density: i8, } -impl Channel { +impl ChannelCreator { const fn new(clock_source: ClockSource) -> Self { Self { clock_source, - pin_guard: None, - clock_guard: None, + prescaler: 1, + pulse_density: 0, } } - /// Configures this channel and connects it to an output pin. - /// - /// Reconnecting a channel replaces the previous pin connection. - pub fn connect<'d>( - &mut self, - pin: impl PeripheralOutput<'d>, - config: ChannelConfig, - ) -> Result<(), Error> { - let prescaler = config.timing.prescaler(self.clock_source)?; - self.clock_guard = Some(SdmClockGuard::new(self.clock_source)?); - set_prescaler_raw(CHANNEL, prescaler); - set_pulse_density_raw(CHANNEL, config.pulse_density); - self.reconnect(pin); - Ok(()) + const fn with_raw_config(clock_source: ClockSource, prescaler: u16, pulse_density: i8) -> Self { + Self { + clock_source, + prescaler, + pulse_density, + } } - /// Connects this channel to a new output pin without changing its channel - /// configuration. + /// Sets the requested output frequency. /// - /// This only changes the GPIO matrix route and replaces the previous pin - /// connection. - pub fn reconnect<'d>(&mut self, pin: impl PeripheralOutput<'d>) { - self.pin_guard = None; - let pin: GpioOutputSignal<'d> = pin.into(); - pin.apply_output_config(&OutputConfig::default()); - pin.set_output_enable(true); - self.pin_guard = Some(pin.connect_with_guard(output_signal(CHANNEL))); + /// The frequency is converted immediately using the SDM clock source + /// selected in [`SdmConfig`]. + pub fn with_frequency(mut self, frequency: Rate) -> Result { + self.prescaler = prescaler_from_frequency(frequency, self.clock_source)?; + Ok(self) } - /// Returns whether the channel is currently routed to an output pin. - pub fn is_connected(&self) -> bool { - self.pin_guard.is_some() + /// Sets the raw hardware prescaler. + /// + /// The hardware divider range is `1..=256`. + pub fn with_prescaler(mut self, prescaler: u16) -> Result { + check_prescaler(prescaler)?; + self.prescaler = prescaler; + Ok(self) } - /// Applies channel timing and pulse density configuration. - /// - /// This is equivalent to setting the prescaler/frequency and pulse density - /// separately. + /// Sets the pulse density. /// - /// The channel must have been successfully connected first. - pub fn apply_config(&mut self, config: ChannelConfig) -> Result<(), Error> { - let prescaler = config.timing.prescaler(self.clock_source)?; - set_prescaler_raw(CHANNEL, prescaler); - set_pulse_density_raw(CHANNEL, config.pulse_density); - Ok(()) + /// The value ranges is `-128..=127` + pub const fn with_pulse_density(mut self, density: i8) -> Self { + self.pulse_density = density; + self + } + + /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the + /// maximum density. + pub const fn with_duty(mut self, duty: u8) -> Self { + self.pulse_density = duty_to_density(duty); + self + } + + /// Configures this channel and connects it to an output pin. + pub fn connect<'d>(self, pin: impl PeripheralOutput<'d>) -> Result, Error> { + let clock_guard = SdmClockGuard::new(self.clock_source)?; + set_prescaler_raw(CHANNEL, self.prescaler); + set_pulse_density_raw(CHANNEL, self.pulse_density); + + Ok(Channel { + clock_source: self.clock_source, + _pin_guard: connect_pin(CHANNEL, pin), + _clock_guard: clock_guard, + }) } +} + +/// A connected sigma-delta channel. +/// +/// Dropping a channel disconnects its output pin and releases the SDM clock +/// guard. Use [`disconnect`](Self::disconnect) to explicitly release the +/// channel and recover its [`ChannelCreator`]. +#[derive(Debug)] +pub struct Channel { + clock_source: ClockSource, + _pin_guard: PinGuard, + _clock_guard: SdmClockGuard, +} +impl Channel { /// Sets raw pulse density. /// /// The value ranges from `-128` to `127`. - /// - /// The channel must have been successfully connected first. pub fn set_pulse_density(&mut self, density: i8) { set_pulse_density_raw(CHANNEL, density); } /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the /// maximum density. - /// - /// The channel must have been successfully connected first. pub fn set_duty(&mut self, duty: u8) { self.set_pulse_density(duty_to_density(duty)) } @@ -350,8 +295,6 @@ impl Channel { /// Sets raw prescaler. /// /// The hardware divider range is `1..=256`. - /// - /// The channel must have been successfully connected first. pub fn set_prescaler(&mut self, prescaler: u16) -> Result<(), Error> { check_prescaler(prescaler)?; set_prescaler_raw(CHANNEL, prescaler); @@ -359,8 +302,6 @@ impl Channel { } /// Sets the output frequency. - /// - /// The channel must have been successfully connected first. pub fn set_frequency(&mut self, frequency: Rate) -> Result<(), Error> { let prescaler = prescaler_from_frequency(frequency, self.clock_source)?; self.set_prescaler(prescaler) @@ -369,8 +310,6 @@ impl Channel { /// Reads the raw hardware prescaler. /// /// The returned value is in the hardware divider range `1..=256`. - /// - /// The channel must have been successfully connected first. pub fn prescaler(&mut self) -> Result { Ok(prescaler_raw(CHANNEL)) } @@ -378,23 +317,33 @@ impl Channel { /// Reads the raw pulse density. /// /// The returned value is in the hardware range `-128..=127`. - /// - /// The channel must have been successfully connected first. pub fn pulse_density(&mut self) -> Result { Ok(pulse_density_raw(CHANNEL)) } + + /// Disconnects this channel and returns its channel creator. + pub fn disconnect(self) -> ChannelCreator { + let clock_source = self.clock_source; + let prescaler = prescaler_raw(CHANNEL); + let pulse_density = pulse_density_raw(CHANNEL); + drop(self); + ChannelCreator::with_raw_config(clock_source, prescaler, pulse_density) + } } impl Drop for Channel { fn drop(&mut self) { - self.pin_guard = None; - if self.clock_guard.is_some() { - set_pulse_density_raw(CHANNEL, 0); - } - self.clock_guard = None; + set_pulse_density_raw(CHANNEL, 0); } } +fn connect_pin<'d>(channel: usize, pin: impl PeripheralOutput<'d>) -> PinGuard { + let pin: GpioOutputSignal<'d> = pin.into(); + pin.apply_output_config(&OutputConfig::default()); + pin.set_output_enable(true); + pin.connect_with_guard(output_signal(channel)) +} + #[derive(Debug)] struct SdmClockGuard { _peripheral: GenericPeripheralGuard<{ Peripheral::GpioSd as u8 }>, diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs index 2600942aa13..de6c0ff37e6 100644 --- a/examples/peripheral/sdm/src/main.rs +++ b/examples/peripheral/sdm/src/main.rs @@ -7,12 +7,7 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - delay::Delay, - main, - sdm::{ChannelConfig, Sdm}, - time::Rate, -}; +use esp_hal::{delay::Delay, main, sdm::Sdm, time::Rate}; use esp_println::print; esp_bootloader_esp_idf::esp_app_desc!(); @@ -23,14 +18,12 @@ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); let sdm = Sdm::new(peripherals.GPIO_SD); - let mut channel = sdm.channel0; - channel - .connect( - peripherals.GPIO2, - ChannelConfig::default() - .with_frequency(Rate::from_khz(500)) - .with_pulse_density(0), - ) + let mut channel = sdm + .channel0 + .with_frequency(Rate::from_khz(500)) + .unwrap() + .with_pulse_density(0) + .connect(peripherals.GPIO2) .unwrap(); let delay = Delay::new(); From 5b4f2723fdb85ccff1dcc4df9991622d9a504b59 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Sun, 31 May 2026 11:45:48 -0700 Subject: [PATCH 05/10] Refine SDM channel configuration API, add apply_config back --- esp-hal/src/sdm.rs | 258 +++++++++++++++------------- examples/peripheral/sdm/src/main.rs | 9 +- 2 files changed, 143 insertions(+), 124 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index c962082ca1c..525e22bfc0a 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -46,6 +46,7 @@ for_each_sdm_channel!( #[non_exhaustive] pub struct Sdm<'d> { _instance: GPIO_SD<'d>, + clock_source: ClockSource, $( #[doc = concat!("Channel ", stringify!($ch), " creator.")] pub []: ChannelCreator<$ch>, @@ -66,11 +67,18 @@ for_each_sdm_channel!( pub fn new_with_config(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { Self { _instance: instance, + clock_source: config.clock_source, $( []: ChannelCreator::new(config.clock_source), )* } } + + /// Creates a channel configuration builder using this peripheral's + /// clock source. + pub const fn channel_config(&self) -> ChannelConfigBuilder { + ChannelConfigBuilder::new(self.clock_source) + } } } }; @@ -171,7 +179,7 @@ pub enum Error { UnreachableTargetFrequency, /// Another SDM channel is already using a different clock source. ClockSourceConflict, - /// The raw prescaler is outside the hardware range. + /// The prescaler is outside the supported range. PrescalerOutOfRange, } @@ -187,59 +195,32 @@ impl fmt::Display for Error { impl core::error::Error for Error {} -/// Creates a connected sigma-delta channel. +/// Sigma-delta channel configuration. /// -/// Channel creators are exposed by [`Sdm`]. Calling [`connect`](Self::connect) -/// consumes the creator and returns an active [`Channel`], which prevents -/// accidentally connecting the same channel twice. -#[derive(Debug)] -pub struct ChannelCreator { - // This is copied from the Sdm-level configuration so partially moved - // channel creators still use the shared clock source selected at construction time. - // It is intentionally not configurable per channel. - clock_source: ClockSource, - prescaler: u16, +/// The hardware stores the prescaler and pulse density in the same register, +/// so applying a complete channel configuration can update both fields with a +/// single register write. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ChannelConfig { + raw_prescaler: u8, pulse_density: i8, } -impl ChannelCreator { - const fn new(clock_source: ClockSource) -> Self { - Self { - clock_source, - prescaler: 1, - pulse_density: 0, - } - } - - const fn with_raw_config(clock_source: ClockSource, prescaler: u16, pulse_density: i8) -> Self { - Self { - clock_source, - prescaler, - pulse_density, - } - } - - /// Sets the requested output frequency. - /// - /// The frequency is converted immediately using the SDM clock source - /// selected in [`SdmConfig`]. - pub fn with_frequency(mut self, frequency: Rate) -> Result { - self.prescaler = prescaler_from_frequency(frequency, self.clock_source)?; - Ok(self) - } - - /// Sets the raw hardware prescaler. +impl ChannelConfig { + /// Sets the hardware prescaler. /// /// The hardware divider range is `1..=256`. pub fn with_prescaler(mut self, prescaler: u16) -> Result { check_prescaler(prescaler)?; - self.prescaler = prescaler; + self.raw_prescaler = raw_prescaler(prescaler); Ok(self) } /// Sets the pulse density. /// - /// The value ranges is `-128..=127` + /// The value ranges is `-128..=127`. pub const fn with_pulse_density(mut self, density: i8) -> Self { self.pulse_density = density; self @@ -251,15 +232,92 @@ impl ChannelCreator { self.pulse_density = duty_to_density(duty); self } +} + +/// Builds a sigma-delta channel configuration. +/// +/// This builder carries the peripheral-wide clock source so frequencies can be +/// converted to raw prescaler values without storing clock selection in the +/// channel configuration itself. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ChannelConfigBuilder { + clock_source: ClockSource, + config: ChannelConfig, +} + +impl ChannelConfigBuilder { + const fn new(clock_source: ClockSource) -> Self { + Self { + clock_source, + config: ChannelConfig { + raw_prescaler: 0, + pulse_density: 0, + }, + } + } + + /// Sets the requested output frequency. + pub fn with_frequency(mut self, frequency: Rate) -> Result { + self.config.raw_prescaler = + raw_prescaler(prescaler_from_frequency(frequency, self.clock_source)?); + Ok(self.config) + } + + /// Sets the hardware prescaler. + /// + /// The hardware divider range is `1..=256`. + pub fn with_prescaler(mut self, prescaler: u16) -> Result { + self.config = self.config.with_prescaler(prescaler)?; + Ok(self.config) + } + + /// Returns the default channel configuration. + pub const fn build(self) -> ChannelConfig { + self.config + } +} + +impl Default for ChannelConfig { + fn default() -> Self { + Self { + raw_prescaler: 0, + pulse_density: 0, + } + } +} + +/// Creates a connected sigma-delta channel. +/// +/// Channel creators are exposed by [`Sdm`]. Calling [`connect`](Self::connect) +/// consumes the creator and returns an active [`Channel`], which prevents +/// accidentally connecting the same channel twice. +#[derive(Debug)] +pub struct ChannelCreator { + // This is copied from the Sdm-level configuration so partially moved + // channel creators still use the shared clock source selected at construction time. + // It is intentionally not configurable per channel. + clock_source: ClockSource, +} + +impl ChannelCreator { + const fn new(clock_source: ClockSource) -> Self { + Self { clock_source } + } /// Configures this channel and connects it to an output pin. - pub fn connect<'d>(self, pin: impl PeripheralOutput<'d>) -> Result, Error> { + pub fn connect<'d>( + self, + pin: impl PeripheralOutput<'d>, + config: ChannelConfig, + ) -> Result, Error> { let clock_guard = SdmClockGuard::new(self.clock_source)?; - set_prescaler_raw(CHANNEL, self.prescaler); - set_pulse_density_raw(CHANNEL, self.pulse_density); + write_config_raw(CHANNEL, config); Ok(Channel { clock_source: self.clock_source, + config, _pin_guard: connect_pin(CHANNEL, pin), _clock_guard: clock_guard, }) @@ -274,16 +332,24 @@ impl ChannelCreator { #[derive(Debug)] pub struct Channel { clock_source: ClockSource, + config: ChannelConfig, _pin_guard: PinGuard, _clock_guard: SdmClockGuard, } impl Channel { + /// Applies a new channel configuration. + pub fn apply_config(&mut self, config: &ChannelConfig) { + write_config_raw(CHANNEL, *config); + self.config = *config; + } + /// Sets raw pulse density. /// /// The value ranges from `-128` to `127`. pub fn set_pulse_density(&mut self, density: i8) { - set_pulse_density_raw(CHANNEL, density); + let config = self.config.with_pulse_density(density); + self.apply_config(&config); } /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the @@ -292,48 +358,25 @@ impl Channel { self.set_pulse_density(duty_to_density(duty)) } - /// Sets raw prescaler. - /// - /// The hardware divider range is `1..=256`. - pub fn set_prescaler(&mut self, prescaler: u16) -> Result<(), Error> { - check_prescaler(prescaler)?; - set_prescaler_raw(CHANNEL, prescaler); - Ok(()) - } - - /// Sets the output frequency. - pub fn set_frequency(&mut self, frequency: Rate) -> Result<(), Error> { - let prescaler = prescaler_from_frequency(frequency, self.clock_source)?; - self.set_prescaler(prescaler) - } - - /// Reads the raw hardware prescaler. + /// Reads the hardware prescaler. /// /// The returned value is in the hardware divider range `1..=256`. - pub fn prescaler(&mut self) -> Result { - Ok(prescaler_raw(CHANNEL)) + pub fn prescaler(&self) -> u16 { + self.config.raw_prescaler as u16 + 1 } /// Reads the raw pulse density. /// /// The returned value is in the hardware range `-128..=127`. - pub fn pulse_density(&mut self) -> Result { - Ok(pulse_density_raw(CHANNEL)) + pub fn pulse_density(&self) -> i8 { + self.config.pulse_density } /// Disconnects this channel and returns its channel creator. pub fn disconnect(self) -> ChannelCreator { let clock_source = self.clock_source; - let prescaler = prescaler_raw(CHANNEL); - let pulse_density = pulse_density_raw(CHANNEL); drop(self); - ChannelCreator::with_raw_config(clock_source, prescaler, pulse_density) - } -} - -impl Drop for Channel { - fn drop(&mut self) { - set_pulse_density_raw(CHANNEL, 0); + ChannelCreator::new(clock_source) } } @@ -417,8 +460,12 @@ fn check_prescaler(prescaler: u16) -> Result<(), Error> { } } +fn raw_prescaler(prescaler: u16) -> u8 { + (prescaler - 1) as u8 +} + const fn duty_to_density(duty: u8) -> i8 { - (duty as i16 - 128) as i8 + duty.wrapping_sub(128) as i8 } fn configure_clock_source(clock_source: ClockSource) { @@ -477,53 +524,26 @@ fn configure_clock_source(clock_source: ClockSource) { } } -fn set_pulse_density_raw(channel: usize, density: i8) { - // The register layout is the same conceptually, but ESP32-C5's PAC names - // the field `sd_in`; the other supported PACs name it `in`. +fn write_config_raw(channel: usize, config: ChannelConfig) { + // ESP32-C5's PAC names these fields `sd_in`/`sd_prescale`; the other + // supported PACs name them `in`/`prescale`. + let prescaler = config.raw_prescaler as _; + let density = config.pulse_density as _; let sd = GPIO_SD::regs(); #[cfg(esp32c5)] - sd.sigmadelta(channel) - .modify(|_, w| unsafe { w.sd_in().bits(density as _) }); + sd.sigmadelta(channel).write(|w| unsafe { + w.sd_in() + .bits(density) + .sd_prescale() + .bits(prescaler) + }); #[cfg(not(esp32c5))] - sd.sigmadelta(channel) - .modify(|_, w| unsafe { w.in_().bits(density as _) }); -} - -fn set_prescaler_raw(channel: usize, prescaler: u16) { - // Hardware stores prescaler - 1. ESP32-C5's PAC names the field - // `sd_prescale`; the other supported PACs name it `prescale`. - let bits = (prescaler - 1) as _; - let sd = GPIO_SD::regs(); - - #[cfg(esp32c5)] - sd.sigmadelta(channel) - .modify(|_, w| unsafe { w.sd_prescale().bits(bits) }); - - #[cfg(not(esp32c5))] - sd.sigmadelta(channel) - .modify(|_, w| unsafe { w.prescale().bits(bits) }); -} - -fn pulse_density_raw(channel: usize) -> i8 { - let sd = GPIO_SD::regs(); - - #[cfg(esp32c5)] - return sd.sigmadelta(channel).read().sd_in().bits() as i8; - - #[cfg(not(esp32c5))] - return sd.sigmadelta(channel).read().in_().bits() as i8; -} - -fn prescaler_raw(channel: usize) -> u16 { - let sd = GPIO_SD::regs(); - - #[cfg(esp32c5)] - let bits = sd.sigmadelta(channel).read().sd_prescale().bits(); - - #[cfg(not(esp32c5))] - let bits = sd.sigmadelta(channel).read().prescale().bits(); - - bits as u16 + 1 + sd.sigmadelta(channel).write(|w| unsafe { + w.in_() + .bits(density) + .prescale() + .bits(prescaler) + }); } diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs index de6c0ff37e6..2368b319043 100644 --- a/examples/peripheral/sdm/src/main.rs +++ b/examples/peripheral/sdm/src/main.rs @@ -18,13 +18,12 @@ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); let sdm = Sdm::new(peripherals.GPIO_SD); - let mut channel = sdm - .channel0 + let config = sdm + .channel_config() .with_frequency(Rate::from_khz(500)) .unwrap() - .with_pulse_density(0) - .connect(peripherals.GPIO2) - .unwrap(); + .with_pulse_density(0); + let mut channel = sdm.channel0.connect(peripherals.GPIO2, config).unwrap(); let delay = Delay::new(); let mut duty = 0; From 658e0b78f82b947a038588665c2394ff8c40d6ff Mon Sep 17 00:00:00 2001 From: ttzytt Date: Sun, 31 May 2026 22:21:09 -0700 Subject: [PATCH 06/10] remove new_with_config --- esp-hal/src/sdm.rs | 8 +------- examples/peripheral/sdm/src/main.rs | 9 +++++++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 525e22bfc0a..927a2397662 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -55,16 +55,10 @@ for_each_sdm_channel!( impl<'d> Sdm<'d> { /// Creates a new sigma-delta peripheral driver. - pub fn new(instance: GPIO_SD<'d>) -> Self { - Self::new_with_config(instance, SdmConfig::default()) - } - - /// Creates a new sigma-delta peripheral driver with the given - /// peripheral-wide configuration. /// /// The SDM clock source is shared by all SDM channels through the IO_MUX /// clock, so it is selected here instead of being configurable per channel. - pub fn new_with_config(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { + pub fn new(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { Self { _instance: instance, clock_source: config.clock_source, diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs index 2368b319043..4faac02e6df 100644 --- a/examples/peripheral/sdm/src/main.rs +++ b/examples/peripheral/sdm/src/main.rs @@ -7,7 +7,12 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{delay::Delay, main, sdm::Sdm, time::Rate}; +use esp_hal::{ + delay::Delay, + main, + sdm::{Sdm, SdmConfig}, + time::Rate, +}; use esp_println::print; esp_bootloader_esp_idf::esp_app_desc!(); @@ -17,7 +22,7 @@ fn main() -> ! { esp_println::logger::init_logger_from_env(); let peripherals = esp_hal::init(esp_hal::Config::default()); - let sdm = Sdm::new(peripherals.GPIO_SD); + let sdm = Sdm::new(peripherals.GPIO_SD, SdmConfig::default()); let config = sdm .channel_config() .with_frequency(Rate::from_khz(500)) From 7a317ef0ece823071456e14d02bde3ce05015f42 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Tue, 2 Jun 2026 15:53:52 -0500 Subject: [PATCH 07/10] add HIL test for SDM --- hil-test/Cargo.toml | 5 + hil-test/src/bin/sdm.rs | 334 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 hil-test/src/bin/sdm.rs diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 74e8119de0b..8f00f3c14f6 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -66,6 +66,11 @@ name = "rmt" harness = false required-features = ["embassy", "esp-alloc"] +[[bin]] +name = "sdm" +harness = false +required-features = ["unstable"] + [[bin]] name = "spi_full_duplex" harness = false diff --git a/hil-test/src/bin/sdm.rs b/hil-test/src/bin/sdm.rs new file mode 100644 index 00000000000..608d8ab7dcb --- /dev/null +++ b/hil-test/src/bin/sdm.rs @@ -0,0 +1,334 @@ +//! SDM Test +//! +//! Uses the common test pins documented in `hil-test/README.md`: +//! - SDM output => first common test pin +//! - RMT RX => second common test pin + +//% CHIPS: esp32 esp32c3 esp32c5 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable + +#![no_std] +#![no_main] + +use esp_hal::{ + Blocking, + delay::Delay, + gpio::{AnyPin, Level, Output, OutputConfig, interconnect::PeripheralInput}, + peripherals::{GPIO_SD, RMT}, + rmt::{CHANNEL_RAM_SIZE, Channel, PulseCode, Rmt, Rx, RxChannelConfig, RxChannelCreator}, + sdm::{Sdm, SdmConfig}, + time::Rate, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(esp32h2)] { + const RMT_FREQUENCY: Rate = Rate::from_mhz(32); + // default clock source for h2 is 32Mhz + const RMT_DIVIDER: u8 = 32; + } else { + const RMT_FREQUENCY: Rate = Rate::from_mhz(80); + // default clock source for other models is 80Mhz + const RMT_DIVIDER: u8 = 80; + } + // make sure that all models uses 1 Mhz clock +} + +const SDM_FREQUENCY: Rate = Rate::from_khz(500); +const SDM_WARM_UP_US: u32 = 1_000; +const SAMPLE_TIME_US: u32 = 200; +const RMT_IDLE_THRESHOLD: u16 = 100; +const RX_LEN: usize = CHANNEL_RAM_SIZE; +const DEBUG_WAVE_MAX_CHARS: usize = 200; +const DUTY_MIN: u8 = 10; +const DUTY_MAX: u8 = 245; +// recommended density is [-90, 90] +// ref: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/sdm.html +const DUTY_STEP: u8 = 20; +const DUTY_RATIO_TOLERANCE_PER_MILLE: u32 = 20; + +cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32s3))] { + macro_rules! rx_channel_creator { + ($rmt:expr) => { + $rmt.channel4 + }; + } + } else { + macro_rules! rx_channel_creator { + ($rmt:expr) => { + $rmt.channel2 + }; + } + } +} + +struct Context { + gpio_sd: GPIO_SD<'static>, + rmt: RMT<'static>, + sdm_pin: AnyPin<'static>, + rmt_pin: AnyPin<'static>, +} + +struct PulseDebug(PulseCode); + +impl core::fmt::Debug for PulseDebug { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let code = self.0; + let level1 = if code.level1() == Level::High { + 'H' + } else { + 'L' + }; + let level2 = if code.level2() == Level::High { + 'H' + } else { + 'L' + }; + + write!(f, "{level1}{}/{level2}{}", code.length1(), code.length2()) + } +} + +struct PulseCodesDebug<'a>(&'a [PulseCode]); + +impl core::fmt::Debug for PulseCodesDebug<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut list = f.debug_list(); + + for code in self.0 { + list.entry(&PulseDebug(*code)); + } + + list.finish() + } +} + +struct PulseWaveDebug<'a>(&'a [PulseCode]); + +impl core::fmt::Display for PulseWaveDebug<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut written = 0; + + for code in self.0 { + for (level, length) in [ + (code.level1(), code.length1()), + (code.level2(), code.length2()), + ] { + if length == 0 { + continue; + } + + let symbol = if level == Level::High { '^' } else { '_' }; + let width = usize::from(length); + + for _ in 0..width { + if written >= DEBUG_WAVE_MAX_CHARS { + return f.write_str("..."); + } + + written += 1; + write!(f, "{symbol}")?; + } + } + } + + Ok(()) + } +} + +struct Measurement { + ratio: u32, + count: usize, + pulses: [PulseCode; RX_LEN], +} + +impl Measurement { + fn new(data: &[PulseCode; RX_LEN], count: usize) -> Self { + Self { + ratio: high_ratio_per_mille(data, count), + count, + pulses: *data, + } + } + + fn valid_pulses(&self) -> &[PulseCode] { + &self.pulses[..self.count.min(RX_LEN)] + } +} + +fn configure_rx_channel<'a>( + rmt: Rmt<'a, Blocking>, + pin: impl PeripheralInput<'a>, +) -> Channel<'a, Blocking, Rx> { + rx_channel_creator!(rmt) + .configure_rx( + &RxChannelConfig::default() + .with_clk_divider(RMT_DIVIDER) + .with_idle_threshold(RMT_IDLE_THRESHOLD), + ) + .unwrap() + .with_pin(pin) +} + +fn high_ratio_per_mille(data: &[PulseCode], count: usize) -> u32 { + let mut high = 0_u32; + let mut low = 0_u32; + let mut remaining = SAMPLE_TIME_US; + + for code in data.iter().take(count) { + if code.length1() != 0 { + let length = u32::from(code.length1()).min(remaining); + + if code.level1() == Level::High { + high += length; + } else { + low += length; + } + + remaining -= length; + if remaining == 0 { + break; + } + } + + if code.length2() != 0 { + let length = u32::from(code.length2()).min(remaining); + + if code.level2() == Level::High { + high += length; + } else { + low += length; + } + + remaining -= length; + if remaining == 0 { + break; + } + } + } + + assert!(high + low > 0, "RMT did not capture any SDM pulse duration"); + high * 1_000 / (high + low) +} + +fn expected_ratio_per_mille(duty: u8) -> u32 { + (u32::from(duty) * 1_000 + 128) / 256 +} + +fn measure_high_ratio(ctx: &mut Context, duty: u8) -> Measurement { + let sdm = Sdm::new(ctx.gpio_sd.reborrow(), SdmConfig::default()); + let config = sdm + .channel_config() + .with_frequency(SDM_FREQUENCY) + .unwrap() + .with_duty(duty); + let channel = sdm + .channel0 + .connect(ctx.sdm_pin.reborrow(), config) + .unwrap(); + + Delay::new().delay_micros(SDM_WARM_UP_US); + + let rmt = Rmt::new(ctx.rmt.reborrow(), RMT_FREQUENCY).unwrap(); + let rx_channel = configure_rx_channel(rmt, ctx.rmt_pin.reborrow()); + let mut rx_data = [PulseCode::end_marker(); RX_LEN]; + let rx_transaction = rx_channel.receive(&mut rx_data).unwrap(); + + Delay::new().delay_micros(SAMPLE_TIME_US); + + let _creator = channel.disconnect(); + let mut output = Output::new(ctx.sdm_pin.reborrow(), Level::Low, OutputConfig::default()); + output.set_low(); + // send idle signal so that RMT will stop recording pulses + + let (count, _rx_channel) = rx_transaction.wait().unwrap(); + + Measurement::new(&rx_data, count) +} + +#[embedded_test::tests(default_timeout = 10)] +mod tests { + use hil_test::assert; + + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let (sdm_pin, rmt_pin) = hil_test::common_test_pins!(peripherals); + + Context { + gpio_sd: peripherals.GPIO_SD, + rmt: peripherals.RMT, + sdm_pin: AnyPin::from(sdm_pin), + rmt_pin: AnyPin::from(rmt_pin), + } + } + + #[test] + fn duties_have_expected_high_ratios(mut ctx: Context) { + let mut duty = DUTY_MIN; + + loop { + let measurement = measure_high_ratio(&mut ctx, duty); + let expected = expected_ratio_per_mille(duty); + let error = measurement.ratio.abs_diff(expected); + let pulses = measurement.valid_pulses(); + + assert!( + error <= DUTY_RATIO_TOLERANCE_PER_MILLE, + "expected duty {} high ratio to be {} +/- {} per mille, got {}; count={}; pulses={:?}; wave={}", + duty, + expected, + DUTY_RATIO_TOLERANCE_PER_MILLE, + measurement.ratio, + measurement.count, + PulseCodesDebug(pulses), + PulseWaveDebug(pulses), + ); + + if duty == DUTY_MAX { + break; + } + + duty = duty.saturating_add(DUTY_STEP).min(DUTY_MAX); + } + } + + #[test] + fn higher_duties_have_higher_high_ratios(mut ctx: Context) { + let mut previous_duty = DUTY_MIN; + let mut previous = measure_high_ratio(&mut ctx, previous_duty); + let mut duty = previous_duty.saturating_add(DUTY_STEP).min(DUTY_MAX); + + while duty != previous_duty { + let current = measure_high_ratio(&mut ctx, duty); + let previous_pulses = previous.valid_pulses(); + let current_pulses = current.valid_pulses(); + + assert!( + current.ratio > previous.ratio, + "expected duty {} high ratio ({}) to be higher than duty {} ({}); current_count={}; current_pulses={:?}; current_wave={}; previous_count={}; previous_pulses={:?}; previous_wave={}", + duty, + current.ratio, + previous_duty, + previous.ratio, + current.count, + PulseCodesDebug(current_pulses), + PulseWaveDebug(current_pulses), + previous.count, + PulseCodesDebug(previous_pulses), + PulseWaveDebug(previous_pulses), + ); + + if duty == DUTY_MAX { + break; + } + + previous_duty = duty; + previous = current; + duty = duty.saturating_add(DUTY_STEP).min(DUTY_MAX); + } + } +} From a209330b9384650aa65266dab3d3af57caecacb9 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Tue, 2 Jun 2026 16:28:54 -0500 Subject: [PATCH 08/10] move the example from /example to module-level rustdoc; lint and format --- esp-hal/Cargo.toml | 2 +- esp-hal/src/sdm.rs | 96 +++++++++++++---------------- examples/peripheral/sdm/Cargo.toml | 62 ------------------- examples/peripheral/sdm/src/main.rs | 43 ------------- 4 files changed, 44 insertions(+), 159 deletions(-) delete mode 100644 examples/peripheral/sdm/Cargo.toml delete mode 100644 examples/peripheral/sdm/src/main.rs diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 73fd5eb3e81..fe9dd659e67 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -347,4 +347,4 @@ requires-unstable = [] mixed_attributes_style = "allow" [lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(host_os, values("windows"))'] } +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(host_os, values("windows"))'] } \ No newline at end of file diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 927a2397662..4b2d24737eb 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -1,8 +1,34 @@ +#![cfg_attr(docsrs, procmacros::doc_replace)] + //! Sigma-delta modulation peripheral. //! //! The sigma-delta modulator produces a pulse-density modulated output on a //! GPIO matrix signal. Each channel can be configured with a carrier frequency //! and pulse density, then routed to one output pin. +//! +//! ## Examples +//! +//! Generate a sigma-delta output signal on a GPIO pin. +//! +//! ```rust, no_run +//! # {before_snippet} +//! use esp_hal::{ +//! sdm::{Sdm, SdmConfig}, +//! time::Rate, +//! }; +//! +//! let sdm = Sdm::new(peripherals.GPIO_SD, SdmConfig::default()); +//! let config = sdm +//! .channel_config() +//! .with_frequency(Rate::from_khz(500))? +//! .with_duty(128); +//! let mut channel = sdm.channel0.connect(peripherals.GPIO2, config)?; +//! +//! channel.set_duty(192); // duty ranges from 0 to 255 +//! channel.set_pulse_density(0); // pulse density ranges from -128 to 127 +//! // +//! # {after_snippet} +//! ``` use core::fmt; @@ -10,7 +36,9 @@ use esp_sync::NonReentrantMutex; use crate::{ gpio::{ - OutputConfig, OutputSignal, PinGuard, + OutputConfig, + OutputSignal, + PinGuard, interconnect::{OutputSignal as GpioOutputSignal, PeripheralOutput}, }, peripherals::GPIO_SD, @@ -92,7 +120,7 @@ for_each_sdm_channel!( ); /// Sigma-delta peripheral configuration. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct SdmConfig { @@ -108,47 +136,28 @@ impl SdmConfig { } } -impl Default for SdmConfig { - fn default() -> Self { - Self { - clock_source: ClockSource::default(), - } - } -} - /// Source clock for the shared SDM/IO_MUX clock. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum ClockSource { /// APB clock. #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + #[default] Apb, /// XTAL clock. #[cfg(any(esp32c5, esp32c6, esp32h2, esp32p4))] Xtal, /// Fixed 80 MHz PLL clock. #[cfg(any(esp32c5, esp32c6, esp32p4))] + #[cfg_attr(any(esp32c5, esp32c6, esp32p4), default)] PllF80m, /// Fixed 48 MHz PLL clock. #[cfg(esp32h2)] + #[cfg_attr(esp32h2, default)] PllF48m, } -impl Default for ClockSource { - fn default() -> Self { - cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] { - Self::Apb - } else if #[cfg(any(esp32c5, esp32c6, esp32p4))] { - Self::PllF80m - } else if #[cfg(esp32h2)] { - Self::PllF48m - } - } - } -} - impl ClockSource { fn frequency(self) -> Rate { match self { @@ -194,7 +203,7 @@ impl core::error::Error for Error {} /// The hardware stores the prescaler and pulse density in the same register, /// so applying a complete channel configuration can update both fields with a /// single register write. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct ChannelConfig { @@ -273,15 +282,6 @@ impl ChannelConfigBuilder { } } -impl Default for ChannelConfig { - fn default() -> Self { - Self { - raw_prescaler: 0, - pulse_density: 0, - } - } -} - /// Creates a connected sigma-delta channel. /// /// Channel creators are exposed by [`Sdm`]. Calling [`connect`](Self::connect) @@ -464,12 +464,10 @@ const fn duty_to_density(duty: u8) -> i8 { fn configure_clock_source(clock_source: ClockSource) { // Newer chips expose this selector in different clock-control blocks: - // - C5/C6/H2 use PCR.iomux_clk_conf.iomux_func_clk_sel, but the selector - // encodings differ by chip. - // - P4 uses HP_SYS_CLKRST.peri_clk_ctrl26.iomux_clk_src_sel, a one-bit - // XTAL/PLL_F80M selector. - // - ESP32/C3/S2/S3 use APB for this driver path and do not need a source - // selector write here. + // - C5/C6/H2 use PCR.iomux_clk_conf.iomux_func_clk_sel, but the selector encodings differ by + // chip. + // - P4 uses HP_SYS_CLKRST.peri_clk_ctrl26.iomux_clk_src_sel, a one-bit XTAL/PLL_F80M selector. + // - ESP32/C3/S2/S3 use APB for this driver path and do not need a source selector write here. cfg_if::cfg_if! { if #[cfg(esp32c5)] { crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { @@ -526,18 +524,10 @@ fn write_config_raw(channel: usize, config: ChannelConfig) { let sd = GPIO_SD::regs(); #[cfg(esp32c5)] - sd.sigmadelta(channel).write(|w| unsafe { - w.sd_in() - .bits(density) - .sd_prescale() - .bits(prescaler) - }); + sd.sigmadelta(channel) + .write(|w| unsafe { w.sd_in().bits(density).sd_prescale().bits(prescaler) }); #[cfg(not(esp32c5))] - sd.sigmadelta(channel).write(|w| unsafe { - w.in_() - .bits(density) - .prescale() - .bits(prescaler) - }); + sd.sigmadelta(channel) + .write(|w| unsafe { w.in_().bits(density).prescale().bits(prescaler) }); } diff --git a/examples/peripheral/sdm/Cargo.toml b/examples/peripheral/sdm/Cargo.toml deleted file mode 100644 index ff94441bf35..00000000000 --- a/examples/peripheral/sdm/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -name = "sdm" -version = "0.0.0" -edition = "2024" -publish = false - -[dependencies] -esp-backtrace = { path = "../../../esp-backtrace", features = [ - "panic-handler", - "println", -] } -esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" } -esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] } -esp-println = { path = "../../../esp-println", features = ["log-04"] } - -[features] -esp32 = [ - "esp-backtrace/esp32", - "esp-bootloader-esp-idf/esp32", - "esp-hal/esp32", -] -esp32c3 = [ - "esp-backtrace/esp32c3", - "esp-bootloader-esp-idf/esp32c3", - "esp-hal/esp32c3", -] -esp32c5 = [ - "esp-backtrace/esp32c5", - "esp-bootloader-esp-idf/esp32c5", - "esp-hal/esp32c5", -] -esp32c6 = [ - "esp-backtrace/esp32c6", - "esp-bootloader-esp-idf/esp32c6", - "esp-hal/esp32c6", -] -esp32h2 = [ - "esp-backtrace/esp32h2", - "esp-bootloader-esp-idf/esp32h2", - "esp-hal/esp32h2", -] -esp32p4 = [ - "esp-backtrace/esp32p4", - "esp-bootloader-esp-idf/esp32p4", - "esp-hal/esp32p4", -] -esp32s2 = [ - "esp-backtrace/esp32s2", - "esp-bootloader-esp-idf/esp32s2", - "esp-hal/esp32s2", -] -esp32s3 = [ - "esp-backtrace/esp32s3", - "esp-bootloader-esp-idf/esp32s3", - "esp-hal/esp32s3", -] - -[profile.release] -debug = true -debug-assertions = true -lto = "fat" -codegen-units = 1 diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs deleted file mode 100644 index 4faac02e6df..00000000000 --- a/examples/peripheral/sdm/src/main.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! This example shows how to generate a sigma-delta output signal. -//! -//! The following wiring is assumed: -//! - SDM output => GPIO2 - -#![no_std] -#![no_main] - -use esp_backtrace as _; -use esp_hal::{ - delay::Delay, - main, - sdm::{Sdm, SdmConfig}, - time::Rate, -}; -use esp_println::print; - -esp_bootloader_esp_idf::esp_app_desc!(); - -#[main] -fn main() -> ! { - esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init(esp_hal::Config::default()); - - let sdm = Sdm::new(peripherals.GPIO_SD, SdmConfig::default()); - let config = sdm - .channel_config() - .with_frequency(Rate::from_khz(500)) - .unwrap() - .with_pulse_density(0); - let mut channel = sdm.channel0.connect(peripherals.GPIO2, config).unwrap(); - - let delay = Delay::new(); - let mut duty = 0; - - loop { - channel.set_duty(duty); - print!("SDM duty: {duty:3}\r"); - - duty = duty.wrapping_add(1); - delay.delay_millis(100); - } -} From 53cca28f9f4ee218fb8de1c2b221853f94150074 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Tue, 2 Jun 2026 16:44:36 -0500 Subject: [PATCH 09/10] stop cacheing config in SDM channels --- esp-hal/src/sdm.rs | 47 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 4b2d24737eb..020e013430a 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -311,7 +311,6 @@ impl ChannelCreator { Ok(Channel { clock_source: self.clock_source, - config, _pin_guard: connect_pin(CHANNEL, pin), _clock_guard: clock_guard, }) @@ -326,7 +325,6 @@ impl ChannelCreator { #[derive(Debug)] pub struct Channel { clock_source: ClockSource, - config: ChannelConfig, _pin_guard: PinGuard, _clock_guard: SdmClockGuard, } @@ -335,15 +333,13 @@ impl Channel { /// Applies a new channel configuration. pub fn apply_config(&mut self, config: &ChannelConfig) { write_config_raw(CHANNEL, *config); - self.config = *config; } /// Sets raw pulse density. /// /// The value ranges from `-128` to `127`. pub fn set_pulse_density(&mut self, density: i8) { - let config = self.config.with_pulse_density(density); - self.apply_config(&config); + modify_pulse_density_raw(CHANNEL, density); } /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the @@ -356,14 +352,14 @@ impl Channel { /// /// The returned value is in the hardware divider range `1..=256`. pub fn prescaler(&self) -> u16 { - self.config.raw_prescaler as u16 + 1 + prescaler_raw(CHANNEL) + 1 } /// Reads the raw pulse density. /// /// The returned value is in the hardware range `-128..=127`. pub fn pulse_density(&self) -> i8 { - self.config.pulse_density + pulse_density_raw(CHANNEL) } /// Disconnects this channel and returns its channel creator. @@ -531,3 +527,40 @@ fn write_config_raw(channel: usize, config: ChannelConfig) { sd.sigmadelta(channel) .write(|w| unsafe { w.in_().bits(density).prescale().bits(prescaler) }); } + +fn modify_pulse_density_raw(channel: usize, density: i8) { + // ESP32-C5's PAC names this field `sd_in`; the other supported PACs name it `in`. + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.sd_in().bits(density as _) }); + + #[cfg(not(esp32c5))] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.in_().bits(density as _) }); +} + +fn prescaler_raw(channel: usize) -> u16 { + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + let bits = sd.sigmadelta(channel).read().sd_prescale().bits(); + + #[cfg(not(esp32c5))] + let bits = sd.sigmadelta(channel).read().prescale().bits(); + + bits as u16 +} + +fn pulse_density_raw(channel: usize) -> i8 { + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + let bits = sd.sigmadelta(channel).read().sd_in().bits(); + + #[cfg(not(esp32c5))] + let bits = sd.sigmadelta(channel).read().in_().bits(); + + bits as i8 +} From f31640b6e3780a07c0a0071389d9592c75b6fb50 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Tue, 2 Jun 2026 18:06:43 -0500 Subject: [PATCH 10/10] modify timeout for HIL --- hil-test/src/bin/sdm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hil-test/src/bin/sdm.rs b/hil-test/src/bin/sdm.rs index 608d8ab7dcb..4df127dcdcc 100644 --- a/hil-test/src/bin/sdm.rs +++ b/hil-test/src/bin/sdm.rs @@ -247,7 +247,7 @@ fn measure_high_ratio(ctx: &mut Context, duty: u8) -> Measurement { Measurement::new(&rx_data, count) } -#[embedded_test::tests(default_timeout = 10)] +#[embedded_test::tests(default_timeout = 3)] mod tests { use hil_test::assert;