avr-oxide 0.4.0

An extremely simple Rusty operating system for AVR microcontrollers
/* ports.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Generic I/O ports provided by the AVR microcontroller.

// Imports ===================================================================
use core::any::Any;
use ufmt::derive::uDebug;
use avr_oxide::concurrency::Isolated;
use oxide_macros::Persist;
use avr_oxide::hal::generic::callback::IsrCallback;
use avr_oxide::hal::generic::port::base::AtmelPortControl;

// Declarations ==============================================================
/**
 * Input/output mode for a pin
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum PinMode {
  /// Pin is configured as output
  Output,
  /// Pin is configured as an input with internal pullup enabled
  InputPullup,
  /// Pin is configured as an input with internal pullup disabled
  InputFloating,
  /// Pin is configured as an input without changing the pullup
  Input
}

/**
 * Interrupt generation mode for a pin
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum InterruptMode {
  /// Do not generate interrupts
  Disabled,
  /// Interrupt on both rising and falling edges
  BothEdges,
  /// Interrupt on rising edge
  RisingEdge,
  /// Interrupt on falling edge
  FallingEdge,
  /// Interrupt while input is low
  LowLevel
}

/**
 * Identifies a particular pin as the source of an event at runtime.
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum PinIdentity {
  /// Port A, pin number (u8)
  #[cfg(feature = "porta")]
  PortA(u8),
  /// Port B, pin number (u8)
  #[cfg(feature = "portb")]
  PortB(u8),
  /// Port C, pin number (u8)
  #[cfg(feature = "portc")]
  PortC(u8),
  /// Port D, pin number (u8)
  #[cfg(feature = "portd")]
  PortD(u8),
  /// Port E, pin number (u8)
  #[cfg(feature = "porte")]
  PortE(u8),
  /// Port F, pin number (u8)
  #[cfg(feature = "portf")]
  PortF(u8)
}

/**
 * An internal ID for any given pin
 */
#[repr(transparent)]
pub struct ProxyPin(u8);

impl ProxyPin {
  pub(crate) const fn for_port_and_pin(port: u8, pin: u8) -> Self {
    Self(port << 3 | pin)
  }
  pub(crate) const fn port_number(&self) -> u8 {
    self.0 >> 3
  }
  pub(crate) const fn pin_number(&self) -> u8 {
    self.0 & 0b0000_0111
  }
  pub(crate) const fn to_index(&self) -> usize {
    self.0 as usize
  }
}

/**
 * Trait for getting a reference to the Port associated with a pin.
 *
 * This will be implemented by the device-specific implementation to map
 * PinInternalIds to their corresponding ports.
 */
pub trait PinToPort {
  fn get_port(&self) -> &'static mut dyn AtmelPortControl;
}

/**
 * Callback called by a pin when it generates an interrupt.  The callback
 * is given the new state read from the pin (true = high).
 * The callback should return a boolean:
 * * true:  Continue generating interrupts for this pin
 * * false: Disable generating interrupts for this pin
 *
 * Note: The callback runs *within the interrupt context* - be careful about
 * using mutual exclusion where necessary, and *DO NOT DO HEAVY PROCESSING
 * IN THE CALLBACK*.
 */
