avr-oxide 0.1.0

An extremely simple Rusty operating system for AVR microcontrollers
/* masterclock.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! A simple master clock device designed to produce regular, frequent clock
//! tick events.


// Imports ===================================================================
use crate::hal::generic::timer::{TimerControl, TimerIdentity, TimerIsrCallback};
use crate::hal::generic::timer::TimerMode::Periodic;
use crate::event::{EventSink, EventSource, OxideEvent, OxideEventEnvelope};

use crate::deviceconsts::clock::{ MASTER_CLOCK_PRESCALER, MASTER_CLOCK_HZ, MASTER_TICK_FREQ_HZ };
use core::marker::PhantomData;
use core::ops::DerefMut;
use core::cell::RefCell;
use crate::alloc::boxed::Box;
use crate::panic_if_none;
use crate::util::OwnOrBorrowMut;

// Declarations ==============================================================
pub trait TickCallback = FnMut(TimerIdentity,u16) -> ();

pub struct MasterClock<'mc,T,S>
where
  T: 'static + TimerControl,
  S: EventSink
{
  timer: OwnOrBorrowMut<'static,T>,
  phantom: PhantomData<S>,

  on_tick: RefCell<Option<Box<dyn TickCallback + 'mc>>>
}

/**
 * Trait implemented by devices which can run code every time the clock ticks.
 */
pub trait TickEvents<'c> {
  /**
   * Call the given closure every time the clock ticks.
   */
  fn on_tick(&mut self, bf: Box<dyn TickCallback + 'c>);
}

// Code ======================================================================
impl<T, S> MasterClock<'_, T, S>
where
  T: 'static + TimerControl,
  S: EventSink
{
  /**
   * Create a MasterClock that uses the given AVR TimerControl device
   * to schedule clock events.  The desired frequency in Hz is provided
   * as a static parameter.  The timer will generate timer events to be
   * consumed by the supervisor, which you can handle with the `on_event`
   * method.
   */
  pub fn using<OT: Into<OwnOrBorrowMut<'static,T>>, const FREQ_HZ: u16>(timer: OT) -> Self {
    const CYCLES_PER_TICK: u16       = (MASTER_CLOCK_HZ as u32/(2u32 * MASTER_CLOCK_PRESCALER as u32 * MASTER_TICK_FREQ_HZ as u32)) as u16; // We use CLK_PER/2 in timer config, hence the 2* term

    debug_assert!(FREQ_HZ < MASTER_TICK_FREQ_HZ);
    debug_assert!(FREQ_HZ > 0);

    let mut timer : OwnOrBorrowMut<T> = timer.into();

    timer.set_mode(Periodic);
    timer.set_count_max(CYCLES_PER_TICK);
    timer.set_interrupt_period((MASTER_TICK_FREQ_HZ/FREQ_HZ) as u16);

    Self {
      timer,
      phantom: PhantomData::default(),
      on_tick: RefCell::new(None)
    }
  }

  pub fn with_timer<const FREQ_HZ: u16>(timer: &'static mut T) -> Self {
    Self::using::<_, FREQ_HZ>(timer)
  }
}

impl<'mc, T, S> TickEvents<'mc> for MasterClock<'mc, T, S>
where
  T: 'static + TimerControl,
  S: EventSink
{
  fn on_tick(&mut self, bf: Box<dyn TickCallback + 'mc>) {
    self.on_tick.replace(Some(bf));
  }
}

impl<T,S> EventSource for MasterClock<'_,T,S>
where
  T: 'static + TimerControl,
  S: EventSink
{
  fn listen(&'static self) {
    self.timer.start(TimerIsrCallback::WithData(|source, ticks, udata| {
      S::event(OxideEventEnvelope::to(unsafe { &*(panic_if_none!(udata) as *const MasterClock<T,S> as *const dyn EventSource) },
                                      OxideEvent::ClockTick(source, ticks)));
      true
    }, self as *const dyn core::any::Any ));
  }

  fn process_event(&self, evt: OxideEvent) {
    match (self.on_tick.borrow_mut().deref_mut(), evt) {
      (Some(f), OxideEvent::ClockTick(source, ticks)) => {
        (*f)(source,ticks)
      },
      _ => {}
    }
  }
}

// Tests =====================================================================