use core::future::Future;
use core::pin::Pin;
use core::sync::atomic::{AtomicBool, Ordering};
use core::task::{Context, Poll};
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use pac::peripherals::lptmr::LPTMR;
use crate::pac;
#[cfg(feature = "dma")]
use crate::dma::{DmaChannel, DmaRequest, RingError, Transfer, WritableRingBuffer};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ClockSource {
Source0 = 0,
Source1 = 1,
Source2 = 2,
Source3 = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PulseInput {
Input0 = 0,
Input1 = 1,
Input2 = 2,
Input3 = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PulsePolarity {
Rising,
Falling,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Config {
pub clock_source: ClockSource,
pub bypass_prescaler: bool,
pub prescaler: u8,
pub free_running: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
clock_source: ClockSource::Source0,
bypass_prescaler: true,
prescaler: 0,
free_running: false,
}
}
}
pub struct State {
pending: AtomicBool,
waker: AtomicWaker,
}
impl State {
pub(crate) const fn new() -> Self {
Self {
pending: AtomicBool::new(false),
waker: AtomicWaker::new(),
}
}
fn on_interrupt(&self, regs: LPTMR) {
if regs.CSR().read().TCF() {
self.pending.store(true, Ordering::Release);
regs.CSR().modify(|w| {
w.set_TIE(false);
w.set_TCF(true);
});
self.waker.wake();
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> LPTMR;
fn state() -> &'static State;
const LPCG: Option<u32>;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType {
const IRQ: Option<u16>;
#[cfg(feature = "dma")]
const DMA_REQUEST: DmaRequest;
}
macro_rules! impl_lptmr {
($instance:ident, $base:expr, $irq:expr) => {
impl crate::lptmr::SealedInstance for crate::peripherals::$instance {
const LPCG: ::core::option::Option<u32> = crate::clock::lpcg(stringify!($instance));
#[inline]
fn regs() -> crate::pac::peripherals::lptmr::LPTMR {
unsafe { crate::pac::peripherals::lptmr::LPTMR::from_ptr($base as *mut ()) }
}
#[inline]
fn state() -> &'static crate::lptmr::State {
static STATE: crate::lptmr::State = crate::lptmr::State::new();
&STATE
}
}
impl crate::lptmr::Instance for crate::peripherals::$instance {
const IRQ: ::core::option::Option<u16> = $irq;
#[cfg(feature = "dma")]
const DMA_REQUEST: crate::dma::DmaRequest = crate::dma::request::$instance;
}
};
}
pub(crate) use impl_lptmr;
pub fn on_interrupt<T: Instance>() {
T::state().on_interrupt(T::regs());
}
fn program(regs: LPTMR, config: Config) {
assert!(config.prescaler <= 15);
regs.CSR().write_value(Default::default());
regs.PSR().write(|w| {
w.set_PCS(config.clock_source as u8);
w.set_PBYP(config.bypass_prescaler);
w.set_PRESCALE(config.prescaler);
});
regs.CSR().modify(|w| w.set_TFC(config.free_running));
}
pub struct Lptmr {
regs: LPTMR,
#[cfg(feature = "dma")]
dma_request: DmaRequest,
state: &'static State,
}
impl Lptmr {
pub fn new<T: Instance>(peri: Peri<'static, T>, config: Config) -> Self {
let _ = peri;
crate::clock::ungate(T::LPCG);
let regs = T::regs();
program(regs, config);
Self {
regs,
#[cfg(feature = "dma")]
dma_request: T::DMA_REQUEST,
state: T::state(),
}
}
#[cfg(test)]
fn over(regs: LPTMR, state: &'static State, config: Config) -> Self {
program(regs, config);
Self {
regs,
#[cfg(feature = "dma")]
dma_request: crate::dma::request::LPTMR1,
state,
}
}
pub fn configure_timer(&mut self) {
self.stop();
self.regs.CSR().modify(|w| w.set_TMS(false));
}
pub fn configure_pulse_counter(&mut self, input: PulseInput, polarity: PulsePolarity) {
self.stop();
self.regs.CSR().modify(|w| {
w.set_TMS(true);
w.set_TPS(input as u8);
w.set_TPP(matches!(polarity, PulsePolarity::Falling));
});
}
pub fn set_compare(&mut self, compare: u32) {
let running = self.is_running();
if running {
self.stop();
}
self.regs.CMR().write(|w| w.set_COMPARE(compare));
if running {
self.start();
}
}
pub fn compare(&self) -> u32 {
self.regs.CMR().read().COMPARE()
}
pub fn counter(&self) -> u32 {
self.regs.CNR().write(|w| w.set_COUNTER(0));
self.regs.CNR().read().COUNTER()
}
pub fn start(&mut self) {
self.regs.CSR().modify(|w| w.set_TEN(true));
}
pub fn stop(&mut self) {
self.regs.CSR().modify(|w| {
w.set_TEN(false);
w.set_TCF(false);
});
}
pub fn is_running(&self) -> bool {
self.regs.CSR().read().TEN()
}
pub fn is_compare_pending(&self) -> bool {
self.regs.CSR().read().TCF()
}
pub fn clear_compare(&mut self) {
self.regs.CSR().modify(|w| w.set_TCF(true));
self.state.pending.store(false, Ordering::Release);
}
pub fn wait_compare(&mut self) -> WaitCompare<'_> {
self.clear_compare();
self.regs.CSR().modify(|w| {
w.set_TCF(false);
w.set_TIE(true);
});
WaitCompare {
timer: self,
complete: false,
}
}
#[cfg(feature = "dma")]
fn dma_request(&self) -> DmaRequest {
self.dma_request
}
#[cfg(feature = "dma")]
pub fn write_compare_dma<'a>(
&'a mut self,
dma: &'a mut DmaChannel,
values: &'a [u32],
) -> CompareDmaTransfer<'a> {
let request = self.dma_request();
let destination = self.regs.CMR().as_ptr().cast::<u32>();
let transfer = unsafe { dma.write_peripheral_with_request(values, destination, request) };
self.regs.CSR().modify(|w| {
w.set_TCF(false);
w.set_TDRE(true);
});
CompareDmaTransfer {
timer: self,
transfer: Some(transfer),
}
}
#[cfg(feature = "dma")]
pub fn into_ring_buffered_compare<'a>(
&'a mut self,
dma: &'a mut DmaChannel,
buffer: &'a mut [u32],
) -> RingBufferedCompare<'a> {
let request = self.dma_request();
let destination = self.regs.CMR().as_ptr().cast::<u32>();
let ring = unsafe { dma.write_circular(buffer, destination, request) };
RingBufferedCompare {
timer: self,
ring: Some(ring),
started: false,
}
}
fn disable_irq(&mut self) {
self.regs.CSR().modify(|w| {
w.set_TCF(false);
w.set_TIE(false);
});
}
#[cfg(feature = "dma")]
fn disable_dma(&mut self) {
self.regs.CSR().modify(|w| {
w.set_TCF(false);
w.set_TDRE(false);
});
}
}
pub struct WaitCompare<'a> {
timer: &'a mut Lptmr,
complete: bool,
}
impl Future for WaitCompare<'_> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if self.timer.state.pending.swap(false, Ordering::AcqRel)
|| self.timer.regs.CSR().read().TCF()
{
self.timer.clear_compare();
self.timer.disable_irq();
self.complete = true;
return Poll::Ready(());
}
self.timer.state.waker.register(cx.waker());
if self.timer.state.pending.swap(false, Ordering::AcqRel) {
self.timer.disable_irq();
self.complete = true;
Poll::Ready(())
} else {
Poll::Pending
}
}
}
impl Drop for WaitCompare<'_> {
fn drop(&mut self) {
if !self.complete {
self.timer.disable_irq();
}
}
}
#[cfg(feature = "dma")]
pub struct CompareDmaTransfer<'a> {
timer: &'a mut Lptmr,
transfer: Option<Transfer<'a>>,
}
#[cfg(feature = "dma")]
impl Future for CompareDmaTransfer<'_> {
type Output = Result<(), crate::dma::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let result = Pin::new(
this.transfer
.as_mut()
.expect("transfer polled after completion"),
)
.poll(cx);
if result.is_ready() {
drop(this.transfer.take());
this.timer.disable_dma();
}
result
}
}
#[cfg(feature = "dma")]
impl CompareDmaTransfer<'_> {
pub fn wait_blocking(mut self) -> Result<(), crate::dma::Error> {
let result = self
.transfer
.take()
.expect("transfer already consumed")
.wait_blocking();
self.timer.disable_dma();
result
}
}
#[cfg(feature = "dma")]
impl Drop for CompareDmaTransfer<'_> {
fn drop(&mut self) {
drop(self.transfer.take());
self.timer.disable_dma();
}
}
#[cfg(feature = "dma")]
pub struct RingBufferedCompare<'a> {
timer: &'a mut Lptmr,
ring: Option<WritableRingBuffer<'a, u32>>,
started: bool,
}
#[cfg(feature = "dma")]
impl<'a> RingBufferedCompare<'a> {
pub fn write_immediate(&mut self, values: &[u32]) -> Result<(usize, usize), RingError> {
self.ring_mut().write_immediate(values)
}
pub fn start(&mut self) {
if !self.started {
self.timer.regs.CSR().modify(|w| {
w.set_TCF(false);
w.set_TDRE(true);
});
self.ring_mut().start();
self.timer.start();
self.started = true;
}
}
pub fn write(&mut self, values: &[u32]) -> Result<(usize, usize), RingError> {
self.ring_mut().write(values)
}
pub async fn write_exact(&mut self, values: &[u32]) -> Result<usize, RingError> {
self.ring_mut().write_exact(values).await
}
pub fn len(&self) -> Result<usize, RingError> {
self.ring.as_ref().expect("ring has been released").len()
}
pub fn is_empty(&self) -> Result<bool, RingError> {
self.ring
.as_ref()
.expect("ring has been released")
.is_empty()
}
pub fn capacity(&self) -> usize {
self.ring
.as_ref()
.expect("ring has been released")
.capacity()
}
pub fn pause(&mut self) {
self.ring_mut().pause();
}
pub fn resume(&mut self) {
self.ring_mut().resume();
}
pub fn is_running(&self) -> bool {
self.ring
.as_ref()
.expect("ring has been released")
.is_running()
}
pub async fn stop(&mut self) {
self.ring_mut().stop().await;
self.timer.disable_dma();
self.started = false;
}
fn ring_mut(&mut self) -> &mut WritableRingBuffer<'a, u32> {
self.ring.as_mut().expect("ring has been released")
}
}
#[cfg(feature = "dma")]
impl Drop for RingBufferedCompare<'_> {
fn drop(&mut self) {
drop(self.ring.take());
self.timer.disable_dma();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn configures_timer_and_pulse_modes() {
static STATE: State = State::new();
let mut registers = [0u32; 4];
let regs = unsafe { LPTMR::from_ptr(registers.as_mut_ptr().cast()) };
let mut timer = Lptmr::over(
regs,
&STATE,
Config {
clock_source: ClockSource::Source2,
bypass_prescaler: false,
prescaler: 3,
free_running: true,
},
);
assert_eq!(registers[1] & 0x7f, 2 | (3 << 3));
timer.configure_pulse_counter(PulseInput::Input2, PulsePolarity::Falling);
assert_eq!(
registers[0] & 0x3e,
(1 << 1) | (1 << 2) | (1 << 3) | (2 << 4)
);
}
#[test]
fn start_stop_and_compare() {
static STATE: State = State::new();
let mut registers = [0u32; 4];
let regs = unsafe { LPTMR::from_ptr(registers.as_mut_ptr().cast()) };
let mut timer = Lptmr::over(regs, &STATE, Config::default());
timer.set_compare(1234);
timer.start();
assert_eq!(registers[2], 1234);
assert!(timer.is_running());
timer.stop();
assert!(!timer.is_running());
}
}