use core::convert::Infallible;
use embedded_hal::delay::DelayNs;
use embedded_hal::pwm::{ErrorType, SetDutyCycle};
use crate::gpio::{Alternate, Pin};
use crate::pac;
use crate::rcu::{Clocks, Enable, Rcu, Reset};
use crate::time::{Duration, Hertz, NanosDuration};
const MAX_COUNT: u32 = 1 << 16;
fn dividers(ticks: u32) -> (u16, u16) {
let ticks = ticks.max(1);
let psc = ticks.div_ceil(MAX_COUNT);
let car = ticks / psc;
((psc - 1) as u16, (car - 1) as u16)
}
fn interval_to_ticks<const NOM: u64, const DENOM: u64>(
interval: Duration<u32, NOM, DENOM>,
clock: Hertz,
) -> u32 {
let raw_time = u64::from(interval.as_ticks());
let raw_freq = u64::from(clock.to_Hz());
(raw_time.saturating_mul(raw_freq).saturating_mul(NOM) / DENOM).min(u32::MAX.into()) as u32
}
fn start_counter<TIMERX: Instance>(timer: &TIMERX, psc: u16, car: u16) {
timer.set_psc(psc);
timer.set_car(car);
timer.gen_update();
timer.clear_upif();
timer.set_cen(true);
}
fn wait_update<TIMERX: Instance>(timer: &TIMERX) {
while !timer.upif() {}
timer.clear_upif();
}
pub trait Instance: Enable + Reset {
fn clk(clocks: &Clocks) -> Hertz;
fn set_psc(&self, psc: u16);
fn read_psc(&self) -> u16;
fn set_car(&self, car: u16);
fn read_car(&self) -> u16;
fn read_cnt(&self) -> u16;
fn gen_update(&self);
fn set_cen(&self, on: bool);
fn upif(&self) -> bool;
fn clear_upif(&self);
unsafe fn steal(&self) -> Self;
}
macro_rules! timer_instance {
($($TIMERX:ty => $clk:ident,)+) => {
$(
impl Instance for $TIMERX {
fn clk(clocks: &Clocks) -> Hertz {
clocks.$clk()
}
fn set_psc(&self, psc: u16) {
self.psc().write(|w| w.psc().bits(psc));
}
fn read_psc(&self) -> u16 {
self.psc().read().psc().bits()
}
#[allow(unused_unsafe)]
fn set_car(&self, car: u16) {
self.car().write(|w| unsafe { w.car().bits(car) });
}
fn read_car(&self) -> u16 {
self.car().read().car().bits()
}
fn read_cnt(&self) -> u16 {
self.cnt().read().cnt().bits()
}
fn gen_update(&self) {
self.swevg().write(|w| w.upg().set_bit());
}
fn set_cen(&self, on: bool) {
self.ctl0().modify(|_, w| w.cen().bit(on));
}
fn upif(&self) -> bool {
self.intf().read().upif().bit_is_set()
}
fn clear_upif(&self) {
self.intf().write(|w| w.upif().clear());
}
unsafe fn steal(&self) -> Self{
unsafe { Self::steal() }
}
}
)+
};
}
timer_instance! {
pac::Timer2 => pclk1_tim,
pac::Timer5 => pclk1_tim,
pac::Timer13 => pclk1_tim,
pac::Timer0 => pclk2_tim,
pac::Timer14 => pclk2_tim,
pac::Timer15 => pclk2_tim,
pac::Timer16 => pclk2_tim,
}
pub struct Timer<TIMERX> {
timer: TIMERX,
clk: Hertz,
}
impl<TIMERX: Instance> Timer<TIMERX> {
pub fn new(rcu: &mut Rcu, timer: TIMERX, clocks: Clocks) -> Timer<TIMERX> {
TIMERX::enable(rcu);
TIMERX::reset(rcu);
Timer {
timer,
clk: TIMERX::clk(&clocks),
}
}
pub fn release(self) -> TIMERX {
self.timer
}
pub fn start(self, psc: u16, car: u16) -> CountDownTimer<TIMERX> {
start_counter(&self.timer, psc, car);
CountDownTimer {
timer: self.timer,
clk: self.clk,
}
}
pub fn start_interval<const NOM: u64, const DENOM: u64>(
self,
interval: Duration<u32, NOM, DENOM>,
) -> CountDownTimer<TIMERX> {
let (psc, car) = dividers(interval_to_ticks(interval, self.clk));
self.start(psc, car)
}
pub fn into_delay(self) -> Delay<TIMERX> {
Delay {
timer: self.timer,
clk: self.clk,
}
}
pub fn into_pwm(self, psc: u16, car: u16) -> Pwm<TIMERX> {
start_counter(&self.timer, psc, car);
Pwm {
timer: self.timer,
clk: self.clk,
}
}
pub fn into_pwm_interval<const NOM: u64, const DENOM: u64>(
self,
interval: Duration<u32, NOM, DENOM>,
) -> Pwm<TIMERX> {
let (psc, car) = dividers(interval_to_ticks(interval, self.clk));
self.into_pwm(psc, car)
}
}
pub struct CountDownTimer<TIMERX> {
timer: TIMERX,
clk: Hertz,
}
impl<TIMERX: Instance> CountDownTimer<TIMERX> {
pub fn cnt(&self) -> u16 {
self.timer.read_cnt()
}
pub fn car(&self) -> u16 {
self.timer.read_car()
}
pub fn psc(&self) -> u16 {
self.timer.read_psc()
}
pub fn elapsed<const NOM: u64, const DENOM: u64>(&self) -> Duration<u32, NOM, DENOM> {
let cnt = u64::from(self.cnt());
let psc = u64::from(self.psc());
let clk = u64::from(self.clk.to_Hz());
Duration::<u32, NOM, DENOM>::from_ticks(
(cnt.saturating_mul(psc + 1).saturating_mul(DENOM) / (clk.saturating_mul(NOM)))
.min(u32::MAX.into()) as u32,
)
}
pub fn wait(&self) {
wait_update(&self.timer);
}
pub fn stop(self) -> Timer<TIMERX> {
self.timer.set_cen(false);
Timer {
timer: self.timer,
clk: self.clk,
}
}
pub fn release(self) -> TIMERX {
self.stop().timer
}
}
pub struct Delay<TIMERX> {
timer: TIMERX,
clk: Hertz,
}
impl<TIMERX: Instance> Delay<TIMERX> {
pub fn delay<const NOM: u64, const DENOM: u64>(&mut self, interval: Duration<u32, NOM, DENOM>) {
let (psc, car) = dividers(interval_to_ticks(interval, self.clk));
start_counter(&self.timer, psc, car);
wait_update(&self.timer);
self.timer.set_cen(false);
}
pub fn into_timer(self) -> Timer<TIMERX> {
Timer {
timer: self.timer,
clk: self.clk,
}
}
pub fn release(self) -> TIMERX {
self.timer
}
}
pub struct Pwm<TIMERX> {
timer: TIMERX,
clk: Hertz,
}
impl<TIMERX: Instance> Pwm<TIMERX> {
pub fn channel<PIN, const C: u8>(&self, pin: PIN) -> PwmChannel<TIMERX, PIN, C>
where
PIN: PwmPin<TIMERX, C>,
TIMERX: PwmOps<C>,
{
self.timer.apply_pwm_mode();
PwmChannel {
timer: unsafe { self.timer.steal() },
pin,
}
}
pub fn set_period(&self, psc: u16, car: u16) {
self.timer.set_psc(psc);
self.timer.set_car(car);
}
pub fn set_period_interval<const NOM: u64, const DENOM: u64>(
&self,
interval: Duration<u32, NOM, DENOM>,
) {
let (psc, car) = dividers(interval_to_ticks(interval, self.clk));
self.set_period(psc, car);
}
}
impl<TIMERX> Pwm<TIMERX>
where
TIMERX: PrimaryOutput,
{
pub fn enable_output(&self) {
self.timer.set_poen(true);
}
pub fn disable_output(&self) {
self.timer.set_poen(false);
}
}
impl<TIMERX: Instance> DelayNs for Delay<TIMERX> {
fn delay_ns(&mut self, ns: u32) {
self.delay(NanosDuration::from_nanos(ns));
}
}
pub trait TimerExt: Sized {
fn constrain(self, rcu: &mut Rcu, clocks: Clocks) -> Timer<Self>;
}
impl<TIMERX: Instance> TimerExt for TIMERX {
fn constrain(self, rcu: &mut Rcu, clocks: Clocks) -> Timer<Self> {
Timer::new(rcu, self, clocks)
}
}
pub trait PwmOps<const C: u8>: Instance {
fn apply_pwm_mode(&self);
fn set_chxcv(&self, cv: u16);
fn set_chxen(&self, on: bool);
}
macro_rules! pwm {
{$($Timer:ty => [
$(
($Ch:expr, ($chctl_reg:ident, $chms:ident, $chcomctl:ident, $chcomsen:ident), ($chcv:ident, $chval:ident), $chen:ident, $chp:ident)$(,)?
)+]$(,)?)+
} => {
$($(impl PwmOps<$Ch> for $Timer {
fn apply_pwm_mode(&self) {
self.chctl2().modify(|_, w| w.$chp().not_inverted());
self.$chctl_reg().modify(|_, w| {
w.$chms()
.output()
.$chcomctl()
.pwm_mode0()
.$chcomsen()
.enabled()
});
}
#[allow(unused_unsafe)]
fn set_chxcv(&self, cv: u16) {
self.$chcv().write(|w| unsafe { w.$chval().bits(cv) });
}
fn set_chxen(&self, on: bool) {
self.chctl2().modify(|_, w| match on {
true => w.$chen().enabled(),
false => w.$chen().disabled(),
})
}
})+)+
};
}
pwm! {
pac::Timer0 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0en, ch0p),
(1, (chctl0_output, ch1ms, ch1comctl, ch1comsen), (ch1cv, ch1val), ch1en, ch1p),
(2, (chctl1_output, ch2ms, ch2comctl, ch2comsen), (ch2cv, ch2val), ch2en, ch2p),
(3, (chctl1_output, ch3ms, ch3comctl, ch3comsen), (ch3cv, ch3val), ch3en, ch3p)],
pac::Timer2 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0en, ch0p),
(1, (chctl0_output, ch1ms, ch1comctl, ch1comsen), (ch1cv, ch1val), ch1en, ch1p),
(2, (chctl1_output, ch2ms, ch2comctl, ch2comsen), (ch2cv, ch2val), ch2en, ch2p),
(3, (chctl1_output, ch3ms, ch3comctl, ch3comsen), (ch3cv, ch3val), ch3en, ch3p)],
pac::Timer13 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0en, ch0p)],
pac::Timer14 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0en, ch0p),
(1, (chctl0_output, ch1ms, ch1comctl, ch1comsen), (ch1cv, ch1val), ch1en, ch1p)],
pac::Timer15 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0en, ch0p)],
pac::Timer16 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0en, ch0p)]
}
pub trait PrimaryOutput: Instance {
fn set_poen(&self, on: bool);
}
macro_rules! poen {
($($Timer:ty$(,)?)+) => {
$(impl PrimaryOutput for $Timer {
fn set_poen(&self, on: bool) {
self.cchp().modify(|_, w| match on {
true => w.poen().enabled(),
false => w.poen().disabled(),
});
}
})+
};
}
poen!(pac::Timer0, pac::Timer14, pac::Timer15, pac::Timer16);
pub trait PwmPin<TIMERX, const C: u8> {}
macro_rules! pwm_pins {
( $( $TIMERX:ty: $( $C:literal: [ $($p:literal $n:literal : $af:literal),* $(,)? ] )* ),* $(,)? ) => {
$($($( impl PwmPin<$TIMERX, $C> for Pin<$p, $n, Alternate<$af>> {} )*)*)*
};
}
pwm_pins! {
pac::Timer0:
0: [ 'A' 8:2 ]
1: [ 'A' 9:2 ]
2: [ 'A' 10:2 ]
3: [ 'A' 11:2 ],
pac::Timer2:
0: [ 'A' 6:1, 'B' 4:1 ]
1: [ 'A' 7:1, 'B' 5:1 ]
2: [ 'B' 0:1 ]
3: [ 'B' 1:1 ],
pac::Timer13:
0: [ 'A' 4:4, 'A' 7:4, 'B' 1:2 ],
pac::Timer15:
0: [ 'A' 6:5, 'B' 8:2 ],
pac::Timer16:
0: [ 'A' 7:5, 'B' 9:2 ],
}
#[cfg(feature = "gd32e230x8")]
pwm_pins! {
pac::Timer14:
0: [ 'A' 2:0, 'B' 14:1 ]
1: [ 'A' 3:0, 'B' 15:1 ],
}
pub struct PwmChannel<TIMERX, PIN, const C: u8> {
timer: TIMERX,
pin: PIN,
}
impl<TIMERX: PwmOps<C>, PIN: PwmPin<TIMERX, C>, const C: u8> PwmChannel<TIMERX, PIN, C> {
pub fn enable(&self) {
self.timer.set_chxen(true);
}
pub fn disable(&self) {
self.timer.set_chxen(false);
}
pub fn set_duty(&self, cv: u16) {
self.timer.set_chxcv(cv);
}
pub fn max_duty(&self) -> u16 {
self.timer.read_car().saturating_add(1)
}
pub fn release(self) -> PIN {
self.pin
}
}
impl<TIMERX: PwmOps<C>, PIN: PwmPin<TIMERX, C>, const C: u8> ErrorType
for PwmChannel<TIMERX, PIN, C>
{
type Error = Infallible;
}
impl<TIMERX: PwmOps<C>, PIN: PwmPin<TIMERX, C>, const C: u8> SetDutyCycle
for PwmChannel<TIMERX, PIN, C>
{
fn max_duty_cycle(&self) -> u16 {
self.max_duty()
}
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
self.set_duty(duty);
Ok(())
}
}