diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d12a94d7e4..0a4e099a3b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `timer_interrupt` example in ESP32-H2 and refactor `clk_src` configuration (#576) - Move `esp-riscv-rt` into esp-hal (#578) - Add initial implementation of radio clocks for ESP32-H2 (#577) +- Add initial support for `esp-hal-smartled` in ESP32-H2 (#589) ### Changed diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 843d815fcb4..758ad634080 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 = "8903688", package = "esp32h2", features = ["critical-section"], optional = true } +esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "4a55004", 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/src/pulse_control.rs b/esp-hal-common/src/pulse_control.rs index 04137f3d194..f56785a6c9b 100644 --- a/esp-hal-common/src/pulse_control.rs +++ b/esp-hal-common/src/pulse_control.rs @@ -24,6 +24,9 @@ //! * The **ESP32-C6** has 4 channels, `Channel0` and `Channel1` hardcoded for //! transmitting signals and `Channel2` and `Channel3` hardcoded for receiving //! signals. +//! * The **ESP32-H2** has 4 channels, `Channel0` and `Channel1` hardcoded for +//! transmitting signals and `Channel2` and `Channel3` hardcoded for receiving +//! signals. //! * The **ESP32-S2** has 4 channels, each of them can be either receiver or //! transmitter. //! * The **ESP32-S3** has 8 channels, `Channel0`-`Channel3` hardcdoded for @@ -86,7 +89,7 @@ use core::slice::Iter; use fugit::NanosDurationU32; pub use paste::paste; -#[cfg(esp32c6)] +#[cfg(any(esp32c6, esp32h2))] use crate::peripherals::PCR; use crate::{ gpio::{OutputPin, OutputSignal}, @@ -150,6 +153,17 @@ pub enum ClockSource { RTC20M = 1, } +// Clock source to bool +#[cfg(esp32h2)] +impl Into for ClockSource { + fn into(self) -> bool { + match self { + ClockSource::XTAL => false, + ClockSource::RTC20M => true, + } + } +} + /// Specify the clock source for the RMT peripheral on the ESP32 and ESP32-S3 /// variants #[cfg(any(esp32s2, esp32))] @@ -165,7 +179,7 @@ pub enum ClockSource { // to the RMT channel #[cfg(any(esp32s2, esp32))] const CHANNEL_RAM_SIZE: u8 = 64; -#[cfg(any(esp32c3, esp32c6, esp32s3, esp32h2))] +#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] const CHANNEL_RAM_SIZE: u8 = 48; // Specifies where the RMT RAM section starts for the particular ESP32 variant @@ -302,7 +316,7 @@ macro_rules! channel_instance { let mut channel = $cxi { mem_offset: 0 }; cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32c6, esp32s3, esp32h2))] { + if #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] { // Apply default configuration unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| unsafe { // Configure memory block size @@ -653,7 +667,7 @@ macro_rules! channel_instance { #[cfg(not(esp32h2))] unsafe { interrupts.ch_tx_thr_event_int_raw($num).bit() }, #[cfg(esp32h2)] - interrupts.[]().bit(), + interrupts.[]().bit(), #[cfg(esp32h2)] interrupts.[]().bit(), )) @@ -962,10 +976,10 @@ macro_rules! rmt { // Configure peripheral - #[cfg(esp32c6)] + #[cfg(any(esp32c6, esp32h2))] let pcr = unsafe { &*PCR::ptr() }; - #[cfg(esp32c6)] + #[cfg(any(esp32c6, esp32h2))] pcr.rmt_sclk_conf.write(|w| w.sclk_en().set_bit()); @@ -986,7 +1000,7 @@ macro_rules! rmt { .apb_fifo_mask() .set_bit()); // Select clock source - #[cfg(not(esp32c6))] + #[cfg(not(any(esp32c6, esp32h2)))] self.reg.sys_conf.modify(|_, w| unsafe { w.sclk_sel() .bits(clk_source as u8) @@ -1010,7 +1024,20 @@ macro_rules! rmt { .bits(div_frac_a) .sclk_div_b() .bits(div_frac_b) - }); + }); + #[cfg(esp32h2)] + pcr.rmt_sclk_conf.modify(|_,w| unsafe { + w.sclk_sel() + .bit(clk_source.into()) + // Set absolute part of divider + .sclk_div_num() + .bits(div_abs) + // Set fractional parts of divider to 0 + .sclk_div_a() + .bits(div_frac_a) + .sclk_div_b() + .bits(div_frac_b) + }); // Disable all interrupts self.reg.int_ena.write(|w| unsafe { w.bits(0) }); diff --git a/esp-hal-smartled/src/lib.rs b/esp-hal-smartled/src/lib.rs index 6e919f0e199..839ec4f8375 100644 --- a/esp-hal-smartled/src/lib.rs +++ b/esp-hal-smartled/src/lib.rs @@ -53,7 +53,7 @@ const SOURCE_CLK_FREQ: u32 = 40_000_000; #[cfg(feature = "esp32c6")] const SOURCE_CLK_FREQ: u32 = 40_000_000; #[cfg(feature = "esp32h2")] -const SOURCE_CLK_FREQ: u32 = 40_000_000; +const SOURCE_CLK_FREQ: u32 = 16_000_000; #[cfg(feature = "esp32s2")] const SOURCE_CLK_FREQ: u32 = 40_000_000; #[cfg(feature = "esp32s3")] diff --git a/esp32h2-hal/Cargo.toml b/esp32h2-hal/Cargo.toml index 64e60be6720..f6d47d27262 100644 --- a/esp32h2-hal/Cargo.toml +++ b/esp32h2-hal/Cargo.toml @@ -41,7 +41,7 @@ crypto-bigint = { version = "0.5.2", default-features = false } embassy-executor = { version = "0.2.0", features = ["nightly", "integrated-timers", "arch-riscv32", "executor-thread"] } embedded-graphics = "0.7.1" esp-backtrace = { version = "0.7.0", features = ["esp32h2", "panic-handler", "exception-handler", "print-uart"] } -# esp-hal-smartled = { version = "0.1.0", features = ["esp32h2"], path = "../esp-hal-smartled" } +esp-hal-smartled = { version = "0.2.0", features = ["esp32h2"], path = "../esp-hal-smartled" } esp-println = { version = "0.5.0", features = ["esp32h2"] } lis3dh-async = "0.7.0" sha2 = { version = "0.10.6", default-features = false} diff --git a/esp32h2-hal/examples/hello_rgb.rs b/esp32h2-hal/examples/hello_rgb.rs new file mode 100644 index 00000000000..95f0766dd96 --- /dev/null +++ b/esp32h2-hal/examples/hello_rgb.rs @@ -0,0 +1,103 @@ +//! //! RGB LED Demo +//! +//! This example drives an SK68XX RGB LED that is connected to the GPIO8 pin. +//! A RGB LED is connected to that pin on the ESP32-H2-DevKitM-1 and board. +//! +//! The demo will leverage the [`smart_leds`](https://crates.io/crates/smart-leds) +//! crate functionality to circle through the HSV hue color space (with +//! saturation and value both at 255). Additionally, we apply a gamma correction +//! and limit the brightness to 10 (out of 255). +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + peripherals, + prelude::*, + pulse_control::ClockSource, + timer::TimerGroup, + Delay, + PulseControl, + Rtc, + IO, +}; +use esp_backtrace as _; +use esp_hal_smartled::{smartLedAdapter, SmartLedsAdapter}; +use smart_leds::{ + brightness, + gamma, + hsv::{hsv2rgb, Hsv}, + SmartLedsWrite, +}; + +#[entry] +fn main() -> ! { + let peripherals = 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(); + + // Configure RMT peripheral globally + let pulse = PulseControl::new( + peripherals.RMT, + &mut system.peripheral_clock_control, + ClockSource::XTAL, + 0, + 0, + 0, + ) + .unwrap(); + + // We use one of the RMT channels to instantiate a `SmartLedsAdapter` which can + // be used directly with all `smart_led` implementations + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut led = ::new(pulse.channel0, io.pins.gpio8); + + // Initialize the Delay peripheral, and use it to toggle the LED state in a + // loop. + let mut delay = Delay::new(&clocks); + + let mut color = Hsv { + hue: 0, + sat: 255, + val: 255, + }; + let mut data; + + loop { + // Iterate over the rainbow! + for hue in 0..=255 { + color.hue = hue; + // Convert from the HSV color space (where we can easily transition from one + // color to the other) to the RGB color space that we can then send to the LED + data = [hsv2rgb(color)]; + // When sending to the LED, we do a gamma correction first (see smart_leds + // documentation for details) and then limit the brightness to 10 out of 255 so + // that the output it's not too bright. + led.write(brightness(gamma(data.iter().cloned()), 10)) + .unwrap(); + delay.delay_ms(20u8); + } + } +}