From 318bb4e4fe0b9ba28847607c7d263453b8855bc3 Mon Sep 17 00:00:00 2001 From: Justin Buchanan Date: Thu, 13 Mar 2025 11:26:23 -0700 Subject: [PATCH] ledc: add support for fading leds (#519) --- CHANGELOG.md | 1 + examples/ledc_fade.rs | 39 ++++++++++++++++++++++++++ src/ledc.rs | 65 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 examples/ledc_fade.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dd986db3..3d7bbb834 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `Send` for `AsyncCanDriver` +- `fade_with_time`, `fade_with_step` for `LedcDriver` ### Fixed - Fix the SDMMC driver for ESP-IDF V5.5+ diff --git a/examples/ledc_fade.rs b/examples/ledc_fade.rs new file mode 100644 index 000000000..5a68ed246 --- /dev/null +++ b/examples/ledc_fade.rs @@ -0,0 +1,39 @@ +use esp_idf_hal::delay::FreeRtos; +use esp_idf_hal::ledc::{config::TimerConfig, LedcDriver, LedcTimerDriver}; +use esp_idf_hal::peripherals::Peripherals; +use esp_idf_hal::prelude::*; +use std::time::Duration; + +fn main() -> anyhow::Result<()> { + esp_idf_hal::sys::link_patches(); + + let peripherals = Peripherals::take()?; + + let timer_driver = LedcTimerDriver::new( + peripherals.ledc.timer0, + &TimerConfig::default().frequency(25.kHz().into()), + )?; + + let mut ledc_driver = LedcDriver::new( + peripherals.ledc.channel0, + timer_driver, + peripherals.pins.gpio7, + )?; + + for _ in 0..2 { + // Fade up over 2 seconds + ledc_driver.fade_with_time( + ledc_driver.get_max_duty(), + Duration::from_secs(2).as_millis() as i32, + true, + )?; + + // Fade down over 2 seconds + ledc_driver.fade_with_time(0, Duration::from_secs(2).as_millis() as i32, true)?; + } + + ledc_driver.set_duty(ledc_driver.get_max_duty() / 10)?; + FreeRtos::delay_ms(10000); + + Ok(()) +} diff --git a/src/ledc.rs b/src/ledc.rs index f2812f5f2..b5d276b1a 100644 --- a/src/ledc.rs +++ b/src/ledc.rs @@ -3,8 +3,6 @@ //! Interface to the [LED Control (LEDC) //! peripheral](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/ledc.html) //! -//! This is an initial implementation supporting the generation of PWM signals -//! but no chrome and spoilers like fading. //! //! # Examples //! @@ -323,6 +321,69 @@ impl<'d> LedcDriver<'d> { pub fn timer(&self) -> ledc_timer_t { self.timer as _ } + + /// Fade the LED to a target duty cycle over a specified time + pub fn fade_with_time( + &mut self, + target_duty: u32, + fade_time_ms: i32, + wait: bool, + ) -> Result<(), EspError> { + let max_duty = self.get_max_duty(); + if target_duty > max_duty { + return Err(EspError::from_infallible::()); + } + + let fade_mode = if wait { + ledc_fade_mode_t_LEDC_FADE_WAIT_DONE + } else { + ledc_fade_mode_t_LEDC_FADE_NO_WAIT + }; + + unsafe { + esp!(ledc_set_fade_with_time( + self.speed_mode, + self.channel(), + target_duty, + fade_time_ms + ))?; + esp!(ledc_fade_start(self.speed_mode, self.channel(), fade_mode))?; + } + Ok(()) + } + + /// Fade the LED to a target duty cycle using steps + pub fn fade_with_step( + &mut self, + target_duty: u32, + step_size: u32, + step_time_ms: u32, + wait: bool, + ) -> Result<(), EspError> { + let max_duty = self.get_max_duty(); + if target_duty > max_duty { + return Err(EspError::from_infallible::()); + } + + let fade_mode = if wait { + ledc_fade_mode_t_LEDC_FADE_WAIT_DONE + } else { + ledc_fade_mode_t_LEDC_FADE_NO_WAIT + }; + + unsafe { + esp!(ledc_set_fade_with_step( + self.speed_mode, + self.channel(), + target_duty, + step_size, + step_time_ms, + ))?; + + esp!(ledc_fade_start(self.speed_mode, self.channel(), fade_mode))?; + } + Ok(()) + } } impl Drop for LedcDriver<'_> {