diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fcd8bddfac..30e66433c08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,8 +44,8 @@ jobs: run: cd esp-hal-smartled/ && cargo +nightly check --features=esp32c3 - name: check (esp32c6) run: cd esp-hal-smartled/ && cargo +nightly check --features=esp32c6 - # - name: check (esp32h2) - # run: cd esp-hal-smartled/ && cargo +nightly check --features=esp32h2 + - name: check (esp32h2) + run: cd esp-hal-smartled/ && cargo +nightly check --features=esp32h2 # Check all Xtensa targets: - name: check (esp32) run: cd esp-hal-smartled/ && cargo +esp check --features=esp32,esp32_40mhz diff --git a/CHANGELOG.md b/CHANGELOG.md index 17ac91983be..fbde6c4091c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add initial support for MCPWM in ESP32-H2 (#544) - Add some miscellaneous examples for the ESP32-H2 (#548) - Add initial support for PCNT in ESP32-H2 (#551) +- Add initial support for RMT in ESP32-H2 (#556) ### Fixed diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 195980174b8..b48b176bb69 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 = "4fe0791", package = "esp32h2", features = ["critical-section"], optional = true } +esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "d22c06a", 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 0e03a7b23ff..8d25180454d 100644 --- a/esp-hal-common/devices/esp32h2.toml +++ b/esp-hal-common/devices/esp32h2.toml @@ -39,7 +39,7 @@ peripherals = [ "pcnt", "pcr", # "pmu", - # "rmt", + "rmt", # "rng", "rsa", "sha", diff --git a/esp-hal-common/src/pulse_control.rs b/esp-hal-common/src/pulse_control.rs index c4256214c7c..04137f3d194 100644 --- a/esp-hal-common/src/pulse_control.rs +++ b/esp-hal-common/src/pulse_control.rs @@ -140,6 +140,16 @@ pub enum ClockSource { XTAL = 3, } +/// Specify the clock source for the RMT peripheral on the ESP32-H2 +#[cfg(esp32h2)] +#[derive(Debug, Copy, Clone)] +pub enum ClockSource { + /// External clock source + XTAL = 0, + /// 20 MHz internal oscillator + RTC20M = 1, +} + /// Specify the clock source for the RMT peripheral on the ESP32 and ESP32-S3 /// variants #[cfg(any(esp32s2, esp32))] @@ -155,7 +165,7 @@ pub enum ClockSource { // to the RMT channel #[cfg(any(esp32s2, esp32))] const CHANNEL_RAM_SIZE: u8 = 64; -#[cfg(any(esp32c3, esp32c6, esp32s3))] +#[cfg(any(esp32c3, esp32c6, esp32s3, esp32h2))] const CHANNEL_RAM_SIZE: u8 = 48; // Specifies where the RMT RAM section starts for the particular ESP32 variant @@ -165,6 +175,8 @@ const RMT_RAM_START: usize = 0x3f416400; const RMT_RAM_START: usize = 0x60016400; #[cfg(esp32c6)] const RMT_RAM_START: usize = 0x60006400; +#[cfg(esp32h2)] +const RMT_RAM_START: usize = 0x60007400; #[cfg(esp32)] const RMT_RAM_START: usize = 0x3ff56800; #[cfg(esp32s3)] @@ -290,7 +302,7 @@ macro_rules! channel_instance { let mut channel = $cxi { mem_offset: 0 }; cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32c6, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32s3, esp32h2))] { // Apply default configuration unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| unsafe { // Configure memory block size @@ -466,7 +478,7 @@ macro_rules! channel_instance { } } - #[cfg(any(esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] conf_reg.modify(|_, w| { // Set config update bit w.conf_update().set_bit() @@ -547,14 +559,14 @@ macro_rules! channel_instance { } // always enable tx wrap - #[cfg(any(esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| { w.mem_tx_wrap_en() .set_bit() }); // apply configuration updates - #[cfg(any(esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| { w.conf_update() .set_bit() @@ -577,18 +589,28 @@ macro_rules! channel_instance { let interrupts = unsafe { &*RMT::PTR }.int_raw.read(); match ( + #[cfg(not(esp32h2))] unsafe { interrupts.ch_tx_end_int_raw($num).bit() }, + #[cfg(esp32h2)] + interrupts.[]().bit(), // The ESP32 variant does not support the loop functionality - #[cfg(not(esp32))] - unsafe {interrupts.ch_tx_loop_int_raw($num).bit()}, #[cfg(esp32)] false, + #[cfg(esp32h2)] + interrupts.[]().bit(), + #[cfg(not(any(esp32, esp32h2)))] + unsafe {interrupts.ch_tx_loop_int_raw($num).bit()}, // The C3/S3 have a slightly different interrupt naming scheme #[cfg(any(esp32, esp32s2))] unsafe { interrupts.ch_err_int_raw($num).bit() }, #[cfg(any(esp32c3, esp32c6, esp32s3))] unsafe { interrupts.ch_tx_err_int_raw($num).bit() }, + #[cfg(not(esp32h2))] unsafe { interrupts.ch_tx_thr_event_int_raw($num).bit() }, + #[cfg(esp32h2)] + interrupts.[]().bit(), + #[cfg(esp32h2)] + interrupts.[]().bit(), ) { // SingleShot completed and no error -> success (true, false, false, _) => break, @@ -612,18 +634,28 @@ macro_rules! channel_instance { // Anything else constitutes an error state _ => { return Err(TransmissionError::Failure( + #[cfg(not(esp32h2))] unsafe { interrupts.ch_tx_end_int_raw($num).bit() }, + #[cfg(esp32h2)] + interrupts.[]().bit(), // The ESP32 variant does not support the loop functionality - #[cfg(not(esp32))] - unsafe {interrupts.ch_tx_loop_int_raw($num).bit()}, #[cfg(esp32)] false, + #[cfg(esp32h2)] + interrupts.[]().bit(), + #[cfg(not(any(esp32, esp32h2)))] + unsafe {interrupts.ch_tx_loop_int_raw($num).bit()}, // The C3/S3 have a slightly different interrupt naming scheme #[cfg(any(esp32, esp32s2))] unsafe { interrupts.ch_err_int_raw($num).bit() }, #[cfg(any(esp32c3, esp32c6, esp32s3))] unsafe { interrupts.ch_tx_err_int_raw($num).bit() }, + #[cfg(not(esp32h2))] unsafe { interrupts.ch_tx_thr_event_int_raw($num).bit() }, + #[cfg(esp32h2)] + interrupts.[]().bit(), + #[cfg(esp32h2)] + interrupts.[]().bit(), )) } } @@ -639,7 +671,7 @@ macro_rules! channel_instance { /// previously a sequence was sent with `RepeatMode::Forever`. fn stop_transmission(&self) { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32c6, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| w.tx_stop().set_bit()); @@ -673,7 +705,7 @@ macro_rules! output_channel { #[inline(always)] fn set_idle_output_level(&mut self, level: bool) -> &mut Self { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32c6, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| w.idle_out_lv().bit(level)); @@ -690,7 +722,7 @@ macro_rules! output_channel { #[inline(always)] fn set_idle_output(&mut self, state: bool) -> &mut Self { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32c6, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| w.idle_out_en().bit(state)); @@ -707,7 +739,7 @@ macro_rules! output_channel { #[inline(always)] fn set_channel_divider(&mut self, divider: u8) -> &mut Self { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32c6, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| unsafe { w.div_cnt().bits(divider) }); @@ -724,7 +756,7 @@ macro_rules! output_channel { #[inline(always)] fn set_carrier_modulation(&mut self, state: bool) -> &mut Self { cfg_if::cfg_if! { - if #[cfg(any(esp32c3, esp32c6, esp32s3))] { + if #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] { unsafe { &*RMT::PTR } .ch_tx_conf0[$num] .modify(|_, w| w.carrier_en().bit(state)); @@ -852,7 +884,7 @@ macro_rules! rmt { impl<'d> PulseControl<'d> { /// Create a new pulse controller instance - #[cfg(any(esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] pub fn new( instance: impl Peripheral

