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 ticks_to_interval<const NOM: u64, const DENOM: u64>(
ticks: u16,
psc: u16,
clock: Hertz,
) -> Duration<u32, NOM, DENOM> {
let ticks = u64::from(ticks);
let psc = u64::from(psc);
let clock = u64::from(clock.to_Hz());
Duration::<u32, NOM, DENOM>::from_ticks(
(ticks.saturating_mul(psc + 1).saturating_mul(DENOM) / clock.saturating_mul(NOM))
.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.read_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 read_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 read_upif(&self) -> bool {
self.intf().read().upif().bit_is_set()
}
fn clear_upif(&self) {
self.intf().modify(|_, 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 fn into_capture(self, psc: u16) -> Capture<TIMERX> {
start_counter(&self.timer, psc, u16::MAX);
Capture {
timer: self.timer,
clk: self.clk,
}
}
}
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> {
ticks_to_interval(self.cnt(), self.psc(), self.clk)
}
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: ChannelPin<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);
}
pub fn into_timer(self) -> Timer<TIMERX> {
self.timer.set_cen(false);
Timer {
timer: self.timer,
clk: self.clk,
}
}
pub fn release(self) -> TIMERX {
self.into_timer().timer
}
}
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);
}
}
pub struct Capture<TIMERX> {
timer: TIMERX,
clk: Hertz,
}
impl<TIMERX: Instance> Capture<TIMERX> {
pub fn channel<PIN, const C: u8>(&self, pin: PIN, edge: Edge) -> CaptureChannel<TIMERX, PIN, C>
where
PIN: ChannelPin<TIMERX, C>,
TIMERX: CaptureOps<C>,
{
self.timer.apply_capture_mode();
self.timer.select_edge(edge);
CaptureChannel {
timer: unsafe { self.timer.steal() },
pin,
clk: self.clk,
}
}
pub fn into_timer(self) -> Timer<TIMERX> {
self.timer.set_cen(false);
Timer {
timer: self.timer,
clk: self.clk,
}
}
pub fn release(self) -> TIMERX {
self.into_timer().timer
}
}
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 ChannelEnable<const C: u8>: Instance {
fn set_chxen(&self, on: bool);
}
macro_rules! channel_enable {
{$($Timer:ty => [$(($Ch:expr, $chen:ident)$(,)?)+]$(,)?)+} => {
$($(impl ChannelEnable<$Ch> for $Timer {
fn set_chxen(&self, on: bool) {
self.chctl2().modify(|_, w| match on {
true => w.$chen().enabled(),
false => w.$chen().disabled(),
})
}
})+)+
};
}
channel_enable! {
pac::Timer0 => [(0, ch0en), (1, ch1en), (2, ch2en), (3, ch3en)],
pac::Timer2 => [(0, ch0en), (1, ch1en), (2, ch2en), (3, ch3en)],
pac::Timer13 => [(0, ch0en)],
pac::Timer14 => [(0, ch0en), (1, ch1en)],
pac::Timer15 => [(0, ch0en)],
pac::Timer16 => [(0, ch0en)]
}
pub trait PwmOps<const C: u8>: ChannelEnable<C> {
fn apply_pwm_mode(&self);
fn set_chxcv(&self, cv: u16);
}
macro_rules! pwm {
{$($Timer:ty => [
$(
($Ch:expr, ($chctl_reg:ident, $chms:ident, $chcomctl:ident, $chcomsen:ident), ($chcv:ident, $chval: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) });
}
})+)+
};
}
pwm! {
pac::Timer0 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0p),
(1, (chctl0_output, ch1ms, ch1comctl, ch1comsen), (ch1cv, ch1val), ch1p),
(2, (chctl1_output, ch2ms, ch2comctl, ch2comsen), (ch2cv, ch2val), ch2p),
(3, (chctl1_output, ch3ms, ch3comctl, ch3comsen), (ch3cv, ch3val), ch3p)],
pac::Timer2 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0p),
(1, (chctl0_output, ch1ms, ch1comctl, ch1comsen), (ch1cv, ch1val), ch1p),
(2, (chctl1_output, ch2ms, ch2comctl, ch2comsen), (ch2cv, ch2val), ch2p),
(3, (chctl1_output, ch3ms, ch3comctl, ch3comsen), (ch3cv, ch3val), ch3p)],
pac::Timer13 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0p)],
pac::Timer14 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0p),
(1, (chctl0_output, ch1ms, ch1comctl, ch1comsen), (ch1cv, ch1val), ch1p)],
pac::Timer15 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), ch0p)],
pac::Timer16 => [(0, (chctl0_output, ch0ms, ch0comctl, ch0comsen), (ch0cv, ch0val), 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 ChannelPin<TIMERX, const C: u8> {}
macro_rules! channel_pins {
( $( $TIMERX:ty: $( $C:literal: [ $($p:literal $n:literal : $af:literal),* $(,)? ] )* ),* $(,)? ) => {
$($($( impl ChannelPin<$TIMERX, $C> for Pin<$p, $n, Alternate<$af>> {} )*)*)*
};
}
channel_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")]
channel_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: ChannelPin<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
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
Overcapture,
}
pub struct CaptureChannel<TIMERX, PIN, const C: u8> {
timer: TIMERX,
pin: PIN,
clk: Hertz,
}
impl<TIMERX: CaptureOps<C>, PIN: ChannelPin<TIMERX, C>, const C: u8>
CaptureChannel<TIMERX, PIN, C>
{
pub fn enable(&self) {
self.timer.set_chxen(true);
}
pub fn disable(&self) {
self.timer.set_chxen(false);
}
pub fn select_edge(&self, edge: Edge) {
self.timer.select_edge(edge);
}
pub fn read(&self) -> nb::Result<u16, Error> {
if !self.timer.read_chxif() {
return Err(nb::Error::WouldBlock);
}
let cv = self.timer.read_chxcv();
let lost = self.timer.read_chxof();
self.timer.clear_chxif();
match lost {
true => {
self.timer.clear_chxof();
Err(nb::Error::Other(Error::Overcapture))
}
false => Ok(cv),
}
}
pub fn interval<const NOM: u64, const DENOM: u64>(
&self,
from: u16,
to: u16,
) -> Duration<u32, NOM, DENOM> {
ticks_to_interval(to.wrapping_sub(from), self.timer.read_psc(), self.clk)
}
pub fn release(self) -> PIN {
self.pin
}
}
impl<TIMERX: PwmOps<C>, PIN: ChannelPin<TIMERX, C>, const C: u8> ErrorType
for PwmChannel<TIMERX, PIN, C>
{
type Error = Infallible;
}
impl<TIMERX: PwmOps<C>, PIN: ChannelPin<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(())
}
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum Edge {
Rising,
Falling,
}
pub trait CaptureOps<const C: u8>: ChannelEnable<C> {
fn apply_capture_mode(&self);
fn select_edge(&self, edge: Edge);
fn read_chxcv(&self) -> u16;
fn read_chxif(&self) -> bool;
fn clear_chxif(&self);
fn read_chxof(&self) -> bool;
fn clear_chxof(&self);
}
macro_rules! capture {
{$($Timer:ty => [
$(
($Ch:expr, ($chctl_reg:ident, $chms:ident, $ci:ident, $capflt:ident, $cappsc:ident), ($chcv:ident, $chval:ident), ($chp:ident $(, $chnp:ident)?), $chif:ident, $chof:ident$(,)?)$(,)?
)+]$(,)?)+
} => {
$($(impl CaptureOps<$Ch> for $Timer {
fn apply_capture_mode(&self) {
$(self.chctl2().modify(|_, w| w.$chnp().not_inverted());)?
self.$chctl_reg().modify(|_, w| {
w.$chms()
.$ci()
.$capflt()
.no_filter()
.$cappsc()
.div1()
})
}
fn select_edge(&self, edge: Edge) {
self.chctl2().modify(|_, w| match edge {
Edge::Rising => w.$chp().not_inverted(),
Edge::Falling => w.$chp().inverted()
})
}
fn read_chxcv(&self) -> u16 {
self.$chcv().read().$chval().bits()
}
fn read_chxif(&self) -> bool {
self.intf().read().$chif().bit_is_set()
}
fn clear_chxif(&self) {
self.intf().modify(|_, w| w.$chif().clear())
}
fn read_chxof(&self) -> bool {
self.intf().read().$chof().bit_is_set()
}
fn clear_chxof(&self) {
self.intf().modify(|_, w| w.$chof().clear())
}
})+)+
};
}
capture! {
pac::Timer0 => [(0, (chctl0_input, ch0ms, ci0, ch0capflt, ch0cappsc), (ch0cv, ch0val), (ch0p, ch0np), ch0if, ch0of),
(1, (chctl0_input, ch1ms, ci0, ch1capflt, ch1cappsc), (ch1cv, ch1val), (ch1p, ch1np), ch1if, ch1of),
(2, (chctl1_input, ch2ms, ci0, ch2capflt, ch2cappsc), (ch2cv, ch2val), (ch2p, ch2np), ch2if, ch2of),
(3, (chctl1_input, ch3ms, ci0, ch3capflt, ch3cappsc), (ch3cv, ch3val), (ch3p), ch3if, ch3of)],
pac::Timer2 => [(0, (chctl0_input, ch0ms, ci0, ch0capflt, ch0cappsc), (ch0cv, ch0val), (ch0p, ch0np), ch0if, ch0of),
(1, (chctl0_input, ch1ms, ci0, ch1capflt, ch1cappsc), (ch1cv, ch1val), (ch1p, ch1np), ch1if, ch1of),
(2, (chctl1_input, ch2ms, ci0, ch2capflt, ch2cappsc), (ch2cv, ch2val), (ch2p, ch2np), ch2if, ch2of),
(3, (chctl1_input, ch3ms, ci0, ch3capflt, ch3cappsc), (ch3cv, ch3val), (ch3p), ch3if, ch3of)],
pac::Timer13 => [(0, (chctl0_input, ch0ms, ci0, ch0capflt, ch0cappsc), (ch0cv, ch0val), (ch0p, ch0np), ch0if, ch0of)],
pac::Timer14 => [(0, (chctl0_input, ch0ms, ci0, ch0capflt, ch0cappsc), (ch0cv, ch0val), (ch0p, ch0np), ch0if, ch0of),
(1, (chctl0_input, ch1ms, ci0, ch1capflt, ch1cappsc), (ch1cv, ch1val), (ch1p, ch1np), ch1if, ch1of)],
pac::Timer15 => [(0, (chctl0_input, ch0ms, ci0, ch0capflt, ch0cappsc), (ch0cv, ch0val), (ch0p, ch0np), ch0if, ch0of)],
pac::Timer16 => [(0, (chctl0_input, ch0ms, ci0, ch0capflt, ch0cappsc), (ch0cv, ch0val), (ch0p, ch0np), ch0if, ch0of)]
}