use core::marker::PhantomData;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use paste::paste;
use crate::{
event_link::InterruptEvent,
gpio::{Flex, Pin, PortFunction, WithOpenDrain},
module_stop::ModuleStop,
pac::{
self,
gpt::vals::{Ccr, Gtio, Mode, Odty, Tpcs},
},
};
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
pub divider: Divider,
pub compare_a: u16,
pub compare_b: u16,
pub top: u16,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Divider {
Div1,
Div4,
Div16,
Div64,
Div256,
Div1024,
}
pub struct Pwm<'d, I: Instance> {
_instance: PhantomData<&'d I>,
channel_a: Option<Flex<'d, WithOpenDrain>>,
channel_b: Option<Flex<'d, WithOpenDrain>>,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum PwmError {
InvalidDutyCycle,
Unknown,
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + ModuleStop + PeripheralType + 'static + Send {}
pub(crate) trait SealedInstance {
#[cfg(feature = "defmt")]
const PERIPHERAL: &'static str;
#[cfg(not(feature = "defmt"))]
const PERIPHERAL: () = ();
const CAPTURE_COMP_A_EVENT: InterruptEvent;
#[allow(dead_code)]
const CAPTURE_COMP_B_EVENT: InterruptEvent;
#[allow(dead_code)]
const COMP_C_EVENT: InterruptEvent;
fn regs() -> pac::gpt::Gpt;
fn cmpa_waker() -> &'static AtomicWaker;
}
#[allow(private_bounds)]
pub trait PwmChannel: SealedPwmChannel {}
pub(crate) trait SealedPwmChannel {}
#[allow(private_bounds)]
pub trait PwmPin<I: Instance, C: PwmChannel>: SealedPwmPin<I, C> {}
pub(crate) trait SealedPwmPin<I: SealedInstance, C: PwmChannel>:
Pin + PeripheralType
{
const PERIPHERAL_FUNC: PortFunction;
#[inline(always)]
fn set_pfunc(&self) {
self.set_as_pf(Self::PERIPHERAL_FUNC);
}
}
trait SealedPwmChansetter {}
impl<'d, I: Instance> SealedPwmChansetter for Pwm<'d, I> {}
#[allow(private_bounds)]
pub trait PwmChansetter<'d, C: PwmChannel, I: Instance>: SealedPwmChansetter {
fn with_channel<A: PwmPin<I, C>>(self, pin_a: Peri<'d, A>) -> Self;
}
impl<'d, I: Instance> PwmChansetter<'d, ChanA, I> for Pwm<'d, I> {
fn with_channel<A: PwmPin<I, ChanA>>(mut self, pin_a: Peri<'d, A>) -> Self {
assert!(self.channel_b.is_none());
let pwm = I::regs();
pwm.gtior().modify(|w| {
w.set_gtioa(Gtio::_00111);
w.set_oae(true);
});
pwm.gtber().modify(|w| w.set_ccra(Ccr::SingleBuffer));
pin_a.set_pfunc();
let pin_a = Flex::new(pin_a);
self.channel_a = Some(pin_a);
self
}
}
impl<'d, I: Instance> PwmChansetter<'d, ChanB, I> for Pwm<'d, I> {
fn with_channel<B: PwmPin<I, ChanB>>(mut self, pin_b: Peri<'d, B>) -> Self {
assert!(self.channel_b.is_none());
let pwm = I::regs();
pwm.gtior().modify(|w| {
w.set_gtiob(Gtio::_00111);
w.set_obe(true);
});
pwm.gtber().modify(|w| w.set_ccrb(Ccr::SingleBuffer));
pin_b.set_pfunc();
let pin_b = Flex::new(pin_b);
self.channel_b = Some(pin_b);
self
}
}
impl Default for Config {
fn default() -> Self {
Self {
divider: Divider::Div4,
compare_a: 0,
compare_b: 0,
top: 0xFFFF,
}
}
}
impl From<Divider> for u16 {
fn from(value: Divider) -> Self {
match value {
Divider::Div1 => 1,
Divider::Div4 => 4,
Divider::Div16 => 16,
Divider::Div64 => 64,
Divider::Div256 => 256,
Divider::Div1024 => 1024,
}
}
}
impl From<Tpcs> for Divider {
fn from(value: Tpcs) -> Self {
match value {
Tpcs::Div1 => Self::Div1,
Tpcs::Div4 => Self::Div4,
Tpcs::Div16 => Self::Div16,
Tpcs::Div64 => Self::Div64,
Tpcs::Div256 => Self::Div256,
Tpcs::Div1024 => Self::Div1024,
Tpcs::_RESERVED_6 => unimplemented!(),
Tpcs::_RESERVED_7 => unimplemented!(),
}
}
}
impl From<Divider> for Tpcs {
fn from(value: Divider) -> Self {
match value {
Divider::Div1 => Self::Div1,
Divider::Div4 => Self::Div4,
Divider::Div16 => Self::Div16,
Divider::Div64 => Self::Div64,
Divider::Div256 => Self::Div256,
Divider::Div1024 => Self::Div1024,
}
}
}
impl<'d, I: Instance> Pwm<'d, I> {
pub fn new(peri: Peri<'d, I>, config: Config) -> Self {
let _ = peri;
I::start_module();
let pwm = I::regs();
pwm.gtcr().modify(|w| w.set_md(Mode::TrianglePwm1));
pwm.gtcnt().write_value(0);
let mut this = Self {
_instance: PhantomData,
channel_a: None,
channel_b: None,
};
pwm.gtssr().modify(|r| r.set_cstrt(true));
this.set_config(config);
this
}
pub fn with_channel_a<A: PwmPin<I, ChanA>>(self, pin_a: Peri<'d, A>) -> Self {
PwmChansetter::<ChanA, I>::with_channel::<A>(self, pin_a)
}
pub fn with_channel_b<B: PwmPin<I, ChanB>>(self, pin_b: Peri<'d, B>) -> Self {
PwmChansetter::<ChanB, I>::with_channel::<B>(self, pin_b)
}
pub fn set_config(&mut self, config: Config) {
let pwm = I::regs();
pwm.gtcr().modify(|w| w.set_tpcs(config.divider.into()));
pwm.gtpr().write_value(config.top as u32);
pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::CompareMatch));
pwm.gtccra().write_value(config.compare_a as u32);
pwm.gtccrc().write_value(config.compare_a as u32);
pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::CompareMatch));
pwm.gtccrb().write_value(config.compare_b as u32);
pwm.gtccre().write_value(config.compare_b as u32);
}
#[inline]
pub fn start(&mut self) {
let pwm = I::regs();
pwm.gtcr().modify(|w| w.set_cst(true));
}
#[inline]
pub fn stop(&mut self) {
let pwm = I::regs();
pwm.gtcr().modify(|w| w.set_cst(false));
}
pub fn set_frequency(&mut self, frequency: u32, pct: f32) -> Result<(), PwmError> {
let pwm = I::regs();
let clocks = crate::clock::clock_status();
let divider: Divider = pwm.gtcr().read().tpcs().into();
let divider: u16 = divider.into();
let divider = divider as f32;
let pwm_clk = clocks.peripheral_d.to_Hz() as f32;
let period = (pwm_clk / divider) / (frequency as f32 * 2.0);
trace!(
"{}set_freq(freq={}, pct={}%, divider={}, clk={}, period={})",
I::PERIPHERAL,
frequency,
pct * 100.0,
divider,
pwm_clk,
period,
);
if period < 2.0 || period > f32::from(u16::MAX - 2) {
error!(
"{}Unable to set frequency={} Hz, max allowable={}",
I::PERIPHERAL,
frequency,
(pwm_clk / (divider * 4.0)) as u32,
);
return Err(PwmError::InvalidDutyCycle);
}
pwm.gtpr().write_value(period as u32);
self.set_duty_pct(pct);
Ok(())
}
#[inline]
pub fn set_duty_pct(&mut self, pct: f32) {
self.set_duty_pct_a(pct);
self.set_duty_pct_b(pct);
}
pub fn set_duty_pct_a(&mut self, pct: f32) {
if self.channel_a.is_none() {
return;
}
let pwm = I::regs();
let pct = pct.clamp(0.0, 1.0);
let period = pwm.gtpr().read() as f32;
let cmp = (period * (1.0 - pct)) as u32;
if cmp == 0 {
pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::On));
} else if cmp >= period as u32 {
pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::Off));
} else {
pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::CompareMatch));
pwm.gtccra().write_value(cmp);
pwm.gtccrc().write_value(cmp);
}
}
pub fn set_duty_a(&mut self, duty: u32) -> Result<(), PwmError> {
if self.channel_a.is_none() {
return Ok(());
}
let pwm = I::regs();
let period = pwm.gtpr().read();
#[cfg(feature = "strict-assert")]
assert!(period <= u32::from(u16::MAX));
if period > u32::from(u16::MAX) {
Err(PwmError::InvalidDutyCycle)?;
}
if duty == 0 {
pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::On));
} else if duty >= period {
pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::Off));
} else {
pwm.gtuddtyc().modify(|w| w.set_oadty(Odty::CompareMatch));
pwm.gtccra().write_value(duty);
pwm.gtccrc().write_value(duty);
}
Ok(())
}
pub fn set_duty_pct_b(&mut self, pct: f32) {
if self.channel_b.is_none() {
return;
}
let pwm = I::regs();
let pct = pct.clamp(0.0, 1.0);
let period = pwm.gtpr().read() as f32;
let cmp = (period * (1.0 - pct)) as u32;
if cmp == 0 {
pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::On));
} else if cmp >= period as u32 {
pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::Off));
} else {
pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::CompareMatch));
pwm.gtccrb().write_value(cmp);
pwm.gtccre().write_value(cmp);
}
}
pub fn set_duty_b(&mut self, duty: u32) -> Result<(), PwmError> {
if self.channel_b.is_none() {
return Ok(());
}
let pwm = I::regs();
let period = pwm.gtpr().read();
#[cfg(feature = "strict-assert")]
assert!(period <= u32::from(u16::MAX));
if period > u32::from(u16::MAX) {
Err(PwmError::InvalidDutyCycle)?;
}
if duty == 0 {
pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::On));
} else if duty >= period {
pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::Off));
} else {
pwm.gtuddtyc().modify(|w| w.set_obdty(Odty::CompareMatch));
pwm.gtccrb().write_value(duty);
pwm.gtccre().write_value(duty);
}
Ok(())
}
}
impl<'d, I: Instance> Drop for Pwm<'d, I> {
fn drop(&mut self) {
self.stop();
I::stop_module();
}
}
macro_rules! declare_pwm_channel {
($chan:ident) => {
paste! {
#[allow(missing_docs)]
pub enum [< Chan $chan >] {}
impl PwmChannel for [< Chan $chan >] {}
impl SealedPwmChannel for [< Chan $chan >] {}
}
};
}
macro_rules! pwm_pin {
($instance:ident, $chan:ident, $pin:ident, $pf:ident) => {
impl crate::pwm::PwmPin<crate::peripherals::$instance, crate::pwm::$chan>
for crate::peripherals::$pin
{
}
impl crate::pwm::SealedPwmPin<crate::peripherals::$instance, crate::pwm::$chan>
for crate::peripherals::$pin
{
const PERIPHERAL_FUNC: crate::gpio::PortFunction = crate::gpio::PortFunction::$pf;
}
};
}
pub(crate) use pwm_pin;
declare_pwm_channel!(A);
declare_pwm_channel!(B);
macro_rules! gpt_instance {
($peripheral:ident, $ccmpa:ident, $ccmpb:ident, $cmpc:ident, $overflow:ident, $underflow:ident) => {
impl crate::pwm::Instance for crate::peripherals::$peripheral {}
impl crate::pwm::SealedInstance for crate::peripherals::$peripheral {
#[cfg(feature = "defmt")]
const PERIPHERAL: &'static str = concat!(stringify!($peripheral), ": ");
const CAPTURE_COMP_A_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::$ccmpa;
const CAPTURE_COMP_B_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::$ccmpb;
const COMP_C_EVENT: crate::event_link::InterruptEvent =
crate::event_link::InterruptEvent::$cmpc;
#[inline(always)]
fn regs() -> crate::pac::gpt::Gpt {
crate::pac::$peripheral
}
fn cmpa_waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
static WAKER: embassy_sync::waitqueue::AtomicWaker =
embassy_sync::waitqueue::AtomicWaker::new();
&WAKER
}
}
};
}
pub(crate) use gpt_instance;
impl embedded_hal_1::pwm::Error for PwmError {
fn kind(&self) -> embedded_hal_1::pwm::ErrorKind {
embedded_hal_1::pwm::ErrorKind::Other
}
}
impl<'d, I: Instance> embedded_hal_1::pwm::ErrorType for Pwm<'d, I> {
type Error = PwmError;
}
impl<'d, I: Instance> embedded_hal_1::pwm::SetDutyCycle for Pwm<'d, I> {
fn max_duty_cycle(&self) -> u16 {
let pwm = I::regs();
let period = pwm.gtpr().read();
assert!(period <= u32::from(u16::MAX));
period as u16
}
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
if self.channel_a.is_some() {
self.set_duty_a(duty as u32)?;
}
if self.channel_b.is_some() {
self.set_duty_b(duty as u32)?;
}
Ok(())
}
}