avr-oxide 0.1.0

An extremely simple Rusty operating system for AVR microcontrollers
/* button.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Simple abstraction of a button attached to a GPIO port.
//!
//!

// Imports ===================================================================
use crate::hal::generic::port::{Pin, PinMode, InterruptMode, PinIdentity};
use crate::event::{EventSink, EventSource, OxideEvent, OxideEventEnvelope};
use core::marker::PhantomData;
use core::ops::DerefMut;
use core::cell::RefCell;
use crate::hal::generic::callback::IsrCallback;
#[cfg(feature="alloc_ftr")]
use crate::alloc::boxed::Box;
use crate::panic_if_none;
use crate::util::OwnOrBorrowMut;

// Declarations ==============================================================

/**
 * Button states passed in Oxide events.
 */
#[derive(PartialEq,Eq,Clone,Copy)]
pub enum ButtonState {
  /// Button was pressed
  Pressed,
  /// Button was released
  Released,
  /// Button is in an unknown state
  Unknown
}

/**
 * Encapsulation of a Button attached to a standard GPIO pin.  Generates Oxide
 * events on button events (press/release.)
 */
pub struct Button<'b, P, S>
where
  P: 'static + Pin,
  S: EventSink
{
  pin: OwnOrBorrowMut<'static,P>,
  phantom: PhantomData<S>,

  on_click: RefCell<Option<Box<dyn FnMut(PinIdentity,ButtonState)->() + 'b>>>
}

// Code ======================================================================
impl<'b,P,S> Button<'b,P,S>
where
  P: Pin,
  S: 'static + EventSink
{
  pub fn using<OP: Into<OwnOrBorrowMut<'static, P>>>(pin: OP) -> Self {
    let pin : OwnOrBorrowMut<P> = pin.into();

    pin.set_mode(PinMode::InputFloating);
    pin.set_interrupt_mode(InterruptMode::BothEdges);
    Button {
      pin,
      phantom: PhantomData::default(),
      on_click: RefCell::new(None)
    }
  }

  pub fn with_pin(pin: &'static mut P) -> Self {
    Self::using(pin)
  }

  /**
   * Have the Oxide supervisor execute the given closure every time an event
   * is generated by this device.
   */
  #[cfg(feature="alloc_ftr")]
  pub fn on_click(&mut self, bf: Box<dyn FnMut(PinIdentity, ButtonState) -> () + 'b>) {
    self.on_click.replace(Some(bf));
  }

  /**
   * Enable or disable the internal pullup on this pin
   */
  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
    }
  }
}

impl<P,S> EventSource for Button<'_,P,S>
where
  P: Pin,
  S: EventSink
{
  fn listen(&'static self) {
    self.pin.listen(IsrCallback::WithData(|_source, id, state, udata|{
      S::event(OxideEventEnvelope::to(unsafe { &*(panic_if_none!(udata) as *const Button<P,S> 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)
      },
      _ => {}
    }
  }
}