+ 'd, peripheral_clock_control: &mut PeripheralClockControl, @@ -909,7 +941,7 @@ macro_rules! rmt { /// clock is calculated as follows: /// /// divider = absolute_part + 1 + (fractional_part_a / fractional_part_b) - #[cfg(any(esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] fn config_global( &self, clk_source: ClockSource, @@ -1047,7 +1079,7 @@ macro_rules! rmt { }; } -#[cfg(any(esp32c3, esp32c6))] +#[cfg(any(esp32c3, esp32c6, esp32h2))] rmt!( sys_conf, (0, Channel0, channel0, OutputSignal::RMT_SIG_0), diff --git a/esp-hal-common/src/soc/esp32h2/gpio.rs b/esp-hal-common/src/soc/esp32h2/gpio.rs index e6f157a26a6..d63c85d490e 100644 --- a/esp-hal-common/src/soc/esp32h2/gpio.rs +++ b/esp-hal-common/src/soc/esp32h2/gpio.rs @@ -80,8 +80,8 @@ pub enum InputSignal { FSPICS0 = 68, PARL_RX_CLK = 69, PARL_TX_CLK = 70, - RMT_SIG0 = 71, - RMT_SIG1 = 72, + RMT_SIG_0 = 71, + RMT_SIG_1 = 72, TWAI0_RX = 73, PWM0_SYNC0 = 87, PWM0_SYNC1 = 88, @@ -170,8 +170,8 @@ pub enum OutputSignal { FSPICS0 = 68, PARL_RX_CLK = 69, PARL_TX_CLK = 70, - RMT_SIG_OUT0 = 71, - RMT_SIG_OUT1 = 72, + RMT_SIG_0 = 71, + RMT_SIG_1 = 72, TWAI0_TX = 73, TWAI0_BUS_OFF_ON = 74, TWAI0_CLKOUT = 75, diff --git a/esp-hal-common/src/soc/esp32h2/peripherals.rs b/esp-hal-common/src/soc/esp32h2/peripherals.rs index c27e15040cb..ad0b7d8b39e 100644 --- a/esp-hal-common/src/soc/esp32h2/peripherals.rs +++ b/esp-hal-common/src/soc/esp32h2/peripherals.rs @@ -41,7 +41,7 @@ crate::peripherals! { PCNT => true, PCR => true, // PMU => true, - // RMT => true, + RMT => true, // RNG => true, RSA => true, SHA => true, diff --git a/esp32-hal/examples/pulse_control.rs b/esp32-hal/examples/pulse_control.rs index f2b244ce7c5..a72e4357d22 100644 --- a/esp32-hal/examples/pulse_control.rs +++ b/esp32-hal/examples/pulse_control.rs @@ -1,6 +1,13 @@ -//! This demos basic usage of RMT / PulseControl -//! Use a logic analyzer to see the generated pulses. -//! The correct output is only achieved when running in release mode. +//! RMT / PulseControl +//! +//! Folowing pins are used: +//! GPIO4 (RMT channel 0) +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This is an example of generating pulses. Attach a Logic Analyzer to the RTM +//! channel 0 pin to see the gerated signal. #![no_std] #![no_main] diff --git a/esp32c3-hal/examples/pulse_control.rs b/esp32c3-hal/examples/pulse_control.rs index c1416df9ae8..e7c70684b14 100644 --- a/esp32c3-hal/examples/pulse_control.rs +++ b/esp32c3-hal/examples/pulse_control.rs @@ -1,6 +1,13 @@ -//! This demos basic usage of RMT / PulseControl -//! Use a logic analyzer to see the generated pulses. -//! The correct output is only achieved when running in release mode. +//! RMT / PulseControl +//! +//! Folowing pins are used: +//! GPIO4 (RMT channel 0) +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This is an example of generating pulses. Attach a Logic Analyzer to the RTM +//! channel 0 pin to see the gerated signal. #![no_std] #![no_main] diff --git a/esp32c6-hal/examples/pulse_control.rs b/esp32c6-hal/examples/pulse_control.rs index 9b628738c31..061e8ad4245 100644 --- a/esp32c6-hal/examples/pulse_control.rs +++ b/esp32c6-hal/examples/pulse_control.rs @@ -1,6 +1,13 @@ -//! This demos basic usage of RMT / PulseControl -//! Use a logic analyzer to see the generated pulses. -//! The correct output is only achieved when running in release mode. +//! RMT / PulseControl +//! +//! Folowing pins are used: +//! GPIO4 (RMT channel 0) +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This is an example of generating pulses. Attach a Logic Analyzer to the RTM +//! channel 0 pin to see the gerated signal. #![no_std] #![no_main] diff --git a/esp32h2-hal/examples/pulse_control.rs b/esp32h2-hal/examples/pulse_control.rs new file mode 100644 index 00000000000..d9cf7dfdeb3 --- /dev/null +++ b/esp32h2-hal/examples/pulse_control.rs @@ -0,0 +1,105 @@ +//! RMT / PulseControl +//! +//! Folowing pins are used: +//! GPIO4 (RMT channel 0) +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This is an example of generating pulses. Attach a Logic Analyzer to the RTM +//! channel 0 pin to see the gerated signal. + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + pulse_control::{ClockSource, ConfiguredChannel, OutputChannel, PulseCode, RepeatMode}, + timer::TimerGroup, + PulseControl, + 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); + + // Configure RMT peripheral globally + let pulse = PulseControl::new( + peripherals.RMT, + &mut system.peripheral_clock_control, + ClockSource::XTAL, + 0, + 0, + 0, + ) + .unwrap(); + + let mut rmt_channel0 = pulse.channel0; + + // Set up channel + rmt_channel0 + .set_idle_output_level(false) + .set_carrier_modulation(false) + .set_channel_divider(1) + .set_idle_output(true); + + // Assign GPIO pin where pulses should be sent to + let mut rmt_channel0 = rmt_channel0.assign_pin(io.pins.gpio4); + + // Create pulse sequence + let mut seq = [PulseCode { + level1: true, + length1: 0u32.nanos(), + level2: false, + length2: 0u32.nanos(), + }; 128]; + + // -1 to make sure that the last element is a transmission end marker (i.e. + // lenght 0) + for i in 0..(seq.len() - 1) { + seq[i] = PulseCode { + level1: true, + length1: (10u32 * (i as u32 + 1u32)).nanos(), + level2: false, + length2: 60u32.nanos(), + }; + } + + loop { + // Send sequence + rmt_channel0 + .send_pulse_sequence(RepeatMode::SingleShot, &seq) + .unwrap(); + } +} diff --git a/esp32h2-hal/examples/ram.rs b/esp32h2-hal/examples/ram.rs new file mode 100644 index 00000000000..6a0104752c8 --- /dev/null +++ b/esp32h2-hal/examples/ram.rs @@ -0,0 +1,107 @@ +//! This shows how to use RTC memory. +//! RTC memory is retained during resets and during most sleep modes. +//! Initialized memory is always re-initialized on startup. +//! Uninitialzed memory isn't initialized on startup and can be used to keep +//! data during resets. Zeroed memory is initialized to zero on startup. +//! We can also run code from RTC memory. + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + macros::ram, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, +}; +use esp_backtrace as _; +use esp_println::println; +use nb::block; + +#[ram(rtc_fast)] +static mut SOME_INITED_DATA: [u8; 2] = [0xaa, 0xbb]; + +#[ram(rtc_fast, uninitialized)] +static mut SOME_UNINITED_DATA: [u8; 2] = [0; 2]; + +#[ram(rtc_fast, zeroed)] +static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8]; + +#[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 timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut timer0 = timer_group0.timer0; + 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 MWDT flash boot protection + wdt0.disable(); + wdt1.disable(); + // The RWDT flash boot protection remains enabled and it being triggered is part + // of the example + + timer0.start(1u64.secs()); + + println!("RAM function located at {:p}", function_in_ram as *const ()); + unsafe { + println!("SOME_INITED_DATA {:x?}", SOME_INITED_DATA); + println!("SOME_UNINITED_DATA {:x?}", SOME_UNINITED_DATA); + println!("SOME_ZEROED_DATA {:x?}", SOME_ZEROED_DATA); + + SOME_INITED_DATA[0] = 0xff; + SOME_ZEROED_DATA[0] = 0xff; + + println!("SOME_INITED_DATA {:x?}", SOME_INITED_DATA); + println!("SOME_UNINITED_DATA {:x?}", SOME_UNINITED_DATA); + println!("SOME_ZEROED_DATA {:x?}", SOME_ZEROED_DATA); + + if SOME_UNINITED_DATA[0] != 0 { + SOME_UNINITED_DATA[0] = 0; + SOME_UNINITED_DATA[1] = 0; + } + + if SOME_UNINITED_DATA[1] == 0xff { + SOME_UNINITED_DATA[1] = 0; + } + + println!("Counter {}", SOME_UNINITED_DATA[1]); + SOME_UNINITED_DATA[1] += 1; + } + + println!( + "RTC_FAST function located at {:p}", + function_in_rtc_ram as *const () + ); + println!("Result {}", function_in_rtc_ram()); + + loop { + function_in_ram(); + block!(timer0.wait()).unwrap(); + } +} + +#[ram] +fn function_in_ram() { + println!("Hello world!"); +} + +#[ram(rtc_fast)] +fn function_in_rtc_ram() -> u32 { + 42 +} diff --git a/esp32s2-hal/examples/pulse_control.rs b/esp32s2-hal/examples/pulse_control.rs index ebab8aef27a..3734c6cad16 100644 --- a/esp32s2-hal/examples/pulse_control.rs +++ b/esp32s2-hal/examples/pulse_control.rs @@ -1,6 +1,13 @@ -//! This demos basic usage of RMT / PulseControl -//! Use a logic analyzer to see the generated pulses. -//! The correct output is only achieved when running in release mode. +//! RMT / PulseControl +//! +//! Folowing pins are used: +//! GPIO4 (RMT channel 0) +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This is an example of generating pulses. Attach a Logic Analyzer to the RTM +//! channel 0 pin to see the gerated signal. #![no_std] #![no_main] diff --git a/esp32s3-hal/examples/pulse_control.rs b/esp32s3-hal/examples/pulse_control.rs index 4171dcd6c98..853b5e7e850 100644 --- a/esp32s3-hal/examples/pulse_control.rs +++ b/esp32s3-hal/examples/pulse_control.rs @@ -1,6 +1,13 @@ -//! This demos basic usage of RMT / PulseControl -//! Use a logic analyzer to see the generated pulses. -//! The correct output is only achieved when running in release mode. +//! RMT / PulseControl +//! +//! Folowing pins are used: +//! GPIO4 (RMT channel 0) +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This is an example of generating pulses. Attach a Logic Analyzer to the RTM +//! channel 0 pin to see the gerated signal. #![no_std] #![no_main]