Skip to content

Commit

Permalink
Timer V2
Browse files Browse the repository at this point in the history
  • Loading branch information
glaeqen committed Jun 9, 2023
1 parent a971cbf commit 75ad5a5
Show file tree
Hide file tree
Showing 11 changed files with 1,180 additions and 182 deletions.
5 changes: 5 additions & 0 deletions boards/atsame54_xpro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ rt = ["cortex-m-rt", "atsamd-hal/same54p-rt"]
unproven = ["atsamd-hal/unproven"]
usb = ["atsamd-hal/usb", "usb-device"]
can = ["atsamd-hal/can"]
rtic = ["atsamd-hal/rtic"]

[profile.dev]
incremental = false
Expand All @@ -52,3 +53,7 @@ opt-level = "s"
[[example]]
name = "mcan"
required-features = ["can"]

[[example]]
name = "timer_v2"
required-features = ["rtic"]
128 changes: 128 additions & 0 deletions boards/atsame54_xpro/examples/timer_v2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//! Timer V2 example
//! WIP
#![no_std]
#![no_main]

use atsame54_xpro as bsp;
use bsp::hal;
use hal::clock::v2 as clock;
use hal::clock::v2::gclk::Gclk1Id;
use hal::clock::v2::types::*;
// TODO: Prelude enforces import of `fugit::ExtU32` that ruins everything..
// Temporarily remove `crate::prelude::*` import and take what's needed.
use hal::ehal::digital::v2::OutputPin as _;
use hal::eic::pin::*;
use hal::fugit::ExtU64;
use hal::gpio::{Interrupt as GpioInterrupt, *};
use hal::timer::v2::*;

use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};

type Button = ExtInt15<Pin<PB31, GpioInterrupt<PullUp>>>;

#[rtic::app(device = hal::pac, peripherals = true, dispatchers = [FREQM])]
mod app {
use super::*;

#[monotonic(binds = TC0, default = true)]
type Mono = MonotonicTimer<32, Tc0Tc1, Gclk1Id>;

#[shared]
struct Shared {}

#[local]
struct Local {
button: Button,
led: bsp::Led,
}

#[init]
fn init(mut ctx: init::Context) -> (Shared, Local, init::Monotonics) {
rtt_init_print!();
rprintln!("Application up!");

let (mut buses, clocks, tokens) = clock::clock_system_at_reset(
ctx.device.OSCCTRL,
ctx.device.OSC32KCTRL,
ctx.device.GCLK,
ctx.device.MCLK,
&mut ctx.device.NVMCTRL,
);

let (_, _, _, mut mclk) = unsafe { clocks.pac.steal() };

let pins = bsp::Pins::new(ctx.device.PORT);

let (pclk_eic, _gclk0) = clock::pclk::Pclk::enable(tokens.pclks.eic, clocks.gclk0);

let mut eic = hal::eic::init_with_ulp32k(&mut mclk, pclk_eic.into(), ctx.device.EIC);
let mut button = bsp::pin_alias!(pins.button).into_pull_up_ei();
eic.button_debounce_pins(&[button.id()]);
button.sense(&mut eic, Sense::FALL);
button.enable_interrupt(&mut eic);
eic.finalize();

let (osculp32k, _) =
clock::osculp32k::OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base);

let (gclk1, _osculp32k) = clock::gclk::Gclk::from_source(tokens.gclks.gclk1, osculp32k);
let gclk1 = gclk1.enable();

let tc0_clk = buses.apb.enable(tokens.apbs.tc0);
let tc1_clk = buses.apb.enable(tokens.apbs.tc1);

let (tc0_tc1_pclk, _gclk1) = clock::pclk::Pclk::enable(tokens.pclks.tc0_tc1, gclk1);

let mono = Timer::paired(
ctx.device.TC0,
tc0_clk,
ctx.device.TC1,
tc1_clk,
tc0_tc1_pclk,
)
.into_32_bit()
.into_monotonic()
.unwrap();

let led: bsp::Led = bsp::pin_alias!(pins.led).into();
bump_activity_led();

(Shared {}, Local { button, led }, init::Monotonics(mono))
}

#[task(binds = EIC_EXTINT_15, local = [button])]
fn button(ctx: button::Context) {
ctx.local.button.clear_interrupt();

rprintln!("Button pressed!");
hello::spawn().unwrap();
hello::spawn_after(50.millis()).unwrap();
hello::spawn_after(100.millis()).unwrap();
hello::spawn_after(500.millis()).unwrap();
hello::spawn_after(1.secs()).unwrap();
hello::spawn_after(5.secs()).unwrap();
}