pub type PinIsrFunction = fn(Isolated,&'static dyn Pin,PinIdentity,bool,Option<*const dyn Any>) -> ();

pub type PinIsrCallback = IsrCallback<PinIsrFunction,()>;

pub trait Pin {
  /// Set the input/output mode for this pin
  fn set_mode(&self, mode: PinMode);

  /// Toggle this pin's output level
  fn toggle(&self);

  /// Set the pin's output to high
  fn set_high(&self);

  /// Set the pin's output to low
  fn set_low(&self);

  /// Set the pin's output according to the boolean (true == high, false == low)
  fn set(&self, high: bool);

  /// Get the pin's input; true == high, false == low.
  fn get(&self) -> bool;

  /// Set when this pin will generate interrupts
  fn set_interrupt_mode(&self, mode: InterruptMode);

  /// Add a handler to be called when this pin generates an interrupt
  fn listen(&'static self, handler: PinIsrCallback);
}

// Code ======================================================================
/**
 * A place to stash handlers for pin interrupts.
 *
 * Originally, each port held a simple array of function references, one for
 * each pin.  Unfortunately, smaller devices like ATmega4809 have a lot of
 * pins, but not a lot of RAM, and this was an unacceptable waste of memory.
 * So now we have this global handler stash instead.
 */
pub mod handlers {
  use avr_oxide::alloc::vec::Vec;
  use avr_oxide::hal::generic::port::{Pin, ProxyPin, PinIsrCallback};
  use avr_oxide::{isr_cb_invoke, mut_singleton};
  use avr_oxide::deviceconsts::oxide::PIN_ARRAY_SIZE;
  use avr_oxide::concurrency::Isolated;

  /**
   * A set of pins, indexed by the index returned by our `global_index_for_pin`
   * function in the `PinIdentityGenerator` trait.
   */
  pub struct PinHandlerSet {
    indexes: [u8; PIN_ARRAY_SIZE],
    handlers: Vec<PinIsrCallback>
  }

  mut_singleton!(
    PinHandlerSet,
    __INSTANCE,
    instance, instance_isolated,
    PinHandlerSet {
      indexes: [0x00; PIN_ARRAY_SIZE],
      handlers: Vec::with_capacity(4)
    });

  impl PinHandlerSet {
    pub(crate) fn set_handler_for_pin(&mut self, _isotoken: Isolated, pin: ProxyPin, handler: PinIsrCallback){
      let vec_idx = self.indexes[pin.to_index()];

      if vec_idx > 0 {
        self.handlers[(vec_idx-1) as usize] = handler;
      } else {
        self.handlers.push(handler);
        self.indexes[pin.to_index()] = self.handlers.len() as u8;
      }
    }

    /**
     * Invoke an interrupt handler routine.
     */
    pub(crate) fn invoke_handler_for_pin(&self, isotoken: Isolated, pin: ProxyPin, source: &'static dyn Pin, state: bool) {
      let vec_idx = self.indexes[pin.to_index()];

      if vec_idx > 0 {
        isr_cb_invoke!(isotoken, self.handlers[(vec_idx-1) as usize], source, pin.into(), state);
      }
    }
  }
}

#[cfg(target_arch="avr")]
pub mod base {
  use avr_oxide::concurrency::Isolated;
  use avr_oxide::hal::generic::port::{InterruptMode, Pin, ProxyPin, PinIsrCallback, PinMode, PinToPort};

  pub trait AtmelPortControl {
    /// Set this pin as an output.
    fn enable_output(&mut self, p: u8);
    /// Disable output on this pin
    fn disable_output(&mut self, p: u8);
    /// Enable internal pullup on this pin
    fn enable_pullup(&mut self, p: u8);
    /// Disable internal pullup on this pin
    fn disable_pullup(&mut self, p: u8);
    /// Set a pin's output state high
    fn set_high(&mut self, p: u8);
    /// Set a pin's output state low
    fn set_low(&mut self, p: u8);
    /// Toggle a pin's input state
    fn toggle(&mut self, p: u8);
    /// Get a pin's state
    fn get(&self, p: u8) -> bool;
    /// Set the mode for interrupt generation for a pin
    fn set_interrupt_mode(&mut self, p: u8, mode: InterruptMode);

    /// Interrupt service routine.  Dispatch any listeners for this port.
    fn isr(&'static mut self, isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin);
    /// Add a listener to this port for the given pin
    fn listen(&'static self, p: u8, handler: PinIsrCallback);
  }

  /**
   * Implementation of the Pin API for our Atmel PINs.
   */
  impl Pin for ProxyPin
  where
    ProxyPin: PinToPort
  {
    fn set_mode(&self, mode: PinMode) {
      let pin = self.pin_number();
      match mode {
        PinMode::Output => {
          self.get_port().enable_output(pin);
        },
        PinMode::InputPullup => {
          self.get_port().enable_pullup(pin);
          self.get_port().disable_output(pin);
        },
        PinMode::InputFloating => {
          self.get_port().disable_pullup(pin);
          self.get_port().disable_output(pin);
        },
        PinMode::Input => {
          self.get_port().disable_output(pin);
        }
      }
    }
    fn toggle(&self) {
      self.get_port().toggle(self.pin_number());
    }
    fn set_high(&self) {
      self.get_port().set_high(self.pin_number());
    }
    fn set_low(&self) {
      self.get_port().set_low(self.pin_number());
    }
    fn set(&self, high: bool) {
      match high {
        true  => self.get_port().set_high(self.pin_number()),
        false => self.get_port().set_low(self.pin_number())
      }
    }
    fn get(&self) -> bool {
      self.get_port().get(self.pin_number())
    }
    fn set_interrupt_mode(&self, mode: InterruptMode) {
      self.get_port().set_interrupt_mode(self.pin_number(), mode)
    }
    fn listen(&self, handler: PinIsrCallback){
      self.get_port().listen(self.pin_number(),handler)
    }
  }


  /**
   * Implementation for the port control blocks found in 0-series AVRs like
   * the ATmega4809
   */
  pub mod zeroseries {
    use avr_oxide::hal::generic::port::{handlers, InterruptMode, Pin, ProxyPin, PinIsrCallback};
    use avr_oxide::concurrency::Isolated;
    use avr_oxide::hal::generic::port::base::{AtmelPortControl};
    use avr_oxide::util::datatypes::{BitFieldAccess, BitIndex, Volatile, VolatileBitField};

    impl InterruptMode {
      fn to_pinctrl_isc(&self) -> u8 {
        match self {
          InterruptMode::Disabled => 0x00,
          InterruptMode::BothEdges => 0x01,
          InterruptMode::RisingEdge => 0x02,
          InterruptMode::FallingEdge => 0x03,
          InterruptMode::LowLevel => 0x05
        }
      }

      fn pinctrl_mask() -> u8 {
        0b00000111
      }
    }

    /**
     * The AVR port control register block.  The structure provided by the
     * auto-generated avr-device crate is just horrible, so we use our own
     * rendition.  This allows accessing pin control by index, and also avoids
     * having different types for every port.
     */
    #[repr(C)]
    pub struct PortRegisterBlock {
      pub(crate) dir:      VolatileBitField,
      pub(crate) dir_set:  VolatileBitField,
      pub(crate) dir_clr:  VolatileBitField,
      pub(crate) dir_tgl:  VolatileBitField,
      pub(crate) out:      VolatileBitField,
      pub(crate) out_set:  VolatileBitField,
      pub(crate) out_clr:  VolatileBitField,
      pub(crate) out_tgl:  VolatileBitField,
      pub(crate) inp:      VolatileBitField,
      pub(crate) intflags: VolatileBitField,
      pub(crate) portctrl: VolatileBitField,
      pub(crate) reserved: [u8; 5],
      pub(crate) pin_ctl:  [Volatile<u8>; 8]
    }

    impl PortRegisterBlock {
      #[cfg(feature="atmega4809")]
      fn get_port_num(&self) -> u8 {
        (self as *const PortRegisterBlock as u8) / 0x20
      }

      #[cfg(not(any(feature="atmega4809")))]
      fn get_port_num(&self) -> u8 {
        unimplemented!()
      }
    }

    impl AtmelPortControl for PortRegisterBlock {
      fn enable_output(&mut self, p: u8) {
        self.dir_set.exc_set(BitIndex::bit(p as usize));
      }
      fn disable_output(&mut self, p: u8) {
        self.dir_clr.exc_set(BitIndex::bit(p as usize));
      }
      fn enable_pullup(&mut self, p: u8) {
        self.pin_ctl[p as usize] |= 0b00001000;
      }
      fn disable_pullup(&mut self, p: u8) {
        self.pin_ctl[p as usize] &= 0b11110111;
      }
      fn set_high(&mut self, p: u8) {
        self.out_set.exc_set(BitIndex::bit(p as usize));
      }
      fn set_low(&mut self, p: u8) {
        self.out_clr.exc_set(BitIndex::bit(p as usize));
      }
      fn toggle(&mut self, p: u8) {
        self.out_tgl.exc_set(BitIndex::bit(p as usize));
      }
      fn get(&self, p: u8) -> bool {
        self.inp.is_set(BitIndex::bit(p as usize))
      }
      fn set_interrupt_mode(&mut self, p: u8, mode: InterruptMode) {
        self.pin_ctl[p as usize] &= !InterruptMode::pinctrl_mask();
        self.pin_ctl[p as usize] |= mode.to_pinctrl_isc();
      }
      fn isr(&'static mut self, isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin) {
        let pins_interrupted = self.intflags.snapshot();
        let pins_state = self.inp.snapshot();

        for pin in 0u8..7u8 {
          if pins_interrupted.is_set(BitIndex::bit(pin as usize)) {
            let source = get_pin(pin);

            handlers::instance_isolated(isotoken).invoke_handler_for_pin(isotoken,
                                                                ProxyPin::for_port_and_pin(self.get_port_num() as u8, pin),
                                                                source,
                                                                pins_state.is_set(BitIndex::bit(pin as usize)));
          }
        }

        // Clear interrupts
        self.intflags.set_all();
      }

      fn listen(&'static self, p: u8, handler: PinIsrCallback) {
        avr_oxide::concurrency::interrupt::isolated(|isotoken|{
          handlers::instance_isolated(isotoken).set_handler_for_pin(isotoken, ProxyPin::for_port_and_pin(self.get_port_num() as u8, p), handler);
        })
      }
    }
  }

  /**
   * Implementation for the simple port control blocks in the basic AVR's
   * like ATmega328P
   */
  pub mod simple {
    use avr_oxide::hal::generic::port::base::{AtmelPortControl};
    use avr_oxide::hal::generic::port::{handlers, InterruptMode, Pin, PinIsrCallback, ProxyPin};
    use avr_oxide::concurrency::Isolated;
    use avr_oxide::util::datatypes::{VolatileBitField, BitFieldAccess, BitIndex};

    /**
     * The older ATmegas like the '328P have a much simpler port control
     * block (if you can even call it that...)
     */
    #[repr(C)]
    pub struct AvrPortRegisterBlock<const ADDR: u16,const PCIEN: u16, const PCICR: u16, const PCIFR: u16, const PORTNUM: usize> {
      pub(crate) pin: VolatileBitField,
      pub(crate) ddr: VolatileBitField,
      pub(crate) port: VolatileBitField,
    }

    impl<const ADDR: u16,const PCIEN: u16, const PCICR: u16, const PCIFR:u16, const PORTNUM: usize> AtmelPortControl for AvrPortRegisterBlock<ADDR,PCIEN,PCICR,PCIFR,PORTNUM> {
      fn enable_output(&mut self, p: u8) {
        self.ddr.set(BitIndex::bit(p as usize));
      }
      fn disable_output(&mut self, p: u8) {
        self.ddr.clr(BitIndex::bit(p as usize));
      }
      fn enable_pullup(&mut self, p: u8) {
        self.port.set(BitIndex::bit(p as usize));
      }
      fn disable_pullup(&mut self, p: u8) {
        self.port.clr(BitIndex::bit(p as usize));
      }
      fn set_high(&mut self, p: u8) {
        self.port.set(BitIndex::bit(p as usize));
      }
      fn set_low(&mut self, p: u8) {
        self.port.clr(BitIndex::bit(p as usize));
      }
      fn toggle(&mut self, p: u8) {
        self.pin.exc_set(BitIndex::bit(p as usize)); // Writing 1 to the input toggles the output on '328p et al
      }
      fn get(&self, p: u8) -> bool {
        self.pin.is_set(BitIndex::bit(p as usize))
      }

      fn set_interrupt_mode(&mut self, p: u8, mode: InterruptMode) {
        unsafe {
          avr_oxide::concurrency::interrupt::isolated(|isotoken|{
            // PCIEN - enable/disable interrupts for an individual pin
            let pcien = &mut *(PCIEN as *mut VolatileBitField);
            // PCICR - enable/disable interrupts for a whole port
            let pcicr = &mut *(PCICR as *mut VolatileBitField);

            match mode {
              // @todo actually change the global level/edge mode
              InterruptMode::Disabled => {
                pcien.clr_isolated(isotoken, BitIndex::bit(p as usize));

              }
              InterruptMode::BothEdges => {
                pcicr.set_isolated(isotoken, BitIndex::bit(PORTNUM));
                pcien.set_isolated(isotoken, BitIndex::bit(p as usize));
              },
              InterruptMode::RisingEdge => {
                pcicr.set_isolated(isotoken, BitIndex::bit(PORTNUM));
                pcien.set_isolated(isotoken, BitIndex::bit(p as usize));
              },
              InterruptMode::FallingEdge => {
                pcicr.set_isolated(isotoken, BitIndex::bit(PORTNUM));
                pcien.set_isolated(isotoken, BitIndex::bit(p as usize));
              },
              InterruptMode::LowLevel => {
                pcicr.set_isolated(isotoken, BitIndex::bit(PORTNUM));
                pcien.set_isolated(isotoken, BitIndex::bit(p as usize));
              }
            }
          })
        }
      }

      fn isr(&'static mut self, isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin) {
        unsafe {
          // We can't actually tell which pin generated the input, so we
          // will call all the handlers with interrupts enabled.
          let pcien = &mut *(PCIEN as *mut VolatileBitField);
          for pin in 0u8..=7u8 {
            if pcien.is_set(BitIndex::bit(pin as usize)){
              let state = self.pin.is_set(BitIndex::bit(pin as usize));
              let source = get_pin(pin);

              handlers::instance_isolated(isotoken).invoke_handler_for_pin(isotoken, ProxyPin::for_port_and_pin(PORTNUM as u8, pin), source, state);
            }
          }
          // Clear interrupts.  Shouldn't be necessary, but the datasheet has
          // lied about this before, so better safe than sorry

          let pcifr = &mut *(PCIFR as *mut VolatileBitField);

          pcifr.exc_set(BitIndex::bit(PORTNUM));
        }
      }

      fn listen(&'static self, p: u8, handler: PinIsrCallback) {
        avr_oxide::concurrency::interrupt::isolated(|isotoken|{
          handlers::instance_isolated(isotoken).set_handler_for_pin(isotoken, ProxyPin::for_port_and_pin(PORTNUM as u8, p), handler);
        })
      }
    }
  }

  #[doc(hidden)]
  #[macro_export]
  macro_rules! atmel_port_tpl {
    ($ref:expr, $portimpl:ty, $portnum:expr, $pinarray:expr, $isr:expr) => {
      use core::any::Any;

      use avr_oxide::hal::generic::port::{PinMode,Pin,PinIdentity,InterruptMode,PinIsrCallback};
      use avr_oxide::hal::generic::port::base::{AtmelPortControl};
      use avr_oxide::isr_cb_invoke;

      pub type PortImpl = $portimpl;

      static mut INITIALISED: bool = false;

      /**
       * Get an instance of this port's register block.
       */
      pub fn instance() -> &'static mut $portimpl  {
        unsafe {
          core::mem::transmute($ref)
        }
      }

      pub fn pin(pin: u8) -> &'static dyn Pin {
        &$pinarray[(($portnum*8)+pin) as usize]
      }

      /**
       * Interrupt service routine.  Checks which pins caused the interrupt
       * and then calls the relevant callback routine.
       */
      #[oxide_macros::interrupt(isr=$isr)]
      fn port_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
        instance().isr(isotoken, pin);
      }
    }
  }
}

#[cfg(not(target_arch="avr"))]
pub mod base {
  use avr_oxide::concurrency::Isolated;
  use avr_oxide::hal::generic::port::{InterruptMode, Pin, ProxyPin, PinIsrCallback, PinMode, PinToPort};


  pub trait AtmelPortControl {
    /// Set this pin as an output.
    fn enable_output(&mut self, p: u8);
    /// Disable output on this pin
    fn disable_output(&mut self, p: u8);
    /// Enable internal pullup on this pin
    fn enable_pullup(&mut self, p: u8);
    /// Disable internal pullup on this pin
    fn disable_pullup(&mut self, p: u8);
    /// Set a pin's output state high
    fn set_high(&mut self, p: u8);
    /// Set a pin's output state low
    fn set_low(&mut self, p: u8);
    /// Toggle a pin's input state
    fn toggle(&mut self, p: u8);
    /// Get a pin's state
    fn get(&self, p: u8) -> bool;
    /// Set the mode for interrupt generation for a pin
    fn set_interrupt_mode(&mut self, p: u8, mode: InterruptMode);

    /// Interrupt service routine.  Dispatch any listeners for this port.
    fn isr(&'static mut self, isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin);
    /// Add a listener to this port for the given pin
    fn listen(&'static self, p: u8, handler: PinIsrCallback);
  }

  impl Pin for ProxyPin
    where
      ProxyPin: PinToPort
  {
    fn set_mode(&self, mode: PinMode) {
      let pin = self.pin_number();
      match mode {
        PinMode::Output => {
          self.get_port().enable_output(pin);
        },
        PinMode::InputPullup => {
          self.get_port().enable_pullup(pin);
          self.get_port().disable_output(pin);
        },
        PinMode::InputFloating => {
          self.get_port().disable_pullup(pin);
          self.get_port().disable_output(pin);
        },
        PinMode::Input => {
          self.get_port().disable_output(pin);
        }
      }
    }

    #[inline(never)]
    fn toggle(&self) {
      self.get_port().toggle(self.pin_number());
    }

    fn set_high(&self) {
      self.get_port().set_high(self.pin_number());
    }

    fn set_low(&self) {
      self.get_port().set_low(self.pin_number());
    }

    fn set(&self, high: bool) {
      match high {
        true  => self.get_port().set_high(self.pin_number()),
        false => self.get_port().set_low(self.pin_number())
      }
    }

    fn get(&self) -> bool {
      self.get_port().get(self.pin_number())
    }
    fn set_interrupt_mode(&self, mode: InterruptMode) {
      self.get_port().set_interrupt_mode(self.pin_number(), mode)
    }
    fn listen(&self, handler: PinIsrCallback){
      self.get_port().listen(self.pin_number(),handler)
    }
  }


  pub mod zeroseries {
    use avr_oxide::hal::generic::port::{handlers, InterruptMode, Pin, ProxyPin, PinIsrCallback};
    use avr_oxide::concurrency::Isolated;
    use avr_oxide::hal::generic::port::base::{AtmelPortControl};
    use avr_oxide::util::datatypes::{BitFieldAccess, BitIndex, Volatile, VolatileBitField};

    /**
     * The AVR port control register block.  The structure provided by the
     * auto-generated avr-device crate is just horrible, so we use our own
     * rendition.  This allows accessing pin control by index, and also avoids
     * having different types for every port.
     */
    #[repr(C)]
    pub struct PortRegisterBlock {
      pub(crate) pin: [bool; 8],
      pub(crate) o_en: [bool; 8],
      pub(crate) pu_en: [bool; 8]
    }



    impl AtmelPortControl for PortRegisterBlock {
      fn enable_output(&mut self, p: u8) {
        println!("*** PORT: Enable output pin {}", p);
        self.o_en[p as usize] = true;
      }

      fn disable_output(&mut self, p: u8) {
        println!("*** PORT: Disable output pin {}", p);
        self.o_en[p as usize] = false;
      }

      fn enable_pullup(&mut self, p: u8) {
        println!("*** PORT: Enable pullup pin {}", p);
        self.pu_en[p as usize] = true;
      }

      fn disable_pullup(&mut self, p: u8) {
        println!("*** PORT: Disable pullup pin {}", p);
        self.pu_en[p as usize] = false;
      }

      fn set_high(&mut self, p: u8) {
        println!("*** PORT: Set HIGH pin {}", p);
        self.pin[p as usize] = true;
      }

      fn set_low(&mut self, p: u8) {
        println!("*** PORT: Set LOW pin {}", p);
        self.pin[p as usize] = false;
      }

      fn toggle(&mut self, p: u8) {
        let old = self.pin[p as usize];

        println!("*** PORT: TOGGLE pin {} ({} => {})", p, old, !old);

        self.pin[p as usize] = !old;
      }

      fn get(&self, p: u8) -> bool {
        let val = self.pin[p as usize];
        println!("*** PORT: GET pin {} ({})", p, val);
        val
      }

      fn set_interrupt_mode(&mut self, p: u8, _mode: InterruptMode) {
        println!("*** PORT: Set interrupt mode pin {}", p);
      }

      fn isr(&'static mut self, _isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin) {
        unimplemented!()
      }
      fn listen(&'static self, p: u8, handler: PinIsrCallback){
        unimplemented!()
      }
    }
  }

  #[doc(hidden)]
  #[macro_export]
  macro_rules! atmel_port_tpl {
    ($ref:expr, $portimpl:ty, $portnum:expr, $pinarray:expr, $isr:expr) => {
      use core::any::Any;

      use avr_oxide::hal::generic::port::{PinMode,Pin,PinIdentity,InterruptMode,PinIsrCallback};
      use avr_oxide::hal::generic::port::base::{AtmelPortControl};
      use avr_oxide::isr_cb_invoke;

      pub type PortImpl = $portimpl;

      static mut INITIALISED: bool = false;

      /**
       * Get an instance of this port's register block.
       */
      #[inline(always)]
      pub fn instance() -> &'static mut $portimpl  {
        unimplemented!()
      }

      #[inline(always)]
      pub fn pin(pin: u8) -> &'static dyn Pin {
        &$pinarray[(($portnum*8)+pin) as usize]
      }
    }
  }
}