use core::marker::PhantomData;
pub use crate::capture_math::{
duty_permille, hz_from_period_ticks, periods_in_span, span_ratio_permille, within_permille,
};
use crate::capture_math;
use crate::clocks::Clocks;
use crate::gpio::{Pin, TimerA, P1};
use crate::pac;
use crate::timer::Divider;
const CTL: usize = 0x00; const CCTL0: usize = 0x02; const R: usize = 0x10; const CCR0: usize = 0x12; const IV: usize = 0x2E;
const TASSEL_ACLK: u16 = 0x0100;
const TASSEL_SMCLK: u16 = 0x0200;
const MC_CONTINUOUS: u16 = 0x0020;
const TACLR: u16 = 0x0004;
#[cfg(feature = "critical-section")]
const TAIE: u16 = 0x0002;
#[cfg(feature = "critical-section")]
const TAIFG: u16 = 0x0001;
const CM_MASK: u16 = 0xC000; const CM_RISING: u16 = 0x4000;
const CM_FALLING: u16 = 0x8000;
const CM_BOTH: u16 = 0xC000;
const CCIS_MASK: u16 = 0x3000; const CCIS_A: u16 = 0x0000; const CCIS_B: u16 = 0x1000; const CCIS_GND: u16 = 0x2000;
const CCIS_VCC: u16 = 0x3000;
const SCS: u16 = 0x0800; const CAP: u16 = 0x0100; #[cfg(feature = "critical-section")]
const CCIE: u16 = 0x0010;
const CCI: u16 = 0x0008; const COV: u16 = 0x0002; const CCIFG: u16 = 0x0001;
#[inline(always)]
unsafe fn read_reg(addr: usize) -> u16 {
(addr as *const u16).read_volatile()
}
#[inline(always)]
unsafe fn write_reg(addr: usize, val: u16) {
(addr as *mut u16).write_volatile(val);
}
#[inline]
fn rmw(addr: usize, f: impl Fn(u16) -> u16) {
#[cfg(feature = "critical-section")]
critical_section::with(|_| unsafe { write_reg(addr, f(read_reg(addr))) });
#[cfg(not(feature = "critical-section"))]
unsafe {
write_reg(addr, f(read_reg(addr)))
};
}
mod sealed {
pub trait Sealed {}
}
pub trait Instance: sealed::Sealed {
const BASE: usize;
}
impl sealed::Sealed for pac::Timer0A3 {}
impl Instance for pac::Timer0A3 {
const BASE: usize = 0x0340;
}
impl sealed::Sealed for pac::Timer1A3 {}
impl Instance for pac::Timer1A3 {
const BASE: usize = 0x0380;
}
pub trait CapturePin<T: Instance>: sealed::Sealed {
const CHANNEL: u8;
}
macro_rules! capture_pins {
($($Timer:ty: $Port:ident $N:literal => $ch:literal),+ $(,)?) => {$(
impl sealed::Sealed for Pin<$Port, $N, TimerA> {}
impl CapturePin<$Timer> for Pin<$Port, $N, TimerA> {
const CHANNEL: u8 = $ch;
}
)+};
}
capture_pins! {
pac::Timer0A3: P1 0 => 1, pac::Timer0A3: P1 1 => 2, pac::Timer1A3: P1 2 => 1, pac::Timer1A3: P1 3 => 2, }
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Edge {
Rising,
Falling,
Both,
}
impl Edge {
const fn cm_bits(self) -> u16 {
match self {
Edge::Rising => CM_RISING,
Edge::Falling => CM_FALLING,
Edge::Both => CM_BOTH,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Slot {
Ccr1 = 1,
Ccr2 = 2,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Error {
Timeout,
Overcapture,
LevelSync,
}
pub struct CaptureTimer<T: Instance> {
_timer: T,
tick_hz: u32,
}
impl<T: Instance> CaptureTimer<T> {
pub fn new_smclk(timer: T, clocks: &Clocks, div: Divider) -> Self {
Self::new(timer, TASSEL_SMCLK, clocks.smclk(), div)
}
pub fn new_aclk(timer: T, clocks: &Clocks, div: Divider) -> Self {
Self::new(timer, TASSEL_ACLK, clocks.aclk(), div)
}
fn new(timer: T, tassel: u16, source_hz: u32, div: Divider) -> Self {
let (id_bits, divisor) = match div {
Divider::Div1 => (0u16 << 6, 1),
Divider::Div2 => (1u16 << 6, 2),
Divider::Div4 => (2u16 << 6, 4),
Divider::Div8 => (3u16 << 6, 8),
};
unsafe { write_reg(T::BASE + CTL, tassel | id_bits | MC_CONTINUOUS | TACLR) };
CaptureTimer {
_timer: timer,
tick_hz: source_hz / divisor,
}
}
pub const fn tick_hz(&self) -> u32 {
self.tick_hz
}
pub fn now(&self) -> u16 {
unsafe { read_reg(T::BASE + R) }
}
pub fn capture_pin<P: CapturePin<T>>(&self, _pin: P, edge: Edge) -> CaptureChannel<T> {
self.channel(P::CHANNEL, CCIS_A, edge.cm_bits(), false)
}
pub fn capture_comparator(&self, edge: Edge) -> CaptureChannel<T> {
self.channel(1, CCIS_B, edge.cm_bits(), false)
}
pub fn capture_aclk(&self, edge: Edge) -> CaptureChannel<T> {
self.channel(2, CCIS_B, edge.cm_bits(), false)
}
pub fn capture_software(&self, slot: Slot) -> CaptureChannel<T> {
self.channel(slot as u8, CCIS_GND, CM_RISING, true)
}
fn channel(&self, channel: u8, ccis: u16, cm: u16, software: bool) -> CaptureChannel<T> {
unsafe { write_reg(T::BASE + CCTL0 + 2 * channel as usize, cm | ccis | SCS | CAP) };
CaptureChannel {
channel,
software,
tick_hz: self.tick_hz,
_instance: PhantomData,
}
}
#[cfg(feature = "critical-section")]
pub fn enable_overflow_interrupt(&self) {
rmw(T::BASE + CTL, |v| (v | TAIE) & !TAIFG);
}
#[cfg(feature = "critical-section")]
pub fn disable_overflow_interrupt(&self) {
rmw(T::BASE + CTL, |v| v & !(TAIE | TAIFG));
}
pub fn free(self) -> T {
self._timer
}
}
pub struct CaptureChannel<T: Instance> {
channel: u8,
software: bool,
tick_hz: u32,
_instance: PhantomData<T>,
}
impl<T: Instance> CaptureChannel<T> {
fn cctl(&self) -> usize {
T::BASE + CCTL0 + 2 * self.channel as usize
}
fn ccr(&self) -> usize {
T::BASE + CCR0 + 2 * self.channel as usize
}
pub const fn tick_hz(&self) -> u32 {
self.tick_hz
}
pub fn pending(&self) -> bool {
(unsafe { read_reg(self.cctl()) }) & CCIFG != 0
}
pub fn take(&mut self) -> Option<u16> {
if !self.pending() {
return None;
}
let ts = unsafe { read_reg(self.ccr()) };
rmw(self.cctl(), |v| v & !CCIFG);
Some(ts)
}
pub fn overcaptured(&self) -> bool {
(unsafe { read_reg(self.cctl()) }) & COV != 0
}
pub fn clear_overcapture(&mut self) {
rmw(self.cctl(), |v| v & !COV);
}
pub fn input_level(&self) -> bool {
(unsafe { read_reg(self.cctl()) }) & CCI != 0
}
pub fn set_edge(&mut self, edge: Edge) {
rmw(self.cctl(), |v| {
(v & !(CM_MASK | CCIFG | COV)) | edge.cm_bits()
});
}
pub fn fire_software(&mut self) {
if !self.software {
return;
}
rmw(self.cctl(), |v| (v & !CCIS_MASK) | CCIS_VCC);
rmw(self.cctl(), |v| (v & !CCIS_MASK) | CCIS_GND);
}
pub fn wait_edge(&mut self, timeout_ticks: u16) -> Result<u16, Error> {
rmw(self.cctl(), |v| v & !(CCIFG | COV));
let start = unsafe { read_reg(T::BASE + R) };
loop {
let cctl = unsafe { read_reg(self.cctl()) };
if cctl & CCIFG != 0 {
let ts = unsafe { read_reg(self.ccr()) };
if unsafe { read_reg(self.cctl()) } & COV != 0 {
rmw(self.cctl(), |v| v & !(CCIFG | COV));
return Err(Error::Overcapture);
}
rmw(self.cctl(), |v| v & !CCIFG);
return Ok(ts);
}
let now = unsafe { read_reg(T::BASE + R) };
if now.wrapping_sub(start) >= timeout_ticks {
return Err(Error::Timeout);
}
}
}
pub fn measure_period_ticks(
&mut self,
periods: u16,
per_edge_timeout: u16,
) -> Result<u32, Error> {
let mut prev = self.wait_edge(per_edge_timeout)?;
let mut total: u32 = 0;
for _ in 0..periods {
let t = self.wait_edge(per_edge_timeout)?;
total += t.wrapping_sub(prev) as u32;
prev = t;
}
Ok(total)
}
pub fn frequency_hz(&mut self, periods: u16, per_edge_timeout: u16) -> Result<u32, Error> {
let total = self.measure_period_ticks(periods, per_edge_timeout)?;
Ok(capture_math::hz_from_period_ticks(
self.tick_hz,
total,
periods as u32,
))
}
pub fn measure_span_ticks(
&mut self,
min_wait_ticks: u16,
per_edge_timeout: u16,
) -> Result<u16, Error> {
let t0 = self.wait_fresh_stamp(per_edge_timeout)?;
while unsafe { read_reg(T::BASE + R) }.wrapping_sub(t0) < min_wait_ticks {}
let t1 = self.wait_fresh_stamp(per_edge_timeout)?;
Ok(t1.wrapping_sub(t0))
}
fn wait_fresh_stamp(&mut self, timeout_ticks: u16) -> Result<u16, Error> {
rmw(self.cctl(), |v| v & !(CCIFG | COV));
let start = unsafe { read_reg(T::BASE + R) };
loop {
if (unsafe { read_reg(self.cctl()) }) & CCIFG != 0 {
let ts = unsafe { read_reg(self.ccr()) };
rmw(self.cctl(), |v| v & !(CCIFG | COV));
return Ok(ts);
}
let now = unsafe { read_reg(T::BASE + R) };
if now.wrapping_sub(start) >= timeout_ticks {
return Err(Error::Timeout);
}
}
}
pub fn measure_duty_permille(&mut self, per_edge_timeout: u16) -> Result<u16, Error> {
let mut t_rise = self.wait_edge(per_edge_timeout)?;
if !self.input_level() {
t_rise = self.wait_edge(per_edge_timeout)?;
if !self.input_level() {
return Err(Error::LevelSync);
}
}
let t_fall = self.wait_edge(per_edge_timeout)?;
if self.input_level() {
return Err(Error::LevelSync);
}
let t_rise2 = self.wait_edge(per_edge_timeout)?;
let high = t_fall.wrapping_sub(t_rise) as u32;
let period = t_rise2.wrapping_sub(t_rise) as u32;
Ok(capture_math::duty_permille(high, period))
}
#[cfg(feature = "critical-section")]
pub fn enable_interrupt(&mut self) {
rmw(self.cctl(), |v| v | CCIE);
}
#[cfg(feature = "critical-section")]
pub fn disable_interrupt(&mut self) {
rmw(self.cctl(), |v| v & !(CCIE | CCIFG));
}
}
pub const IV_CCR1: u16 = 0x0002;
pub const IV_CCR2: u16 = 0x0004;
pub const IV_OVERFLOW: u16 = 0x000E;
pub fn read_iv<T: Instance>() -> u16 {
unsafe { read_reg(T::BASE + IV) }
}
#[cfg(feature = "critical-section")]
pub fn isr_disable_interrupt<T: Instance>(slot: Slot) {
let addr = T::BASE + CCTL0 + 2 * (slot as u8 as usize);
unsafe { write_reg(addr, read_reg(addr) & !CCIE) };
}