aboutsummaryrefslogtreecommitdiff
path: root/rtic-monotonics/src/imxrt.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rtic-monotonics/src/imxrt.rs')
-rw-r--r--rtic-monotonics/src/imxrt.rs227
1 files changed, 113 insertions, 114 deletions
diff --git a/rtic-monotonics/src/imxrt.rs b/rtic-monotonics/src/imxrt.rs
index 2299bea..6d07be6 100644
--- a/rtic-monotonics/src/imxrt.rs
+++ b/rtic-monotonics/src/imxrt.rs
@@ -1,112 +1,158 @@
-//! [`Monotonic`] implementations for i.MX RT's GPT peripherals.
+//! [`Monotonic`](rtic_time::Monotonic) implementations for i.MX RT's GPT peripherals.
//!
//! # Example
//!
//! ```
-//! use rtic_monotonics::imxrt::*;
-//! use rtic_monotonics::imxrt::Gpt1 as Mono;
+//! use rtic_monotonics::imxrt::prelude::*;
+//! imxrt_gpt1_monotonic!(Mono, 1_000_000);
//!
//! fn init() {
-//! // Obtain ownership of the timer register block
+//! // Obtain ownership of the timer register block.
//! let gpt1 = unsafe { imxrt_ral::gpt::GPT1::instance() };
//!
-//! // Configure the timer clock source and determine its tick rate
-//! let timer_tickrate_hz = 1_000_000;
-//!
-//! // Generate timer token to ensure correct timer interrupt handler is used
-//! let token = rtic_monotonics::create_imxrt_gpt1_token!();
+//! // Configure the timer tick rate as specified earlier
+//! todo!("Configure the gpt1 peripheral to a tick rate of 1_000_000");
//!
//! // Start the monotonic
-//! Mono::start(timer_tickrate_hz, gpt1, token);
+//! Mono::start(gpt1);
//! }
//!
//! async fn usage() {
//! loop {
//! // Use the monotonic
-//! let timestamp = Mono::now().ticks();
+//! let timestamp = Mono::now();
//! Mono::delay(100.millis()).await;
//! }
//! }
//! ```
-use crate::{Monotonic, TimeoutError, TimerQueue};
use atomic_polyfill::{AtomicU32, Ordering};
-pub use fugit::{self, ExtU64, ExtU64Ceil};
-use rtic_time::half_period_counter::calculate_now;
-
-use imxrt_ral as ral;
-
-const TIMER_HZ: u32 = 1_000_000;
+use rtic_time::{
+ half_period_counter::calculate_now,
+ timer_queue::{TimerQueue, TimerQueueBackend},
+};
+
+pub use imxrt_ral as ral;
+
+/// Common definitions and traits for using the i.MX RT monotonics
+pub mod prelude {
+ #[cfg(feature = "imxrt_gpt1")]
+ pub use crate::imxrt_gpt1_monotonic;
+ #[cfg(feature = "imxrt_gpt2")]
+ pub use crate::imxrt_gpt2_monotonic;
+
+ pub use crate::Monotonic;
+ pub use fugit::{self, ExtU64, ExtU64Ceil};
+}
#[doc(hidden)]
#[macro_export]
macro_rules! __internal_create_imxrt_timer_interrupt {
- ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{
+ ($mono_backend:ident, $timer:ident) => {
#[no_mangle]
#[allow(non_snake_case)]
unsafe extern "C" fn $timer() {
- $crate::imxrt::$mono_timer::__tq().on_monotonic_interrupt();
+ use $crate::TimerQueueBackend;
+ $crate::imxrt::$mono_backend::timer_queue().on_monotonic_interrupt();
}
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __internal_create_imxrt_timer_struct {
+ ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => {
+ /// A `Monotonic` based on the GPT peripheral.
+ struct $name;
+
+ impl $name {
+ /// Starts the `Monotonic`.
+ ///
+ /// This method must be called only once.
+ pub fn start(gpt: $crate::imxrt::ral::gpt::$timer) {
+ $crate::__internal_create_imxrt_timer_interrupt!($mono_backend, $timer);
- pub struct $timer_token;
+ $crate::imxrt::$mono_backend::_start(gpt);
+ }
+ }
- unsafe impl $crate::InterruptToken<$crate::imxrt::$mono_timer> for $timer_token {}
+ impl $crate::TimerQueueBasedMonotonic for $name {
+ type Backend = $crate::imxrt::$mono_backend;
+ type Instant = $crate::fugit::Instant<
+ <Self::Backend as $crate::TimerQueueBackend>::Ticks,
+ 1,
+ { $tick_rate_hz },
+ >;
+ type Duration = $crate::fugit::Duration<
+ <Self::Backend as $crate::TimerQueueBackend>::Ticks,
+ 1,
+ { $tick_rate_hz },
+ >;
+ }
- $timer_token
- }};
+ $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
+ $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
+ };
}
-/// Register the GPT1 interrupt for the monotonic.
+/// Create a GPT1 based monotonic and register the GPT1 interrupt for it.
+///
+/// See [`crate::imxrt`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
+/// * `tick_rate_hz` - The tick rate of the timer peripheral. It's the user's responsibility
+/// to configure the peripheral to the given frequency before starting the
+/// monotonic.
#[cfg(feature = "imxrt_gpt1")]
#[macro_export]
-macro_rules! create_imxrt_gpt1_token {
- () => {{
- $crate::__internal_create_imxrt_timer_interrupt!(Gpt1, GPT1, Gpt1Token)
- }};
+macro_rules! imxrt_gpt1_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_imxrt_timer_struct!($name, Gpt1Backend, GPT1, $tick_rate_hz);
+ };
}
-/// Register the GPT2 interrupt for the monotonic.
+/// Create a GPT2 based monotonic and register the GPT2 interrupt for it.
+///
+/// See [`crate::imxrt`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
+/// * `tick_rate_hz` - The tick rate of the timer peripheral. It's the user's responsibility
+/// to configure the peripheral to the given frequency before starting the
+/// monotonic.
#[cfg(feature = "imxrt_gpt2")]
#[macro_export]
-macro_rules! create_imxrt_gpt2_token {
- () => {{
- $crate::__internal_create_imxrt_timer_interrupt!(Gpt2, GPT2, Gpt2Token)
- }};
+macro_rules! imxrt_gpt2_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_imxrt_timer_struct!($name, Gpt2Backend, GPT2, $tick_rate_hz);
+ };
}
macro_rules! make_timer {
- ($mono_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
- /// Timer implementing [`Monotonic`] which runs at 1 MHz.
+ ($mono_name:ident, $backend_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
+ /// GPT based [`TimerQueueBackend`].
$(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)?
- pub struct $mono_name;
+ pub struct $backend_name;
use ral::gpt::$timer;
/// Number of 2^31 periods elapsed since boot.
static $period: AtomicU32 = AtomicU32::new(0);
- static $tq: TimerQueue<$mono_name> = TimerQueue::new();
+ static $tq: TimerQueue<$backend_name> = TimerQueue::new();
- impl $mono_name {
- /// Starts the monotonic timer.
+ impl $backend_name {
+ /// Starts the timer.
///
- /// - `tick_freq_hz`: The tick frequency of the given timer.
- /// - `gpt`: The GPT timer register block instance.
- /// - `_interrupt_token`: Required for correct timer interrupt handling.
+ /// **Do not use this function directly.**
///
- /// This method must be called only once.
- pub fn start(tick_freq_hz: u32, gpt: $timer, _interrupt_token: impl crate::InterruptToken<Self>) {
- // Find a prescaler that creates our desired tick frequency
- let previous_prescaler = ral::read_reg!(ral::gpt, gpt, PR, PRESCALER) + 1;
- let previous_clock_freq = tick_freq_hz * previous_prescaler;
- assert!((previous_clock_freq % TIMER_HZ) == 0,
- "Unable to find a fitting prescaler value!\n Input: {}/{}\n Desired: {}",
- previous_clock_freq, previous_prescaler, TIMER_HZ);
- let prescaler = previous_clock_freq / TIMER_HZ;
- assert!(prescaler > 0);
- assert!(prescaler <= 4096);
+ /// Use the prelude macros instead.
+ pub fn _start(gpt: $timer) {
// Disable the timer.
ral::modify_reg!(ral::gpt, gpt, CR, EN: 0);
@@ -122,11 +168,6 @@ macro_rules! make_timer {
// Reset period
$period.store(0, Ordering::SeqCst);
- // Prescaler
- ral::modify_reg!(ral::gpt, gpt, PR,
- PRESCALER: (prescaler - 1), // Scale to our desired clock rate
- );
-
// Enable interrupts
ral::write_reg!(ral::gpt, gpt, IR,
ROVIE: 1, // Rollover interrupt
@@ -150,7 +191,6 @@ macro_rules! make_timer {
ENMOD: 0, // Keep state when disabled
);
-
// SAFETY: We take full ownership of the peripheral and interrupt vector,
// plus we are not using any external shared resources so we won't impact
// basepri/source masking based critical sections.
@@ -159,65 +199,21 @@ macro_rules! make_timer {
cortex_m::peripheral::NVIC::unmask(ral::Interrupt::$timer);
}
}
-
- /// Used to access the underlying timer queue
- #[doc(hidden)]
- pub fn __tq() -> &'static TimerQueue<$mono_name> {
- &$tq
- }
-
- /// Delay for some duration of time.
- #[inline]
- pub async fn delay(duration: <Self as Monotonic>::Duration) {
- $tq.delay(duration).await;
- }
-
- /// Timeout at a specific time.
- pub async fn timeout_at<F: core::future::Future>(
- instant: <Self as rtic_time::Monotonic>::Instant,
- future: F,
- ) -> Result<F::Output, TimeoutError> {
- $tq.timeout_at(instant, future).await
- }
-
- /// Timeout after a specific duration.
- #[inline]
- pub async fn timeout_after<F: core::future::Future>(
- duration: <Self as Monotonic>::Duration,
- future: F,
- ) -> Result<F::Output, TimeoutError> {
- $tq.timeout_after(duration, future).await
- }
-
- /// Delay to some specific time instant.
- #[inline]
- pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
- $tq.delay_until(instant).await;
- }
}
- rtic_time::embedded_hal_delay_impl_fugit64!($mono_name);
-
- #[cfg(feature = "embedded-hal-async")]
- rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name);
-
- impl Monotonic for $mono_name {
- type Instant = fugit::TimerInstantU64<TIMER_HZ>;
- type Duration = fugit::TimerDurationU64<TIMER_HZ>;
+ impl TimerQueueBackend for $backend_name {
+ type Ticks = u64;
- const ZERO: Self::Instant = Self::Instant::from_ticks(0);
- const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
-
- fn now() -> Self::Instant {
+ fn now() -> Self::Ticks {
let gpt = unsafe{ $timer::instance() };
- Self::Instant::from_ticks(calculate_now(
+ calculate_now(
|| $period.load(Ordering::Relaxed),
|| ral::read_reg!(ral::gpt, gpt, CNT)
- ))
+ )
}
- fn set_compare(instant: Self::Instant) {
+ fn set_compare(instant: Self::Ticks) {
let gpt = unsafe{ $timer::instance() };
// Set the timer regardless of whether it is multiple periods in the future,
@@ -225,8 +221,7 @@ macro_rules! make_timer {
// The worst thing that can happen is a spurious wakeup, and with a timer
// period of half an hour, this is hardly a problem.
- let ticks = instant.duration_since_epoch().ticks();
- let ticks_wrapped = ticks as u32;
+ let ticks_wrapped = instant as u32;
ral::write_reg!(ral::gpt, gpt, OCR[1], ticks_wrapped);
}
@@ -257,12 +252,16 @@ macro_rules! make_timer {
assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
}
}
+
+ fn timer_queue() -> &'static TimerQueue<Self> {
+ &$tq
+ }
}
};
}
#[cfg(feature = "imxrt_gpt1")]
-make_timer!(Gpt1, GPT1, GPT1_HALFPERIODS, GPT1_TQ);
+make_timer!(Gpt1, Gpt1Backend, GPT1, GPT1_HALFPERIODS, GPT1_TQ);
#[cfg(feature = "imxrt_gpt2")]
-make_timer!(Gpt2, GPT2, GPT2_HALFPERIODS, GPT2_TQ);
+make_timer!(Gpt2, Gpt2Backend, GPT2, GPT2_HALFPERIODS, GPT2_TQ);