use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel};
use crate::gpio::{AfType, AnyPin, OutputType, Speed};
use crate::time::Hertz;
use crate::Peripheral;
pub enum Ch1 {}
pub enum Ch2 {}
pub enum Ch3 {}
pub enum Ch4 {}
pub struct PwmPin<'d, T, C> {
_pin: PeripheralRef<'d, AnyPin>,
phantom: PhantomData<(T, C)>,
}
macro_rules! channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> {
#[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
into_ref!(pin);
critical_section::with(|_| {
pin.set_low();
pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh));
});
PwmPin {
_pin: pin.map_into(),
phantom: PhantomData,
}
}
}
};
}
channel_impl!(new_ch1, Ch1, Channel1Pin);
channel_impl!(new_ch2, Ch2, Channel2Pin);
channel_impl!(new_ch3, Ch3, Channel3Pin);
channel_impl!(new_ch4, Ch4, Channel4Pin);
pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> {
timer: ManuallyDrop<Timer<'d, T>>,
channel: Channel,
}
impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
pub fn enable(&mut self) {
self.timer.enable_channel(self.channel, true);
}
pub fn disable(&mut self) {
self.timer.enable_channel(self.channel, false);
}
pub fn is_enabled(&self) -> bool {
self.timer.get_channel_enable_state(self.channel)
}
pub fn max_duty_cycle(&self) -> u16 {
let max = self.timer.get_max_compare_value();
assert!(max < u16::MAX as u32);
max as u16 + 1
}
pub fn set_duty_cycle(&mut self, duty: u16) {
assert!(duty <= (*self).max_duty_cycle());
self.timer.set_compare_value(self.channel, duty.into())
}
pub fn set_duty_cycle_fully_off(&mut self) {
self.set_duty_cycle(0);
}
pub fn set_duty_cycle_fully_on(&mut self) {
self.set_duty_cycle((*self).max_duty_cycle());
}
pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) {
assert!(denom != 0);
assert!(num <= denom);
let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom);
#[allow(clippy::cast_possible_truncation)]
self.set_duty_cycle(duty as u16);
}
pub fn set_duty_cycle_percent(&mut self, percent: u8) {
self.set_duty_cycle_fraction(u16::from(percent), 100)
}
pub fn current_duty_cycle(&self) -> u16 {
unwrap!(self.timer.get_compare_value(self.channel).try_into())
}
pub fn set_polarity(&mut self, polarity: OutputPolarity) {
self.timer.set_output_polarity(self.channel, polarity);
}
pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {
self.timer.set_output_compare_mode(self.channel, mode);
}
}
pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> {
pub ch1: SimplePwmChannel<'d, T>,
pub ch2: SimplePwmChannel<'d, T>,
pub ch3: SimplePwmChannel<'d, T>,
pub ch4: SimplePwmChannel<'d, T>,
}
pub struct SimplePwm<'d, T: GeneralInstance4Channel> {
inner: Timer<'d, T>,
}
impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
pub fn new(
tim: impl Peripheral<P = T> + 'd,
_ch1: Option<PwmPin<'d, T, Ch1>>,
_ch2: Option<PwmPin<'d, T, Ch2>>,
_ch3: Option<PwmPin<'d, T, Ch3>>,
_ch4: Option<PwmPin<'d, T, Ch4>>,
freq: Hertz,
counting_mode: CountingMode,
) -> Self {
Self::new_inner(tim, freq, counting_mode)
}
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
let mut this = Self { inner: Timer::new(tim) };
this.inner.set_counting_mode(counting_mode);
this.set_frequency(freq);
this.inner.enable_outputs(); this.inner.start();
[Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
.iter()
.for_each(|&channel| {
this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
this.inner.set_output_compare_preload(channel, true);
});
this
}
pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> {
SimplePwmChannel {
timer: unsafe { self.inner.clone_unchecked() },
channel,
}
}
pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> {
self.channel(Channel::Ch1)
}
pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> {
self.channel(Channel::Ch2)
}
pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> {
self.channel(Channel::Ch3)
}
pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> {
self.channel(Channel::Ch4)
}
pub fn split(self) -> SimplePwmChannels<'static, T>
where
'd: 'static,
{
let timer = ManuallyDrop::new(self.inner);
let ch = |channel| SimplePwmChannel {
timer: unsafe { timer.clone_unchecked() },
channel,
};
SimplePwmChannels {
ch1: ch(Channel::Ch1),
ch2: ch(Channel::Ch2),
ch3: ch(Channel::Ch3),
ch4: ch(Channel::Ch4),
}
}
pub fn set_frequency(&mut self, freq: Hertz) {
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
2u8
} else {
1u8
};
self.inner.set_frequency_internal(freq * multiplier, 16);
}
pub fn max_duty_cycle(&self) -> u16 {
let max = self.inner.get_max_compare_value();
assert!(max < u16::MAX as u32);
max as u16 + 1
}
pub async fn waveform_up(
&mut self,
dma: impl Peripheral<P = impl super::UpDma<T>>,
channel: Channel,
duty: &[u16],
) {
into_ref!(dma);
#[allow(clippy::let_unit_value)] let req = dma.request();
let original_duty_state = self.channel(channel).current_duty_cycle();
let original_enable_state = self.channel(channel).is_enabled();
let original_update_dma_state = self.inner.get_update_dma_state();
if !original_update_dma_state {
self.inner.enable_update_dma(true);
}
if !original_enable_state {
self.channel(channel).enable();
}
unsafe {
#[cfg(not(any(bdma, gpdma)))]
use crate::dma::{Burst, FifoThreshold};
use crate::dma::{Transfer, TransferOptions};
let dma_transfer_option = TransferOptions {
#[cfg(not(any(bdma, gpdma)))]
fifo_threshold: Some(FifoThreshold::Full),
#[cfg(not(any(bdma, gpdma)))]
mburst: Burst::Incr8,
..Default::default()
};
Transfer::new_write(
&mut dma,
req,
duty,
self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut _,
dma_transfer_option,
)
.await
};
if !original_enable_state {
self.channel(channel).disable();
}
self.channel(channel).set_duty_cycle(original_duty_state);
if !original_update_dma_state {
self.inner.enable_update_dma(false);
}
}
}
macro_rules! impl_waveform_chx {
($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => {
impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
pub async fn $fn_name(&mut self, dma: impl Peripheral<P = impl super::$dma_ch<T>>, duty: &[u16]) {
use crate::pac::timer::vals::Ccds;
into_ref!(dma);
#[allow(clippy::let_unit_value)] let req = dma.request();
let cc_channel = Channel::$cc_ch;
let original_duty_state = self.channel(cc_channel).current_duty_cycle();
let original_enable_state = self.channel(cc_channel).is_enabled();
let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
if !original_cc_dma_on_update {
self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
}
if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, true);
}
if !original_enable_state {
self.channel(cc_channel).enable();
}
unsafe {
#[cfg(not(any(bdma, gpdma)))]
use crate::dma::{Burst, FifoThreshold};
use crate::dma::{Transfer, TransferOptions};
let dma_transfer_option = TransferOptions {
#[cfg(not(any(bdma, gpdma)))]
fifo_threshold: Some(FifoThreshold::Full),
#[cfg(not(any(bdma, gpdma)))]
mburst: Burst::Incr8,
..Default::default()
};
Transfer::new_write(
&mut dma,
req,
duty,
self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _,
dma_transfer_option,
)
.await
};
if !original_enable_state {
self.channel(cc_channel).disable();
}
self.channel(cc_channel).set_duty_cycle(original_duty_state);
if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, false);
}
if !original_cc_dma_on_update {
self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
}
}
}
};
}
impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1);
impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {
type Error = core::convert::Infallible;
}
impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> {
fn max_duty_cycle(&self) -> u16 {
self.max_duty_cycle()
}
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
self.set_duty_cycle(duty);
Ok(())
}
fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> {
self.set_duty_cycle_fully_off();
Ok(())
}
fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> {
self.set_duty_cycle_fully_on();
Ok(())
}
fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> {
self.set_duty_cycle_fraction(num, denom);
Ok(())
}
fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> {
self.set_duty_cycle_percent(percent);
Ok(())
}
}
impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
type Channel = Channel;
type Time = Hertz;
type Duty = u32;
fn disable(&mut self, channel: Self::Channel) {
self.inner.enable_channel(channel, false);
}
fn enable(&mut self, channel: Self::Channel) {
self.inner.enable_channel(channel, true);
}
fn get_period(&self) -> Self::Time {
self.inner.get_frequency()
}
fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
self.inner.get_compare_value(channel)
}
fn get_max_duty(&self) -> Self::Duty {
self.inner.get_max_compare_value() + 1
}
fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
assert!(duty <= self.max_duty_cycle() as u32);
self.inner.set_compare_value(channel, duty)
}
fn set_period<P>(&mut self, period: P)
where
P: Into<Self::Time>,
{
self.inner.set_frequency(period.into());
}
}