use core::marker::PhantomData;
use atsamd_hal_macros::{hal_cfg, hal_module};
use seq_macro::seq;
use crate::{
clock::EicClock,
gpio::{AnyPin, Pin},
pac,
typelevel::{NoneT, Sealed},
};
#[hal_module(
any("eic-d11", "eic-d21") => "eic/d11/mod.rs",
"eic-d5x" => "eic/d5x/mod.rs",
)]
mod impls {}
#[cfg(feature = "async")]
pub use impls::async_api::*;
#[hal_cfg("eic-d5x")]
use super::clock::v2::{self, gclk::GclkId, osculp32k::OscUlp32kId, pclk::Pclk, rtcosc::RtcOsc};
pub type Sense = pac::eic::config::Sense0select;
pub trait ChId {
const ID: usize;
}
#[cfg(feature = "async")]
pub enum EicFuture {}
pub trait EicPin: AnyPin + Sealed {
type Floating;
type PullUp;
type PullDown;
type ChId: ChId;
#[hal_cfg("eic-d5x")]
#[cfg(feature = "async")]
type InterruptSource: crate::async_hal::interrupts::InterruptSource;
fn into_floating_ei(self, chan: Channel<Self::ChId>) -> Self::Floating;
fn into_pull_up_ei(self, chan: Channel<Self::ChId>) -> Self::PullUp;
fn into_pull_down_ei(self, chan: Channel<Self::ChId>) -> Self::PullDown;
}
pub struct ExtInt<P, Id, F = NoneT>
where
P: EicPin,
Id: ChId,
{
chan: Channel<Id, F>,
pin: Pin<P::Id, P::Mode>,
}
impl<P, Id, F> ExtInt<P, Id, F>
where
P: EicPin,
Id: ChId,
{
pub fn free(self) -> (Pin<P::Id, P::Mode>, Channel<Id, F>) {
(self.pin, self.chan)
}
fn new(pin: P, chan: Channel<Id, F>) -> Self {
ExtInt {
pin: pin.into(),
chan,
}
}
#[cfg(all(doc, feature = "async"))]
#[hal_cfg(not("eic-d5x"))]
pub fn into_future(self) {
unimplemented!()
}
}
pub struct Channel<Id: ChId, F = NoneT> {
eic: core::mem::ManuallyDrop<pac::Eic>,
_id: PhantomData<Id>,
_irqs: PhantomData<F>,
}
impl<Id: ChId, F> Channel<Id, F> {
pub fn with_pin<P: EicPin<ChId = Id>>(self, pin: P) -> ExtInt<P, Id, F> {
ExtInt::new(pin, self)
}
fn new(eic: pac::Eic) -> Self {
Self {
eic: core::mem::ManuallyDrop::new(eic),
_id: PhantomData,
_irqs: PhantomData,
}
}
#[hal_cfg("eic-d5x")]
#[cfg(feature = "async")]
fn change_mode<N>(self) -> Channel<Id, N> {
Channel {
eic: self.eic,
_id: self._id,
_irqs: PhantomData,
}
}
}
pub struct Eic<I = NoneT> {
eic: pac::Eic,
_irqs: PhantomData<I>,
}
impl Eic {
#[hal_cfg(any("eic-d11", "eic-d21"))]
pub fn new(pm: &mut pac::Pm, _clock: EicClock, eic: pac::Eic) -> Self {
pm.apbamask().modify(|_, w| w.eic_().set_bit());
eic.ctrl().modify(|_, w| w.swrst().set_bit());
while eic.ctrl().read().swrst().bit_is_set() {
core::hint::spin_loop();
}
eic.ctrl().modify(|_, w| w.enable().set_bit());
while eic.status().read().syncbusy().bit_is_set() {
cortex_m::asm::nop();
}
Self {
eic,
_irqs: PhantomData,
}
}
#[hal_cfg("eic-d5x")]
pub fn new(mclk: &mut pac::Mclk, _clock: &EicClock, eic: pac::Eic) -> Self {
mclk.apbamask().modify(|_, w| w.eic_().set_bit());
let mut eic = Self {
eic,
_irqs: PhantomData,
};
eic.swreset();
eic.eic.ctrla().modify(|_, w| {
w.cksel().set_bit();
w.enable().set_bit()
});
while eic.eic.syncbusy().read().enable().bit_is_set() {
core::hint::spin_loop();
}
eic
}
#[hal_cfg("eic-d5x")]
pub fn switch_to_osc32k(&mut self, _rtc: &RtcOsc<OscUlp32kId>) {
self.eic.ctrla().write(|w| w.enable().clear_bit());
self.sync();
self.eic.ctrla().write(|w| {
w.cksel().clk_ulp32k();
w.enable().set_bit()
});
self.sync();
}
#[hal_cfg("eic-d5x")]
pub fn switch_to_gclk<S: GclkId>(&mut self, _gclk: &Pclk<v2::pclk::ids::Eic, S>) {
self.eic.ctrla().write(|w| w.enable().clear_bit());
self.sync();
self.eic.ctrla().write(|w| {
w.cksel().clk_gclk();
w.enable().set_bit()
});
self.sync();
}
#[hal_cfg("eic-d5x")]
fn sync(&self) {
while self.eic.syncbusy().read().enable().bit_is_set() {
core::hint::spin_loop();
}
}
#[cfg(all(doc, feature = "async"))]
#[hal_cfg(not(any("eic-d11", "eic-d21")))]
pub fn into_future(self) {
unimplemented!()
}
pub fn free(mut self, _channels: Channels) -> pac::Eic {
self.swreset();
self.eic
}
}
impl<F> Eic<F> {
#[atsamd_hal_macros::hal_macro_helper]
fn swreset(&mut self) {
#[hal_cfg(any("eic-d11", "eic-d21"))]
let ctrl = self.eic.ctrl();
#[hal_cfg("eic-d5x")]
let ctrl = self.eic.ctrla();
ctrl.modify(|_, w| w.swrst().set_bit());
while ctrl.read().swrst().bit_is_set() {
core::hint::spin_loop();
}
}
}
#[cfg(feature = "async")]
impl Eic<EicFuture> {
pub fn free(mut self, _channels: FutureChannels) -> pac::Eic {
self.swreset();
self.eic
}
}
#[hal_cfg("eic-d11")]
macro_rules! with_num_channels {
($some_macro:ident) => {
$some_macro! {8}
};
}
#[hal_cfg(any("eic-d5x", "eic-d21"))]
macro_rules! with_num_channels {
($some_macro:ident) => {
$some_macro! {16}
};
}
macro_rules! get {
($literal:literal) => {
$literal
};
}
pub const NUM_CHANNELS: usize = with_num_channels!(get);
macro_rules! define_channels_struct {
($num_channels:literal) => {
seq!(N in 0..$num_channels {
#(
pub enum Ch~N {}
impl ChId for Ch~N {
const ID: usize = N;
}
)*
pub struct Channels(
#(
pub Channel<Ch~N>,
)*
);
});
};
}
with_num_channels!(define_channels_struct);
#[cfg(feature = "async")]
macro_rules! define_channels_struct_future {
($num_channels:literal) => {
seq!(N in 0..$num_channels {
pub struct FutureChannels(
#(
pub Channel<Ch~N, EicFuture>,
)*
);
});
};
}
#[cfg(feature = "async")]
with_num_channels!(define_channels_struct_future);
macro_rules! define_split {
($num_channels:literal) => {
seq!(N in 0..$num_channels {
#[inline]
pub fn split(self) -> Channels {
Channels(
#(
unsafe { Channel::new(core::ptr::read(&self.eic as *const _)) },
)*
)
}
});
};
}
impl Eic {
with_num_channels!(define_split);
}
#[cfg(feature = "async")]
macro_rules! define_split_future {
($num_channels:literal) => {
seq!(N in 0..$num_channels {
#[inline]
pub fn split(self) -> FutureChannels {
FutureChannels(
#(
unsafe { Channel::new(core::ptr::read(&self.eic as *const _)) },
)*
)
}
});
};
}
#[cfg(feature = "async")]
impl Eic<EicFuture> {
with_num_channels!(define_split_future);
}