avr-oxide 0.2.2

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.
//!
//! # Usage
//! The frequency, in Hz, is passed as a constant parameter when the
//! device is constructed using the `using()` constructor methods.  Also
//! passed is the underlying AVR hardware timer device which it should use
//! to generate the clock.
//!
//! ```rust,no_run
//! # #![no_std]
//! # #![no_main]
//! # use avr_oxide::alloc::boxed::Box;
//! # use avr_oxide::hal::atmega4809::hardware;
//! # use avr_oxide::arduino;
//! # use avr_oxide::devices::{ OxideMasterClock, Handle};
//! # use avr_oxide::devices::masterclock::TickEvents;
//! #
//! # #[avr_oxide::main(chip="atmega4809")]
//! # pub fn main() -> ! {
//! #   let supervisor = avr_oxide::oxide::instance();
//! #   let hardware = hardware::instance();
//! #   let arduino = arduino::nanoevery::Arduino::from(hardware);
//!
//!   let master_clock = Handle::new(OxideMasterClock::with_timer::<20>(arduino.timer0));
//!
//!   // An event handler every time the master clock ticks
//!   master_clock.on_tick(Box::new(move |_timerid, _duration|{
//!     // Do something 20 times every second
//!   }));
//!
//!  supervisor.listen_handle(master_clock);
//!  supervisor.run();
//! #  }
//! ```
//!
//! # Features
//! For accurate frequency calculations, this depends on various constants
//! defined in the [`avr_oxide::deviceconsts::clock`] module.  These depend
//! on the correct AVRoxide clockspeed feature being enabled in your `Cargo.toml`.
//!
//! | CPU Feature | Clockspeed features (pick 1) |
//! | ----------- | ---------------------------- |
//! | `atmega4809` | `16MHz`, `20MHz` |

// Imports ===================================================================
use avr_oxide::hal::generic::timer::{TimerControl, TimerIdentity, TimerIsrCallback};
use avr_oxide::hal::generic::timer::TimerMode::Periodic;
use avr_oxide::event::{EventSink, EventSource, OxideEvent, OxideEventEnvelope};
use avr_oxide::devices::internal::StaticShareable;
use avr_oxide::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 avr_oxide::alloc::boxed::Box;
use avr_oxide::panic_if_none;
use avr_oxide::util::OwnOrBorrowMut;
use avr_oxide::time::Duration;

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

pub trait DelayCallback = FnMut(TimerIdentity) -> ();

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

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

impl<'mc,T,S> StaticShareable for MasterClock<'mc,T,S>
  where
    T: 'static + TimerControl,
    S: EventSink
{}

/**
 * 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(&self, bf: Box<dyn TickCallback + 'c>);
}

/**
 * Trait implemented by devices which can run code after a delay has passed.
 */
pub trait DelayEvents<'c> {
  /**
   * Call the given callback after /at least/ `delay` time has passed.
   */
  fn after_delay(&self, delay: Duration, bf: Box<dyn DelayCallback + '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,
      freq_hz: FREQ_HZ,
      phantom: PhantomData::default(),
      on_tick: RefCell::new(None)
    }
  }

  pub fn static_using<OT: Into<OwnOrBorrowMut<'static,T>>, const FREQ_HZ: u16>(timer: OT) -> &'static mut Self {
    Box::leak(Box::new(Self::using::<_,FREQ_HZ>(timer)))
  }

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

  pub fn static_with_timer<const FREQ_HZ: u16>(timer: &'static mut T) -> &'static mut Self {
    Box::leak(Box::new(Self::with_timer::<FREQ_HZ>(timer)))
  }
}

impl<'mc, T, S> TickEvents<'mc> for MasterClock<'mc, T, S>
where
  T: 'static + TimerControl,
  S: EventSink
{
  fn on_tick(&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
{
  #[optimize(speed)]
  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 ));
  }

  #[optimize(speed)]
  fn process_event(&self, evt: OxideEvent) {
    match (self.on_tick.borrow_mut().deref_mut(), evt) {
      (Some(f), OxideEvent::ClockTick(source, ticks)) => {
        (*f)(source,Duration::from_millis((ticks as u32 * self.freq_hz as u32) / 1000u32))
      },
      _ => {}
    }
  }
}

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