#![deny(missing_docs)]
use core::{marker::PhantomData, ops::Deref};
use fugit::HertzU32;
use operator::Operator;
use timer::Timer;
use crate::{
clock::Clocks,
gpio::OutputSignal,
peripheral::{Peripheral, PeripheralRef},
system::{Peripheral as PeripheralEnable, PeripheralClockControl},
};
pub mod operator;
pub mod timer;
type RegisterBlock = crate::peripherals::mcpwm0::RegisterBlock;
#[non_exhaustive]
pub struct MCPWM<'d, PWM> {
_inner: PeripheralRef<'d, PWM>,
pub timer0: Timer<0, PWM>,
pub timer1: Timer<1, PWM>,
pub timer2: Timer<2, PWM>,
pub operator0: Operator<0, PWM>,
pub operator1: Operator<1, PWM>,
pub operator2: Operator<2, PWM>,
}
impl<'d, PWM: PwmPeripheral> MCPWM<'d, PWM> {
pub fn new(
peripheral: impl Peripheral<P = PWM> + 'd,
peripheral_clock: PeripheralClockConfig,
) -> Self {
crate::into_ref!(peripheral);
PWM::enable();
#[cfg(not(esp32c6))]
{
peripheral
.clk_cfg()
.write(|w| w.clk_prescale().variant(peripheral_clock.prescaler));
peripheral.clk().write(|w| w.en().set_bit());
}
#[cfg(esp32c6)]
{
unsafe { &*crate::peripherals::PCR::PTR }
.pwm_clk_conf()
.modify(|_, w| unsafe {
w.pwm_div_num()
.variant(peripheral_clock.prescaler)
.pwm_clkm_en()
.set_bit()
.pwm_clkm_sel()
.bits(1)
});
}
#[cfg(esp32h2)]
{
unsafe { &*crate::peripherals::PCR::PTR }
.pwm_clk_conf()
.modify(|_, w| unsafe {
w.pwm_div_num()
.variant(peripheral_clock.prescaler)
.pwm_clkm_en()
.set_bit()
.pwm_clkm_sel()
.bits(0)
});
}
Self {
_inner: peripheral,
timer0: Timer::new(),
timer1: Timer::new(),
timer2: Timer::new(),
operator0: Operator::new(),
operator1: Operator::new(),
operator2: Operator::new(),
}
}
}
#[derive(Copy, Clone)]
pub struct PeripheralClockConfig<'a> {
frequency: HertzU32,
prescaler: u8,
phantom: PhantomData<&'a Clocks<'a>>,
}
impl<'a> PeripheralClockConfig<'a> {
pub fn with_prescaler(clocks: &'a Clocks, prescaler: u8) -> Self {
#[cfg(esp32)]
let source_clock = clocks.pwm_clock;
#[cfg(esp32c6)]
let source_clock = clocks.crypto_clock;
#[cfg(esp32s3)]
let source_clock = clocks.crypto_pwm_clock;
#[cfg(esp32h2)]
let source_clock = clocks.xtal_clock;
Self {
frequency: source_clock / (prescaler as u32 + 1),
prescaler,
phantom: PhantomData,
}
}
pub fn with_frequency(
clocks: &'a Clocks,
target_freq: HertzU32,
) -> Result<Self, FrequencyError> {
#[cfg(esp32)]
let source_clock = clocks.pwm_clock;
#[cfg(esp32c6)]
let source_clock = clocks.crypto_clock;
#[cfg(esp32s3)]
let source_clock = clocks.crypto_pwm_clock;
#[cfg(esp32h2)]
let source_clock = clocks.xtal_clock;
if target_freq.raw() == 0 || target_freq > source_clock {
return Err(FrequencyError);
}
let prescaler = source_clock / target_freq - 1;
if prescaler > u8::MAX as u32 {
return Err(FrequencyError);
}
Ok(Self::with_prescaler(clocks, prescaler as u8))
}
pub fn frequency(&self) -> HertzU32 {
self.frequency
}
pub fn timer_clock_with_prescaler(
&self,
period: u16,
mode: timer::PwmWorkingMode,
prescaler: u8,
) -> timer::TimerClockConfig<'a> {
timer::TimerClockConfig::with_prescaler(self, period, mode, prescaler)
}
pub fn timer_clock_with_frequency(
&self,
period: u16,
mode: timer::PwmWorkingMode,
target_freq: HertzU32,
) -> Result<timer::TimerClockConfig<'a>, FrequencyError> {
timer::TimerClockConfig::with_frequency(self, period, mode, target_freq)
}
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FrequencyError;
pub trait PwmPeripheral: Deref<Target = RegisterBlock> + crate::private::Sealed {
fn enable();
fn block() -> *const RegisterBlock;
fn output_signal<const OP: u8, const IS_A: bool>() -> OutputSignal;
}
#[cfg(mcpwm0)]
impl PwmPeripheral for crate::peripherals::MCPWM0 {
fn enable() {
PeripheralClockControl::enable(PeripheralEnable::Mcpwm0)
}
fn block() -> *const RegisterBlock {
Self::PTR
}
fn output_signal<const OP: u8, const IS_A: bool>() -> OutputSignal {
match (OP, IS_A) {
(0, true) => OutputSignal::PWM0_0A,
(1, true) => OutputSignal::PWM0_1A,
(2, true) => OutputSignal::PWM0_2A,
(0, false) => OutputSignal::PWM0_0B,
(1, false) => OutputSignal::PWM0_1B,
(2, false) => OutputSignal::PWM0_2B,
_ => unreachable!(),
}
}
}
#[cfg(mcpwm1)]
impl PwmPeripheral for crate::peripherals::MCPWM1 {
fn enable() {
PeripheralClockControl::enable(PeripheralEnable::Mcpwm1)
}
fn block() -> *const RegisterBlock {
Self::PTR
}
fn output_signal<const OP: u8, const IS_A: bool>() -> OutputSignal {
match (OP, IS_A) {
(0, true) => OutputSignal::PWM1_0A,
(1, true) => OutputSignal::PWM1_1A,
(2, true) => OutputSignal::PWM1_2A,
(0, false) => OutputSignal::PWM1_0B,
(1, false) => OutputSignal::PWM1_1B,
(2, false) => OutputSignal::PWM1_2B,
_ => unreachable!(),
}
}
}