use super::{
compute_arr_presc, Advanced, CPin, Channel, FTimer, IdleState, Instance, NCPin, Ocm, Polarity,
Timer, WithPwm,
};
pub use super::{Ch, C1, C2, C3, C4};
use crate::gpio::{OpenDrain, PushPull};
use crate::rcc::Clocks;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use fugit::{HertzU32 as Hertz, TimerDurationU32};
pub type Channel1<TIM, const COMP: bool = false> = ChannelBuilder<TIM, C1, COMP, PushPull>;
pub type Channel2<TIM, const COMP: bool = false> = ChannelBuilder<TIM, C2, COMP, PushPull>;
pub type Channel3<TIM, const COMP: bool = false> = ChannelBuilder<TIM, C3, COMP, PushPull>;
pub type Channel4<TIM, const COMP: bool = false> = ChannelBuilder<TIM, C4, COMP, PushPull>;
pub type Channel1OD<TIM, const COMP: bool = false> = ChannelBuilder<TIM, C1, COMP, OpenDrain>;
pub type Channel2OD<TIM, const COMP: bool = false> = ChannelBuilder<TIM, C2, COMP, OpenDrain>;
pub type Channel3OD<TIM, const COMP: bool = false> = ChannelBuilder<TIM, C3, COMP, OpenDrain>;
pub type Channel4OD<TIM, const COMP: bool = false> = ChannelBuilder<TIM, C4, COMP, OpenDrain>;
pub struct ChannelBuilder<TIM, const C: u8, const COMP: bool = false, Otype = PushPull> {
pub(super) _tim: PhantomData<(TIM, Otype)>,
}
impl<TIM, Otype, const C: u8> ChannelBuilder<TIM, C, false, Otype>
where
TIM: CPin<C>,
{
pub fn new(pin: impl Into<TIM::Ch<Otype>>) -> Self {
let _pin = pin.into();
Self { _tim: PhantomData }
}
}
impl<TIM, Otype, const C: u8, const COMP: bool> ChannelBuilder<TIM, C, COMP, Otype>
where
TIM: CPin<C>,
{
pub fn with(self, pin: impl Into<TIM::Ch<Otype>>) -> Self {
let _pin = pin.into();
self
}
}
impl<TIM, Otype, const C: u8, const COMP: bool> ChannelBuilder<TIM, C, COMP, Otype>
where
TIM: NCPin<C>,
{
pub fn with_complementary(
self,
pin: impl Into<TIM::ChN<Otype>>,
) -> ChannelBuilder<TIM, C, true, Otype> {
let _pin = pin.into();
ChannelBuilder { _tim: PhantomData }
}
}
impl<TIM, Otype, const C: u8, const COMP: bool> sealed::Split
for ChannelBuilder<TIM, C, COMP, Otype>
{
type Channels = PwmChannel<TIM, C, COMP>;
fn split() -> Self::Channels {
PwmChannel::new()
}
}
mod sealed {
pub trait Split {
type Channels;
fn split() -> Self::Channels;
}
macro_rules! split {
($($T:ident),+) => {
impl<$($T),+> Split for ($($T),+)
where
$($T: Split,)+
{
type Channels = ($($T::Channels),+);
fn split() -> Self::Channels {
($($T::split()),+)
}
}
};
}
split!(T1, T2);
split!(T1, T2, T3);
split!(T1, T2, T3, T4);
}
pub trait Pins<TIM>: sealed::Split {
const C1: bool = false;
const C2: bool = false;
const C3: bool = false;
const C4: bool = false;
const NC1: bool = false;
const NC2: bool = false;
const NC3: bool = false;
const NC4: bool = false;
fn check_used(c: Channel) -> Channel {
if (c == Channel::C1 && Self::C1)
|| (c == Channel::C2 && Self::C2)
|| (c == Channel::C3 && Self::C3)
|| (c == Channel::C4 && Self::C4)
{
c
} else {
panic!("Unused channel")
}
}
fn check_complementary_used(c: Channel) -> Channel {
if (c == Channel::C1 && Self::NC1)
|| (c == Channel::C2 && Self::NC2)
|| (c == Channel::C3 && Self::NC3)
|| (c == Channel::C4 && Self::NC4)
{
c
} else {
panic!("Unused channel")
}
}
}
macro_rules! pins_impl {
( $( $(($Otype:ident, $ENCHX:ident, $COMP:ident)),+; )+ ) => {
$(
#[allow(unused_parens)]
impl<TIM, $($Otype, const $COMP: bool,)+> Pins<TIM> for ($(ChannelBuilder<TIM, $ENCHX, $COMP, $Otype>),+) {
$(
const $ENCHX: bool = true;
const $COMP: bool = $COMP;
)+
}
)+
};
}
pins_impl!(
(O1, C1, NC1), (O2, C2, NC2), (O3, C3, NC3), (O4, C4, NC4);
(O2, C2, NC2), (O3, C3, NC3), (O4, C4, NC4);
(O1, C1, NC1), (O3, C3, NC3), (O4, C4, NC4);
(O1, C1, NC1), (O2, C2, NC2), (O4, C4, NC4);
(O1, C1, NC1), (O2, C2, NC2), (O3, C3, NC3);
(O3, C3, NC3), (O4, C4, NC4);
(O2, C2, NC2), (O4, C4, NC4);
(O2, C2, NC2), (O3, C3, NC3);
(O1, C1, NC1), (O4, C4, NC4);
(O1, C1, NC1), (O3, C3, NC3);
(O1, C1, NC1), (O2, C2, NC2);
(O1, C1, NC1);
(O2, C2, NC2);
(O3, C3, NC3);
(O4, C4, NC4);
);
pub struct PwmChannel<TIM, const C: u8, const COMP: bool = false> {
pub(super) _tim: PhantomData<TIM>,
}
pub trait PwmExt
where
Self: Sized + Instance + WithPwm,
{
fn pwm<PINS, const FREQ: u32>(
self,
pins: PINS,
time: TimerDurationU32<FREQ>,
clocks: &Clocks,
) -> Pwm<Self, PINS, FREQ>
where
PINS: Pins<Self>;
fn pwm_hz<PINS>(self, pins: PINS, freq: Hertz, clocks: &Clocks) -> PwmHz<Self, PINS>
where
PINS: Pins<Self>;
fn pwm_us<PINS>(
self,
pins: PINS,
time: TimerDurationU32<1_000_000>,
clocks: &Clocks,
) -> Pwm<Self, PINS, 1_000_000>
where
PINS: Pins<Self>,
{
self.pwm::<_, 1_000_000>(pins, time, clocks)
}
}
impl<TIM> PwmExt for TIM
where
Self: Sized + Instance + WithPwm,
{
fn pwm<PINS, const FREQ: u32>(
self,
pins: PINS,
time: TimerDurationU32<FREQ>,
clocks: &Clocks,
) -> Pwm<TIM, PINS, FREQ>
where
PINS: Pins<Self>,
{
FTimer::<Self, FREQ>::new(self, clocks).pwm(pins, time)
}
fn pwm_hz<PINS>(self, pins: PINS, time: Hertz, clocks: &Clocks) -> PwmHz<TIM, PINS>
where
PINS: Pins<Self>,
{
Timer::new(self, clocks).pwm_hz(pins, time)
}
}
impl<TIM, const C: u8, const COMP: bool> PwmChannel<TIM, C, COMP> {
pub(crate) fn new() -> Self {
Self {
_tim: core::marker::PhantomData,
}
}
}
impl<TIM: Instance + WithPwm, const C: u8, const COMP: bool> PwmChannel<TIM, C, COMP> {
#[inline]
pub fn disable(&mut self) {
TIM::enable_channel(C, false);
}
#[inline]
pub fn enable(&mut self) {
TIM::enable_channel(C, true);
}
#[inline]
pub fn set_polarity(&mut self, p: Polarity) {
TIM::set_channel_polarity(C, p);
}
#[inline]
pub fn get_duty(&self) -> u16 {
TIM::read_cc_value(C) as u16
}
#[inline]
pub fn get_max_duty(&self) -> u16 {
(TIM::read_auto_reload() as u16).wrapping_add(1)
}
#[inline]
pub fn set_duty(&mut self, duty: u16) {
TIM::set_cc_value(C, duty as u32)
}
#[inline]
pub fn set_complementary_polarity(&mut self, p: Polarity) {
TIM::set_nchannel_polarity(C, p);
}
}
impl<TIM: Instance + WithPwm + Advanced, const C: u8> PwmChannel<TIM, C, true> {
#[inline]
pub fn disable_complementary(&mut self) {
TIM::enable_nchannel(C, false);
}
#[inline]
pub fn enable_complementary(&mut self) {
TIM::enable_nchannel(C, true);
}
#[inline]
pub fn set_idle_state(&mut self, s: IdleState) {
TIM::idle_state(C, false, s);
}
#[inline]
pub fn set_complementary_idle_state(&mut self, s: IdleState) {
TIM::idle_state(C, true, s);
}
}
pub struct PwmHz<TIM, PINS>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
timer: Timer<TIM>,
_pins: PhantomData<PINS>,
}
impl<TIM, PINS> PwmHz<TIM, PINS>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
pub fn release(mut self) -> Timer<TIM> {
self.tim.cr1_reset();
self.timer
}
pub fn split(self) -> PINS::Channels {
PINS::split()
}
}
impl<TIM, PINS> Deref for PwmHz<TIM, PINS>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
type Target = Timer<TIM>;
fn deref(&self) -> &Self::Target {
&self.timer
}
}
impl<TIM, PINS> DerefMut for PwmHz<TIM, PINS>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.timer
}
}
impl<TIM: Instance + WithPwm> Timer<TIM> {
pub fn pwm_hz<PINS>(mut self, _pins: PINS, freq: Hertz) -> PwmHz<TIM, PINS>
where
PINS: Pins<TIM>,
{
if PINS::C1 {
self.tim
.preload_output_channel_in_mode(Channel::C1, Ocm::PwmMode1);
}
if PINS::C2 && TIM::CH_NUMBER > 1 {
self.tim
.preload_output_channel_in_mode(Channel::C2, Ocm::PwmMode1);
}
if PINS::C3 && TIM::CH_NUMBER > 2 {
self.tim
.preload_output_channel_in_mode(Channel::C3, Ocm::PwmMode1);
}
if PINS::C4 && TIM::CH_NUMBER > 3 {
self.tim
.preload_output_channel_in_mode(Channel::C4, Ocm::PwmMode1);
}
self.tim.enable_preload(true);
let (psc, arr) = compute_arr_presc(freq.raw(), self.clk.raw());
self.tim.set_prescaler(psc);
self.tim.set_auto_reload(arr).unwrap();
self.tim.trigger_update();
self.tim.start_pwm();
PwmHz {
timer: self,
_pins: PhantomData,
}
}
}
impl<TIM, PINS> PwmHz<TIM, PINS>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
#[inline]
pub fn enable(&mut self, channel: Channel) {
TIM::enable_channel(PINS::check_used(channel) as u8, true)
}
#[inline]
pub fn disable(&mut self, channel: Channel) {
TIM::enable_channel(PINS::check_used(channel) as u8, false)
}
#[inline]
pub fn set_polarity(&mut self, channel: Channel, p: Polarity) {
TIM::set_channel_polarity(PINS::check_used(channel) as u8, p);
}
#[inline]
pub fn get_duty(&self, channel: Channel) -> u16 {
TIM::read_cc_value(PINS::check_used(channel) as u8) as u16
}
#[inline]
pub fn set_duty(&mut self, channel: Channel, duty: u16) {
TIM::set_cc_value(PINS::check_used(channel) as u8, duty as u32)
}
pub fn get_max_duty(&self) -> u16 {
(TIM::read_auto_reload() as u16).wrapping_add(1)
}
pub fn get_period(&self) -> Hertz {
let clk = self.clk;
let psc = self.tim.read_prescaler() as u32;
let arr = TIM::read_auto_reload();
clk / ((psc + 1) * (arr + 1))
}
pub fn set_period(&mut self, period: Hertz) {
let clk = self.clk;
let (psc, arr) = compute_arr_presc(period.raw(), clk.raw());
self.tim.set_prescaler(psc);
self.tim.set_auto_reload(arr).unwrap();
self.tim.cnt_reset();
}
#[inline]
pub fn set_complementary_polarity(&mut self, channel: Channel, p: Polarity) {
TIM::set_channel_polarity(PINS::check_complementary_used(channel) as u8, p);
}
}
impl<TIM, PINS> PwmHz<TIM, PINS>
where
TIM: Instance + WithPwm + Advanced,
PINS: Pins<TIM>,
{
#[inline]
pub fn enable_complementary(&mut self, channel: Channel) {
TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, true)
}
#[inline]
pub fn disable_complementary(&mut self, channel: Channel) {
TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, false)
}
#[inline]
pub fn set_dead_time(&mut self, dts_ticks: u16) {
let bits = pack_ceil_dead_time(dts_ticks);
TIM::set_dtg_value(bits);
}
#[inline]
pub fn set_dead_time_bits(&mut self, bits: u8) {
TIM::set_dtg_value(bits);
}
#[inline]
pub fn get_dead_time(&self) -> u16 {
unpack_dead_time(TIM::read_dtg_value())
}
#[inline]
pub fn get_dead_time_bits(&self) -> u8 {
TIM::read_dtg_value()
}
#[inline]
pub fn set_idle_state(&mut self, channel: Channel, s: IdleState) {
TIM::idle_state(PINS::check_used(channel) as u8, false, s);
}
#[inline]
pub fn set_complementary_idle_state(&mut self, channel: Channel, s: IdleState) {
TIM::idle_state(PINS::check_complementary_used(channel) as u8, true, s);
}
}
pub struct Pwm<TIM, PINS, const FREQ: u32>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
timer: FTimer<TIM, FREQ>,
_pins: PhantomData<PINS>,
}
impl<TIM, PINS, const FREQ: u32> Pwm<TIM, PINS, FREQ>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
pub fn split(self) -> PINS::Channels {
PINS::split()
}
pub fn release(mut self) -> FTimer<TIM, FREQ> {
self.tim.cr1_reset();
self.timer
}
}
impl<TIM, PINS, const FREQ: u32> Deref for Pwm<TIM, PINS, FREQ>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
type Target = FTimer<TIM, FREQ>;
fn deref(&self) -> &Self::Target {
&self.timer
}
}
impl<TIM, PINS, const FREQ: u32> DerefMut for Pwm<TIM, PINS, FREQ>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.timer
}
}
impl<TIM: Instance + WithPwm, const FREQ: u32> FTimer<TIM, FREQ> {
pub fn pwm<PINS>(mut self, _pins: PINS, time: TimerDurationU32<FREQ>) -> Pwm<TIM, PINS, FREQ>
where
PINS: Pins<TIM>,
{
if PINS::C1 {
self.tim
.preload_output_channel_in_mode(Channel::C1, Ocm::PwmMode1);
}
if PINS::C2 && TIM::CH_NUMBER > 1 {
self.tim
.preload_output_channel_in_mode(Channel::C2, Ocm::PwmMode1);
}
if PINS::C3 && TIM::CH_NUMBER > 2 {
self.tim
.preload_output_channel_in_mode(Channel::C3, Ocm::PwmMode1);
}
if PINS::C4 && TIM::CH_NUMBER > 3 {
self.tim
.preload_output_channel_in_mode(Channel::C4, Ocm::PwmMode1);
}
self.tim.enable_preload(true);
self.tim.set_auto_reload(time.ticks() - 1).unwrap();
self.tim.trigger_update();
self.tim.start_pwm();
Pwm {
timer: self,
_pins: PhantomData,
}
}
}
impl<TIM, PINS, const FREQ: u32> Pwm<TIM, PINS, FREQ>
where
TIM: Instance + WithPwm,
PINS: Pins<TIM>,
{
#[inline]
pub fn enable(&mut self, channel: Channel) {
TIM::enable_channel(PINS::check_used(channel) as u8, true)
}
#[inline]
pub fn disable(&mut self, channel: Channel) {
TIM::enable_channel(PINS::check_used(channel) as u8, false)
}
#[inline]
pub fn set_polarity(&mut self, channel: Channel, p: Polarity) {
TIM::set_channel_polarity(PINS::check_used(channel) as u8, p);
}
#[inline]
pub fn get_duty(&self, channel: Channel) -> u16 {
TIM::read_cc_value(PINS::check_used(channel) as u8) as u16
}
#[inline]
pub fn get_duty_time(&self, channel: Channel) -> TimerDurationU32<FREQ> {
TimerDurationU32::from_ticks(TIM::read_cc_value(PINS::check_used(channel) as u8))
}
#[inline]
pub fn set_duty(&mut self, channel: Channel, duty: u16) {
TIM::set_cc_value(PINS::check_used(channel) as u8, duty.into())
}
#[inline]
pub fn set_duty_time(&mut self, channel: Channel, duty: TimerDurationU32<FREQ>) {
TIM::set_cc_value(PINS::check_used(channel) as u8, duty.ticks())
}
pub fn get_max_duty(&self) -> u16 {
(TIM::read_auto_reload() as u16).wrapping_add(1)
}
pub fn get_period(&self) -> TimerDurationU32<FREQ> {
TimerDurationU32::from_ticks(TIM::read_auto_reload() + 1)
}
pub fn set_period(&mut self, period: TimerDurationU32<FREQ>) {
self.tim.set_auto_reload(period.ticks() - 1).unwrap();
self.tim.cnt_reset();
}
#[inline]
pub fn set_complementary_polarity(&mut self, channel: Channel, p: Polarity) {
TIM::set_channel_polarity(PINS::check_complementary_used(channel) as u8, p);
}
}
impl<TIM, PINS, const FREQ: u32> Pwm<TIM, PINS, FREQ>
where
TIM: Instance + WithPwm + Advanced,
PINS: Pins<TIM>,
{
#[inline]
pub fn enable_complementary(&mut self, channel: Channel) {
TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, true)
}
#[inline]
pub fn disable_complementary(&mut self, channel: Channel) {
TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, false)
}
#[inline]
pub fn set_dead_time(&mut self, dts_ticks: u16) {
let bits = pack_ceil_dead_time(dts_ticks);
TIM::set_dtg_value(bits);
}
#[inline]
pub fn set_dead_time_bits(&mut self, bits: u8) {
TIM::set_dtg_value(bits);
}
#[inline]
pub fn get_dead_time(&self) -> u16 {
unpack_dead_time(TIM::read_dtg_value())
}
#[inline]
pub fn get_dead_time_bits(&self) -> u8 {
TIM::read_dtg_value()
}
#[inline]
pub fn set_idle_state(&mut self, channel: Channel, s: IdleState) {
TIM::idle_state(PINS::check_used(channel) as u8, false, s);
}
#[inline]
pub fn set_complementary_idle_state(&mut self, channel: Channel, s: IdleState) {
TIM::idle_state(PINS::check_complementary_used(channel) as u8, true, s);
}
}
const fn pack_ceil_dead_time(dts_ticks: u16) -> u8 {
match dts_ticks {
0..=127 => dts_ticks as u8,
128..=254 => ((((dts_ticks + 1) >> 1) - 64) as u8) | 0b_1000_0000,
255..=504 => ((((dts_ticks + 7) >> 3) - 32) as u8) | 0b_1100_0000,
505..=1008 => ((((dts_ticks + 15) >> 4) - 32) as u8) | 0b_1110_0000,
1009.. => 0xff,
}
}
const fn unpack_dead_time(bits: u8) -> u16 {
if bits & 0b_1000_0000 == 0 {
bits as u16
} else if bits & 0b_0100_0000 == 0 {
(((bits & !0b_1000_0000) as u16) + 64) * 2
} else if bits & 0b_0010_0000 == 0 {
(((bits & !0b_1100_0000) as u16) + 32) * 8
} else {
(((bits & !0b_1110_0000) as u16) + 32) * 16
}
}