#[task(capacity = 30)]
fn hello(_: hello::Context) {
bump_activity_led();
rprintln!(
"Hello at {} ms since epoch!",
monotonics::now().duration_since_epoch().to_millis()
);
}

#[task(local = [led])]
fn activity_led(ctx: activity_led::Context, led_on: bool) {
let _ = ctx.local.led.set_state((!led_on).into());
if led_on {
let _ = activity_led::spawn_after(100.millis(), false);
}
}

fn bump_activity_led() {
let _ = activity_led::spawn(true);
}
}
2 changes: 2 additions & 0 deletions hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ fugit = "0.3"
modular-bitfield = "0.11"
nb = "1.0"
num-traits = {version = "0.2.14", default-features = false}
num-derive = "0.3"
opaque-debug = "0.3.0"
paste = "1.0.11"
rand_core = "0.6"
seq-macro = "0.3"
typenum = "1.12.0"
vcell = "0.1"
void = {version = "1.0", default-features = false}
rtt-target = { version = "0.3", features = ["cortex-m"] }

#===============================================================================
# Optional depdendencies
Expand Down
4 changes: 2 additions & 2 deletions hal/src/thumbv7em/clock/v2/apb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ impl<A: ApbId> ApbToken<A> {
/// instances with the same [`ApbId`]. See the notes on `Token` types and
/// memory safety in the root of the `clock` module for more details.
#[inline]
unsafe fn new() -> Self {
pub(crate) unsafe fn new() -> Self {
ApbToken { id: PhantomData }
}
}
Expand All @@ -492,7 +492,7 @@ pub struct ApbClk<A: ApbId> {

impl<A: ApbId> ApbClk<A> {
#[inline]
fn new(token: ApbToken<A>) -> Self {
pub(crate) fn new(token: ApbToken<A>) -> Self {
ApbClk { token }
}

Expand Down
4 changes: 2 additions & 2 deletions hal/src/thumbv7em/clock/v2/pclk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl<P: PclkId> PclkToken<P> {
/// instances with the same [`PclkId`]. See the notes on `Token` types and
/// memory safety in the root of the `clock` module for more details.
#[inline]
pub(super) unsafe fn new() -> Self {
pub(crate) unsafe fn new() -> Self {
PclkToken { pclk: PhantomData }
}

Expand Down Expand Up @@ -443,7 +443,7 @@ where
P: PclkId,
I: PclkSourceId,
{
pub(super) fn new(token: PclkToken<P>, freq: Hertz) -> Self {
pub(crate) fn new(token: PclkToken<P>, freq: Hertz) -> Self {
Self {
token,
src: PhantomData,
Expand Down
185 changes: 7 additions & 178 deletions hal/src/thumbv7em/timer.rs
Original file line number Diff line number Diff line change
@@ -1,180 +1,9 @@
//! Working with timer counter hardware
use crate::ehal::timer::{CountDown, Periodic};
use crate::pac::tc0::COUNT16;
use crate::pac::{MCLK, TC2, TC3};
#[cfg(all(feature = "has-tc4", feature = "has-tc5"))]
use crate::pac::{TC4, TC5};
use crate::timer_params::TimerParams;
use crate::timer_traits::InterruptDrivenTimer;
//! # Timer
//!
//! Users are encouraged to use [`v2`] variant of an API because of the richer
//! feature set and safety.
use crate::clock;
use crate::time::{Hertz, Nanoseconds};
use void::Void;
pub mod v1;
pub use v1::*;

// Note:
// TC3 + TC4 can be paired to make a 32-bit counter
// TC5 + TC6 can be paired to make a 32-bit counter

/// A generic hardware timer counter.
/// The counters are exposed in 16-bit mode only.
/// The hardware allows configuring the 8-bit mode
/// and pairing up some instances to run in 32-bit
/// mode, but that functionality is not currently
/// exposed by this hal implementation.
/// TimerCounter implements both the `Periodic` and
/// the `CountDown` embedded_hal timer traits.
/// Before a hardware timer can be used, it must first
/// have a clock configured.
pub struct TimerCounter<TC> {
freq: Hertz,
tc: TC,
}

/// This is a helper trait to make it easier to make most of the
/// TimerCounter impl generic. It doesn't make too much sense to
/// to try to implement this trait outside of this module.
pub trait Count16 {
fn count_16(&self) -> &COUNT16;
}

impl<TC> Periodic for TimerCounter<TC> {}
impl<TC> CountDown for TimerCounter<TC>
where
TC: Count16,
{
type Time = Nanoseconds;

fn start<T>(&mut self, timeout: T)
where
T: Into<Self::Time>,
{
let params = TimerParams::new_us(timeout.into(), self.freq);
let divider = params.divider;
let cycles = params.cycles;
let count = self.tc.count_16();

// Disable the timer while we reconfigure it
count.ctrla.modify(|_, w| w.enable().clear_bit());
while count.status.read().perbufv().bit_is_set() {}

// Now that we have a clock routed to the peripheral, we
// can ask it to perform a reset.
count.ctrla.write(|w| w.swrst().set_bit());

while count.status.read().perbufv().bit_is_set() {}
// the SVD erroneously marks swrst as write-only, so we
// need to manually read the bit here
while count.ctrla.read().bits() & 1 != 0 {}

count.ctrlbset.write(|w| {
// Count up when the direction bit is zero
w.dir().clear_bit();
// Periodic
w.oneshot().clear_bit()
});

// Set TOP value for mfrq mode
count.cc[0].write(|w| unsafe { w.cc().bits(cycles as u16) });

// Enable Match Frequency Waveform generation
count.wave.modify(|_, w| w.wavegen().mfrq());

count.ctrla.modify(|_, w| {
match divider {
1 => w.prescaler().div1(),
2 => w.prescaler().div2(),
4 => w.prescaler().div4(),
8 => w.prescaler().div8(),
16 => w.prescaler().div16(),
64 => w.prescaler().div64(),
256 => w.prescaler().div256(),
1024 => w.prescaler().div1024(),
_ => unreachable!(),
};
w.enable().set_bit();
w.runstdby().set_bit()
});
}

fn wait(&mut self) -> nb::Result<(), Void> {
let count = self.tc.count_16();
if count.intflag.read().ovf().bit_is_set() {
// Writing a 1 clears the flag
count.intflag.modify(|_, w| w.ovf().set_bit());
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
}

impl<TC> InterruptDrivenTimer for TimerCounter<TC>
where
TC: Count16,
{
/// Enable the interrupt generation for this hardware timer.
/// This method only sets the clock configuration to trigger
/// the interrupt; it does not configure the interrupt controller
/// or define an interrupt handler.
fn enable_interrupt(&mut self) {
self.tc.count_16().intenset.write(|w| w.ovf().set_bit());
}

/// Disables interrupt generation for this hardware timer.
/// This method only sets the clock configuration to prevent
/// triggering the interrupt; it does not configure the interrupt
/// controller.
fn disable_interrupt(&mut self) {
self.tc.count_16().intenclr.write(|w| w.ovf().set_bit());
}
}

macro_rules! tc {
($($TYPE:ident: ($TC:ident, $mclk:ident, $clock:ident, $apmask:ident),)+) => {
$(
pub type $TYPE = TimerCounter<$TC>;

impl Count16 for $TC {
fn count_16(&self) -> &COUNT16 {
self.count16()
}
}

impl TimerCounter<$TC>
{
/// Configure this timer counter instance.
/// The clock is obtained from the `GenericClockController` instance
/// and its frequency impacts the resolution and maximum range of
/// the timeout values that can be passed to the `start` method.
/// Note that some hardware timer instances share the same clock
/// generator instance and thus will be clocked at the same rate.
pub fn $mclk(clock: &clock::$clock, tc: $TC, mclk: &mut MCLK) -> Self {
// this is safe because we're constrained to just the tc3 bit
mclk.$apmask.modify(|_, w| w.$mclk().set_bit());
{
let count = tc.count16();

// Disable the timer while we reconfigure it
count.ctrla.modify(|_, w| w.enable().clear_bit());
while count.status.read().perbufv().bit_is_set() {}
}
Self {
freq: clock.freq(),
tc,
}
}
}
)+
}
}

tc! {
TimerCounter2: (TC2, tc2_, Tc2Tc3Clock, apbbmask),
TimerCounter3: (TC3, tc3_, Tc2Tc3Clock, apbbmask),
}

#[cfg(all(feature = "has-tc4", feature = "has-tc5"))]
tc! {
TimerCounter4: (TC4, tc4_, Tc4Tc5Clock, apbcmask),
TimerCounter5: (TC5, tc5_, Tc4Tc5Clock, apbcmask),
}
pub mod v2;
Loading

0 comments on commit 75ad5a5

Please sign in to comment.