#![cfg_attr(docsrs, procmacros::doc_replace(
"mcpwm_freq" => {
cfg(not(esp32h2)) => "40",
cfg(esp32h2) => "32"
},
"clock_src" => {
cfg(esp32) => "PLL_F160M (160 MHz)",
cfg(esp32s3) => "CRYPTO_PWM_CLK (160 MHz)",
cfg(esp32c6) => "PLL_F160M (160 MHz)",
cfg(esp32h2) => "PLL_F96M_CLK (96 MHz)",
}
))]
use operator::Operator;
use timer::Timer;
use crate::{
gpio::OutputSignal,
pac,
private::DropGuard,
soc::clocks::{self, ClockTree},
system::{Peripheral, PeripheralGuard},
time::Rate,
};
pub mod operator;
pub mod timer;
type RegisterBlock = pac::mcpwm0::RegisterBlock;
#[allow(dead_code)] struct PwmClockGuard(DropGuard<(), fn(())>);
impl PwmClockGuard {
fn instance<PWM: PwmPeripheral>() -> clocks::McpwmInstance {
match PWM::peripheral() {
Peripheral::Mcpwm0 => clocks::McpwmInstance::Mcpwm0,
#[cfg(soc_has_mcpwm1)]
Peripheral::Mcpwm1 => clocks::McpwmInstance::Mcpwm1,
_ => unreachable!(),
}
}
pub fn new<PWM: PwmPeripheral>() -> Self {
ClockTree::with(move |clocks| Self::instance::<PWM>().request_function_clock(clocks));
Self(DropGuard::new((), |_| {
ClockTree::with(move |clocks| Self::instance::<PWM>().release_function_clock(clocks));
}))
}
}
#[non_exhaustive]
pub struct McPwm<'d, PWM> {
_inner: PWM,
pub timer0: Timer<0, PWM>,
pub timer1: Timer<1, PWM>,
pub timer2: Timer<2, PWM>,
pub operator0: Operator<'d, 0, PWM>,
pub operator1: Operator<'d, 1, PWM>,
pub operator2: Operator<'d, 2, PWM>,
}
impl<'d, PWM: PwmPeripheral + 'd> McPwm<'d, PWM> {
pub fn new(peripheral: PWM, peripheral_clock: PeripheralClockConfig) -> Self {
let guard = PeripheralGuard::new(PWM::peripheral());
let register_block = unsafe { &*PWM::block() };
register_block
.clk_cfg()
.write(|w| unsafe { w.clk_prescale().bits(peripheral_clock.prescaler) });
register_block.clk().write(|w| w.en().set_bit());
Self {
_inner: peripheral,
timer0: Timer::new(guard.clone()),
timer1: Timer::new(guard.clone()),
timer2: Timer::new(guard.clone()),
operator0: Operator::new(guard.clone()),
operator1: Operator::new(guard.clone()),
operator2: Operator::new(guard),
}
}
}
#[derive(Copy, Clone)]
pub struct PeripheralClockConfig {
frequency: Rate,
prescaler: u8,
}
impl PeripheralClockConfig {
fn source_clock() -> Rate {
ClockTree::with(|clocks| {
Rate::from_hz(clocks::McpwmInstance::Mcpwm0.function_clock_frequency(clocks))
})
}
pub fn with_prescaler(prescaler: u8) -> Self {
let source_clock = Self::source_clock();
Self {
frequency: source_clock / (prescaler as u32 + 1),
prescaler,
}
}
pub fn with_frequency(target_freq: Rate) -> Result<Self, FrequencyError> {
let source_clock = Self::source_clock();
if target_freq.as_hz() == 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(prescaler as u8))
}
pub fn frequency(&self) -> Rate {
self.frequency
}
pub fn timer_clock_with_prescaler(
&self,
period: u16,
mode: timer::PwmWorkingMode,
prescaler: u8,
) -> timer::TimerClockConfig {
timer::TimerClockConfig::with_prescaler(self, period, mode, prescaler)
}
pub fn timer_clock_with_frequency(
&self,
period: u16,
mode: timer::PwmWorkingMode,
target_freq: Rate,
) -> Result<timer::TimerClockConfig, 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: crate::private::Sealed {
fn block() -> *const RegisterBlock;
fn output_signal<const OP: u8, const IS_A: bool>() -> OutputSignal;
fn peripheral() -> Peripheral;
}
#[cfg(soc_has_mcpwm0)]
impl PwmPeripheral for crate::peripherals::MCPWM0<'_> {
fn block() -> *const RegisterBlock {
Self::regs()
}
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!(),
}
}
fn peripheral() -> Peripheral {
Peripheral::Mcpwm0
}
}
#[cfg(soc_has_mcpwm1)]
impl PwmPeripheral for crate::peripherals::MCPWM1<'_> {
fn block() -> *const RegisterBlock {
Self::regs()
}
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!(),
}
}
fn peripheral() -> Peripheral {
Peripheral::Mcpwm1
}
}