diff --git a/CHANGELOG.md b/CHANGELOG.md index 4acd3678a84..a1dd5a46bd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add initial support for PCNT in ESP32-H2 (#551) - Add initial support for RMT in ESP32-H2 (#556) - Add a fn to poll DMA transfers +- Add initial support for LEDC in ESP32-H2 (#560) ### Changed diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index b48b176bb69..6af45d15e8b 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -55,7 +55,7 @@ esp32 = { version = "0.23.0", features = ["critical-section"], optional = true esp32c2 = { version = "0.11.0", features = ["critical-section"], optional = true } esp32c3 = { version = "0.14.0", features = ["critical-section"], optional = true } esp32c6 = { version = "0.4.0", features = ["critical-section"], optional = true } -esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "d22c06a", package = "esp32h2", features = ["critical-section"], optional = true } +esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "687a383", package = "esp32h2", features = ["critical-section"], optional = true } esp32s2 = { version = "0.14.0", features = ["critical-section"], optional = true } esp32s3 = { version = "0.18.0", features = ["critical-section"], optional = true } diff --git a/esp-hal-common/devices/esp32h2.toml b/esp-hal-common/devices/esp32h2.toml index 8d25180454d..cf2ab889f5b 100644 --- a/esp-hal-common/devices/esp32h2.toml +++ b/esp-hal-common/devices/esp32h2.toml @@ -21,7 +21,7 @@ peripherals = [ "interrupt_core0", "intpri", "io_mux", - # "ledc", + "ledc", # "lp_ana", # "lp_aon", # "lp_apm", diff --git a/esp-hal-common/src/ledc/channel.rs b/esp-hal-common/src/ledc/channel.rs index 1866b6f2530..55ee52d30f5 100644 --- a/esp-hal-common/src/ledc/channel.rs +++ b/esp-hal-common/src/ledc/channel.rs @@ -47,9 +47,9 @@ pub enum Number { Channel3, Channel4, Channel5, - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Channel6, - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Channel7, } @@ -327,7 +327,7 @@ macro_rules! start_duty_without_fading { }; } -#[cfg(esp32c6)] +#[cfg(any(esp32c6, esp32h2))] /// Macro to start duty cycle, without fading macro_rules! start_duty_without_fading { ($self: ident, $num: literal) => { @@ -350,7 +350,7 @@ macro_rules! start_duty_without_fading { }; } -#[cfg(not(any(esp32, esp32c6)))] +#[cfg(not(any(esp32, esp32c6, esp32h2)))] /// Macro to start duty cycle, without fading macro_rules! start_duty_without_fading { ($self: ident, $num: literal) => { @@ -392,7 +392,7 @@ macro_rules! start_duty_fade { }; } -#[cfg(esp32c6)] +#[cfg(any(esp32c6, esp32h2))] /// Macro to start a duty cycle fade macro_rules! start_duty_fade { ($self: ident, $num: literal, $duty_inc: ident, $duty_steps: ident, $cycles_per_step: ident, $duty_per_cycle: ident) => { @@ -423,7 +423,7 @@ macro_rules! start_duty_fade { }; } -#[cfg(not(any(esp32, esp32c6)))] +#[cfg(not(any(esp32, esp32c6, esp32h2)))] /// Macro to start a duty cycle fade macro_rules! start_duty_fade { ($self: ident, $num: literal, $duty_inc: ident, $duty_steps: ident, $cycles_per_step: ident, $duty_per_cycle: ident) => { @@ -852,14 +852,14 @@ where self.output_pin .connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG5); } - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Number::Channel6 => { set_channel!(self, l, 6, timer_number); update_channel!(self, l, 6); self.output_pin .connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG6); } - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Number::Channel7 => { set_channel!(self, l, 7, timer_number); update_channel!(self, l, 7); @@ -883,9 +883,9 @@ where Number::Channel3 => set_duty!(self, l, 3, duty), Number::Channel4 => set_duty!(self, l, 4, duty), Number::Channel5 => set_duty!(self, l, 5, duty), - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Number::Channel6 => set_duty!(self, l, 6, duty), - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Number::Channel7 => set_duty!(self, l, 7, duty), }; } @@ -960,7 +960,7 @@ where cycles_per_step, duty_per_cycle ), - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Number::Channel6 => set_duty_fade!( self, l, @@ -971,7 +971,7 @@ where cycles_per_step, duty_per_cycle ), - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Number::Channel7 => set_duty_fade!( self, l, @@ -993,9 +993,9 @@ where Number::Channel3 => is_duty_fade_running!(self, l, 3), Number::Channel4 => is_duty_fade_running!(self, l, 4), Number::Channel5 => is_duty_fade_running!(self, l, 5), - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Number::Channel6 => is_duty_fade_running!(self, l, 6), - #[cfg(not(any(esp32c2, esp32c3, esp32c6)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] Number::Channel7 => is_duty_fade_running!(self, l, 7), } } diff --git a/esp-hal-common/src/ledc/mod.rs b/esp-hal-common/src/ledc/mod.rs index 4916f53a8b9..bb51cdc2667 100644 --- a/esp-hal-common/src/ledc/mod.rs +++ b/esp-hal-common/src/ledc/mod.rs @@ -131,19 +131,22 @@ impl<'d> LEDC<'d> { #[cfg(not(esp32))] /// Set global slow clock source pub fn set_global_slow_clock(&mut self, clock_source: LSGlobalClkSource) { - #[cfg(esp32c6)] + #[cfg(any(esp32c6, esp32h2))] let pcr = unsafe { &*crate::peripherals::PCR::ptr() }; - #[cfg(esp32c6)] + #[cfg(any(esp32c6, esp32h2))] pcr.ledc_sclk_conf.write(|w| w.ledc_sclk_en().set_bit()); match clock_source { LSGlobalClkSource::APBClk => { - #[cfg(not(esp32c6))] + #[cfg(not(any(esp32c6, esp32h2)))] self.ledc.conf.write(|w| unsafe { w.apb_clk_sel().bits(1) }); #[cfg(esp32c6)] pcr.ledc_sclk_conf .write(|w| unsafe { w.ledc_sclk_sel().bits(1) }); + #[cfg(esp32h2)] + pcr.ledc_sclk_conf + .write(|w| unsafe { w.ledc_sclk_sel().bits(0) }); } } self.ledc.timer0_conf.modify(|_, w| w.para_up().set_bit()); diff --git a/esp-hal-common/src/soc/esp32h2/gpio.rs b/esp-hal-common/src/soc/esp32h2/gpio.rs index 7c4235f5469..f4b8c01f0a3 100644 --- a/esp-hal-common/src/soc/esp32h2/gpio.rs +++ b/esp-hal-common/src/soc/esp32h2/gpio.rs @@ -119,12 +119,12 @@ pub enum InputSignal { #[allow(non_camel_case_types)] #[derive(PartialEq, Copy, Clone)] pub enum OutputSignal { - LEDC_LS_SIG_OUT0 = 0, - LEDC_LS_SIG_OUT1 = 1, - LEDC_LS_SIG_OUT2 = 2, - LEDC_LS_SIG_OUT3 = 3, - LEDC_LS_SIG_OUT4 = 4, - LEDC_LS_SIG_OUT5 = 5, + LEDC_LS_SIG0 = 0, + LEDC_LS_SIG1 = 1, + LEDC_LS_SIG2 = 2, + LEDC_LS_SIG3 = 3, + LEDC_LS_SIG4 = 4, + LEDC_LS_SIG5 = 5, U0TXD = 6, U0RTS = 7, U0DTR = 8, diff --git a/esp-hal-common/src/soc/esp32h2/peripherals.rs b/esp-hal-common/src/soc/esp32h2/peripherals.rs index ad0b7d8b39e..2b0a1b41d6a 100644 --- a/esp-hal-common/src/soc/esp32h2/peripherals.rs +++ b/esp-hal-common/src/soc/esp32h2/peripherals.rs @@ -23,7 +23,7 @@ crate::peripherals! { INTERRUPT_CORE0 => true, INTPRI => true, IO_MUX => true, - // LEDC => true, + LEDC => true, // LP_ANA => true, // LP_AON => true, // LP_APM => true, diff --git a/esp32h2-hal/examples/ledc.rs b/esp32h2-hal/examples/ledc.rs new file mode 100644 index 00000000000..13c82292ffe --- /dev/null +++ b/esp32h2-hal/examples/ledc.rs @@ -0,0 +1,94 @@ +//! Turns on LED with the option to change LED intensity depending on `duty` +//! value. Possible values (`u32`) are in range 0..100. +//! +//! This assumes that a LED is connected to the pin assigned to `led`. (GPIO4) + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + gpio::IO, + ledc::{ + channel::{self, ChannelIFace}, + timer::{self, TimerIFace}, + LSGlobalClkSource, + LowSpeed, + LEDC, + }, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let led = io.pins.gpio4.into_push_pull_output(); + + let mut ledc = LEDC::new( + peripherals.LEDC, + &clocks, + &mut system.peripheral_clock_control, + ); + ledc.set_global_slow_clock(LSGlobalClkSource::APBClk); + let mut lstimer0 = ledc.get_timer::(timer::Number::Timer2); + + lstimer0 + .configure(timer::config::Config { + duty: timer::config::Duty::Duty5Bit, + clock_source: timer::LSClockSource::APBClk, + frequency: 24u32.kHz(), + }) + .unwrap(); + + let mut channel0 = ledc.get_channel(channel::Number::Channel0, led); + channel0 + .configure(channel::config::Config { + timer: &lstimer0, + duty_pct: 10, + pin_config: channel::config::PinConfig::PushPull, + }) + .unwrap(); + + channel0.start_duty_fade(0, 100, 2000).expect_err( + "Fading from 0% to 100%, at 24kHz and 5-bit resolution, over 2 seconds, should fail", + ); + + loop { + // Set up a breathing LED: fade from off to on over a second, then + // from on back off over the next second. Then loop. + channel0.start_duty_fade(0, 100, 1000).unwrap(); + while channel0.is_duty_fade_running() {} + channel0.start_duty_fade(100, 0, 1000).unwrap(); + while channel0.is_duty_fade_running() {} + } +}