use avr_oxide::hal::generic::port::{Pin, PinMode, InterruptMode, PinIdentity};
use avr_oxide::event::{EventSink, EventSource, OxideEvent, OxideEventEnvelope};
use core::marker::PhantomData;
use core::ops::DerefMut;
use core::cell::{RefCell, UnsafeCell};
use ufmt::derive::uDebug;
use oxide_macros::Persist;
use avr_oxide::hal::generic::callback::IsrCallback;
use avr_oxide::alloc::boxed::Box;
use avr_oxide::devices::UsesPin;
use avr_oxide::panic_if_none;
use avr_oxide::private::binaryringq::BinaryRingQ;
use avr_oxide::util::OwnOrBorrow;
#[derive(PartialEq,Eq,Clone,Copy,uDebug,Persist)]
pub enum ButtonState {
Pressed,
Released,
Unknown
}
pub struct Button<'b, S>
where
S: EventSink
{
pin: OwnOrBorrow<'static,dyn Pin>,
phantom: PhantomData<S>,
on_click: RefCell<Option<Box<dyn FnMut(PinIdentity,ButtonState)->() + 'b>>>,
wait_events: UnsafeCell<BinaryRingQ>
}
impl<'b,S> UsesPin for Button<'b,S>
where
S: 'static + EventSink
{
fn using<OP: Into<OwnOrBorrow<'static, dyn Pin>>>(pin: OP) -> Self {
let pin : OwnOrBorrow<dyn Pin> = pin.into();
pin.set_mode(PinMode::Input);
pin.set_interrupt_mode(InterruptMode::BothEdges);
Button {
pin,
phantom: PhantomData::default(),
on_click: RefCell::new(None),
wait_events: UnsafeCell::new(BinaryRingQ::new())
}
}
}
impl<'b,S> Button<'b,S>
where
S: 'static + EventSink
{
pub fn on_click(&self, bf: Box<dyn FnMut(PinIdentity, ButtonState) -> () + 'b>) {
self.on_click.replace(Some(bf));
}
pub fn pullup(self, enable: bool) -> Self {
match enable {
true => self.pin.set_mode(PinMode::InputPullup),
false => self.pin.set_mode(PinMode::InputFloating)
}
self
}
pub fn is_pressed(&self) -> bool {
!self.pin.get()
}
pub fn state(&self) -> ButtonState {
match self.pin.get() {
true => ButtonState::Pressed,
false => ButtonState::Released
}
}
pub fn wait_for_change(&self) -> ButtonState {
match unsafe {
let wait_events = &mut *self.wait_events.get();
wait_events.consume_blocking()
} {
true => ButtonState::Pressed,
false => ButtonState::Released
}
}
}
impl<S> EventSource for Button<'_,S>
where
S: EventSink
{
fn listen(&'static self) {
self.pin.listen(IsrCallback::WithData(|isotoken, _source, id, state, udata|{
unsafe {
let myself = panic_if_none!(udata, avr_oxide::oserror::OsError::InternalError) as *const Button<S>;
let wait_events = &mut *((*myself).wait_events.get());
let _ = wait_events.append(isotoken, state);
S::event(isotoken, OxideEventEnvelope::to(&*(myself as *const dyn EventSource),
OxideEvent::ButtonEvent(id,
match state {
true => ButtonState::Pressed,
false => ButtonState::Released
})));
}
}, self as *const dyn core::any::Any ))
}
fn process_event(&self, evt: OxideEvent) {
match (self.on_click.borrow_mut().deref_mut(), evt) {
(Some(f), OxideEvent::ButtonEvent(source, state)) => {
(*f)(source,state)
},
_ => {}
}
}
}
unsafe impl <S> Send for Button<'_,S>
where
S: EventSink
{}
unsafe impl <S> Sync for Button<'_,S>
where
S: EventSink
{}