#![deny(missing_docs)]
use core::slice::Iter;
use fugit::NanosDurationU32;
pub use paste::paste;
#[cfg(esp32c6)]
use crate::peripherals::PCR;
use crate::{
gpio::{OutputPin, OutputSignal},
peripheral::{Peripheral, PeripheralRef},
peripherals::RMT,
system::PeripheralClockControl,
};
#[derive(Debug)]
pub enum SetupError {
InvalidGlobalConfig,
}
#[derive(Debug)]
pub enum TransmissionError {
Failure(bool, bool, bool, bool),
RepetitionOverflow,
IncompatibleRepeatMode,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RepeatMode {
SingleShot,
#[cfg(not(esp32))]
RepeatNtimes(u16),
Forever,
}
#[cfg(any(esp32c3, esp32c6, esp32s3))]
#[derive(Debug, Copy, Clone)]
pub enum ClockSource {
APB = 1,
RTC20M = 2,
XTAL = 3,
}
#[cfg(any(esp32s2, esp32))]
#[derive(Debug, Copy, Clone)]
pub enum ClockSource {
RefTick = 0,
APB = 1,
}
#[cfg(any(esp32s2, esp32))]
const CHANNEL_RAM_SIZE: u8 = 64;
#[cfg(any(esp32c3, esp32c6, esp32s3))]
const CHANNEL_RAM_SIZE: u8 = 48;
#[cfg(esp32s2)]
const RMT_RAM_START: usize = 0x3f416400;
#[cfg(esp32c3)]
const RMT_RAM_START: usize = 0x60016400;
#[cfg(esp32c6)]
const RMT_RAM_START: usize = 0x60006400;
#[cfg(esp32)]
const RMT_RAM_START: usize = 0x3ff56800;
#[cfg(esp32s3)]
const RMT_RAM_START: usize = 0x60016800;
#[derive(Clone, Copy, Debug)]
pub struct PulseCode {
pub level1: bool,
pub length1: NanosDurationU32,
pub level2: bool,
pub length2: NanosDurationU32,
}
impl From<PulseCode> for u32 {
#[inline(always)]
fn from(p: PulseCode) -> u32 {
let mut entry: u32 = p.length1.ticks() as u32;
if p.level1 {
entry |= 1 << 15;
} else {
entry &= !(1 << 15);
}
if p.level2 {
entry |= 1 << 31;
} else {
entry &= !(1 << 31);
}
entry |= (p.length2.ticks() as u32) << 16;
entry
}
}
pub trait OutputChannel {
type ConfiguredChannel<'d, P>
where
P: OutputPin + 'd;
fn set_idle_output_level(&mut self, level: bool) -> &mut Self;
fn set_idle_output(&mut self, state: bool) -> &mut Self;
fn set_channel_divider(&mut self, divider: u8) -> &mut Self;
fn set_carrier_modulation(&mut self, state: bool) -> &mut Self;
#[cfg(any(esp32s2, esp32))]
fn set_clock_source(&mut self, source: ClockSource) -> &mut Self;
fn assign_pin<'d, P: OutputPin>(
self,
pin: impl Peripheral<P = P> + 'd,
) -> Self::ConfiguredChannel<'d, P>;
}
pub trait ConfiguredChannel {
fn send_pulse_sequence<const N: usize>(
&mut self,
repeat_mode: RepeatMode,
sequence: &[PulseCode; N],
) -> Result<(), TransmissionError>;
fn send_pulse_sequence_raw<const N: usize>(
&mut self,
repeat_mode: RepeatMode,
sequence: &[u32; N],
) -> Result<(), TransmissionError>;
fn stop_transmission(&self);
}
macro_rules! channel_instance {
($num:literal, $cxi:ident, $output_signal:path
) => {
pub struct $cxi {
mem_offset: usize,
}
impl $cxi {
pub fn new() -> Self {
let mut channel = $cxi { mem_offset: 0 };
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32c6, esp32s3))] {
unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| unsafe {
w.mem_size()
.bits(1)
});
}
else {
conf0!($num).modify(|_, w| unsafe {
w.mem_size()
.bits(1)
});
conf1!($num).modify(|_, w|
w.mem_owner()
.clear_bit()
);
}
};
#[cfg(esp32)]
conf0!($num).modify(|_, w|
w.clk_en()
.set_bit()
.mem_pd()
.clear_bit()
);
channel.set_carrier_modulation(false);
channel.set_idle_output_level(false);
channel.set_idle_output(false);
channel.set_channel_divider(1);
channel
}
#[inline(always)]
fn write_sequence(
&mut self,
seq_iter: &mut Iter<u32>,
max_inserted_elements: u8,
){
for _ in 0..max_inserted_elements {
match seq_iter.next() {
None => {
break;
}
Some(pulse) => self.load_fifo(*pulse),
}
}
}
#[inline(always)]
fn load_fifo(&mut self, value: u32) {
let base_ptr: usize = RMT_RAM_START + ($num * CHANNEL_RAM_SIZE as usize * 4);
let ram_ptr = (base_ptr + self.mem_offset) as *mut u32;
unsafe {
ram_ptr.write_volatile(value);
}
self.mem_offset += 4;
if self.mem_offset >= CHANNEL_RAM_SIZE as usize * 4 {
self.mem_offset = 0;
}
}
#[inline(always)]
fn reset_fifo(&mut self) {
self.mem_offset = 0;
}
}
paste!(
#[doc = "Wrapper for`" $cxi "` object."]
pub struct [<Configured $cxi>]<'d, P> {
channel: $cxi,
_pin: PeripheralRef<'d, P>
}
impl<'d, P: OutputPin> ConfiguredChannel for [<Configured $cxi>]<'d, P> {
fn send_pulse_sequence<const N: usize>(
&mut self,
repeat_mode: RepeatMode,
sequence: &[PulseCode; N],
) -> Result<(), TransmissionError> {
let precomputed_sequence = sequence.map(|x| u32::from(x));
self.send_pulse_sequence_raw(repeat_mode, &precomputed_sequence)
}
fn send_pulse_sequence_raw<const N: usize>(
&mut self,
repeat_mode: RepeatMode,
sequence: &[u32; N],
) -> Result<(), TransmissionError> {
match repeat_mode {
#[cfg(not(esp32))]
RepeatMode::RepeatNtimes(val) => {
if val >= 1024 {
return Err(TransmissionError::RepetitionOverflow);
}
if sequence.len() > CHANNEL_RAM_SIZE as usize {
return Err(TransmissionError::IncompatibleRepeatMode);
}
}
RepeatMode::Forever => {
if sequence.len() > CHANNEL_RAM_SIZE as usize {
return Err(TransmissionError::IncompatibleRepeatMode);
}
}
_ => (),
};
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2))] {
let conf_reg = & conf1!($num);
} else {
let conf_reg = & unsafe{ &*RMT::PTR }.ch_tx_conf0[$num];
}
}
cfg_if::cfg_if! {
if #[cfg(esp32)] {
unsafe { &*RMT::PTR }.ch_tx_lim[$num].modify(|_, w| unsafe {
w.tx_lim()
.bits(CHANNEL_RAM_SIZE as u16 /2)
});
} else {
let mut reps = 0;
if let RepeatMode::RepeatNtimes(val) = repeat_mode {
reps = val;
}
unsafe { &*RMT::PTR }.ch_tx_lim[$num].modify(|_, w| unsafe {
w.tx_loop_num()
.bits(reps)
.tx_loop_cnt_en()
.bit(reps != 0)
.loop_count_reset()
.set_bit()
.tx_lim()
.bits(CHANNEL_RAM_SIZE as u16/2)
});
}
}
#[cfg(any(esp32c3, esp32c6, esp32s3))]
conf_reg.modify(|_, w| {
w.conf_update().set_bit()
});
conf_reg.modify(|_, w| {
w.tx_conti_mode()
.bit(repeat_mode != RepeatMode::SingleShot)
.mem_rd_rst()
.set_bit()
.apb_mem_rst()
.set_bit()
});
self.channel.reset_fifo();
let mut sequence_iter = sequence.iter();
if sequence.len() >= CHANNEL_RAM_SIZE as usize {
self.channel.write_sequence(&mut sequence_iter, CHANNEL_RAM_SIZE);
} else {
self.channel.write_sequence(&mut sequence_iter, CHANNEL_RAM_SIZE);
}
cfg_if::cfg_if! {
if #[cfg(esp32)] {
unsafe { &*RMT::PTR }.int_clr.write(|w| {
paste!(
w.[<ch $num _tx_end_int_clr>]()
.set_bit()
.[<ch $num _err_int_clr>]()
.set_bit()
.[<ch $num _tx_thr_event_int_clr>]()
.set_bit()
)
});
} else if #[cfg(esp32s2)] {
unsafe { &*RMT::PTR }.int_clr.write(|w| {
paste!(
w.[<ch $num _tx_end_int_clr>]()
.set_bit()
.[<ch $num _tx_loop_int_clr>]()
.set_bit()
.[<ch $num _err_int_clr>]()
.set_bit()
.[<ch $num _tx_thr_event_int_clr>]()
.set_bit()
)
});
} else {
unsafe { &*RMT::PTR }.int_clr.write(|w| {
paste!(
w.[<ch $num _tx_end_int_clr>]()
.set_bit()
.[<ch $num _tx_loop_int_clr>]()
.set_bit()
.[<ch $num _tx_err_int_clr>]()
.set_bit()
.[<ch $num _tx_thr_event_int_clr>]()
.set_bit()
)
});
}
}
#[cfg(any(esp32c3, esp32c6, esp32s3))]
unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| {
w.mem_tx_wrap_en()
.set_bit()
});
#[cfg(any(esp32c3, esp32c6, esp32s3))]
unsafe { &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| {
w.conf_update()
.set_bit()
});
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2))] {
conf1!($num).modify(|_, w| w.tx_start().set_bit());
} else {
unsafe{ &*RMT::PTR }.ch_tx_conf0[$num].modify(|_, w| w.tx_start().set_bit());
}
}
if repeat_mode != RepeatMode::Forever {
loop {
let interrupts = unsafe { &*RMT::PTR }.int_raw.read();
match (
unsafe { interrupts.ch_tx_end_int_raw($num).bit() },
#[cfg(not(esp32))]
unsafe {interrupts.ch_tx_loop_int_raw($num).bit()},
#[cfg(esp32)]
false,
#[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() },
unsafe { interrupts.ch_tx_thr_event_int_raw($num).bit() },
) {
(true, false, false, _) => break,
(false, true, false, _) => {
self.stop_transmission();
break;
}
(false, false, false, true) => {
self.channel.write_sequence(&mut sequence_iter, CHANNEL_RAM_SIZE / 2);
unsafe { &*RMT::PTR }.int_clr.write(|w| {
paste!(w.[<ch $num _tx_thr_event_int_clr>]().set_bit())
});
}
(false, false, false, false) => (),
_ => {
return Err(TransmissionError::Failure(
unsafe { interrupts.ch_tx_end_int_raw($num).bit() },
#[cfg(not(esp32))]
unsafe {interrupts.ch_tx_loop_int_raw($num).bit()},
#[cfg(esp32)]
false,
#[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() },
unsafe { interrupts.ch_tx_thr_event_int_raw($num).bit() },
))
}
}
}
}
Ok(())
}
fn stop_transmission(&self) {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32c6, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.tx_stop().set_bit());
}
else if #[cfg(esp32s2)] {
conf1!($num)
.modify(|_, w| w.tx_stop().set_bit());
}
};
}
}
);
};
}
macro_rules! output_channel {
($num:literal, $cxi:ident, $output_signal:path
) => {
paste!(
impl OutputChannel for $cxi {
type ConfiguredChannel<'d, P> = [<Configured $cxi>]<'d, P>
where P: OutputPin + 'd;
#[inline(always)]
fn set_idle_output_level(&mut self, level: bool) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32c6, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.idle_out_lv().bit(level));
}
else {
conf1!($num)
.modify(|_, w| w.idle_out_lv().bit(level));
}
};
self
}
#[inline(always)]
fn set_idle_output(&mut self, state: bool) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32c6, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.idle_out_en().bit(state));
}
else {
conf1!($num)
.modify(|_, w| w.idle_out_en().bit(state));
}
};
self
}
#[inline(always)]
fn set_channel_divider(&mut self, divider: u8) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32c6, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| unsafe { w.div_cnt().bits(divider) });
}
else {
conf0!($num)
.modify(|_, w| unsafe { w.div_cnt().bits(divider) });
}
};
self
}
#[inline(always)]
fn set_carrier_modulation(&mut self, state: bool) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32c6, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.carrier_en().bit(state));
}
else {
conf0!($num)
.modify(|_, w| w.carrier_en().bit(state));
}
};
self
}
#[cfg(any(esp32s2, esp32))]
#[inline(always)]
fn set_clock_source(&mut self, source: ClockSource) -> &mut Self {
let bit_value = match source {
ClockSource::RefTick => false,
ClockSource::APB => true,
};
conf1!($num)
.modify(|_, w| w.ref_always_on().bit(bit_value));
self
}
fn assign_pin<'d, RmtPin: OutputPin >(
self,
pin: impl Peripheral<P = RmtPin> + 'd
) -> [<Configured $cxi>]<'d, RmtPin> {
crate::into_ref!(pin);
pin.set_to_push_pull_output()
.connect_peripheral_to_output($output_signal);
[<Configured $cxi>] {
channel: self,
_pin: pin
}
}
}
);
};
}
#[cfg(esp32)]
macro_rules! conf0 {
($channel: literal) => {
match $channel {
0 => &unsafe { &*RMT::PTR }.ch0conf0,
1 => &unsafe { &*RMT::PTR }.ch1conf0,
2 => &unsafe { &*RMT::PTR }.ch2conf0,
3 => &unsafe { &*RMT::PTR }.ch3conf0,
4 => &unsafe { &*RMT::PTR }.ch4conf0,
5 => &unsafe { &*RMT::PTR }.ch5conf0,
6 => &unsafe { &*RMT::PTR }.ch6conf0,
7 => &unsafe { &*RMT::PTR }.ch7conf0,
_ => panic!("Attempted access to non-existing channel!"),
}
};
}
#[cfg(esp32)]
macro_rules! conf1 {
($channel: literal) => {
match $channel {
0 => &unsafe { &*RMT::PTR }.ch0conf1,
1 => &unsafe { &*RMT::PTR }.ch1conf1,
2 => &unsafe { &*RMT::PTR }.ch2conf1,
3 => &unsafe { &*RMT::PTR }.ch3conf1,
4 => &unsafe { &*RMT::PTR }.ch4conf1,
5 => &unsafe { &*RMT::PTR }.ch5conf1,
6 => &unsafe { &*RMT::PTR }.ch6conf1,
7 => &unsafe { &*RMT::PTR }.ch7conf1,
_ => panic!("Attempted access to non-existing channel!"),
}
};
}
#[cfg(esp32s2)]
macro_rules! conf0 {
($channel: literal) => {
match $channel {
0 => &unsafe { &*RMT::PTR }.ch0conf0,
1 => &unsafe { &*RMT::PTR }.ch1conf0,
2 => &unsafe { &*RMT::PTR }.ch2conf0,
3 => &unsafe { &*RMT::PTR }.ch3conf0,
_ => panic!("Attempted access to non-existing channel!"),
}
};
}
#[cfg(esp32s2)]
macro_rules! conf1 {
($channel: literal) => {
match $channel {
0 => &unsafe { &*RMT::PTR }.ch0conf1,
1 => &unsafe { &*RMT::PTR }.ch1conf1,
2 => &unsafe { &*RMT::PTR }.ch2conf1,
3 => &unsafe { &*RMT::PTR }.ch3conf1,
_ => panic!("Attempted access to non-existing channel!"),
}
};
}
macro_rules! rmt {
(
$global_conf_reg:ident,
$(
($num:literal, $cxi:ident, $obj_name:ident, $output_signal:path),
)+
)
=> {
pub struct PulseControl<'d> {
reg: PeripheralRef<'d, RMT>,
$(
/// RMT channel $cxi
pub $obj_name: $cxi,
)+
}
impl<'d> PulseControl<'d> {
#[cfg(any(esp32c3, esp32c6, esp32s3))]
pub fn new(
instance: impl Peripheral<P = RMT> + 'd,
peripheral_clock_control: &mut PeripheralClockControl,
clk_source: ClockSource,
div_abs: u8,
div_frac_a: u8,
div_frac_b: u8,
) -> Result<Self, SetupError> {
crate::into_ref!(instance);
let pc = PulseControl {
reg: instance,
$(
$obj_name: $cxi::new(),
)+
};
pc.enable_peripheral(peripheral_clock_control);
pc.config_global(clk_source, div_abs, div_frac_a, div_frac_b)?;
Ok(pc)
}
#[cfg(any(esp32, esp32s2))]
pub fn new(
instance: impl Peripheral<P = RMT> + 'd,
peripheral_clock_control: &mut PeripheralClockControl,
) -> Result<Self, SetupError> {
crate::into_ref!(instance);
let pc = PulseControl {
reg: instance,
$(
$obj_name: $cxi::new(),
)+
};
pc.enable_peripheral(peripheral_clock_control);
pc.config_global()?;
Ok(pc)
}
fn enable_peripheral(&self, peripheral_clock_control: &mut PeripheralClockControl) {
peripheral_clock_control.enable(crate::system::Peripheral::Rmt);
}
#[cfg(any(esp32c3, esp32c6, esp32s3))]
fn config_global(
&self,
clk_source: ClockSource,
div_abs: u8,
div_frac_a: u8,
div_frac_b: u8,
) -> Result<(), SetupError> {
if div_frac_a > 64 || div_frac_b > 64 {
return Err(SetupError::InvalidGlobalConfig);
}
#[cfg(esp32c6)]
let pcr = unsafe { &*PCR::ptr() };
#[cfg(esp32c6)]
pcr.rmt_sclk_conf.write(|w| w.sclk_en().set_bit());
self.reg.sys_conf.modify(|_, w|
w.clk_en()
.set_bit()
.mem_clk_force_on()
.set_bit()
.sclk_active()
.set_bit()
.mem_force_pd()
.clear_bit()
.apb_fifo_mask()
.set_bit());
#[cfg(not(esp32c6))]
self.reg.sys_conf.modify(|_, w| unsafe {
w.sclk_sel()
.bits(clk_source as u8)
.sclk_div_num()
.bits(div_abs)
.sclk_div_a()
.bits(div_frac_a)
.sclk_div_b()
.bits(div_frac_b) });
#[cfg(esp32c6)]
pcr.rmt_sclk_conf.modify(|_,w| unsafe {
w.sclk_sel()
.bits(clk_source as u8)
.sclk_div_num()
.bits(div_abs)
.sclk_div_a()
.bits(div_frac_a)
.sclk_div_b()
.bits(div_frac_b)
});
self.reg.int_ena.write(|w| unsafe { w.bits(0) });
self.reg.int_clr.write(|w| unsafe { w.bits(0xffffffff) });
Ok(())
}
#[cfg(any(esp32s2, esp32))]
fn config_global(&self) -> Result<(), SetupError> {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
self.reg.apb_conf.modify(|_, w|
w.apb_fifo_mask()
.set_bit()
.mem_tx_wrap_en()
.set_bit()
);
}
else {
self.reg.apb_conf.modify(|_, w|
w.clk_en()
.set_bit()
.mem_clk_force_on()
.set_bit()
.mem_force_pd()
.clear_bit()
.apb_fifo_mask()
.set_bit()
.mem_tx_wrap_en()
.set_bit()
);
}
};
self.reg.int_ena.write(|w| unsafe { w.bits(0) });
self.reg.int_clr.write(|w| unsafe { w.bits(0) });
Ok(())
}
}
$(
channel_instance!($num, $cxi, $output_signal);
output_channel!($num, $cxi, $output_signal);
)+
};
}
#[cfg(any(esp32c3, esp32c6))]
rmt!(
sys_conf,
(0, Channel0, channel0, OutputSignal::RMT_SIG_0),
(1, Channel1, channel1, OutputSignal::RMT_SIG_1),
);
#[cfg(esp32s2)]
rmt!(
apb_conf,
(0, Channel0, channel0, OutputSignal::RMT_SIG_OUT0),
(1, Channel1, channel1, OutputSignal::RMT_SIG_OUT1),
(2, Channel2, channel2, OutputSignal::RMT_SIG_OUT2),
(3, Channel3, channel3, OutputSignal::RMT_SIG_OUT3),
);
#[cfg(esp32)]
rmt!(
apb_conf,
(0, Channel0, channel0, OutputSignal::RMT_SIG_0),
(1, Channel1, channel1, OutputSignal::RMT_SIG_1),
(2, Channel2, channel2, OutputSignal::RMT_SIG_2),
(3, Channel3, channel3, OutputSignal::RMT_SIG_3),
(4, Channel4, channel4, OutputSignal::RMT_SIG_4),
(5, Channel5, channel5, OutputSignal::RMT_SIG_5),
(6, Channel6, channel6, OutputSignal::RMT_SIG_6),
(7, Channel7, channel7, OutputSignal::RMT_SIG_7),
);
#[cfg(esp32s3)]
rmt!(
sys_conf,
(0, Channel0, channel0, OutputSignal::RMT_SIG_OUT0),
(1, Channel1, channel1, OutputSignal::RMT_SIG_OUT1),
(2, Channel2, channel2, OutputSignal::RMT_SIG_OUT2),
(3, Channel3, channel3, OutputSignal::RMT_SIG_OUT3),
);