use embedded_hal::digital::PinState;
use frunk::{hlist::Plucker, HCons, HNil};
use crate::typelevel::Sealed;
use super::{
pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionSio, FunctionSioInput, FunctionSioOutput, Pin,
PinId, PullType, SioConfig,
};
pub trait ReadPinHList: Sealed {
fn read_mask(&self) -> u32;
}
impl ReadPinHList for HNil {
fn read_mask(&self) -> u32 {
0
}
}
impl<H: AnyPin, T: ReadPinHList> ReadPinHList for HCons<H, T> {
fn read_mask(&self) -> u32 {
(1 << self.head.borrow().id().num) | self.tail.read_mask()
}
}
pub trait WritePinHList: Sealed {
fn write_mask(&self) -> u32;
}
impl WritePinHList for HNil {
fn write_mask(&self) -> u32 {
0
}
}
impl<P: PinId, M: PullType, T: WritePinHList> WritePinHList
for HCons<Pin<P, FunctionSioInput, M>, T>
{
fn write_mask(&self) -> u32 {
self.tail.write_mask()
}
}
impl<P: PinId, M: PullType, T: WritePinHList> WritePinHList
for HCons<Pin<P, FunctionSioOutput, M>, T>
{
fn write_mask(&self) -> u32 {
(1 << self.head.id().num) | self.tail.write_mask()
}
}
pub struct PinGroup<T = HNil>(T);
impl PinGroup<HNil> {
pub fn new() -> Self {
PinGroup(HNil)
}
pub fn add_pin<P, C>(self, pin: P) -> PinGroup<HCons<P, HNil>>
where
C: SioConfig,
P: AnyPin<Function = FunctionSio<C>>,
P::Id: TypeLevelPinId,
{
PinGroup(HCons {
head: pin,
tail: self.0,
})
}
}
impl<T, H> PinGroup<HCons<H, T>>
where
H::Id: TypeLevelPinId,
H: AnyPin,
{
pub fn add_pin<C, P>(self, pin: P) -> PinGroup<HCons<P, HCons<H, T>>>
where
C: SioConfig,
P: AnyPin<Function = FunctionSio<C>>,
P::Id: TypeLevelPinId<Bank = <H::Id as TypeLevelPinId>::Bank>,
{
PinGroup(HCons {
head: pin,
tail: self.0,
})
}
#[allow(clippy::type_complexity)]
pub fn remove_pin<P, Index>(
self,
) -> (P, PinGroup<<HCons<H, T> as Plucker<P, Index>>::Remainder>)
where
HCons<H, T>: Plucker<P, Index>,
{
let (p, rest): (P, _) = self.0.pluck();
(p, PinGroup(rest))
}
}
impl<H, T> PinGroup<HCons<H, T>>
where
HCons<H, T>: ReadPinHList + WritePinHList,
H: AnyPin,
{
pub fn read(&self) -> u32 {
let mask = self.0.read_mask();
crate::sio::Sio::read_bank0() & mask
}
pub fn set(&mut self, state: PinState) {
use super::pin::pin_sealed::PinIdOps;
let mask = self.0.write_mask();
let head_id = self.0.head.borrow().id();
if state == PinState::Low {
head_id.sio_out_clr().write(|w| unsafe { w.bits(mask) });
} else {
head_id.sio_out_set().write(|w| unsafe { w.bits(mask) });
}
}
pub fn set_u32(&mut self, state: u32) {
use super::pin::pin_sealed::PinIdOps;
let mask = self.0.write_mask();
let state_masked = mask & state;
let head_id = self.0.head.borrow().id();
head_id.sio_out().modify(|r, w| unsafe {
let cleared = r.bits() & !mask;
w.bits(cleared | state_masked)
});
}
pub fn toggle(&mut self) {
use super::pin::pin_sealed::PinIdOps;
let mask = self.0.write_mask();
self.0
.head
.borrow()
.id()
.sio_out_xor()
.write(|w| unsafe { w.bits(mask) });
}
}
impl Default for PinGroup<HNil> {
fn default() -> Self {
Self::new()
}
}