avr-oxide 0.0.4

An extremely simply Rusty operating system for AVR microcontrollers
/* timer.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Generic trait for controlling timer devices

// Imports ===================================================================

// Declarations ==============================================================
#[derive(Clone,Copy)]
pub enum TimerMode {
  Periodic
}

/**
 * Callback called by a timer when it generates an interrupt.  The callback
 * is given the number of ticks counted since the last such event.  The
 * callback should return a boolean:
 * * true:  Continue timer running
 * * false: Stop timer running
 *
 * 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 TimerInterruptHandler = fn(u16) ->bool;

pub trait TimerControl {
  /// Builder method that sets the number of underlying timer events that
  /// trigger an interrupt callback (i.e. if 10, every 10 timer events
  /// the callback passed to start() will be called.)
  fn interrupting(&mut self, period: u16) -> &mut Self;

  /// Builder method that sets the clock's mode
  fn mode(&mut self, mode: TimerMode) -> &mut Self;

  /// Builder method that sets the clock's counter trigger
  fn count_max(&mut self, max: u16) -> &mut Self;

  /// Start this timer.  The given callback will be called periodically
  /// when timer interrupts occur (see the interrupting(period) method.)
  ///
  /// If the timer is in a constant-run mode (e.g. TimerMode::Periodic), it
  /// will run constantly until either stopped (with the stop() method!)
  /// or until the callback function returns false.
  fn start(&mut self, handler: Option<TimerInterruptHandler>);

  /// Stop this timer
  fn stop(&mut self);

  /// Get the timer's current count value
  fn get_count(&self) -> u16;

  /// Reset the timer's current count value
  fn reset_count(&mut self);
}


// Code ======================================================================
pub mod base {
  use crate::hal::generic::timer::{TimerInterruptHandler, TimerMode, TimerControl};

  #[repr(C)]
  pub struct AvrTypeBTimerControl {
    pub(crate) ctrla: u8,
    pub(crate) ctrlb: u8,
    pub(crate) reserved: [u8; 2],
    pub(crate) evctrl: u8,
    pub(crate) intctrl: u8,
    pub(crate) intflags: u8,
    pub(crate) status: u8,
    pub(crate) dbgctrl: u8,
    pub(crate) temp: u8,
    pub(crate) cnt: u16,
    pub(crate) ccmp: u16
  }

  pub trait AtmelTCB {
    fn enable(&mut self);
    fn disable(&mut self);
    fn enable_interrupt(&mut self);
    fn clear_interrupt(&mut self);
    fn mask_interrupt(&mut self);
    fn set_top(&mut self, top: u16);
    fn set_periodic_mode(&mut self);
  }

  impl AtmelTCB for AvrTypeBTimerControl {
    #[inline(always)]
    fn enable(&mut self) {
      // Flags == Run in standby, divide clock by 2, enable
      unsafe {
        core::ptr::write_volatile(&mut self.ctrla as *mut u8, 0b01000011);
      }
    }

    #[inline(always)]
    fn disable(&mut self) {
      unsafe {
        core::ptr::write_volatile(&mut self.ctrla as *mut u8, 0b00000000);
      }
    }

    #[inline(always)]
    fn enable_interrupt(&mut self) {
      unsafe {
        core::ptr::write_volatile(&mut self.intctrl as *mut u8, 0b00000001);
      }
    }

    #[inline(always)]
    fn clear_interrupt(&mut self) {
      unsafe {
        core::ptr::write_volatile(&mut self.intflags as *mut u8, 0b00000001);
      }
    }

    #[inline(always)]
    fn mask_interrupt(&mut self) {
      unsafe {
        core::ptr::write_volatile(&mut self.intctrl as *mut u8, 0b00000000);
      }
    }

    #[inline(always)]
    fn set_top(&mut self, top: u16) {
      unsafe {
        core::ptr::write_volatile(&mut self.cnt as *mut u16, 0x0000);
        core::ptr::write_volatile(&mut self.ccmp as *mut u16, top);
      }
    }

    #[inline(always)]
    fn set_periodic_mode(&mut self) {
      unsafe {
        let ctrlb_reg = &mut self.ctrlb as *mut u8;

        let ctrlb = core::ptr::read_volatile(ctrlb_reg);
        core::ptr::write_volatile(ctrlb_reg, ctrlb | 0b00010000);
      }
    }
  }



  pub struct AtmelTimer<T>
    where
      T: 'static + AtmelTCB
  {
    pub(crate) interrupt_handler: Option<TimerInterruptHandler>,
    pub(crate) interrupt_period: u16,
    pub(crate) tcb: &'static mut T,
    pub(crate) count_max: u16,
    pub(crate) mode: TimerMode
  }

  impl<T> TimerControl for AtmelTimer<T>
    where
      T: AtmelTCB
  {
    fn interrupting(&mut self, period: u16) -> &mut Self {
      unsafe {
        core::ptr::write_volatile(&mut self.interrupt_period as *mut u16, period)
      }
      self
    }

    fn mode(&mut self, mode: TimerMode) -> &mut Self {
      self.mode = mode;
      self
    }

    fn count_max(&mut self, max: u16) -> &mut Self {
      self.count_max = max;
      self
    }

    fn start(&mut self, handler: Option<TimerInterruptHandler>) {
      self.tcb.disable();
      match self.mode {
        TimerMode::Periodic => {
          self.tcb.set_periodic_mode();
          self.tcb.set_top(self.count_max.clone());
        }
      };

      self.tcb.clear_interrupt();
      match handler {
        None => {
          self.tcb.mask_interrupt();
        },
        Some(handler) => {
          self.interrupt_handler = Some(handler);
          self.tcb.enable_interrupt();
        }
      }
      self.tcb.enable();
    }

    fn stop(&mut self) {
      self.tcb.disable();
    }

    fn get_count(&self) -> u16 {
      todo!()
    }

    fn reset_count(&mut self) {
      todo!()
    }
  }

  impl<T> AtmelTimer<T>
    where
      T: AtmelTCB
  {
    #[inline(always)]
    pub(crate) fn call_interrupt(&mut self, ticks: u16) {
      match self.interrupt_handler {
        Some(handler) => {
          match handler(ticks) {
            true => {},
            false => self.tcb.disable()
          }
        },
        None => {}
      }
    }

    #[inline(always)]
    pub(crate) fn interrupt_period(&self) -> u16 {
      unsafe {
        core::ptr::read_volatile(&self.interrupt_period as *const u16)
      }
    }
  }

  #[macro_export]
  macro_rules! atmel_tcb {
    ($tcbref:expr, $isr:ident) => {


      extern crate avr_device_snowgoons as avr_device;

      use crate::hal::generic::timer::TimerMode;
      use crate::hal::generic::timer::base::{ AtmelTimer, AtmelTCB, AvrTypeBTimerControl };

      use crate::mut_singleton;

      pub type TimerImpl = AtmelTimer<AvrTypeBTimerControl>;

      mut_singleton!(
        AtmelTimer<AvrTypeBTimerControl>,
        INSTANCE,
        instance,
        AtmelTimer {
          interrupt_handler: None,
          interrupt_period: 0,
          tcb: core::mem::transmute($tcbref),
          count_max: 0,
          mode: TimerMode::Periodic
        });


      #[no_mangle]
      pub unsafe extern "avr-interrupt" fn $isr() {
        static mut COUNT_INTS : u16 = 0;

        let counter = core::ptr::read_volatile(&COUNT_INTS as *const u16);

        match &mut INSTANCE {
          None => {},
          Some(atmeltimer) => {

            if counter == 0 {
              atmeltimer.call_interrupt(COUNT_INTS.clone());
              core::ptr::write_volatile(&mut COUNT_INTS as *mut u16, atmeltimer.interrupt_period())
            } else {
              core::ptr::write_volatile(&mut COUNT_INTS as *mut u16, counter-1);
            }

            atmeltimer.tcb.clear_interrupt();
          }
        }
      }
    }
  }
}