aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFinomnis <Finomnis@users.noreply.github.com>2024-04-11 00:00:38 +0200
committerGitHub <noreply@github.com>2024-04-10 22:00:38 +0000
commit8c23e178f3838bcdd13662a2ffefd39ec144e869 (patch)
treef2d2cefcd6bb2876e74ee6035b5489a4a2d9590f
parente4cc5fd17b8a2df332af0ee25c8bd7092e66afb0 (diff)
Monotonic rewrite (#874)
* Rework timer_queue and monotonic architecture Goals: * make Monotonic purely internal * make Monotonic purely tick passed, no fugit involved * create a wrapper struct in the user's code via a macro that then converts the "now" from the tick based monotonic to a fugit based timestamp We need to proxy the delay functions of the timer queue anyway, so we could simply perform the conversion in those proxy functions. * Update cargo.lock * Update readme of rtic-time * CI: ESP32: Redact esp_image: Too volatile * Fixup: Changelog double entry rebase mistake --------- Co-authored-by: Henrik Tjäder <henrik@tjaders.com>
-rw-r--r--.github/workflows/build.yml2
-rw-r--r--ci/expected/esp32c3/sw_and_hw.run8
-rw-r--r--examples/embassy-stm32g4/src/bin/blinky.rs9
-rwxr-xr-xexamples/esp32c3/runner.sh4
-rw-r--r--examples/lm3s6965/Cargo.lock9
-rw-r--r--examples/lm3s6965/examples/async-delay.rs13
-rw-r--r--examples/lm3s6965/examples/async-timeout.rs25
-rw-r--r--examples/nrf52840_blinky/Cargo.lock40
-rw-r--r--examples/nrf52840_blinky/Cargo.toml2
-rw-r--r--examples/nrf52840_blinky/src/bin/blinky_rtc.rs10
-rw-r--r--examples/nrf52840_blinky/src/bin/blinky_timer.rs10
-rw-r--r--examples/rp2040_local_i2c_init/Cargo.lock185
-rw-r--r--examples/rp2040_local_i2c_init/Cargo.toml4
-rw-r--r--examples/rp2040_local_i2c_init/src/main.rs50
-rw-r--r--examples/stm32f3_blinky/Cargo.lock118
-rw-r--r--examples/stm32f3_blinky/Cargo.toml12
-rw-r--r--examples/stm32f3_blinky/src/main.rs9
-rw-r--r--examples/stm32g030f6_periodic_prints/.cargo/config.toml24
-rw-r--r--examples/stm32g030f6_periodic_prints/.vscode/settings.json9
-rw-r--r--examples/stm32g030f6_periodic_prints/Cargo.lock511
-rw-r--r--examples/stm32g030f6_periodic_prints/Cargo.toml48
-rw-r--r--examples/stm32g030f6_periodic_prints/README.md31
-rw-r--r--examples/stm32g030f6_periodic_prints/memory.x6
-rw-r--r--examples/stm32g030f6_periodic_prints/src/main.rs57
-rw-r--r--examples/teensy4_blinky/Cargo.lock22
-rw-r--r--examples/teensy4_blinky/Cargo.toml2
-rw-r--r--examples/teensy4_blinky/examples/with_logs.rs8
-rw-r--r--examples/teensy4_blinky/src/main.rs7
-rw-r--r--rtic-monotonics/CHANGELOG.md4
-rw-r--r--rtic-monotonics/Cargo.toml15
-rw-r--r--rtic-monotonics/src/imxrt.rs227
-rw-r--r--rtic-monotonics/src/lib.rs18
-rw-r--r--rtic-monotonics/src/nrf/rtc.rs255
-rw-r--r--rtic-monotonics/src/nrf/timer.rs285
-rw-r--r--rtic-monotonics/src/rp2040.rs184
-rw-r--r--rtic-monotonics/src/stm32.rs309
-rw-r--r--rtic-monotonics/src/systick.rs236
-rw-r--r--rtic-time/CHANGELOG.md9
-rw-r--r--rtic-time/Cargo.toml10
-rw-r--r--rtic-time/README.md29
-rw-r--r--rtic-time/src/lib.rs301
-rw-r--r--rtic-time/src/monotonic.rs240
-rw-r--r--rtic-time/src/monotonic/embedded_hal_macros.rs77
-rw-r--r--rtic-time/src/monotonic/timer_queue_based_monotonic.rs113
-rw-r--r--rtic-time/src/timer_queue.rs281
-rw-r--r--rtic-time/src/timer_queue/backend.rs44
-rw-r--r--rtic-time/src/timer_queue/tick_type.rs49
-rw-r--r--rtic-time/tests/delay_precision_subtick.rs101
-rw-r--r--rtic-time/tests/timer_queue.rs194
-rw-r--r--rtic/CHANGELOG.md4
-rw-r--r--rtic/Cargo.toml6
-rw-r--r--rtic/src/lib.rs1
-rw-r--r--xtask/src/argument_parsing.rs37
-rw-r--r--xtask/src/run.rs21
54 files changed, 2623 insertions, 1662 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4136b4c..81ba3c7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -463,7 +463,7 @@ jobs:
which qemu-system-riscv32
- name: Run-pass tests
- run: cargo xtask -vvv --platform esp32-c3 qemu
+ run: cargo xtask -v --platform esp32-c3 qemu
# Run test suite
tests:
diff --git a/ci/expected/esp32c3/sw_and_hw.run b/ci/expected/esp32c3/sw_and_hw.run
index 0effdbd..9421a9c 100644
--- a/ci/expected/esp32c3/sw_and_hw.run
+++ b/ci/expected/esp32c3/sw_and_hw.run
@@ -22,10 +22,10 @@ entry 0x403cc710
I (1) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (1) boot: 2 factory factory app 00 00 00010000 003f0000
I (1) boot: End of partition table
-I (1) esp_image: segment 0: paddr=00010020 vaddr=3c010020 size=022e4h ( 8932) map
-I (3) esp_image: segment 1: paddr=0001230c vaddr=40380000 size=01250h ( 4688) load
-I (3) esp_image: segment 2: paddr=00013564 vaddr=00000000 size=0cab4h ( 51892) 
-I (8) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=05db4h ( 23988) map
+I (1) esp_image: REDACTED
+I (3) esp_image: REDACTED
+I (3) esp_image: REDACTED
+I (8) esp_image: REDACTED
I (11) boot: Loaded app from partition at offset 0x10000
I (11) boot: Disabling RNG early entropy source...
init
diff --git a/examples/embassy-stm32g4/src/bin/blinky.rs b/examples/embassy-stm32g4/src/bin/blinky.rs
index 6855b1e..617aa66 100644
--- a/examples/embassy-stm32g4/src/bin/blinky.rs
+++ b/examples/embassy-stm32g4/src/bin/blinky.rs
@@ -5,9 +5,11 @@
use embassy_stm32::gpio::{Level, Output, Speed};
use rtic::app;
-use rtic_monotonics::systick::*;
+use rtic_monotonics::systick::prelude::*;
use {defmt_rtt as _, panic_probe as _};
+systick_monotonic!(Mono, 1_000);
+
pub mod pac {
pub use embassy_stm32::pac::Interrupt as interrupt;
pub use embassy_stm32::pac::*;
@@ -26,8 +28,7 @@ mod app {
#[init]
fn init(cx: init::Context) -> (Shared, Local) {
// Initialize the systick interrupt & obtain the token to prove that we did
- let systick_mono_token = rtic_monotonics::create_systick_token!();
- Systick::start(cx.core.SYST, 25_000_000, systick_mono_token);
+ Mono::start(cx.core.SYST, 25_000_000);
let p = embassy_stm32::init(Default::default());
defmt::info!("Hello World!");
@@ -53,7 +54,7 @@ mod app {
led.set_low();
}
state = !state;
- Systick::delay(1000.millis()).await;
+ Mono::delay(1000.millis()).await;
}
}
}
diff --git a/examples/esp32c3/runner.sh b/examples/esp32c3/runner.sh
index 8b61068..3310aca 100755
--- a/examples/esp32c3/runner.sh
+++ b/examples/esp32c3/runner.sh
@@ -26,4 +26,6 @@ sleep 3s
# Kill QEMU nicely by sending 'q' (quit) over tcp
echo q | nc -N 127.0.0.1 55555
-cat "$logfile"
+# Output that will be compared, remove the esp_image segments as they change
+# between runs
+cat "$logfile" | sed 's/esp_image: .*$/esp_image: REDACTED/'
diff --git a/examples/lm3s6965/Cargo.lock b/examples/lm3s6965/Cargo.lock
index 076a156..848a06f 100644
--- a/examples/lm3s6965/Cargo.lock
+++ b/examples/lm3s6965/Cargo.lock
@@ -362,7 +362,6 @@ dependencies = [
"critical-section",
"rtic-core",
"rtic-macros",
- "rtic-monotonics",
]
[[package]]
@@ -392,12 +391,11 @@ dependencies = [
[[package]]
name = "rtic-monotonics"
-version = "1.5.0"
+version = "2.0.0"
dependencies = [
"atomic-polyfill",
"cfg-if",
"cortex-m",
- "embedded-hal 1.0.0",
"fugit",
"rtic-time",
]
@@ -417,9 +415,12 @@ dependencies = [
[[package]]
name = "rtic-time"
-version = "1.3.0"
+version = "2.0.0"
dependencies = [
"critical-section",
+ "embedded-hal 1.0.0",
+ "embedded-hal-async",
+ "fugit",
"futures-util",
"rtic-common",
]
diff --git a/examples/lm3s6965/examples/async-delay.rs b/examples/lm3s6965/examples/async-delay.rs
index 9ccfc02..0f151cb 100644
--- a/examples/lm3s6965/examples/async-delay.rs
+++ b/examples/lm3s6965/examples/async-delay.rs
@@ -11,7 +11,9 @@ use panic_semihosting as _;
#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)]
mod app {
use cortex_m_semihosting::{debug, hprintln};
- use rtic_monotonics::systick::*;
+ use rtic_monotonics::systick::prelude::*;
+
+ systick_monotonic!(Mono, 100);
#[shared]
struct Shared {}
@@ -23,8 +25,7 @@ mod app {
fn init(cx: init::Context) -> (Shared, Local) {
hprintln!("init");
- let systick_token = rtic_monotonics::create_systick_token!();
- Systick::start(cx.core.SYST, 12_000_000, systick_token);
+ Mono::start(cx.core.SYST, 12_000_000);
foo::spawn().ok();
bar::spawn().ok();
@@ -36,21 +37,21 @@ mod app {
#[task]
async fn foo(_cx: foo::Context) {
hprintln!("hello from foo");
- Systick::delay(100.millis()).await;
+ Mono::delay(100.millis()).await;
hprintln!("bye from foo");
}
#[task]
async fn bar(_cx: bar::Context) {
hprintln!("hello from bar");
- Systick::delay(200.millis()).await;
+ Mono::delay(200.millis()).await;
hprintln!("bye from bar");
}
#[task]
async fn baz(_cx: baz::Context) {
hprintln!("hello from baz");
- Systick::delay(300.millis()).await;
+ Mono::delay(300.millis()).await;
hprintln!("bye from baz");
debug::exit(debug::EXIT_SUCCESS);
diff --git a/examples/lm3s6965/examples/async-timeout.rs b/examples/lm3s6965/examples/async-timeout.rs
index e5e129f..169b132 100644
--- a/examples/lm3s6965/examples/async-timeout.rs
+++ b/examples/lm3s6965/examples/async-timeout.rs
@@ -8,13 +8,13 @@
use cortex_m_semihosting::{debug, hprintln};
use panic_semihosting as _;
-use rtic_monotonics::systick::*;
+use rtic_monotonics::systick::prelude::*;
+systick_monotonic!(Mono, 100);
#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)]
mod app {
use super::*;
use futures::{future::FutureExt, select_biased};
- use rtic_monotonics::Monotonic;
#[shared]
struct Shared {}
@@ -27,8 +27,7 @@ mod app {
fn init(cx: init::Context) -> (Shared, Local) {
hprintln!("init");
- let systick_token = rtic_monotonics::create_systick_token!();
- Systick::start(cx.core.SYST, 12_000_000, systick_token);
+ Mono::start(cx.core.SYST, 12_000_000);
// ANCHOR_END: init
foo::spawn().ok();
@@ -42,19 +41,19 @@ mod app {
// Call hal with short relative timeout using `select_biased`
select_biased! {
v = hal_get(1).fuse() => hprintln!("hal returned {}", v),
- _ = Systick::delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first
+ _ = Mono::delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first
}
// Call hal with long relative timeout using `select_biased`
select_biased! {
v = hal_get(1).fuse() => hprintln!("hal returned {}", v), // hal finish first
- _ = Systick::delay(1000.millis()).fuse() => hprintln!("timeout", ),
+ _ = Mono::delay(1000.millis()).fuse() => hprintln!("timeout", ),
}
// ANCHOR_END: select_biased
// ANCHOR: timeout_after_basic
// Call hal with long relative timeout using monotonic `timeout_after`
- match Systick::timeout_after(1000.millis(), hal_get(1)).await {
+ match Mono::timeout_after(1000.millis(), hal_get(1)).await {
Ok(v) => hprintln!("hal returned {}", v),
_ => hprintln!("timeout"),
}
@@ -62,20 +61,20 @@ mod app {
// ANCHOR: timeout_at_basic
// get the current time instance
- let mut instant = Systick::now();
+ let mut instant = Mono::now();
// do this 3 times
for n in 0..3 {
// absolute point in time without drift
instant += 1000.millis();
- Systick::delay_until(instant).await;
+ Mono::delay_until(instant).await;
// absolute point in time for timeout
let timeout = instant + 500.millis();
- hprintln!("now is {:?}, timeout at {:?}", Systick::now(), timeout);
+ hprintln!("now is {:?}, timeout at {:?}", Mono::now(), timeout);
- match Systick::timeout_at(timeout, hal_get(n)).await {
- Ok(v) => hprintln!("hal returned {} at time {:?}", v, Systick::now()),
+ match Mono::timeout_at(timeout, hal_get(n)).await {
+ Ok(v) => hprintln!("hal returned {} at time {:?}", v, Mono::now()),
_ => hprintln!("timeout"),
}
}
@@ -90,7 +89,7 @@ async fn hal_get(n: u32) -> u32 {
// emulate some delay time dependent on n
let d = 350.millis() + n * 100.millis();
hprintln!("the hal takes a duration of {:?}", d);
- Systick::delay(d).await;
+ Mono::delay(d).await;
// emulate some return value
5
}
diff --git a/examples/nrf52840_blinky/Cargo.lock b/examples/nrf52840_blinky/Cargo.lock
index a3d7542..ec5f4f9 100644
--- a/examples/nrf52840_blinky/Cargo.lock
+++ b/examples/nrf52840_blinky/Cargo.lock
@@ -136,7 +136,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.51",
]
[[package]]
@@ -179,9 +179,18 @@ dependencies = [
[[package]]
name = "embedded-hal"
-version = "1.0.0-rc.2"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
+
+[[package]]
+name = "embedded-hal-async"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e57ec6ad0bc8eb967cf9c9f144177f5e8f2f6f02dad0b8b683f9f05f6b22def"
+checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
+dependencies = [
+ "embedded-hal 1.0.0",
+]
[[package]]
name = "embedded-storage"
@@ -412,18 +421,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.70"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.33"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -468,18 +477,18 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn 2.0.51",
]
[[package]]
name = "rtic-monotonics"
-version = "1.4.0"
+version = "2.0.0"
dependencies = [
"atomic-polyfill",
"cfg-if",
"cortex-m",
"critical-section",
- "embedded-hal 1.0.0-rc.2",
+ "embedded-hal 1.0.0",
"fugit",
"nrf52840-pac",
"rtic-time",
@@ -487,9 +496,12 @@ dependencies = [
[[package]]
name = "rtic-time"
-version = "1.1.0"
+version = "2.0.0"
dependencies = [
"critical-section",
+ "embedded-hal 1.0.0",
+ "embedded-hal-async",
+ "fugit",
"futures-util",
"rtic-common",
]
@@ -537,9 +549,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.39"
+version = "2.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
dependencies = [
"proc-macro2",
"quote",
@@ -563,7 +575,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.51",
]
[[package]]
diff --git a/examples/nrf52840_blinky/Cargo.toml b/examples/nrf52840_blinky/Cargo.toml
index 8b13649..5569019 100644
--- a/examples/nrf52840_blinky/Cargo.toml
+++ b/examples/nrf52840_blinky/Cargo.toml
@@ -26,7 +26,7 @@ features = ["thumbv7-backend"]
[dependencies.rtic-monotonics]
path = "../../rtic-monotonics"
-version = "1.4.0"
+version = "2.0.0"
features = ["nrf52840"]
# cargo build/run
diff --git a/examples/nrf52840_blinky/src/bin/blinky_rtc.rs b/examples/nrf52840_blinky/src/bin/blinky_rtc.rs
index dbdd0b0..fa654b8 100644
--- a/examples/nrf52840_blinky/src/bin/blinky_rtc.rs
+++ b/examples/nrf52840_blinky/src/bin/blinky_rtc.rs
@@ -5,6 +5,9 @@
use nrf52840_blinky::hal;
+use rtic_monotonics::nrf::rtc::prelude::*;
+nrf_rtc0_monotonic!(Mono);
+
#[rtic::app(device = hal::pac, dispatchers = [SWI0_EGU0])]
mod app {
use super::*;
@@ -12,10 +15,6 @@ mod app {
use hal::gpio::{Level, Output, Pin, PushPull};
use hal::prelude::*;
- use rtic_monotonics::nrf::rtc::Rtc0 as Mono;
- use rtic_monotonics::nrf::rtc::*;
- use rtic_monotonics::Monotonic;
-
#[shared]
struct Shared {}
@@ -30,8 +29,7 @@ mod app {
hal::clocks::Clocks::new(cx.device.CLOCK).start_lfclk();
// Initialize Monotonic
- let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!();
- Mono::start(cx.device.RTC0, token);
+ Mono::start(cx.device.RTC0);
// Setup LED
let port0 = hal::gpio::p0::Parts::new(cx.device.P0);
diff --git a/examples/nrf52840_blinky/src/bin/blinky_timer.rs b/examples/nrf52840_blinky/src/bin/blinky_timer.rs
index c1342f4..ebb20fc 100644
--- a/examples/nrf52840_blinky/src/bin/blinky_timer.rs
+++ b/examples/nrf52840_blinky/src/bin/blinky_timer.rs
@@ -5,6 +5,9 @@
use nrf52840_blinky::hal;
+use rtic_monotonics::nrf::timer::prelude::*;
+nrf_timer0_monotonic!(Mono, 8_000_000);
+
#[rtic::app(device = hal::pac, dispatchers = [SWI0_EGU0])]
mod app {
use super::*;
@@ -12,10 +15,6 @@ mod app {
use hal::gpio::{Level, Output, Pin, PushPull};
use hal::prelude::*;
- use rtic_monotonics::nrf::timer::Timer0 as Mono;
- use rtic_monotonics::nrf::timer::*;
- use rtic_monotonics::Monotonic;
-
#[shared]
struct Shared {}
@@ -27,8 +26,7 @@ mod app {
#[init]
fn init(cx: init::Context) -> (Shared, Local) {
// Initialize Monotonic
- let token = rtic_monotonics::create_nrf_timer0_monotonic_token!();
- Mono::start(cx.device.TIMER0, token);
+ Mono::start(cx.device.TIMER0);
// Setup LED
let port0 = hal::gpio::p0::Parts::new(cx.device.P0);
diff --git a/examples/rp2040_local_i2c_init/Cargo.lock b/examples/rp2040_local_i2c_init/Cargo.lock
index d40e616..d4c0155 100644
--- a/examples/rp2040_local_i2c_init/Cargo.lock
+++ b/examples/rp2040_local_i2c_init/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "atomic-polyfill"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c314e70d181aa6053b26e3f7fbf86d1dfff84f816a6175b967666b3506ef7289"
+checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
@@ -52,7 +52,7 @@ checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
dependencies = [
"bare-metal 0.2.5",
"bitfield",
- "embedded-hal",
+ "embedded-hal 0.2.7",
"volatile-register",
]
@@ -73,23 +73,23 @@ checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
name = "crc-any"
-version = "2.4.3"
+version = "2.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "774646b687f63643eb0f4bf13dc263cb581c8c9e57973b6ddf78bda3994d88df"
+checksum = "c01a5e1f881f6fb6099a7bdf949e946719fd4f1fefa56264890574febf0eb6d0"
dependencies = [
"debug-helper",
]
[[package]]
name = "critical-section"
-version = "1.1.1"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52"
+checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "debug-helper"
@@ -99,9 +99,9 @@ checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e"
[[package]]
name = "either"
-version = "1.8.1"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
[[package]]
name = "embedded-dma"
@@ -123,10 +123,64 @@ dependencies = [
]
[[package]]
-name = "equivalent"
+name = "embedded-hal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
+
+[[package]]
+name = "embedded-hal-async"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
+checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
+dependencies = [
+ "embedded-hal 1.0.0",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "frunk"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11a351b59e12f97b4176ee78497dff72e4276fb1ceb13e19056aca7fa0206287"
+dependencies = [
+ "frunk_core",
+ "frunk_derives",
+]
+
+[[package]]
+name = "frunk_core"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af2469fab0bd07e64ccf0ad57a1438f63160c69b2e57f04a439653d68eb558d6"
+
+[[package]]
+name = "frunk_derives"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e"
+dependencies = [
+ "frunk_proc_macro_helpers",
+ "quote",
+ "syn 2.0.51",
+]
+
+[[package]]
+name = "frunk_proc_macro_helpers"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35b54add839292b743aeda6ebedbd8b11e93404f902c56223e51b9ec18a13d2c"
+dependencies = [
+ "frunk_core",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.51",
+]
[[package]]
name = "fugit"
@@ -139,21 +193,21 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-task"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-core",
"futures-task",
@@ -169,15 +223,15 @@ checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]]
name = "hashbrown"
-version = "0.14.0"
+version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
-version = "2.0.0"
+version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
dependencies = [
"equivalent",
"hashbrown",
@@ -224,7 +278,7 @@ checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
@@ -238,15 +292,15 @@ dependencies = [
[[package]]
name = "paste"
-version = "1.0.12"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "pin-project-lite"
-version = "0.2.9"
+version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pin-utils"
@@ -266,6 +320,12 @@ dependencies = [
]
[[package]]
+name = "portable-atomic"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+
+[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -274,7 +334,7 @@ dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
"version_check",
]
@@ -291,18 +351,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.63"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.29"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -315,11 +375,10 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rp-pico"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aab28f6f4e19cec2d61b64cdd685e69794b81c579fd3b765579c46018fe616d0"
+checksum = "6341771e6f8e5d130b2b3cbc23435b7847761adf198af09f4b2a60407d43bd56"
dependencies = [
- "cortex-m",
"cortex-m-rt",
"fugit",
"rp2040-boot2",
@@ -329,23 +388,24 @@ dependencies = [
[[package]]
name = "rp2040-boot2"
-version = "0.2.1"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c773ec49b836077aa144b58dc7654a243e1eecdb6cf0d25361ae7c7600fabd8"
+checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21"
dependencies = [
"crc-any",
]
[[package]]
name = "rp2040-hal"
-version = "0.8.2"
+version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1369bb84862d7f69391a96606b2f29a00bfce7f29a749e23d5f01fc3f607ada0"
+checksum = "1ff2b9ae7e6dd6720fd9f64250c9087260e50fe98b6b032ccca65be3581167ca"
dependencies = [
"cortex-m",
"critical-section",
"embedded-dma",
- "embedded-hal",
+ "embedded-hal 0.2.7",
+ "frunk",
"fugit",
"itertools",
"nb 1.1.0",
@@ -368,17 +428,18 @@ dependencies = [
"cortex-m-rt",
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.109",
]
[[package]]
name = "rp2040-pac"
-version = "0.4.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9192cafbb40d717c9e0ddf767aaf9c69fee1b4e48d22ed853b57b11f6d9f3d7e"
+checksum = "12d9d8375815f543f54835d01160d4e47f9e2cae75f17ff8f1ec19ce1da96e4c"
dependencies = [
"cortex-m",
"cortex-m-rt",
+ "critical-section",
"vcell",
]
@@ -387,7 +448,7 @@ name = "rp2040_local_i2c_init"
version = "0.1.0"
dependencies = [
"cortex-m",
- "embedded-hal",
+ "embedded-hal 0.2.7",
"fugit",
"panic-probe",
"rp-pico",
@@ -397,7 +458,7 @@ dependencies = [
[[package]]
name = "rtic"
-version = "2.0.0"
+version = "2.0.1"
dependencies = [
"atomic-polyfill",
"bare-metal 1.0.0",
@@ -409,9 +470,10 @@ dependencies = [
[[package]]
name = "rtic-common"
-version = "1.0.0"
+version = "1.0.1"
dependencies = [
"critical-section",
+ "portable-atomic",
]
[[package]]
@@ -422,22 +484,23 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
[[package]]
name = "rtic-macros"
-version = "2.0.0"
+version = "2.0.1"
dependencies = [
"indexmap",
"proc-macro-error",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.51",
]
[[package]]
name = "rtic-monotonics"
-version = "1.0.0"
+version = "2.0.0"
dependencies = [
"atomic-polyfill",
"cfg-if",
"cortex-m",
+ "embedded-hal 1.0.0",
"fugit",
"rp2040-pac",
"rtic-time",
@@ -445,9 +508,12 @@ dependencies = [
[[package]]
name = "rtic-time"
-version = "1.0.0"
+version = "2.0.0"
dependencies = [
"critical-section",
+ "embedded-hal 1.0.0",
+ "embedded-hal-async",
+ "fugit",
"futures-util",
"rtic-common",
]
@@ -494,10 +560,21 @@ dependencies = [
]
[[package]]
+name = "syn"
+version = "2.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
name = "unicode-ident"
-version = "1.0.9"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "usb-device"
@@ -525,9 +602,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "volatile-register"
-version = "0.2.1"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6"
+checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
dependencies = [
"vcell",
]
diff --git a/examples/rp2040_local_i2c_init/Cargo.toml b/examples/rp2040_local_i2c_init/Cargo.toml
index 6e501b6..300bede 100644
--- a/examples/rp2040_local_i2c_init/Cargo.toml
+++ b/examples/rp2040_local_i2c_init/Cargo.toml
@@ -15,14 +15,14 @@ features = ["thumbv6-backend"]
[dependencies.rtic-monotonics]
path = "../../rtic-monotonics"
-version = "1.0.0"
+version = "2.0.0"
features = ["rp2040"]
[dependencies]
cortex-m = "0.7"
embedded-hal = { version = "0.2.7", features = ["unproven"] }
fugit = "0.3"
-rp-pico = "0.7.0"
+rp-pico = "0.8.0"
panic-probe = "0.3"
[profile.dev]
diff --git a/examples/rp2040_local_i2c_init/src/main.rs b/examples/rp2040_local_i2c_init/src/main.rs
index 97049dc..799376c 100644
--- a/examples/rp2040_local_i2c_init/src/main.rs
+++ b/examples/rp2040_local_i2c_init/src/main.rs
@@ -1,15 +1,21 @@
#![no_std]
#![no_main]
-#[rtic::app(
- device = rp_pico::hal::pac,
- dispatchers = [TIMER_IRQ_1]
-)]
+use rtic_monotonics::rp2040::prelude::*;
+
+rp2040_timer_monotonic!(Mono);
+
+#[rtic::app(device = rp_pico::hal::pac)]
mod app {
+ use super::*;
+
use rp_pico::hal::{
- clocks, gpio,
- gpio::pin::bank0::{Gpio2, Gpio25, Gpio3},
- gpio::pin::PushPullOutput,
+ clocks,
+ gpio::{
+ self,
+ bank0::{Gpio2, Gpio25, Gpio3},
+ FunctionSioOutput, PullNone, PullUp,
+ },
pac,
sio::Sio,
watchdog::Watchdog,
@@ -20,15 +26,13 @@ mod app {
use core::mem::MaybeUninit;
use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin};
use fugit::RateExtU32;
- use rtic_monotonics::rp2040::*;
-
use panic_probe as _;
type I2CBus = I2C<
pac::I2C1,
(
- gpio::Pin<Gpio2, gpio::FunctionI2C>,
- gpio::Pin<Gpio3, gpio::FunctionI2C>,
+ gpio::Pin<Gpio2, gpio::FunctionI2C, PullUp>,
+ gpio::Pin<Gpio3, gpio::FunctionI2C, PullUp>,
),
>;
@@ -37,7 +41,7 @@ mod app {
#[local]
struct Local {
- led: gpio::Pin<Gpio25, PushPullOutput>,
+ led: gpio::Pin<Gpio25, FunctionSioOutput, PullNone>,
i2c: &'static mut I2CBus,
}
@@ -48,11 +52,8 @@ mod app {
i2c_ctx: MaybeUninit<I2CBus> = MaybeUninit::uninit()
])]
fn init(mut ctx: init::Context) -> (Shared, Local) {
- // Initialize the interrupt for the RP2040 timer and obtain the token
- // proving that we have.
- let rp2040_timer_token = rtic_monotonics::create_rp2040_monotonic_token!();
// Configure the clocks, watchdog - The default is to generate a 125 MHz system clock
- Timer::start(ctx.device.TIMER, &mut ctx.device.RESETS, rp2040_timer_token); // default rp2040 clock-rate is 125MHz
+ Mono::start(ctx.device.TIMER, &mut ctx.device.RESETS); // default rp2040 clock-rate is 125MHz
let mut watchdog = Watchdog::new(ctx.device.WATCHDOG);
let clocks = clocks::init_clocks_and_plls(
XOSC_CRYSTAL_FREQ,
@@ -74,12 +75,21 @@ mod app {
sio.gpio_bank0,
&mut ctx.device.RESETS,
);
- let mut led = gpioa.led.into_push_pull_output();
+ let mut led = gpioa
+ .led
+ .into_pull_type::<PullNone>()
+ .into_push_pull_output();
led.set_low().unwrap();
// Init I2C pins
- let sda_pin = gpioa.gpio2.into_mode::<gpio::FunctionI2C>();
- let scl_pin = gpioa.gpio3.into_mode::<gpio::FunctionI2C>();
+ let sda_pin = gpioa
+ .gpio2
+ .into_pull_up_disabled()
+ .into_function::<gpio::FunctionI2C>();
+ let scl_pin = gpioa
+ .gpio3
+ .into_pull_up_disabled()
+ .into_function::<gpio::FunctionI2C>();
// Init I2C itself, using MaybeUninit to overwrite the previously
// uninitialized i2c_ctx variable without dropping its value
@@ -118,7 +128,7 @@ mod app {
// now to do something with it!
// Delay for 1 second
- Timer::delay(1000.millis()).await;
+ Mono::delay(1000.millis()).await;
}
}
}
diff --git a/examples/stm32f3_blinky/Cargo.lock b/examples/stm32f3_blinky/Cargo.lock
index 3fbec9e..527d4eb 100644
--- a/examples/stm32f3_blinky/Cargo.lock
+++ b/examples/stm32f3_blinky/Cargo.lock
@@ -52,11 +52,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bxcan"
-version = "0.6.2"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b13b4b2ea9ab2ba924063ebb86ad895cb79f4a79bf90f27949eb20c335b30f9"
+checksum = "40ac3d0c0a542d0ab5521211f873f62706a7136df415676f676d347e5a41dd80"
dependencies = [
"bitflags",
+ "embedded-hal 0.2.7",
"nb 1.1.0",
"vcell",
]
@@ -86,7 +87,7 @@ dependencies = [
"bare-metal 0.2.5",
"bitfield",
"critical-section",
- "embedded-hal",
+ "embedded-hal 0.2.7",
"volatile-register",
]
@@ -112,9 +113,9 @@ dependencies = [
[[package]]
name = "critical-section"
-version = "1.1.1"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52"
+checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "darling"
@@ -136,7 +137,7 @@ dependencies = [
"ident_case",
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.50",
]
[[package]]
@@ -147,7 +148,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
dependencies = [
"darling_core",
"quote",
- "syn 2.0.22",
+ "syn 2.0.50",
]
[[package]]
@@ -170,6 +171,21 @@ dependencies = [
]
[[package]]
+name = "embedded-hal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
+
+[[package]]
+name = "embedded-hal-async"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
+dependencies = [
+ "embedded-hal 1.0.0",
+]
+
+[[package]]
name = "embedded-time"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -180,9 +196,9 @@ dependencies = [
[[package]]
name = "enumset"
-version = "1.1.2"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb"
+checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d"
dependencies = [
"enumset_derive",
]
@@ -196,7 +212,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.50",
]
[[package]]
@@ -343,28 +359,28 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.15"
+version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[package]]
name = "panic-rtt-target"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d6ab67bc881453e4c90f958c657c1303670ea87bc1a16e87fd71a40f656dce9"
+checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe"
dependencies = [
- "cortex-m",
- "rtt-target 0.3.1",
+ "critical-section",
+ "rtt-target",
]
[[package]]
name = "paste"
-version = "1.0.12"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "pin-project-lite"
@@ -379,6 +395,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
+name = "portable-atomic"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+
+[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -404,18 +426,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.63"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.29"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -431,7 +453,7 @@ dependencies = [
[[package]]
name = "rtic"
-version = "2.0.0"
+version = "2.0.1"
dependencies = [
"atomic-polyfill",
"bare-metal 1.0.0",
@@ -443,9 +465,10 @@ dependencies = [
[[package]]
name = "rtic-common"
-version = "1.0.0"
+version = "1.0.1"
dependencies = [
"critical-section",
+ "portable-atomic",
]
[[package]]
@@ -456,49 +479,44 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
[[package]]
name = "rtic-macros"
-version = "2.0.0"
+version = "2.0.1"
dependencies = [
"indexmap",
"proc-macro-error",
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn 2.0.50",
]
[[package]]
name = "rtic-monotonics"
-version = "1.0.0"
+version = "2.0.0"
dependencies = [
"atomic-polyfill",
"cfg-if",
"cortex-m",
+ "embedded-hal 1.0.0",
"fugit",
"rtic-time",
]
[[package]]
name = "rtic-time"
-version = "1.0.0"
+version = "2.0.0"
dependencies = [
"critical-section",
+ "embedded-hal 1.0.0",
+ "embedded-hal-async",
+ "fugit",
"futures-util",
"rtic-common",
]
[[package]]
name = "rtt-target"
-version = "0.3.1"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "065d6058bb1204f51a562a67209e1817cf714759d5cf845aa45c75fa7b0b9d9b"
-dependencies = [
- "ufmt-write",
-]
-
-[[package]]
-name = "rtt-target"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3afa12c77ba1b9bf560e4039a9b9a08bb9cde0e9e6923955eeb917dd8d5cf303"
+checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0"
dependencies = [
"critical-section",
"ufmt-write",
@@ -553,9 +571,9 @@ dependencies = [
[[package]]
name = "stm32f3"
-version = "0.14.0"
+version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "265cda62ac13307414de4aca58dbbbd8038ddba85cffbb335823aa216f2e3200"
+checksum = "b28b37228ef3fa47956af38c6abd756e912f244c1657f14e66d42fc8d74ea96f"
dependencies = [
"bare-metal 1.0.0",
"cortex-m",
@@ -567,30 +585,32 @@ dependencies = [
name = "stm32f3-blinky"
version = "0.1.0"
dependencies = [
- "embedded-hal",
+ "cortex-m",
+ "embedded-hal 0.2.7",
"panic-rtt-target",
"rtic",
"rtic-monotonics",
- "rtt-target 0.4.0",
+ "rtt-target",
"stm32f3xx-hal",
]
[[package]]
name = "stm32f3xx-hal"
-version = "0.9.2"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c9d827f02df3826371c897404dfbea8a1abd544eed9d6cdc3e5f6e9f04b9e06"
+checksum = "4c73e8b6e63435b75198d2fe2b27cd7f5c8e0b07bd5da9f82cffddf23210f77f"
dependencies = [
- "bare-metal 1.0.0",
"bxcan",
"cfg-if",
"cortex-m",
"cortex-m-rt",
+ "critical-section",
"embedded-dma",
- "embedded-hal",
+ "embedded-hal 0.2.7",
"embedded-time",
"enumset",
"nb 1.1.0",
+ "num-traits",
"paste",
"rtcc",
"slice-group-by",
@@ -612,9 +632,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.22"
+version = "2.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
+checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
dependencies = [
"proc-macro2",
"quote",
diff --git a/examples/stm32f3_blinky/Cargo.toml b/examples/stm32f3_blinky/Cargo.toml
index 3773f0e..5e28188 100644
--- a/examples/stm32f3_blinky/Cargo.toml
+++ b/examples/stm32f3_blinky/Cargo.toml
@@ -14,17 +14,21 @@ features = ["thumbv7-backend"]
[dependencies.rtic-monotonics]
path = "../../rtic-monotonics"
-version = "1.0.0"
+version = "2.0.0"
features = ["cortex-m-systick"]
+[dependencies.cortex-m]
+version = "0.7.7"
+features = ["critical-section-single-core"]
+
[dependencies]
embedded-hal = "0.2.7"
-panic-rtt-target = { version = "0.1.2", features = ["cortex-m"] }
-rtt-target = { version = "0.4.0" }
+panic-rtt-target = { version = "0.1.3" }
+rtt-target = { version = "0.5.0" }
[dependencies.stm32f3xx-hal]
features = ["stm32f303xc", "rt"]
-version = "0.9.2"
+version = "0.10.0"
# this lets you use `cargo fix`!
[[bin]]
diff --git a/examples/stm32f3_blinky/src/main.rs b/examples/stm32f3_blinky/src/main.rs
index 28dc7c2..e8c2e50 100644
--- a/examples/stm32f3_blinky/src/main.rs
+++ b/examples/stm32f3_blinky/src/main.rs
@@ -5,11 +5,13 @@
use panic_rtt_target as _;
use rtic::app;
-use rtic_monotonics::systick::*;
+use rtic_monotonics::systick::prelude::*;
use rtt_target::{rprintln, rtt_init_print};
use stm32f3xx_hal::gpio::{Output, PushPull, PA5};
use stm32f3xx_hal::prelude::*;
+systick_monotonic!(Mono, 1000);
+
#[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [SPI1])]
mod app {
use super::*;
@@ -30,8 +32,7 @@ mod app {
let mut rcc = cx.device.RCC.constrain();
// Initialize the systick interrupt & obtain the token to prove that we did
- let systick_mono_token = rtic_monotonics::create_systick_token!();
- Systick::start(cx.core.SYST, 36_000_000, systick_mono_token); // default STM32F303 clock-rate is 36MHz
+ Mono::start(cx.core.SYST, 36_000_000); // default STM32F303 clock-rate is 36MHz
rtt_init_print!();
rprintln!("init");
@@ -67,7 +68,7 @@ mod app {
cx.local.led.set_low().unwrap();
*cx.local.state = true;
}
- Systick::delay(1000.millis()).await;
+ Mono::delay(1000.millis()).await;
}
}
}
diff --git a/examples/stm32g030f6_periodic_prints/.cargo/config.toml b/examples/stm32g030f6_periodic_prints/.cargo/config.toml
new file mode 100644
index 0000000..15ddd2a
--- /dev/null
+++ b/examples/stm32g030f6_periodic_prints/.cargo/config.toml
@@ -0,0 +1,24 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output)
+runner = "probe-run --chip STM32G030F6Px"
+rustflags = [
+ "-C", "linker=flip-link",
+ "-C", "link-arg=-Tlink.x",
+ "-C", "link-arg=-Tdefmt.x",
+ # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
+ # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
+ "-C", "link-arg=--nmagic",
+]
+
+[build]
+# TODO(3) Adjust the compilation target.
+# (`thumbv6m-*` is compatible with all ARM Cortex-M chips but using the right
+# target improves performance)
+target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
+# target = "thumbv7m-none-eabi" # Cortex-M3
+# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
+# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
+
+[alias]
+rb = "run --bin"
+rrb = "run --release --bin"
diff --git a/examples/stm32g030f6_periodic_prints/.vscode/settings.json b/examples/stm32g030f6_periodic_prints/.vscode/settings.json
new file mode 100644
index 0000000..c684c24
--- /dev/null
+++ b/examples/stm32g030f6_periodic_prints/.vscode/settings.json
@@ -0,0 +1,9 @@
+{
+ // override the default setting (`cargo check --all-targets`) which produces the following error
+ // "can't find crate for `test`" when the default compilation target is a no_std target
+ // with these changes RA will call `cargo check --bins` on save
+ "rust-analyzer.checkOnSave.allTargets": false,
+ "rust-analyzer.checkOnSave.extraArgs": [
+ "--bins"
+ ]
+}
diff --git a/examples/stm32g030f6_periodic_prints/Cargo.lock b/examples/stm32g030f6_periodic_prints/Cargo.lock
new file mode 100644
index 0000000..65dbf61
--- /dev/null
+++ b/examples/stm32g030f6_periodic_prints/Cargo.lock
@@ -0,0 +1,511 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "atomic-polyfill"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
+dependencies = [
+ "critical-section",
+]
+
+[[package]]
+name = "bare-metal"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
+dependencies = [
+ "rustc_version",
+]
+
+[[package]]
+name = "bare-metal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
+
+[[package]]
+name = "bitfield"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cortex-m"
+version = "0.7.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
+dependencies = [
+ "bare-metal 0.2.5",
+ "bitfield",
+ "critical-section",
+ "embedded-hal 0.2.7",
+ "volatile-register",
+]
+
+[[package]]
+name = "cortex-m-rt"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1"
+dependencies = [
+ "cortex-m-rt-macros",
+]
+
+[[package]]
+name = "cortex-m-rt-macros"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "critical-section"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
+
+[[package]]
+name = "defmt"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3939552907426de152b3c2c6f51ed53f98f448babd26f28694c95f5906194595"
+dependencies = [
+ "bitflags",
+ "defmt-macros",
+]
+
+[[package]]
+name = "defmt-macros"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18bdc7a7b92ac413e19e95240e75d3a73a8d8e78aa24a594c22cbb4d44b4bbda"
+dependencies = [
+ "defmt-parser",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.51",
+]
+
+[[package]]
+name = "defmt-parser"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "defmt-rtt"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "609923761264dd99ed9c7d209718cda4631c5fe84668e0f0960124cbb844c49f"
+dependencies = [
+ "critical-section",
+ "defmt",
+]
+
+[[package]]
+name = "embedded-hal"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
+dependencies = [
+ "nb 0.1.3",
+ "void",
+]
+
+[[package]]
+name = "embedded-hal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
+
+[[package]]
+name = "embedded-hal-async"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
+dependencies = [
+ "embedded-hal 1.0.0",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "fugit"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
+dependencies = [
+ "gcd",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
+name = "gcd"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "indexmap"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "nb"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
+dependencies = [
+ "nb 1.1.0",
+]
+
+[[package]]
+name = "nb"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
+
+[[package]]
+name = "panic-probe"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa6fa5645ef5a760cd340eaa92af9c1ce131c8c09e7f8926d8a24b59d26652b9"
+dependencies = [
+ "cortex-m",
+ "defmt",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "portable-atomic"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rtic"
+version = "2.0.1"
+dependencies = [
+ "atomic-polyfill",
+ "bare-metal 1.0.0",
+ "cortex-m",
+ "critical-section",
+ "rtic-core",
+ "rtic-macros",
+]
+
+[[package]]
+name = "rtic-common"
+version = "1.0.1"
+dependencies = [
+ "critical-section",
+ "portable-atomic",
+]
+
+[[package]]
+name = "rtic-core"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
+
+[[package]]
+name = "rtic-macros"
+version = "2.0.1"
+dependencies = [
+ "indexmap",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.51",
+]
+
+[[package]]
+name = "rtic-monotonics"
+version = "2.0.0"
+dependencies = [
+ "atomic-polyfill",
+ "cfg-if",
+ "cortex-m",
+ "embedded-hal 1.0.0",
+ "fugit",
+ "proc-macro2",
+ "quote",
+ "rtic-time",
+ "stm32-metapac",
+]
+
+[[package]]
+name = "rtic-time"
+version = "2.0.0"
+dependencies = [
+ "critical-section",
+ "embedded-hal 1.0.0",
+ "embedded-hal-async",
+ "fugit",
+ "futures-util",
+ "rtic-common",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "stm32-metapac"
+version = "15.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "deabea56a8821dcea05d0109f3ab3135f31eb572444e5da203d06149c594c8c6"
+dependencies = [
+ "cortex-m",
+]
+
+[[package]]
+name = "stm32g0"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc2ac544cea741c92a501bfd027d197354cd22ee92b439aea26d2ee0b55bcd7"
+dependencies = [
+ "bare-metal 1.0.0",
+ "cortex-m",
+ "cortex-m-rt",
+ "vcell",
+]
+
+[[package]]
+name = "stm32g030f6_periodic_prints"
+version = "0.1.0"
+dependencies = [
+ "cortex-m",
+ "cortex-m-rt",
+ "defmt",
+ "defmt-rtt",
+ "fugit",
+ "panic-probe",
+ "rtic",
+ "rtic-monotonics",
+ "stm32g0xx-hal",
+]
+
+[[package]]
+name = "stm32g0xx-hal"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fae457e81f9601121c5b92dca20e3612c80ea957898f8e0e68efcaab6b242067"
+dependencies = [
+ "bare-metal 1.0.0",
+ "cortex-m",
+ "embedded-hal 0.2.7",
+ "fugit",
+ "nb 1.1.0",
+ "stm32g0",
+ "void",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.51",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "vcell"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "volatile-register"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
+dependencies = [
+ "vcell",
+]
diff --git a/examples/stm32g030f6_periodic_prints/Cargo.toml b/examples/stm32g030f6_periodic_prints/Cargo.toml
new file mode 100644
index 0000000..d8d8e35
--- /dev/null
+++ b/examples/stm32g030f6_periodic_prints/Cargo.toml
@@ -0,0 +1,48 @@
+[package]
+authors = ["Finomnis <finomnis@gmail.com>"]
+name = "stm32g030f6_periodic_prints"
+edition = "2021"
+version = "0.1.0"
+
+[workspace]
+
+
+[dependencies.rtic]
+path = "../../rtic"
+version = "2.0.1"
+features = ["thumbv6-backend"]
+
+[dependencies.rtic-monotonics]
+path = "../../rtic-monotonics"
+version = "2.0.0"
+features = ["stm32g030f6", "stm32_tim3"]
+
+
+[dependencies]
+cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
+cortex-m-rt = "0.7.3"
+defmt = "0.3.4"
+defmt-rtt = "0.4.0"
+fugit = "0.3.7"
+panic-probe = { version = "0.3.1", features = ["print-defmt"] }
+
+stm32g0xx-hal = { version = "0.2.0", features = ["rt", "stm32g030"] }
+
+# cargo build/run
+[profile.dev]
+codegen-units = 1
+debug = 2
+debug-assertions = true # <-
+incremental = false
+opt-level = 3 # <-
+overflow-checks = true # <-
+
+# cargo build/run --release
+[profile.release]
+codegen-units = 1
+debug = 2
+debug-assertions = false # <-
+incremental = false
+lto = 'fat'
+opt-level = 3 # <-
+overflow-checks = false # <-
diff --git a/examples/stm32g030f6_periodic_prints/README.md b/examples/stm32g030f6_periodic_prints/README.md
new file mode 100644
index 0000000..e669378
--- /dev/null
+++ b/examples/stm32g030f6_periodic_prints/README.md
@@ -0,0 +1,31 @@
+# `stm32g030f6_periodic_prints`
+
+An RTIC periodic print example intended for the stm32g030f6 chip.
+
+## Dependencies
+
+#### 1. `flip-link`:
+
+```console
+$ cargo install flip-link
+```
+
+#### 2. `probe-rs`:
+
+``` console
+$ # make sure to install v0.2.0 or later
+$ cargo install probe-rs --features cli
+```
+
+
+## Run
+
+The stm32g030f6 chip needs to be connected to the computer via an SWD probe, like a [J-Link EDU Mini].
+
+Then, run:
+
+```
+cargo run --release
+```
+
+[J-Link EDU Mini]: https://www.segger.com/products/debug-probes/j-link/models/j-link-edu-mini/
diff --git a/examples/stm32g030f6_periodic_prints/memory.x b/examples/stm32g030f6_periodic_prints/memory.x
new file mode 100644
index 0000000..3f89120
--- /dev/null
+++ b/examples/stm32g030f6_periodic_prints/memory.x
@@ -0,0 +1,6 @@
+/* Linker script for the STM32G030F6 */
+MEMORY
+{
+ FLASH : ORIGIN = 0x08000000, LENGTH = 32K
+ RAM : ORIGIN = 0x20000000, LENGTH = 8K
+}
diff --git a/examples/stm32g030f6_periodic_prints/src/main.rs b/examples/stm32g030f6_periodic_prints/src/main.rs
new file mode 100644
index 0000000..00fa802
--- /dev/null
+++ b/examples/stm32g030f6_periodic_prints/src/main.rs
@@ -0,0 +1,57 @@
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use defmt_rtt as _; // global logger
+
+pub use stm32g0xx_hal as hal; // memory layout
+
+use panic_probe as _;
+
+// same panicking *behavior* as `panic-probe` but doesn't print a panic message
+// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
+#[defmt::panic_handler]
+fn panic() -> ! {
+ cortex_m::asm::udf()
+}
+
+use rtic_monotonics::stm32::prelude::*;
+stm32_tim3_monotonic!(Mono, 1_000_000);
+
+#[rtic::app(device = hal::stm32, peripherals = true, dispatchers = [USART1, USART2])]
+mod app {
+ use super::*;
+
+ #[local]
+ struct LocalResources {}
+
+ #[shared]
+ struct SharedResources {}
+
+ #[init]
+ fn init(ctx: init::Context) -> (SharedResources, LocalResources) {
+ // enable dma clock during sleep, otherwise defmt doesn't work
+ ctx.device.RCC.ahbenr.modify(|_, w| w.dmaen().set_bit());
+
+ defmt::println!("TIM Monotonic blinker example!");
+
+ // Start the monotonic
+ Mono::start(16_000_000);
+
+ print_messages::spawn().unwrap();
+
+ (SharedResources {}, LocalResources {})
+ }
+
+ #[task(priority = 2)]
+ async fn print_messages(_cx: print_messages::Context) {
+ let mut next_update = <Mono as Monotonic>::Instant::from_ticks(0u64);
+
+ loop {
+ defmt::println!("Time: {} ticks", Mono::now().ticks());
+ next_update += 1000u64.millis();
+ Mono::delay_until(next_update).await;
+ }
+ }
+}
diff --git a/examples/teensy4_blinky/Cargo.lock b/examples/teensy4_blinky/Cargo.lock
index 314eae0..8ae5b95 100644
--- a/examples/teensy4_blinky/Cargo.lock
+++ b/examples/teensy4_blinky/Cargo.lock
@@ -133,9 +133,18 @@ dependencies = [
[[package]]
name = "embedded-hal"
-version = "1.0.0-rc.2"
+version = "1.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e57ec6ad0bc8eb967cf9c9f144177f5e8f2f6f02dad0b8b683f9f05f6b22def"
+checksum = "bc402f79e1fd22731ca945b4f97b5ff37e7b3f379312595c42bb2e8811c29920"
+
+[[package]]
+name = "embedded-hal-async"
+version = "1.0.0-rc.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa1fba2ef2ffb35d614acc6fb323ddf7facc45c069f24544d49ea54e5043626d"
+dependencies = [
+ "embedded-hal 1.0.0-rc.3",
+]
[[package]]
name = "equivalent"
@@ -440,12 +449,12 @@ dependencies = [
[[package]]
name = "rtic-monotonics"
-version = "1.4.0"
+version = "2.0.0"
dependencies = [
"atomic-polyfill",
"cfg-if",
"cortex-m",
- "embedded-hal 1.0.0-rc.2",
+ "embedded-hal 1.0.0-rc.3",
"fugit",
"imxrt-ral",
"rtic-time",
@@ -453,9 +462,12 @@ dependencies = [
[[package]]
name = "rtic-time"
-version = "1.1.0"
+version = "2.0.0"
dependencies = [
"critical-section",
+ "embedded-hal 1.0.0-rc.3",
+ "embedded-hal-async",
+ "fugit",
"futures-util",
"rtic-common",
]
diff --git a/examples/teensy4_blinky/Cargo.toml b/examples/teensy4_blinky/Cargo.toml
index a81a155..e050213 100644
--- a/examples/teensy4_blinky/Cargo.toml
+++ b/examples/teensy4_blinky/Cargo.toml
@@ -14,7 +14,7 @@ features = ["thumbv7-backend"]
[dependencies.rtic-monotonics]
path = "../../rtic-monotonics"
-version = "1.2.1"
+version = "2.0.0"
features = ["imxrt_gpt1"]
[dependencies]
diff --git a/examples/teensy4_blinky/examples/with_logs.rs b/examples/teensy4_blinky/examples/with_logs.rs
index 82c7154..111e21e 100644
--- a/examples/teensy4_blinky/examples/with_logs.rs
+++ b/examples/teensy4_blinky/examples/with_logs.rs
@@ -13,9 +13,8 @@ use bsp::logging;
use embedded_hal::serial::Write;
-use rtic_monotonics::imxrt::Gpt1 as Mono;
-use rtic_monotonics::imxrt::*;
-use rtic_monotonics::Monotonic;
+use rtic_monotonics::imxrt::prelude::*;
+imxrt_gpt1_monotonic!(Mono, board::PERCLK_FREQUENCY);
#[rtic::app(device = teensy4_bsp, dispatchers = [LPSPI1])]
mod app {
@@ -61,8 +60,7 @@ mod app {
// Initialize Monotonic
gpt1.set_clock_source(hal::gpt::ClockSource::PeripheralClock);
- let gpt1_mono_token = rtic_monotonics::create_imxrt_gpt1_token!();
- Mono::start(board::PERCLK_FREQUENCY, gpt1.release(), gpt1_mono_token);
+ Mono::start(gpt1.release());
// Setup LED
let led = board::led(&mut gpio2, pins.p13);
diff --git a/examples/teensy4_blinky/src/main.rs b/examples/teensy4_blinky/src/main.rs
index 443e605..b3b1773 100644
--- a/examples/teensy4_blinky/src/main.rs
+++ b/examples/teensy4_blinky/src/main.rs
@@ -10,8 +10,8 @@ fn panic(_: &::core::panic::PanicInfo) -> ! {
use teensy4_bsp::{board, hal};
-use rtic_monotonics::imxrt::Gpt1 as Mono;
-use rtic_monotonics::imxrt::*;
+use rtic_monotonics::imxrt::prelude::*;
+imxrt_gpt1_monotonic!(Mono, board::PERCLK_FREQUENCY);
#[rtic::app(device = teensy4_bsp, dispatchers = [LPSPI1])]
mod app {
@@ -36,8 +36,7 @@ mod app {
// Initialize Monotonic
gpt1.set_clock_source(hal::gpt::ClockSource::PeripheralClock);
- let gpt1_mono_token = rtic_monotonics::create_imxrt_gpt1_token!();
- Mono::start(board::PERCLK_FREQUENCY, gpt1.release(), gpt1_mono_token);
+ Mono::start(gpt1.release());
// Setup LED
let led = board::led(&mut gpio2, pins.p13);
diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md
index ef5840f..f114a18 100644
--- a/rtic-monotonics/CHANGELOG.md
+++ b/rtic-monotonics/CHANGELOG.md
@@ -5,10 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
-## Unreleased
+## Unreleased - v2.0.0
### Changed
+- Rework all timers based on `rtic-time 2.0.0`
+- Most timer tick rates are now configurable
- Tweak `build.rs` to avoid warnings in Nightly 1.78+
- Removed unused `rust-toolchain.toml`
diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml
index 5860653..81df240 100644
--- a/rtic-monotonics/Cargo.toml
+++ b/rtic-monotonics/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rtic-monotonics"
-version = "1.5.0"
+version = "2.0.0"
edition = "2021"
authors = [
@@ -24,13 +24,17 @@ features = [
"imxrt_gpt1",
"imxrt_gpt2",
"imxrt-ral/imxrt1011",
+ "stm32h725ag",
+ "stm32_tim2",
+ "stm32_tim3",
+ "stm32_tim4",
+ "stm32_tim5",
+ "stm32_tim15",
]
rustdoc-flags = ["--cfg", "docsrs"]
[dependencies]
-rtic-time = { version = "1.1.0", path = "../rtic-time" }
-embedded-hal = { version = "1.0" }
-embedded-hal-async = { version = "1.0", optional = true }
+rtic-time = { version = "2.0.0", path = "../rtic-time" }
fugit = { version = "0.3.6" }
atomic-polyfill = "1"
cfg-if = "1.0.0"
@@ -69,8 +73,6 @@ defmt = ["fugit/defmt"]
# Systick on Cortex-M, default 1 kHz
cortex-m-systick = ["dep:cortex-m"]
-systick-100hz = []
-systick-10khz = []
# Use 64-bit wide backing storage for the Instant
systick-64bit = []
@@ -99,7 +101,6 @@ stm32_tim2 = []
stm32_tim3 = []
stm32_tim4 = []
stm32_tim5 = []
-stm32_tim12 = []
stm32_tim15 = []
stm32-metapac = ["dep:stm32-metapac", "dep:quote", "dep:proc-macro2"]
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);
diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs
index 6dc703e..65180b1 100644
--- a/rtic-monotonics/src/lib.rs
+++ b/rtic-monotonics/src/lib.rs
@@ -21,14 +21,18 @@
//! `Available on crate features X only` tag are available on any `nrf52*` feature.
//!
// To build these docs correctly:
-// RUSTFLAGS="--cfg docsrs" cargo doc --featuers cortex-m-systick,rp2040,nrf52840
+// RUSTFLAGS="--cfg docsrs" cargo +nightly doc --features thumbv7-backend,cortex-m-systick,rp2040,nrf52840,imxrt_gpt1,imxrt_gpt2,imxrt-ral/imxrt1011,stm32h725ag,stm32_tim2,stm32_tim3,stm32_tim4,stm32_tim5,stm32_tim15
#![no_std]
#![deny(missing_docs)]
#![allow(incomplete_features)]
#![cfg_attr(docsrs, feature(doc_cfg))]
-pub use rtic_time::{Monotonic, TimeoutError, TimerQueue};
+pub use fugit;
+pub use rtic_time::{
+ self, monotonic::TimerQueueBasedMonotonic, timer_queue::TimerQueueBackend, Monotonic,
+ TimeoutError,
+};
#[cfg(feature = "cortex-m-systick")]
pub mod systick;
@@ -92,13 +96,3 @@ pub(crate) unsafe fn set_monotonic_prio(
nvic.set_priority(interrupt, hw_prio);
}
-
-/// This marker is implemented on an interrupt token to enforce that the right tokens
-/// are given to the correct monotonic implementation.
-///
-/// This trait is implemented by this crate and not intended for user implementation.
-///
-/// # Safety
-///
-/// This is only safely implemented by this crate.
-pub unsafe trait InterruptToken<Periperhal> {}
diff --git a/rtic-monotonics/src/nrf/rtc.rs b/rtic-monotonics/src/nrf/rtc.rs
index d425b11..39b26c4 100644
--- a/rtic-monotonics/src/nrf/rtc.rs
+++ b/rtic-monotonics/src/nrf/rtc.rs
@@ -1,96 +1,152 @@
-//! [`Monotonic`] implementation for the nRF Real Time Clocks (RTC).
+//! [`Monotonic`](rtic_time::Monotonic) implementation for the nRF Real Time Clocks (RTC).
//!
//! # Example
//!
//! ```
-//! use rtic_monotonics::nrf::rtc::*;
+//! use rtic_monotonics::nrf::rtc::prelude::*;
+//! nrf_rtc0_monotonic!(Mono);
//!
//! fn init() {
//! # // This is normally provided by the selected PAC
//! # let rtc = unsafe { core::mem::transmute(()) };
-//! // Generate the required token
-//! let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!();
-//!
//! // Start the monotonic
-//! Rtc0::start(rtc, token);
+//! Mono::start(rtc);
//! }
//!
//! async fn usage() {
//! loop {
//! // Use the monotonic
-//! Rtc0::delay(100.millis()).await;
+//! let timestamp = Mono::now();
+//! Mono::delay(100.millis()).await;
//! }
//! }
//! ```
+/// Common definitions and traits for using the nRF RTC monotonics
+pub mod prelude {
+ pub use crate::nrf_rtc0_monotonic;
+ pub use crate::nrf_rtc1_monotonic;
+ #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
+ pub use crate::nrf_rtc2_monotonic;
+
+ pub use crate::Monotonic;
+ pub use fugit::{self, ExtU64, ExtU64Ceil};
+}
+
#[cfg(feature = "nrf52810")]
-use nrf52810_pac::{self as pac, Interrupt, RTC0, RTC1};
+#[doc(hidden)]
+pub use nrf52810_pac::{self as pac, RTC0, RTC1};
#[cfg(feature = "nrf52811")]
-use nrf52811_pac::{self as pac, Interrupt, RTC0, RTC1};
+#[doc(hidden)]
+pub use nrf52811_pac::{self as pac, RTC0, RTC1};
#[cfg(feature = "nrf52832")]
-use nrf52832_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2};
+#[doc(hidden)]
+pub use nrf52832_pac::{self as pac, RTC0, RTC1, RTC2};
#[cfg(feature = "nrf52833")]
-use nrf52833_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2};
+#[doc(hidden)]
+pub use nrf52833_pac::{self as pac, RTC0, RTC1, RTC2};
#[cfg(feature = "nrf52840")]
-use nrf52840_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2};
+#[doc(hidden)]
+pub use nrf52840_pac::{self as pac, RTC0, RTC1, RTC2};
#[cfg(feature = "nrf5340-app")]
-use nrf5340_app_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1};
+#[doc(hidden)]
+pub use nrf5340_app_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
#[cfg(feature = "nrf5340-net")]
-use nrf5340_net_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1};
+#[doc(hidden)]
+pub use nrf5340_net_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
#[cfg(feature = "nrf9160")]
-use nrf9160_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1};
+#[doc(hidden)]
+pub use nrf9160_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
-use crate::{Monotonic, TimeoutError, TimerQueue};
use atomic_polyfill::{AtomicU32, Ordering};
-use core::future::Future;
-pub use fugit::{self, ExtU64, ExtU64Ceil};
-use rtic_time::half_period_counter::calculate_now;
+use rtic_time::{
+ half_period_counter::calculate_now,
+ timer_queue::{TimerQueue, TimerQueueBackend},
+};
#[doc(hidden)]
#[macro_export]
macro_rules! __internal_create_nrf_rtc_interrupt {
- ($mono_timer:ident, $rtc:ident, $rtc_token:ident) => {{
+ ($mono_backend:ident, $rtc:ident) => {
#[no_mangle]
#[allow(non_snake_case)]
unsafe extern "C" fn $rtc() {
- $crate::nrf::rtc::$mono_timer::__tq().on_monotonic_interrupt();
+ use $crate::TimerQueueBackend;
+ $crate::nrf::rtc::$mono_backend::timer_queue().on_monotonic_interrupt();
}
+ };
+}
- pub struct $rtc_token;
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __internal_create_nrf_rtc_struct {
+ ($name:ident, $mono_backend:ident, $timer:ident) => {
+ /// A `Monotonic` based on the nRF RTC peripheral.
+ struct $name;
+
+ impl $name {
+ /// Starts the `Monotonic`.
+ ///
+ /// This method must be called only once.
+ pub fn start(rtc: $crate::nrf::rtc::$timer) {
+ $crate::__internal_create_nrf_rtc_interrupt!($mono_backend, $timer);
+
+ $crate::nrf::rtc::$mono_backend::_start(rtc);
+ }
+ }
- unsafe impl $crate::InterruptToken<$crate::nrf::rtc::$mono_timer> for $rtc_token {}
+ impl $crate::TimerQueueBasedMonotonic for $name {
+ type Backend = $crate::nrf::rtc::$mono_backend;
+ type Instant = $crate::fugit::Instant<
+ <Self::Backend as $crate::TimerQueueBackend>::Ticks,
+ 1,
+ 32_768,
+ >;
+ type Duration = $crate::fugit::Duration<
+ <Self::Backend as $crate::TimerQueueBackend>::Ticks,
+ 1,
+ 32_768,
+ >;
+ }
- $rtc_token
- }};
+ $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
+ $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
+ };
}
-/// Register the Rtc0 interrupt for the monotonic.
+/// Create an RTC0 based monotonic and register the RTC0 interrupt for it.
+///
+/// See [`crate::nrf::rtc`] for more details.
#[macro_export]
-macro_rules! create_nrf_rtc0_monotonic_token {
- () => {{
- $crate::__internal_create_nrf_rtc_interrupt!(Rtc0, RTC0, Rtc0Token)
- }};
+macro_rules! nrf_rtc0_monotonic {
+ ($name:ident) => {
+ $crate::__internal_create_nrf_rtc_struct!($name, Rtc0Backend, RTC0);
+ };
}
-/// Register the Rtc1 interrupt for the monotonic.
+/// Create an RTC1 based monotonic and register the RTC1 interrupt for it.
+///
+/// See [`crate::nrf::rtc`] for more details.
#[macro_export]
-macro_rules! create_nrf_rtc1_monotonic_token {
- () => {{
- $crate::__internal_create_nrf_rtc_interrupt!(Rtc1, RTC1, Rtc1Token)
- }};
+macro_rules! nrf_rtc1_monotonic {
+ ($name:ident) => {
+ $crate::__internal_create_nrf_rtc_struct!($name, Rtc1Backend, RTC1);
+ };
}
-/// Register the Rtc2 interrupt for the monotonic.
+/// Create an RTC2 based monotonic and register the RTC2 interrupt for it.
+///
+/// See [`crate::nrf::rtc`] for more details.
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
)]
#[macro_export]
-macro_rules! create_nrf_rtc2_monotonic_token {
- () => {{
- $crate::__internal_create_nrf_rtc_interrupt!(Rtc2, RTC2, Rtc2Token)
- }};
+macro_rules! nrf_rtc2_monotonic {
+ ($name:ident) => {
+ $crate::__internal_create_nrf_rtc_struct!($name, Rtc2Backend, RTC2);
+ };
}
struct TimerValueU24(u32);
@@ -104,19 +160,23 @@ impl From<TimerValueU24> for u64 {
}
macro_rules! make_rtc {
- ($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
- /// Monotonic timer queue implementation.
+ ($backend_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
+ /// RTC based [`TimerQueueBackend`].
$(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)?
- pub struct $mono_name;
+ pub struct $backend_name;
static $overflow: AtomicU32 = AtomicU32::new(0);
- static $tq: TimerQueue<$mono_name> = TimerQueue::new();
-
- impl $mono_name {
- /// Start the timer monotonic.
- pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken<Self>) {
+ static $tq: TimerQueue<$backend_name> = TimerQueue::new();
+
+ impl $backend_name {
+ /// Starts the timer.
+ ///
+ /// **Do not use this function directly.**
+ ///
+ /// Use the prelude macros instead.
+ pub fn _start(rtc: $rtc) {
unsafe { rtc.prescaler.write(|w| w.bits(0)) };
// Disable interrupts, as preparation
@@ -166,67 +226,21 @@ macro_rules! make_rtc {
// plus we are not using any external shared resources so we won't impact
// basepri/source masking based critical sections.
unsafe {
- crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$rtc);
- pac::NVIC::unmask(Interrupt::$rtc);
+ crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$rtc);
+ pac::NVIC::unmask(pac::Interrupt::$rtc);
}
}
-
- /// Used to access the underlying timer queue
- #[doc(hidden)]
- pub fn __tq() -> &'static TimerQueue<$mono_name> {
- &$tq
- }
-
- /// Timeout at a specific time.
- #[inline]
- pub async fn timeout_at<F: Future>(
- instant: <Self as 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: Future>(
- duration: <Self as Monotonic>::Duration,
- future: F,
- ) -> Result<F::Output, TimeoutError> {
- $tq.timeout_after(duration, future).await
- }
-
- /// Delay for some duration of time.
- #[inline]
- pub async fn delay(duration: <Self as Monotonic>::Duration) {
- $tq.delay(duration).await;
- }
-
- /// Delay to some specific time instant.
- #[inline]
- pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
- $tq.delay_until(instant).await;
- }
}
+ impl TimerQueueBackend for $backend_name {
+ type Ticks = u64;
- 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 {
- const ZERO: Self::Instant = Self::Instant::from_ticks(0);
- const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
-
- type Instant = fugit::TimerInstantU64<32_768>;
- type Duration = fugit::TimerDurationU64<32_768>;
-
- fn now() -> Self::Instant {
+ fn now() -> Self::Ticks {
let rtc = unsafe { &*$rtc::PTR };
- Self::Instant::from_ticks(calculate_now(
+ calculate_now(
|| $overflow.load(Ordering::Relaxed),
|| TimerValueU24(rtc.counter.read().bits())
- ))
+ )
}
fn on_interrupt() {
@@ -243,28 +257,35 @@ macro_rules! make_rtc {
}
}
- fn enable_timer() {}
-
- fn disable_timer() {}
-
- fn set_compare(mut instant: Self::Instant) {
+ fn set_compare(mut instant: Self::Ticks) {
let rtc = unsafe { &*$rtc::PTR };
+ const MAX: u64 = 0xff_ffff;
+
// Disable interrupts because this section is timing critical.
// We rely on the fact that this entire section runs within one
// RTC clock tick. (which it will do easily if it doesn't get
// interrupted)
critical_section::with(|_|{
let now = Self::now();
- if let Some(diff) = instant.checked_duration_since(now) {
+ // wrapping_sub deals with the u64 overflow corner case
+ let diff = instant.wrapping_sub(now);
+ let val = if diff <= MAX {
+ // Now we know `instant` whill happen within one `MAX` time duration.
+
// Errata: Timer interrupts don't fire if they are scheduled less than
// two ticks in the future. Make it three, because the timer could
// tick right now.
- if diff.ticks() < 3 {
- instant = Self::Instant::from_ticks(now.ticks().wrapping_add(3));
+ if diff < 3 {
+ instant = now.wrapping_add(3);
}
- unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xff_ffff)) };
- }
+
+ (instant & MAX) as u32
+ } else {
+ 0
+ };
+
+ unsafe { rtc.cc[0].write(|w| w.bits(val)) };
});
}
@@ -274,13 +295,17 @@ macro_rules! make_rtc {
}
fn pend_interrupt() {
- pac::NVIC::pend(Interrupt::$rtc);
+ pac::NVIC::pend(pac::Interrupt::$rtc);
+ }
+
+ fn timer_queue() -> &'static TimerQueue<Self> {
+ &$tq
}
}
};
}
-make_rtc!(Rtc0, RTC0, RTC0_OVERFLOWS, RTC0_TQ);
-make_rtc!(Rtc1, RTC1, RTC1_OVERFLOWS, RTC1_TQ);
+make_rtc!(Rtc0Backend, RTC0, RTC0_OVERFLOWS, RTC0_TQ);
+make_rtc!(Rtc1Backend, RTC1, RTC1_OVERFLOWS, RTC1_TQ);
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
-make_rtc!(Rtc2, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
+make_rtc!(Rtc2Backend, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
diff --git a/rtic-monotonics/src/nrf/timer.rs b/rtic-monotonics/src/nrf/timer.rs
index 7b760e4..60ee7dc 100644
--- a/rtic-monotonics/src/nrf/timer.rs
+++ b/rtic-monotonics/src/nrf/timer.rs
@@ -1,4 +1,4 @@
-//! [`Monotonic`] impl for the 32-bit timers of the nRF series.
+//! [`Monotonic`](rtic_time::Monotonic) implementation for the 32-bit timers of the nRF series.
//!
//! Not all timers are available on all parts. Ensure that only the available
//! timers are exposed by having the correct `nrf52*` feature enabled for `rtic-monotonics`.
@@ -6,139 +6,217 @@
//! # Example
//!
//! ```
-//! use rtic_monotonics::nrf::timer::*;
+//! use rtic_monotonics::nrf::timer::prelude::*;
+//! nrf_timer0_monotonic!(Mono);
//!
//! fn init() {
//! # // This is normally provided by the selected PAC
//! # let timer = unsafe { core::mem::transmute(()) };
-//! // Generate the required token
-//! let token = rtic_monotonics::create_nrf_timer0_monotonic_token!();
-//!
//! // Start the monotonic
-//! Timer0::start(timer, token);
+//! Mono::start(timer);
//! }
//!
//! async fn usage() {
//! loop {
//! // Use the monotonic
-//! Timer0::delay(100.millis()).await;
+//! let timestamp = Mono::now();
+//! Mono::delay(100.millis()).await;
//! }
//! }
//! ```
-use crate::{Monotonic, TimeoutError, TimerQueue};
-use atomic_polyfill::{AtomicU32, Ordering};
-use core::future::Future;
-pub use fugit::{self, ExtU64, ExtU64Ceil};
-use rtic_time::half_period_counter::calculate_now;
+/// Common definitions and traits for using the nRF Timer monotonics
+pub mod prelude {
+ pub use crate::nrf_timer0_monotonic;
+ pub use crate::nrf_timer1_monotonic;
+ pub use crate::nrf_timer2_monotonic;
+ #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
+ pub use crate::nrf_timer3_monotonic;
+ #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
+ pub use crate::nrf_timer4_monotonic;
+
+ pub use crate::Monotonic;
+ pub use fugit::{self, ExtU64, ExtU64Ceil};
+}
#[cfg(feature = "nrf52810")]
-use nrf52810_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2};
+#[doc(hidden)]
+pub use nrf52810_pac::{self as pac, TIMER0, TIMER1, TIMER2};
#[cfg(feature = "nrf52811")]
-use nrf52811_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2};
+#[doc(hidden)]
+pub use nrf52811_pac::{self as pac, TIMER0, TIMER1, TIMER2};
#[cfg(feature = "nrf52832")]
-use nrf52832_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
+#[doc(hidden)]
+pub use nrf52832_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
#[cfg(feature = "nrf52833")]
-use nrf52833_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
+#[doc(hidden)]
+pub use nrf52833_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
#[cfg(feature = "nrf52840")]
-use nrf52840_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
+#[doc(hidden)]
+pub use nrf52840_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
#[cfg(feature = "nrf5340-app")]
-use nrf5340_app_pac::{
- self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2,
+#[doc(hidden)]
+pub use nrf5340_app_pac::{
+ self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2,
};
#[cfg(feature = "nrf5340-net")]
-use nrf5340_net_pac::{
- self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2,
+#[doc(hidden)]
+pub use nrf5340_net_pac::{
+ self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2,
};
#[cfg(feature = "nrf9160")]
-use nrf9160_pac::{
- self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2,
+#[doc(hidden)]
+pub use nrf9160_pac::{self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2};
+
+use atomic_polyfill::{AtomicU32, Ordering};
+use rtic_time::{
+ half_period_counter::calculate_now,
+ timer_queue::{TimerQueue, TimerQueueBackend},
};
#[doc(hidden)]
#[macro_export]
macro_rules! __internal_create_nrf_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::nrf::timer::$mono_timer::__tq().on_monotonic_interrupt();
+ use $crate::TimerQueueBackend;
+ $crate::nrf::timer::$mono_backend::timer_queue().on_monotonic_interrupt();
}
+ };
+}
- pub struct $timer_token;
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __internal_create_nrf_timer_struct {
+ ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => {
+ /// A `Monotonic` based on the nRF Timer peripheral.
+ struct $name;
+
+ impl $name {
+ /// Starts the `Monotonic`.
+ ///
+ /// This method must be called only once.
+ pub fn start(timer: $crate::nrf::timer::$timer) {
+ $crate::__internal_create_nrf_timer_interrupt!($mono_backend, $timer);
+
+ const PRESCALER: u8 = match $tick_rate_hz {
+ 16_000_000 => 0,
+ 8_000_000 => 1,
+ 4_000_000 => 2,
+ 2_000_000 => 3,
+ 1_000_000 => 4,
+ 500_000 => 5,
+ 250_000 => 6,
+ 125_000 => 7,
+ 62_500 => 8,
+ 31_250 => 9,
+ _ => panic!("Timer cannot run at desired tick rate!"),
+ };
+
+ $crate::nrf::timer::$mono_backend::_start(timer, PRESCALER);
+ }
+ }
- unsafe impl $crate::InterruptToken<$crate::nrf::timer::$mono_timer> for $timer_token {}
+ impl $crate::TimerQueueBasedMonotonic for $name {
+ type Backend = $crate::nrf::timer::$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 Timer0 interrupt for the monotonic.
+/// Create an Timer0 based monotonic and register the TIMER0 interrupt for it.
+///
+/// See [`crate::nrf::timer`] for more details.
#[macro_export]
-macro_rules! create_nrf_timer0_monotonic_token {
- () => {{
- $crate::__internal_create_nrf_timer_interrupt!(Timer0, TIMER0, Timer0Token)
- }};
+macro_rules! nrf_timer0_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_nrf_timer_struct!($name, Timer0Backend, TIMER0, $tick_rate_hz);
+ };
}
-/// Register the Timer1 interrupt for the monotonic.
+/// Create an Timer1 based monotonic and register the TIMER1 interrupt for it.
+///
+/// See [`crate::nrf::timer`] for more details.
#[macro_export]
-macro_rules! create_nrf_timer1_monotonic_token {
- () => {{
- $crate::__internal_create_nrf_timer_interrupt!(Timer1, TIMER1, Timer1Token)
- }};
+macro_rules! nrf_timer1_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_nrf_timer_struct!($name, Timer1Backend, TIMER1, $tick_rate_hz);
+ };
}
-/// Register the Timer2 interrupt for the monotonic.
+/// Create an Timer2 based monotonic and register the TIMER2 interrupt for it.
+///
+/// See [`crate::nrf::timer`] for more details.
#[macro_export]
-macro_rules! create_nrf_timer2_monotonic_token {
- () => {{
- $crate::__internal_create_nrf_timer_interrupt!(Timer2, TIMER2, Timer2Token)
- }};
+macro_rules! nrf_timer2_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_nrf_timer_struct!($name, Timer2Backend, TIMER2, $tick_rate_hz);
+ };
}
-/// Register the Timer3 interrupt for the monotonic.
+/// Create an Timer3 based monotonic and register the TIMER3 interrupt for it.
+///
+/// See [`crate::nrf::timer`] for more details.
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
)]
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
#[macro_export]
-macro_rules! create_nrf_timer3_monotonic_token {
- () => {{
- $crate::__internal_create_nrf_timer_interrupt!(Timer3, TIMER3, Timer3Token)
- }};
+macro_rules! nrf_timer3_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_nrf_timer_struct!($name, Timer3Backend, TIMER3, $tick_rate_hz);
+ };
}
-/// Register the Timer4 interrupt for the monotonic.
+/// Create an Timer4 based monotonic and register the TIMER4 interrupt for it.
+///
+/// See [`crate::nrf::timer`] for more details.
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
)]
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
#[macro_export]
-macro_rules! create_nrf_timer4_monotonic_token {
- () => {{
- $crate::__internal_create_nrf_timer_interrupt!(Timer4, TIMER4, Timer4Token)
- }};
+macro_rules! nrf_timer4_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_nrf_timer_struct!($name, Timer4Backend, TIMER4, $tick_rate_hz);
+ };
}
macro_rules! make_timer {
- ($mono_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
- /// Monotonic timer queue implementation.
+ ($backend_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
+ /// Timer peripheral based [`TimerQueueBackend`].
$(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)?
- pub struct $mono_name;
+ pub struct $backend_name;
static $overflow: AtomicU32 = AtomicU32::new(0);
- static $tq: TimerQueue<$mono_name> = TimerQueue::new();
-
- impl $mono_name {
- /// Start the timer monotonic.
- pub fn start(timer: $timer, _interrupt_token: impl crate::InterruptToken<Self>) {
- // 1 MHz
- timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) });
+ static $tq: TimerQueue<$backend_name> = TimerQueue::new();
+
+ impl $backend_name {
+ /// Starts the timer.
+ ///
+ /// **Do not use this function directly.**
+ ///
+ /// Use the prelude macros instead.
+ pub fn _start(timer: $timer, prescaler: u8) {
+ timer.prescaler.write(|w| unsafe { w.prescaler().bits(prescaler) });
timer.bitmode.write(|w| w.bitmode()._32bit());
// Disable interrupts, as preparation
@@ -184,70 +262,25 @@ macro_rules! make_timer {
// plus we are not using any external shared resources so we won't impact
// basepri/source masking based critical sections.
unsafe {
- crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$timer);
- pac::NVIC::unmask(Interrupt::$timer);
+ crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$timer);
+ pac::NVIC::unmask(pac::Interrupt::$timer);
}
}
-
- /// Used to access the underlying timer queue
- #[doc(hidden)]
- pub fn __tq() -> &'static TimerQueue<$mono_name> {
- &$tq
- }
-
- /// Timeout at a specific time.
- #[inline]
- pub async fn timeout_at<F: Future>(
- instant: <Self as 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: Future>(
- duration: <Self as Monotonic>::Duration,
- future: F,
- ) -> Result<F::Output, TimeoutError> {
- $tq.timeout_after(duration, future).await
- }
-
- /// Delay for some duration of time.
- #[inline]
- pub async fn delay(duration: <Self as Monotonic>::Duration) {
- $tq.delay(duration).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 TimerQueueBackend for $backend_name {
+ type Ticks = u64;
- impl Monotonic for $mono_name {
- const ZERO: Self::Instant = Self::Instant::from_ticks(0);
- const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
-
- type Instant = fugit::TimerInstantU64<1_000_000>;
- type Duration = fugit::TimerDurationU64<1_000_000>;
-
- fn now() -> Self::Instant {
+ fn now() -> Self::Ticks {
let timer = unsafe { &*$timer::PTR };
- Self::Instant::from_ticks(calculate_now(
+ calculate_now(
|| $overflow.load(Ordering::Relaxed),
|| {
timer.tasks_capture[3].write(|w| unsafe { w.bits(1) });
timer.cc[3].read().bits()
}
- ))
+ )
}
fn on_interrupt() {
@@ -268,9 +301,9 @@ macro_rules! make_timer {
}
}
- fn set_compare(instant: Self::Instant) {
+ fn set_compare(instant: Self::Ticks) {
let timer = unsafe { &*$timer::PTR };
- timer.cc[0].write(|w| unsafe { w.cc().bits(instant.ticks() as u32) });
+ timer.cc[0].write(|w| unsafe { w.cc().bits(instant as u32) });
}
fn clear_compare_flag() {
@@ -279,16 +312,20 @@ macro_rules! make_timer {
}
fn pend_interrupt() {
- pac::NVIC::pend(Interrupt::$timer);
+ pac::NVIC::pend(pac::Interrupt::$timer);
+ }
+
+ fn timer_queue() -> &'static TimerQueue<$backend_name> {
+ &$tq
}
}
};
}
-make_timer!(Timer0, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ);
-make_timer!(Timer1, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ);
-make_timer!(Timer2, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ);
+make_timer!(Timer0Backend, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ);
+make_timer!(Timer1Backend, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ);
+make_timer!(Timer2Backend, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ);
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
-make_timer!(Timer3, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
+make_timer!(Timer3Backend, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
-make_timer!(Timer4, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
+make_timer!(Timer4Backend, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs
index 998b532..d4f590f 100644
--- a/rtic-monotonics/src/rp2040.rs
+++ b/rtic-monotonics/src/rp2040.rs
@@ -1,46 +1,56 @@
-//! [`Monotonic`] implementation for RP2040's Timer peripheral.
+//! [`Monotonic`](rtic_time::Monotonic) implementation for RP2040's Timer peripheral.
+//!
+//! Always runs at a fixed rate of 1 MHz.
//!
//! # Example
//!
//! ```
-//! use rtic_monotonics::rp2040::*;
+//! use rtic_monotonics::rp2040::prelude::*;
+//!
+//! rp2040_timer_monotonic!(Mono);
//!
//! fn init() {
//! # // This is normally provided by the selected PAC
//! # let timer = unsafe { core::mem::transmute(()) };
//! # let mut resets = unsafe { core::mem::transmute(()) };
-//! // Generate the required token
-//! let token = rtic_monotonics::create_rp2040_monotonic_token!();
-//!
+//! #
//! // Start the monotonic
-//! Timer::start(timer, &mut resets, token);
+//! Mono::start(timer, &mut resets);
//! }
//!
//! async fn usage() {
//! loop {
//! // Use the monotonic
-//! Timer::delay(100.millis()).await;
+//! let timestamp = Mono::now();
+//! Mono::delay(100.millis()).await;
//! }
//! }
//! ```
-use super::Monotonic;
+/// Common definitions and traits for using the RP2040 timer monotonic
+pub mod prelude {
+ pub use crate::rp2040_timer_monotonic;
-pub use super::{TimeoutError, TimerQueue};
-use core::future::Future;
-pub use fugit::{self, ExtU64, ExtU64Ceil};
-use rp2040_pac::{timer, Interrupt, NVIC, RESETS, TIMER};
+ pub use crate::Monotonic;
-/// Timer implementing [`Monotonic`] which runs at 1 MHz.
-pub struct Timer;
+ pub use fugit::{self, ExtU64, ExtU64Ceil};
+}
-impl Timer {
- /// Start a `Monotonic` based on RP2040's Timer.
- pub fn start(
- timer: TIMER,
- resets: &RESETS,
- _interrupt_token: impl crate::InterruptToken<Self>,
- ) {
+use crate::TimerQueueBackend;
+use rp2040_pac::{timer, Interrupt, NVIC};
+pub use rp2040_pac::{RESETS, TIMER};
+use rtic_time::timer_queue::TimerQueue;
+
+/// Timer implementing [`TimerQueueBackend`].
+pub struct TimerBackend;
+
+impl TimerBackend {
+ /// Starts the monotonic timer.
+ ///
+ /// **Do not use this function directly.**
+ ///
+ /// Use the prelude macros instead.
+ pub fn _start(timer: TIMER, resets: &RESETS) {
resets.reset.modify(|_, w| w.timer().clear_bit());
while resets.reset_done.read().timer().bit_is_clear() {}
timer.inte.modify(|_, w| w.alarm_0().bit(true));
@@ -58,55 +68,12 @@ impl Timer {
}
}
-static TIMER_QUEUE: TimerQueue<Timer> = TimerQueue::new();
-
-// Forward timerqueue interface
-impl Timer {
- /// Used to access the underlying timer queue
- #[doc(hidden)]
- pub fn __tq() -> &'static TimerQueue<Timer> {
- &TIMER_QUEUE
- }
-
- /// Timeout at a specific time.
- #[inline]
- pub async fn timeout_at<F: Future>(
- instant: <Self as Monotonic>::Instant,
- future: F,
- ) -> Result<F::Output, TimeoutError> {
- TIMER_QUEUE.timeout_at(instant, future).await
- }
-
- /// Timeout after a specific duration.
- #[inline]
- pub async fn timeout_after<F: Future>(
- duration: <Self as Monotonic>::Duration,
- future: F,
- ) -> Result<F::Output, TimeoutError> {
- TIMER_QUEUE.timeout_after(duration, future).await
- }
-
- /// Delay for some duration of time.
- #[inline]
- pub async fn delay(duration: <Self as Monotonic>::Duration) {
- TIMER_QUEUE.delay(duration).await;
- }
-
- /// Delay to some specific time instant.
- #[inline]
- pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
- TIMER_QUEUE.delay_until(instant).await;
- }
-}
-
-impl Monotonic for Timer {
- type Instant = fugit::TimerInstantU64<1_000_000>;
- type Duration = fugit::TimerDurationU64<1_000_000>;
+static TIMER_QUEUE: TimerQueue<TimerBackend> = TimerQueue::new();
- const ZERO: Self::Instant = Self::Instant::from_ticks(0);
- const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
+impl TimerQueueBackend for TimerBackend {
+ type Ticks = u64;
- fn now() -> Self::Instant {
+ fn now() -> Self::Ticks {
let timer = Self::timer();
let mut hi0 = timer.timerawh.read().bits();
@@ -114,22 +81,24 @@ impl Monotonic for Timer {
let low = timer.timerawl.read().bits();
let hi1 = timer.timerawh.read().bits();
if hi0 == hi1 {
- break Self::Instant::from_ticks((u64::from(hi0) << 32) | u64::from(low));
+ break ((u64::from(hi0) << 32) | u64::from(low));
}
hi0 = hi1;
}
}
- fn set_compare(instant: Self::Instant) {
+ fn set_compare(instant: Self::Ticks) {
let now = Self::now();
- let max = u32::MAX as u64;
+ const MAX: u64 = u32::MAX as u64;
// Since the timer may or may not overflow based on the requested compare val, we check
// how many ticks are left.
- let val = match instant.checked_duration_since(now) {
- Some(x) if x.ticks() <= max => instant.duration_since_epoch().ticks() & max, // Will not overflow
- _ => 0, // Will overflow or in the past, set the same value as after overflow to not get extra interrupts
+ // `wrapping_sup` takes care of the u64 integer overflow special case.
+ let val = if instant.wrapping_sub(now) <= MAX {
+ instant & MAX
+ } else {
+ 0
};
Self::timer()
@@ -145,32 +114,55 @@ impl Monotonic for Timer {
rp2040_pac::NVIC::pend(Interrupt::TIMER_IRQ_0);
}
- fn on_interrupt() {}
-
- fn enable_timer() {}
-
- fn disable_timer() {}
+ fn timer_queue() -> &'static TimerQueue<Self> {
+ &TIMER_QUEUE
+ }
}
-rtic_time::embedded_hal_delay_impl_fugit64!(Timer);
-
-#[cfg(feature = "embedded-hal-async")]
-rtic_time::embedded_hal_async_delay_impl_fugit64!(Timer);
-
-/// Register the Timer interrupt for the monotonic.
+/// Create an RP2040 timer based monotonic and register the necessary interrupt for it.
+///
+/// See [`crate::rp2040`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
#[macro_export]
-macro_rules! create_rp2040_monotonic_token {
- () => {{
- #[no_mangle]
- #[allow(non_snake_case)]
- unsafe extern "C" fn TIMER_IRQ_0() {
- $crate::rp2040::Timer::__tq().on_monotonic_interrupt();
+macro_rules! rp2040_timer_monotonic {
+ ($name:ident) => {
+ /// A `Monotonic` based on the RP2040 Timer peripheral.
+ struct $name;
+
+ impl $name {
+ /// Starts the `Monotonic`.
+ ///
+ /// This method must be called only once.
+ pub fn start(timer: $crate::rp2040::TIMER, resets: &$crate::rp2040::RESETS) {
+ #[no_mangle]
+ #[allow(non_snake_case)]
+ unsafe extern "C" fn TIMER_IRQ_0() {
+ use $crate::TimerQueueBackend;
+ $crate::rp2040::TimerBackend::timer_queue().on_monotonic_interrupt();
+ }
+
+ $crate::rp2040::TimerBackend::_start(timer, resets);
+ }
}
- pub struct Rp2040Token;
-
- unsafe impl $crate::InterruptToken<$crate::rp2040::Timer> for Rp2040Token {}
+ impl $crate::TimerQueueBasedMonotonic for $name {
+ type Backend = $crate::rp2040::TimerBackend;
+ type Instant = $crate::fugit::Instant<
+ <Self::Backend as $crate::TimerQueueBackend>::Ticks,
+ 1,
+ 1_000_000,
+ >;
+ type Duration = $crate::fugit::Duration<
+ <Self::Backend as $crate::TimerQueueBackend>::Ticks,
+ 1,
+ 1_000_000,
+ >;
+ }
- Rp2040Token
- }};
+ $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
+ $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
+ };
}
diff --git a/rtic-monotonics/src/stm32.rs b/rtic-monotonics/src/stm32.rs
index 68f95a2..92800c7 100644
--- a/rtic-monotonics/src/stm32.rs
+++ b/rtic-monotonics/src/stm32.rs
@@ -1,4 +1,4 @@
-//! [`Monotonic`] impl for the STM32.
+//! [`Monotonic`](rtic_time::Monotonic) implementations for STM32 chips.
//!
//! Not all timers are available on all parts. Ensure that only available
//! timers are exposed by having the correct `stm32*` feature enabled for `rtic-monotonics`.
@@ -6,38 +6,56 @@
//! # Example
//!
//! ```
-//! use rtic_monotonics::stm32::*;
-//! use rtic_monotonics::stm32::Tim2 as Mono;
-//! use rtic_monotonics::Monotonic;
-//! use embassy_stm32::peripherals::TIM2;
-//! use embassy_stm32::rcc::low_level::RccPeripheral;
+//! use rtic_monotonics::stm32::prelude::*;
//!
-//! fn init() {
-//! // Generate timer token to ensure correct timer interrupt handler is used.
-//! let token = rtic_monotonics::create_stm32_tim2_monotonic_token!();
+//! // Define the monotonic and set it to 1MHz tick rate
+//! stm32_tim2_monotonic!(Mono, 1_000_000);
//!
+//! fn init() {
//! // If using `embassy-stm32` HAL, timer clock can be read out like this:
-//! let timer_clock_hz = TIM2::frequency();
+//! let timer_clock_hz = embassy_stm32::peripherals::TIM2::frequency();
//! // Or define it manually if you are using other HAL or know correct frequency:
//! let timer_clock_hz = 64_000_000;
//!
//! // Start the monotonic
-//! Mono::start(timer_clock_hz, token);
+//! Mono::start(timer_clock_hz);
//! }
//!
//! 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};
+/// Common definitions and traits for using the STM32 monotonics
+pub mod prelude {
+ #[cfg(feature = "stm32_tim2")]
+ pub use crate::stm32_tim2_monotonic;
+
+ #[cfg(feature = "stm32_tim3")]
+ pub use crate::stm32_tim3_monotonic;
+
+ #[cfg(feature = "stm32_tim4")]
+ pub use crate::stm32_tim4_monotonic;
+
+ #[cfg(feature = "stm32_tim5")]
+ pub use crate::stm32_tim5_monotonic;
+
+ #[cfg(feature = "stm32_tim15")]
+ pub use crate::stm32_tim15_monotonic;
+
+ pub use crate::Monotonic;
+ pub use fugit::{self, ExtU64, ExtU64Ceil};
+}
+
use atomic_polyfill::{AtomicU64, Ordering};
-pub use fugit::{self, ExtU64, ExtU64Ceil};
-use rtic_time::half_period_counter::calculate_now;
+use rtic_time::{
+ half_period_counter::calculate_now,
+ timer_queue::{TimerQueue, TimerQueueBackend},
+};
use stm32_metapac as pac;
mod _generated {
@@ -48,116 +66,180 @@ mod _generated {
include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
}
-const TIMER_HZ: u32 = 1_000_000;
-
#[doc(hidden)]
#[macro_export]
macro_rules! __internal_create_stm32_timer_interrupt {
- ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{
+ ($mono_backend:ident, $interrupt_name:ident) => {
#[no_mangle]
#[allow(non_snake_case)]
- unsafe extern "C" fn $timer() {
- $crate::stm32::$mono_timer::__tq().on_monotonic_interrupt();
+ unsafe extern "C" fn $interrupt_name() {
+ use $crate::TimerQueueBackend;
+ $crate::stm32::$mono_backend::timer_queue().on_monotonic_interrupt();
}
+ };
+}
- pub struct $timer_token;
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __internal_create_stm32_timer_struct {
+ ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => {
+ struct $name;
- unsafe impl $crate::InterruptToken<$crate::stm32::$mono_timer> for $timer_token {}
+ impl $name {
+ /// Starts the `Monotonic`.
+ ///
+ /// - `tim_clock_hz`: `TIMx` peripheral clock frequency.
+ ///
+ /// Panics if it is impossible to achieve the desired monotonic tick rate based
+ /// on the given `tim_clock_hz` parameter. If that happens, adjust the desired monotonic tick rate.
+ ///
+ /// This method must be called only once.
+ pub fn start(tim_clock_hz: u32) {
+ $crate::__internal_create_stm32_timer_interrupt!($mono_backend, $timer);
- $timer_token
- }};
+ $crate::stm32::$mono_backend::_start(tim_clock_hz, $tick_rate_hz);
+ }
+ }
+
+ impl $crate::TimerQueueBasedMonotonic for $name {
+ type Backend = $crate::stm32::$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 },
+ >;
+ }
+
+ $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
+ $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
+ };
}
-/// Register TIM2 interrupt for the monotonic.
+/// Create a TIM2 based monotonic and register the TIM2 interrupt for it.
+///
+/// See [`crate::stm32`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
+/// * `tick_rate_hz` - The tick rate of the timer peripheral.
+///
#[cfg(feature = "stm32_tim2")]
#[macro_export]
-macro_rules! create_stm32_tim2_monotonic_token {
- () => {{
- $crate::__internal_create_stm32_timer_interrupt!(Tim2, TIM2, Tim2Token)
- }};
+macro_rules! stm32_tim2_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_stm32_timer_struct!($name, Tim2Backend, TIM2, $tick_rate_hz);
+ };
}
-/// Register TIM3 interrupt for the monotonic.
+/// Create a TIM3 based monotonic and register the TIM3 interrupt for it.
+///
+/// See [`crate::stm32`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
+/// * `tick_rate_hz` - The tick rate of the timer peripheral.
+///
#[cfg(feature = "stm32_tim3")]
#[macro_export]
-macro_rules! create_stm32_tim3_monotonic_token {
- () => {{
- $crate::__internal_create_stm32_timer_interrupt!(Tim3, TIM3, Tim3Token)
- }};
+macro_rules! stm32_tim3_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_stm32_timer_struct!($name, Tim3Backend, TIM3, $tick_rate_hz);
+ };
}
-/// Register TIM4 interrupt for the monotonic.
+/// Create a TIM4 based monotonic and register the TIM4 interrupt for it.
+///
+/// See [`crate::stm32`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
+/// * `tick_rate_hz` - The tick rate of the timer peripheral.
+///
#[cfg(feature = "stm32_tim4")]
#[macro_export]
-macro_rules! create_stm32_tim4_monotonic_token {
- () => {{
- $crate::__internal_create_stm32_timer_interrupt!(Tim4, TIM4, Tim4Token)
- }};
+macro_rules! stm32_tim4_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_stm32_timer_struct!($name, Tim4Backend, TIM4, $tick_rate_hz);
+ };
}
-/// Register TIM5 interrupt for the monotonic.
+/// Create a TIM5 based monotonic and register the TIM5 interrupt for it.
+///
+/// See [`crate::stm32`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
+/// * `tick_rate_hz` - The tick rate of the timer peripheral.
+///
#[cfg(feature = "stm32_tim5")]
#[macro_export]
-macro_rules! create_stm32_tim5_monotonic_token {
- () => {{
- $crate::__internal_create_stm32_timer_interrupt!(Tim5, TIM5, Tim5Token)
- }};
-}
-
-/// Register TIM12 interrupt for the monotonic.
-#[cfg(feature = "stm32_tim12")]
-#[macro_export]
-macro_rules! create_stm32_tim12_monotonic_token {
- () => {{
- $crate::__internal_create_stm32_timer_interrupt!(Tim12, TIM12, Tim12Token)
- }};
+macro_rules! stm32_tim5_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_stm32_timer_struct!($name, Tim5Backend, TIM5, $tick_rate_hz);
+ };
}
-/// Register TIM15 interrupt for the monotonic.
+/// Create a TIM15 based monotonic and register the TIM15 interrupt for it.
+///
+/// See [`crate::stm32`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
+/// * `tick_rate_hz` - The tick rate of the timer peripheral.
+///
#[cfg(feature = "stm32_tim15")]
#[macro_export]
-macro_rules! create_stm32_tim15_monotonic_token {
- () => {{
- $crate::__internal_create_stm32_timer_interrupt!(Tim15, TIM15, Tim15Token)
- }};
+macro_rules! stm32_tim15_monotonic {
+ ($name:ident, $tick_rate_hz:expr) => {
+ $crate::__internal_create_stm32_timer_struct!($name, Tim15Backend, TIM15, $tick_rate_hz);
+ };
}
macro_rules! make_timer {
- ($mono_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
- /// Monotonic timer queue implementation.
+ ($backend_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
+ /// Monotonic timer backend implementation.
$(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)?
- pub struct $mono_name;
+ pub struct $backend_name;
use pac::$timer;
static $overflow: AtomicU64 = AtomicU64::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.
///
- /// - `tim_clock_hz`: `TIMx` peripheral clock frequency.
- /// - `_interrupt_token`: Required for correct timer interrupt handling.
+ /// **Do not use this function directly.**
///
- /// This method must be called only once.
- pub fn start(tim_clock_hz: u32, _interrupt_token: impl crate::InterruptToken<Self>) {
+ /// Use the prelude macros instead.
+ pub fn _start(tim_clock_hz: u32, timer_hz: u32) {
_generated::$timer::enable();
_generated::$timer::reset();
$timer.cr1().modify(|r| r.set_cen(false));
- assert!((tim_clock_hz % TIMER_HZ) == 0, "Unable to find suitable timer prescaler value!");
- let psc = tim_clock_hz / TIMER_HZ - 1;
+ assert!((tim_clock_hz % timer_hz) == 0, "Unable to find suitable timer prescaler value!");
+ let psc = tim_clock_hz / timer_hz - 1;
$timer.psc().write(|r| r.set_psc(psc as u16));
// Enable full-period interrupt.
$timer.dier().modify(|r| r.set_uie(true));
// Configure and enable half-period interrupt
- $timer.ccr(2).write(|r| r.set_ccr($bits::MAX - ($bits::MAX >> 1)));
+ $timer.ccr(2).write(|r| r.set_ccr(($bits::MAX - ($bits::MAX >> 1)).into()));
$timer.dier().modify(|r| r.set_ccie(2, true));
// Trigger an update event to load the prescaler value to the clock.
@@ -183,73 +265,31 @@ macro_rules! make_timer {
cortex_m::peripheral::NVIC::unmask(pac::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 TimerQueueBackend for $backend_name {
+ type Ticks = u64;
- impl Monotonic for $mono_name {
- type Instant = fugit::TimerInstantU64<TIMER_HZ>;
- type Duration = fugit::TimerDurationU64<TIMER_HZ>;
-
- const ZERO: Self::Instant = Self::Instant::from_ticks(0);
- const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
-
- fn now() -> Self::Instant {
- Self::Instant::from_ticks(calculate_now(
+ fn now() -> Self::Ticks {
+ calculate_now(
|| $overflow.load(Ordering::Relaxed),
|| $timer.cnt().read().cnt()
- ))
+ )
}
- fn set_compare(instant: Self::Instant) {
+ fn set_compare(instant: Self::Ticks) {
let now = Self::now();
// Since the timer may or may not overflow based on the requested compare val, we check how many ticks are left.
- let val = match instant.checked_duration_since(now) {
- None => 0, // In the past
- Some(x) if x.ticks() <= ($bits::MAX as u64) => instant.duration_since_epoch().ticks() as $bits, // Will not overflow
- Some(_x) => 0, // Will overflow
+ // `wrapping_sup` takes care of the u64 integer overflow special case.
+ let val = if instant.wrapping_sub(now) <= ($bits::MAX as u64) {
+ instant as $bits
+ } else {
+ // In the past or will overflow
+ 0
};
- $timer.ccr(1).write(|r| r.set_ccr(val));
+ $timer.ccr(1).write(|r| r.set_ccr(val.into()));
}
fn clear_compare_flag() {
@@ -282,24 +322,25 @@ macro_rules! make_timer {
assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!");
}
}
+
+ fn timer_queue() -> &'static TimerQueue<$backend_name> {
+ &$tq
+ }
}
};
}
#[cfg(feature = "stm32_tim2")]
-make_timer!(Tim2, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ);
+make_timer!(Tim2Backend, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ);
#[cfg(feature = "stm32_tim3")]
-make_timer!(Tim3, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ);
+make_timer!(Tim3Backend, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ);
#[cfg(feature = "stm32_tim4")]
-make_timer!(Tim4, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ);
+make_timer!(Tim4Backend, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ);
#[cfg(feature = "stm32_tim5")]
-make_timer!(Tim5, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ);
-
-#[cfg(feature = "stm32_tim12")]
-make_timer!(Tim12, TIM12, u16, TIMER12_OVERFLOWS, TIMER12_TQ);
+make_timer!(Tim5Backend, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ);
#[cfg(feature = "stm32_tim15")]
-make_timer!(Tim15, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ);
+make_timer!(Tim15Backend, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ);
diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs
index 9bd056c..cc6ea3e 100644
--- a/rtic-monotonics/src/systick.rs
+++ b/rtic-monotonics/src/systick.rs
@@ -1,93 +1,79 @@
-//! [`Monotonic`] based on Cortex-M SysTick. Note: this implementation is inefficient as it
+//! [`Monotonic`](rtic_time::Monotonic) based on Cortex-M SysTick.
+//! Note: this implementation is inefficient as it
//! ticks and generates interrupts at a constant rate.
//!
-//! Currently, the following tick rates are supported:
-//!
-//! | Feature | Tick rate | Precision |
-//! |:----------------:|----------:|----------:|
-//! | (none / default) | 1 kHz | 1 ms |
-//! | systick-100hz | 100 Hz | 10 ms |
-//! | systick-10khz | 10 kHz | 0.1 ms |
-
//! # Example
//!
//! ```
-//! use rtic_monotonics::systick::*;
+//! use rtic_monotonics::systick::prelude::*;
+//! systick_monotonic!(Mono, 1_000);
//!
//! fn init() {
//! # // This is normally provided by the selected PAC
//! # let systick = unsafe { core::mem::transmute(()) };
-//! // Generate the required token
-//! let systick_token = rtic_monotonics::create_systick_token!();
-//!
+//! #
//! // Start the monotonic
-//! Systick::start(systick, 12_000_000, systick_token);
+//! Mono::start(systick, 12_000_000);
//! }
//!
//! async fn usage() {
//! loop {
//! // Use the monotonic
+//! let timestamp = Mono::now();
//! Systick::delay(100.millis()).await;
//! }
//! }
//! ```
-use super::Monotonic;
-pub use super::{TimeoutError, TimerQueue};
+/// Common definitions and traits for using the systick monotonic
+pub mod prelude {
+ pub use crate::systick_monotonic;
+
+ pub use crate::Monotonic;
+
+ cfg_if::cfg_if! {
+ if #[cfg(feature = "systick-64bit")] {
+ pub use fugit::{self, ExtU64, ExtU64Ceil};
+ } else {
+ pub use fugit::{self, ExtU32, ExtU32Ceil};
+ }
+ }
+}
+
+pub use cortex_m::peripheral::SYST;
+
use atomic_polyfill::Ordering;
-use core::future::Future;
-use cortex_m::peripheral::SYST;
-pub use fugit;
+use rtic_time::timer_queue::TimerQueue;
+
+use crate::TimerQueueBackend;
+
cfg_if::cfg_if! {
if #[cfg(feature = "systick-64bit")] {
- pub use fugit::{ExtU64, ExtU64Ceil};
use atomic_polyfill::AtomicU64;
static SYSTICK_CNT: AtomicU64 = AtomicU64::new(0);
} else {
- pub use fugit::{ExtU32, ExtU32Ceil};
use atomic_polyfill::AtomicU32;
static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0);
}
}
-static SYSTICK_TIMER_QUEUE: TimerQueue<Systick> = TimerQueue::new();
-// Features should be additive, here systick-100hz gets picked if both
-// `systick-100hz` and `systick-10khz` are enabled.
+static SYSTICK_TIMER_QUEUE: TimerQueue<SystickBackend> = TimerQueue::new();
-cfg_if::cfg_if! {
- if #[cfg(feature = "systick-100hz")]
- {
- const TIMER_HZ: u32 = 100;
- } else if #[cfg(feature = "systick-10khz")]
- {
- const TIMER_HZ: u32 = 10_000;
- } else {
- // Default case is 1 kHz
- const TIMER_HZ: u32 = 1_000;
- }
-}
-
-/// Systick implementing [`Monotonic`] which runs at 1 kHz, 100Hz or 10 kHz.
-pub struct Systick;
+/// Systick based [`TimerQueueBackend`].
+pub struct SystickBackend;
-impl Systick {
- /// Start a `Monotonic` based on SysTick.
- ///
- /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from
- /// the clock generation function of the used HAL.
+impl SystickBackend {
+ /// Starts the monotonic timer.
///
- /// Notice that the actual rate of the timer is a best approximation based on the given
- /// `sysclk` and `TIMER_HZ`.
+ /// **Do not use this function directly.**
///
- /// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue.
- pub fn start(
- mut systick: cortex_m::peripheral::SYST,
- sysclk: u32,
- _interrupt_token: impl crate::InterruptToken<Self>,
- ) {
- // + TIMER_HZ / 2 provides round to nearest instead of round to 0.
- // - 1 as the counter range is inclusive [0, reload]
- let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1;
+ /// Use the prelude macros instead.
+ pub fn _start(mut systick: SYST, sysclk: u32, timer_hz: u32) {
+ assert!(
+ (sysclk % timer_hz) == 0,
+ "timer_hz cannot evenly divide sysclk! Please adjust the timer or sysclk frequency."
+ );
+ let reload = sysclk / timer_hz - 1;
assert!(reload <= 0x00ff_ffff);
assert!(reload > 0);
@@ -98,7 +84,7 @@ impl Systick {
systick.enable_interrupt();
systick.enable_counter();
- SYSTICK_TIMER_QUEUE.initialize(Systick {});
+ SYSTICK_TIMER_QUEUE.initialize(SystickBackend {});
}
fn systick() -> SYST {
@@ -106,67 +92,24 @@ impl Systick {
}
}
-// Forward timerqueue interface
-impl Systick {
- /// Used to access the underlying timer queue
- #[doc(hidden)]
- pub fn __tq() -> &'static TimerQueue<Systick> {
- &SYSTICK_TIMER_QUEUE
- }
-
- /// Timeout at a specific time.
- pub async fn timeout_at<F: Future>(
- instant: <Self as Monotonic>::Instant,
- future: F,
- ) -> Result<F::Output, TimeoutError> {
- SYSTICK_TIMER_QUEUE.timeout_at(instant, future).await
- }
-
- /// Timeout after a specific duration.
- #[inline]
- pub async fn timeout_after<F: Future>(
- duration: <Self as Monotonic>::Duration,
- future: F,
- ) -> Result<F::Output, TimeoutError> {
- SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await
- }
-
- /// Delay for some duration of time.
- #[inline]
- pub async fn delay(duration: <Self as Monotonic>::Duration) {
- SYSTICK_TIMER_QUEUE.delay(duration).await;
- }
-
- /// Delay to some specific time instant.
- #[inline]
- pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
- SYSTICK_TIMER_QUEUE.delay_until(instant).await;
- }
-}
-
-impl Monotonic for Systick {
+impl TimerQueueBackend for SystickBackend {
cfg_if::cfg_if! {
if #[cfg(feature = "systick-64bit")] {
- type Instant = fugit::TimerInstantU64<TIMER_HZ>;
- type Duration = fugit::TimerDurationU64<TIMER_HZ>;
+ type Ticks = u64;
} else {
- type Instant = fugit::TimerInstantU32<TIMER_HZ>;
- type Duration = fugit::TimerDurationU32<TIMER_HZ>;
+ type Ticks = u32;
}
}
- 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 {
if Self::systick().has_wrapped() {
SYSTICK_CNT.fetch_add(1, Ordering::AcqRel);
}
- Self::Instant::from_ticks(SYSTICK_CNT.load(Ordering::Relaxed))
+ SYSTICK_CNT.load(Ordering::Relaxed)
}
- fn set_compare(_: Self::Instant) {
+ fn set_compare(_: Self::Ticks) {
// No need to do something here, we get interrupts anyway.
}
@@ -184,39 +127,66 @@ impl Monotonic for Systick {
}
}
- fn enable_timer() {}
-
- fn disable_timer() {}
-}
-
-cfg_if::cfg_if! {
- if #[cfg(feature = "systick-64bit")] {
- rtic_time::embedded_hal_delay_impl_fugit64!(Systick);
-
- #[cfg(feature = "embedded-hal-async")]
- rtic_time::embedded_hal_async_delay_impl_fugit64!(Systick);
- } else {
- rtic_time::embedded_hal_delay_impl_fugit32!(Systick);
-
- #[cfg(feature = "embedded-hal-async")]
- rtic_time::embedded_hal_async_delay_impl_fugit32!(Systick);
+ fn timer_queue() -> &'static TimerQueue<Self> {
+ &SYSTICK_TIMER_QUEUE
}
}
-/// Register the Systick interrupt for the monotonic.
+/// Create a Systick based monotonic and register the Systick interrupt for it.
+///
+/// See [`crate::systick`] for more details.
+///
+/// # Arguments
+///
+/// * `name` - The name that the monotonic type will have.
+/// * `tick_rate_hz` - The tick rate of the timer peripheral.
+/// Can be omitted; defaults to 1kHz.
#[macro_export]
-macro_rules! create_systick_token {
- () => {{
- #[no_mangle]
- #[allow(non_snake_case)]
- unsafe extern "C" fn SysTick() {
- $crate::systick::Systick::__tq().on_monotonic_interrupt();
+macro_rules! systick_monotonic {
+ ($name:ident) => {
+ $crate::systick_monotonic($name, 1_000);
+ };
+ ($name:ident, $tick_rate_hz:expr) => {
+ /// A `Monotonic` based on SysTick.
+ struct $name;
+
+ impl $name {
+ /// Starts the `Monotonic`.
+ ///
+ /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from
+ /// the clock generation function of the used HAL.
+ ///
+ /// Panics if it is impossible to achieve the desired monotonic tick rate based
+ /// on the given `sysclk` parameter. If that happens, adjust the desired monotonic tick rate.
+ ///
+ /// This method must be called only once.
+ pub fn start(systick: $crate::systick::SYST, sysclk: u32) {
+ #[no_mangle]
+ #[allow(non_snake_case)]
+ unsafe extern "C" fn SysTick() {
+ use $crate::TimerQueueBackend;
+ $crate::systick::SystickBackend::timer_queue().on_monotonic_interrupt();
+ }
+
+ $crate::systick::SystickBackend::_start(systick, sysclk, $tick_rate_hz);
+ }
}
- pub struct SystickToken;
-
- unsafe impl $crate::InterruptToken<$crate::systick::Systick> for SystickToken {}
+ impl $crate::TimerQueueBasedMonotonic for $name {
+ type Backend = $crate::systick::SystickBackend;
+ 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 },
+ >;
+ }
- SystickToken
- }};
+ $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
+ $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
+ };
}
diff --git a/rtic-time/CHANGELOG.md b/rtic-time/CHANGELOG.md
index f3c9792..197c426 100644
--- a/rtic-time/CHANGELOG.md
+++ b/rtic-time/CHANGELOG.md
@@ -5,11 +5,18 @@ This project adheres to [Semantic Versioning](http://semver.org/).
For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
-## Unreleased
+## Unreleased - v2.0.0
+
### Added
### Changed
+- Full rewrite of the `Monotonic` API.
+ - Now split into multiple traits:
+ - `Monotonic` - A user-facing trait that defines what the functionality of a monotonic is.
+ - `TimerQueueBackend` - The set of functionality a backend must provide in order to be used with the `TimerQueue`.
+ - `TimerQueue` is now purely based on ticks and has no concept of real time.
+ - The `TimerQueueBasedMonotonic` trait implements a `Monotonic` based on a `TimerQueueBackend`, translating ticks into `Instant` and `Duration`.
### Fixed
diff --git a/rtic-time/Cargo.toml b/rtic-time/Cargo.toml
index 4ad91cd..93d3224 100644
--- a/rtic-time/Cargo.toml
+++ b/rtic-time/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "rtic-time"
-version = "1.3.0"
+version = "2.0.0"
edition = "2021"
authors = [
@@ -11,7 +11,7 @@ authors = [
"Per Lindgren <per.lindgren@ltu.se>",
]
categories = ["concurrency", "embedded", "no-std", "asynchronous"]
-description = "rtic-time lib TODO"
+description = "Basic definitions and utilities that can be used to keep track of time"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rtic-rs/rtic"
@@ -21,11 +21,11 @@ repository = "https://github.com/rtic-rs/rtic"
critical-section = "1"
futures-util = { version = "0.3.25", default-features = false }
rtic-common = { version = "1.0.0", path = "../rtic-common" }
+embedded-hal = { version = "1.0.0" }
+embedded-hal-async = { version = "1.0.0" }
+fugit = "0.3.7"
[dev-dependencies]
-embedded-hal = { version = "1.0" }
-embedded-hal-async = { version = "1.0" }
-fugit = "0.3.7"
parking_lot = "0.12"
cassette = "0.2"
cooked-waker = "5.0.0"
diff --git a/rtic-time/README.md b/rtic-time/README.md
new file mode 100644
index 0000000..53c2944
--- /dev/null
+++ b/rtic-time/README.md
@@ -0,0 +1,29 @@
+# rtic-time
+
+Basic definitions and utilities that can be used to keep track of time.
+
+[![crates.io](https://img.shields.io/crates/v/rtic-time)](https://crates.io/crates/rtic-time)
+[![docs.rs](https://docs.rs/rtic-time/badge.svg)](https://docs.rs/rtic-time)
+[![matrix](https://img.shields.io/matrix/rtic:matrix.org)](https://matrix.to/#/#rtic:matrix.org)
+
+
+## Content
+
+The main contribution of this crate is to define the [`Monotonic`](https://docs.rs/rtic-time/latest/rtic_time/trait.Monotonic.html) trait. It serves as a standardized interface for libraries to interact with the system's monotonic timers.
+
+Additionally, this crate provides tools and utilities that help with implementing monotonic timers.
+
+## Implementations of the `Monotonic` trait
+
+For implementations of [`Monotonic`](https://docs.rs/rtic-time/latest/rtic_time/trait.Monotonic.html)
+on various hardware, see [`rtic-monotonics`](https://docs.rs/rtic-monotonics/).
+
+
+## Chat
+
+Join us and talk about RTIC in the [Matrix room][matrix-room].
+
+Weekly meeting minutes can be found over at [RTIC HackMD][hackmd].
+
+[matrix-room]: https://matrix.to/#/#rtic:matrix.org
+[hackmd]: https://rtic.rs/meeting
diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs
index 9cd20d5..7b051da 100644
--- a/rtic-time/src/lib.rs
+++ b/rtic-time/src/lib.rs
@@ -5,285 +5,60 @@
#![no_std]
#![deny(missing_docs)]
-#![allow(incomplete_features)]
-
-use core::future::{poll_fn, Future};
-use core::pin::Pin;
-use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
-use core::task::{Poll, Waker};
-use futures_util::{
- future::{select, Either},
- pin_mut,
-};
-use linked_list::{Link, LinkedList};
-pub use monotonic::Monotonic;
-use rtic_common::dropper::OnDrop;
+#![allow(async_fn_in_trait)]
pub mod half_period_counter;
mod linked_list;
-mod monotonic;
-
-/// Holds a waker and at which time instant this waker shall be awoken.
-struct WaitingWaker<Mono: Monotonic> {
- waker: Waker,
- release_at: Mono::Instant,
- was_popped: AtomicBool,
-}
-
-impl<Mono: Monotonic> Clone for WaitingWaker<Mono> {
- fn clone(&self) -> Self {
- Self {
- waker: self.waker.clone(),
- release_at: self.release_at,
- was_popped: AtomicBool::new(self.was_popped.load(Ordering::Relaxed)),
- }
- }
-}
-
-impl<Mono: Monotonic> PartialEq for WaitingWaker<Mono> {
- fn eq(&self, other: &Self) -> bool {
- self.release_at == other.release_at
- }
-}
-
-impl<Mono: Monotonic> PartialOrd for WaitingWaker<Mono> {
- fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
- self.release_at.partial_cmp(&other.release_at)
- }
-}
-
-/// A generic timer queue for async executors.
-///
-/// # Blocking
-///
-/// The internal priority queue uses global critical sections to manage access. This means that
-/// `await`ing a delay will cause a lock of the entire system for O(n) time. In practice the lock
-/// duration is ~10 clock cycles per element in the queue.
-///
-/// # Safety
-///
-/// This timer queue is based on an intrusive linked list, and by extension the links are strored
-/// on the async stacks of callers. The links are deallocated on `drop` or when the wait is
-/// complete.
-///
-/// Do not call `mem::forget` on an awaited future, or there will be dragons!
-pub struct TimerQueue<Mono: Monotonic> {
- queue: LinkedList<WaitingWaker<Mono>>,
- initialized: AtomicBool,
-}
+pub mod monotonic;
+pub mod timer_queue;
/// This indicates that there was a timeout.
pub struct TimeoutError;
-/// This is needed to make the async closure in `delay_until` accept that we "share"
-/// the link possible between threads.
-struct LinkPtr<Mono: Monotonic>(*mut Option<linked_list::Link<WaitingWaker<Mono>>>);
-
-impl<Mono: Monotonic> Clone for LinkPtr<Mono> {
- fn clone(&self) -> Self {
- LinkPtr(self.0)
- }
-}
-
-impl<Mono: Monotonic> LinkPtr<Mono> {
- /// This will dereference the pointer stored within and give out an `&mut`.
- unsafe fn get(&mut self) -> &mut Option<linked_list::Link<WaitingWaker<Mono>>> {
- &mut *self.0
- }
-}
-
-unsafe impl<Mono: Monotonic> Send for LinkPtr<Mono> {}
-unsafe impl<Mono: Monotonic> Sync for LinkPtr<Mono> {}
-
-impl<Mono: Monotonic> TimerQueue<Mono> {
- /// Make a new queue.
- pub const fn new() -> Self {
- Self {
- queue: LinkedList::new(),
- initialized: AtomicBool::new(false),
- }
- }
-
- /// Forwards the `Monotonic::now()` method.
- #[inline(always)]
- pub fn now(&self) -> Mono::Instant {
- Mono::now()
- }
-
- /// Takes the initialized monotonic to initialize the TimerQueue.
- pub fn initialize(&self, monotonic: Mono) {
- self.initialized.store(true, Ordering::SeqCst);
-
- // Don't run drop on `Mono`
- core::mem::forget(monotonic);
- }
+/// Re-export for macros
+pub use embedded_hal;
+/// Re-export for macros
+pub use embedded_hal_async;
- /// Call this in the interrupt handler of the hardware timer supporting the `Monotonic`
+/// # A monotonic clock / counter definition.
+///
+/// ## Correctness
+///
+/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This
+/// is a requirement on the time library that the user chooses to use.
+pub trait Monotonic {
+ /// The type for instant, defining an instant in time.
///
- /// # Safety
+ /// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used.
+ type Instant: Ord
+ + Copy
+ + core::ops::Add<Self::Duration, Output = Self::Instant>
+ + core::ops::Sub<Self::Duration, Output = Self::Instant>
+ + core::ops::Sub<Self::Instant, Output = Self::Duration>;
+
+ /// The type for duration, defining a duration of time.
///
- /// It's always safe to call, but it must only be called from the interrupt of the
- /// monotonic timer for correct operation.
- pub unsafe fn on_monotonic_interrupt(&self) {
- Mono::clear_compare_flag();
- Mono::on_interrupt();
-
- loop {
- let mut release_at = None;
- let head = self.queue.pop_if(|head| {
- release_at = Some(head.release_at);
-
- let should_pop = Mono::now() >= head.release_at;
- head.was_popped.store(should_pop, Ordering::Relaxed);
+ /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used.
+ type Duration: Copy;
- should_pop
- });
+ /// Get the current time.
+ fn now() -> Self::Instant;
- match (head, release_at) {
- (Some(link), _) => {
- link.waker.wake();
- }
- (None, Some(instant)) => {
- Mono::enable_timer();
- Mono::set_compare(instant);
+ /// Delay for some duration of time.
+ async fn delay(duration: Self::Duration);
- if Mono::now() >= instant {
- // The time for the next instant passed while handling it,
- // continue dequeueing
- continue;
- }
-
- break;
- }
- (None, None) => {
- // Queue is empty
- Mono::disable_timer();
-
- break;
- }
- }
- }
- }
+ /// Delay to some specific time instant.
+ async fn delay_until(instant: Self::Instant);
/// Timeout at a specific time.
- pub async fn timeout_at<F: Future>(
- &self,
- instant: Mono::Instant,
+ async fn timeout_at<F: core::future::Future>(
+ instant: Self::Instant,
future: F,
- ) -> Result<F::Output, TimeoutError> {
- let delay = self.delay_until(instant);
+ ) -> Result<F::Output, TimeoutError>;
- pin_mut!(future);
- pin_mut!(delay);
-
- match select(future, delay).await {
- Either::Left((r, _)) => Ok(r),
- Either::Right(_) => Err(TimeoutError),
- }
- }
-
- /// Timeout after at least a specific duration.
- #[inline]
- pub async fn timeout_after<F: Future>(
- &self,
- duration: Mono::Duration,
+ /// Timeout after a specific duration.
+ async fn timeout_after<F: core::future::Future>(
+ duration: Self::Duration,
future: F,
- ) -> Result<F::Output, TimeoutError> {
- let now = Mono::now();
- let mut timeout = now + duration;
- if now != timeout {
- timeout = timeout + Mono::TICK_PERIOD;
- }
-
- // Wait for one period longer, because by definition timers have an uncertainty
- // of one period, so waiting for 'at least' needs to compensate for that.
- self.timeout_at(timeout, future).await
- }
-
- /// Delay for at least some duration of time.
- #[inline]
- pub async fn delay(&self, duration: Mono::Duration) {
- let now = Mono::now();
- let mut timeout = now + duration;
- if now != timeout {
- timeout = timeout + Mono::TICK_PERIOD;
- }
-
- // Wait for one period longer, because by definition timers have an uncertainty
- // of one period, so waiting for 'at least' needs to compensate for that.
- self.delay_until(timeout).await;
- }
-
- /// Delay to some specific time instant.
- pub async fn delay_until(&self, instant: Mono::Instant) {
- if !self.initialized.load(Ordering::Relaxed) {
- panic!(
- "The timer queue is not initialized with a monotonic, you need to run `initialize`"
- );
- }
-
- let mut link_ptr: Option<linked_list::Link<WaitingWaker<Mono>>> = None;
-
- // Make this future `Drop`-safe
- // SAFETY(link_ptr): Shadow the original definition of `link_ptr` so we can't abuse it.
- let mut link_ptr =
- LinkPtr(&mut link_ptr as *mut Option<linked_list::Link<WaitingWaker<Mono>>>);
- let mut link_ptr2 = link_ptr.clone();
-
- let queue = &self.queue;
- let marker = &AtomicUsize::new(0);
-
- let dropper = OnDrop::new(|| {
- queue.delete(marker.load(Ordering::Relaxed));
- });
-
- poll_fn(|cx| {
- if Mono::now() >= instant {
- return Poll::Ready(());
- }
-
- // SAFETY: This pointer is only dereferenced here and on drop of the future
- // which happens outside this `poll_fn`'s stack frame, so this mutable access cannot
- // happen at the same time as `dropper` runs.
- let link = unsafe { link_ptr2.get() };
- if link.is_none() {
- let link_ref = link.insert(Link::new(WaitingWaker {
- waker: cx.waker().clone(),
- release_at: instant,
- was_popped: AtomicBool::new(false),
- }));
-
- // SAFETY(new_unchecked): The address to the link is stable as it is defined
- //outside this stack frame.
- // SAFETY(insert): `link_ref` lifetime comes from `link_ptr` that is shadowed, and
- // we make sure in `dropper` that the link is removed from the queue before
- // dropping `link_ptr` AND `dropper` makes sure that the shadowed `link_ptr` lives
- // until the end of the stack frame.
- let (head_updated, addr) = unsafe { queue.insert(Pin::new_unchecked(link_ref)) };
-
- marker.store(addr, Ordering::Relaxed);
-
- if head_updated {
- // Pend the monotonic handler if the queue head was updated.
- Mono::pend_interrupt()
- }
- }
-
- Poll::Pending
- })
- .await;
-
- // SAFETY: We only run this and dereference the pointer if we have
- // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference
- // of this pointer is in the `poll_fn`.
- if let Some(link) = unsafe { link_ptr.get() } {
- if link.val.was_popped.load(Ordering::Relaxed) {
- // If it was popped from the queue there is no need to run delete
- dropper.defuse();
- }
- } else {
- // Make sure that our link is deleted from the list before we drop this stack
- drop(dropper);
- }
- }
+ ) -> Result<F::Output, TimeoutError>;
}
diff --git a/rtic-time/src/monotonic.rs b/rtic-time/src/monotonic.rs
index e6a160d..7c9d915 100644
--- a/rtic-time/src/monotonic.rs
+++ b/rtic-time/src/monotonic.rs
@@ -1,236 +1,8 @@
-//! A monotonic clock / counter definition.
+//! Structs and traits surrounding the [`Monotonic`](crate::Monotonic) trait.
-/// # A monotonic clock / counter definition.
-///
-/// ## Correctness
-///
-/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This
-/// is a requirement on the time library that the user chooses to use.
-pub trait Monotonic {
- /// The time at time zero.
- const ZERO: Self::Instant;
+pub use timer_queue_based_monotonic::{
+ TimerQueueBasedDuration, TimerQueueBasedInstant, TimerQueueBasedMonotonic,
+};
- /// The duration between two timer ticks.
- const TICK_PERIOD: Self::Duration;
-
- /// The type for instant, defining an instant in time.
- ///
- /// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used.
- type Instant: Ord
- + Copy
- + core::ops::Add<Self::Duration, Output = Self::Instant>
- + core::ops::Sub<Self::Duration, Output = Self::Instant>
- + core::ops::Sub<Self::Instant, Output = Self::Duration>;
-
- /// The type for duration, defining an duration of time.
- ///
- /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used.
- type Duration;
-
- /// Get the current time.
- fn now() -> Self::Instant;
-
- /// Set the compare value of the timer interrupt.
- ///
- /// **Note:** This method does not need to handle race conditions of the monotonic, the timer
- /// queue in RTIC checks this.
- fn set_compare(instant: Self::Instant);
-
- /// This method used to be required by an errata workaround
- /// for the nrf52 family, but it has been disabled as the
- /// workaround was erroneous.
- #[deprecated(
- since = "1.2.0",
- note = "this method is erroneous and has been disabled"
- )]
- fn should_dequeue_check(_: Self::Instant) -> bool {
- panic!("This method should not be used as it is erroneous.")
- }
-
- /// Clear the compare interrupt flag.
- fn clear_compare_flag();
-
- /// Pend the timer's interrupt.
- fn pend_interrupt();
-
- /// Optional. Runs on interrupt before any timer queue handling.
- fn on_interrupt() {}
-
- /// Optional. This is used to save power, this is called when the timer queue is not empty.
- ///
- /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
- /// based of `now()` is still valid.
- ///
- /// NOTE: This may be called more than once.
- fn enable_timer() {}
-
- /// Optional. This is used to save power, this is called when the timer queue is empty.
- ///
- /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
- /// based of `now()` is still valid.
- ///
- /// NOTE: This may be called more than once.
- fn disable_timer() {}
-}
-
-/// Creates impl blocks for [`embedded_hal::delay::DelayNs`][DelayNs],
-/// based on [`fugit::ExtU64Ceil`][ExtU64Ceil].
-///
-/// [DelayNs]: https://docs.rs/embedded-hal/latest/embedded_hal/delay/trait.DelayNs.html
-/// [ExtU64Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU64Ceil.html
-#[macro_export]
-macro_rules! embedded_hal_delay_impl_fugit64 {
- ($t:ty) => {
- impl ::embedded_hal::delay::DelayNs for $t {
- fn delay_ns(&mut self, ns: u32) {
- use ::fugit::ExtU64Ceil;
-
- let now = Self::now();
- let mut done = now + u64::from(ns).nanos_at_least();
- if now != done {
- // Compensate for sub-tick uncertainty
- done += Self::TICK_PERIOD;
- }
-
- while Self::now() < done {}
- }
-
- fn delay_us(&mut self, us: u32) {
- use ::fugit::ExtU64Ceil;
-
- let now = Self::now();
- let mut done = now + u64::from(us).micros_at_least();
- if now != done {
- // Compensate for sub-tick uncertainty
- done += Self::TICK_PERIOD;
- }
-
- while Self::now() < done {}
- }
-
- fn delay_ms(&mut self, ms: u32) {
- use ::fugit::ExtU64Ceil;
-
- let now = Self::now();
- let mut done = now + u64::from(ms).millis_at_least();
- if now != done {
- // Compensate for sub-tick uncertainty
- done += Self::TICK_PERIOD;
- }
-
- while Self::now() < done {}
- }
- }
- };
-}
-
-/// Creates impl blocks for [`embedded_hal_async::delay::DelayNs`][DelayNs],
-/// based on [`fugit::ExtU64Ceil`][ExtU64Ceil].
-///
-/// [DelayNs]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/trait.DelayNs.html
-/// [ExtU64Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU64Ceil.html
-#[macro_export]
-macro_rules! embedded_hal_async_delay_impl_fugit64 {
- ($t:ty) => {
- impl ::embedded_hal_async::delay::DelayNs for $t {
- #[inline]
- async fn delay_ns(&mut self, ns: u32) {
- use ::fugit::ExtU64Ceil;
- Self::delay(u64::from(ns).nanos_at_least()).await;
- }
-
- #[inline]
- async fn delay_us(&mut self, us: u32) {
- use ::fugit::ExtU64Ceil;
- Self::delay(u64::from(us).micros_at_least()).await;
- }
-
- #[inline]
- async fn delay_ms(&mut self, ms: u32) {
- use ::fugit::ExtU64Ceil;
- Self::delay(u64::from(ms).millis_at_least()).await;
- }
- }
- };
-}
-
-/// Creates impl blocks for [`embedded_hal::delay::DelayNs`][DelayNs],
-/// based on [`fugit::ExtU32Ceil`][ExtU32Ceil].
-///
-/// [DelayNs]: https://docs.rs/embedded-hal/latest/embedded_hal/delay/trait.DelayNs.html
-/// [ExtU32Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU32Ceil.html
-#[macro_export]
-macro_rules! embedded_hal_delay_impl_fugit32 {
- ($t:ty) => {
- impl ::embedded_hal::delay::DelayNs for $t {
- fn delay_ns(&mut self, ns: u32) {
- use ::fugit::ExtU32Ceil;
-
- let now = Self::now();
- let mut done = now + ns.nanos_at_least();
- if now != done {
- // Compensate for sub-tick uncertainty
- done += Self::TICK_PERIOD;
- }
-
- while Self::now() < done {}
- }
-
- fn delay_us(&mut self, us: u32) {
- use ::fugit::ExtU32Ceil;
-
- let now = Self::now();
- let mut done = now + us.micros_at_least();
- if now != done {
- // Compensate for sub-tick uncertainty
- done += Self::TICK_PERIOD;
- }
-
- while Self::now() < done {}
- }
-
- fn delay_ms(&mut self, ms: u32) {
- use ::fugit::ExtU32Ceil;
-
- let now = Self::now();
- let mut done = now + ms.millis_at_least();
- if now != done {
- // Compensate for sub-tick uncertainty
- done += Self::TICK_PERIOD;
- }
-
- while Self::now() < done {}
- }
- }
- };
-}
-
-/// Creates impl blocks for [`embedded_hal_async::delay::DelayNs`][DelayNs],
-/// based on [`fugit::ExtU32Ceil`][ExtU32Ceil].
-///
-/// [DelayNs]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/trait.DelayNs.html
-/// [ExtU32Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU32Ceil.html
-#[macro_export]
-macro_rules! embedded_hal_async_delay_impl_fugit32 {
- ($t:ty) => {
- impl ::embedded_hal_async::delay::DelayNs for $t {
- #[inline]
- async fn delay_ns(&mut self, ns: u32) {
- use ::fugit::ExtU32Ceil;
- Self::delay(ns.nanos_at_least()).await;
- }
-
- #[inline]
- async fn delay_us(&mut self, us: u32) {
- use ::fugit::ExtU32Ceil;
- Self::delay(us.micros_at_least()).await;
- }
-
- #[inline]
- async fn delay_ms(&mut self, ms: u32) {
- use ::fugit::ExtU32Ceil;
- Self::delay(ms.millis_at_least()).await;
- }
- }
- };
-}
+mod embedded_hal_macros;
+mod timer_queue_based_monotonic;
diff --git a/rtic-time/src/monotonic/embedded_hal_macros.rs b/rtic-time/src/monotonic/embedded_hal_macros.rs
new file mode 100644
index 0000000..25ac791
--- /dev/null
+++ b/rtic-time/src/monotonic/embedded_hal_macros.rs
@@ -0,0 +1,77 @@
+//! Macros that implement embedded-hal traits for Monotonics
+
+/// Implements [`embedded_hal::delay::DelayNs`] for a given monotonic.
+#[macro_export]
+macro_rules! impl_embedded_hal_delay_fugit {
+ ($t:ty) => {
+ impl $crate::embedded_hal::delay::DelayNs for $t {
+ fn delay_ns(&mut self, ns: u32) {
+ let now = <Self as $crate::Monotonic>::now();
+ let mut done =
+ now + <Self as $crate::Monotonic>::Duration::nanos_at_least(ns.into());
+ if now != done {
+ // Compensate for sub-tick uncertainty
+ done = done + <Self as $crate::Monotonic>::Duration::from_ticks(1);
+ }
+
+ while <Self as $crate::Monotonic>::now() < done {}
+ }
+
+ fn delay_us(&mut self, us: u32) {
+ let now = <Self as $crate::Monotonic>::now();
+ let mut done =
+ now + <Self as $crate::Monotonic>::Duration::micros_at_least(us.into());
+ if now != done {
+ // Compensate for sub-tick uncertainty
+ done = done + <Self as $crate::Monotonic>::Duration::from_ticks(1);
+ }
+
+ while <Self as $crate::Monotonic>::now() < done {}
+ }
+
+ fn delay_ms(&mut self, ms: u32) {
+ let now = <Self as $crate::Monotonic>::now();
+ let mut done =
+ now + <Self as $crate::Monotonic>::Duration::millis_at_least(ms.into());
+ if now != done {
+ // Compensate for sub-tick uncertainty
+ done = done + <Self as $crate::Monotonic>::Duration::from_ticks(1);
+ }
+
+ while <Self as $crate::Monotonic>::now() < done {}
+ }
+ }
+ };
+}
+
+/// Implements [`embedded_hal_async::delay::DelayNs`] for a given monotonic.
+#[macro_export]
+macro_rules! impl_embedded_hal_async_delay_fugit {
+ ($t:ty) => {
+ impl $crate::embedded_hal_async::delay::DelayNs for $t {
+ #[inline]
+ async fn delay_ns(&mut self, ns: u32) {
+ <Self as $crate::Monotonic>::delay(
+ <Self as $crate::Monotonic>::Duration::nanos_at_least(ns.into()),
+ )
+ .await;
+ }
+
+ #[inline]
+ async fn delay_us(&mut self, us: u32) {
+ <Self as $crate::Monotonic>::delay(
+ <Self as $crate::Monotonic>::Duration::micros_at_least(us.into()),
+ )
+ .await;
+ }
+
+ #[inline]
+ async fn delay_ms(&mut self, ms: u32) {
+ <Self as $crate::Monotonic>::delay(
+ <Self as $crate::Monotonic>::Duration::millis_at_least(ms.into()),
+ )
+ .await;
+ }
+ }
+ };
+}
diff --git a/rtic-time/src/monotonic/timer_queue_based_monotonic.rs b/rtic-time/src/monotonic/timer_queue_based_monotonic.rs
new file mode 100644
index 0000000..699958b
--- /dev/null
+++ b/rtic-time/src/monotonic/timer_queue_based_monotonic.rs
@@ -0,0 +1,113 @@
+use crate::{timer_queue::TimerQueueBackend, TimeoutError};
+
+use crate::Monotonic;
+
+/// A [`Monotonic`] that is backed by the [`TimerQueue`](crate::timer_queue::TimerQueue).
+pub trait TimerQueueBasedMonotonic {
+ /// The backend for the timer queue
+ type Backend: TimerQueueBackend;
+
+ /// The type for instant, defining an instant in time.
+ ///
+ /// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used.
+ type Instant: TimerQueueBasedInstant<Ticks = <Self::Backend as TimerQueueBackend>::Ticks>
+ + core::ops::Add<Self::Duration, Output = Self::Instant>
+ + core::ops::Sub<Self::Duration, Output = Self::Instant>
+ + core::ops::Sub<Self::Instant, Output = Self::Duration>;
+
+ /// The type for duration, defining a duration of time.
+ ///
+ /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used.
+ type Duration: TimerQueueBasedDuration<Ticks = <Self::Backend as TimerQueueBackend>::Ticks>;
+}
+
+impl<T: TimerQueueBasedMonotonic> Monotonic for T {
+ type Instant = T::Instant;
+ type Duration = T::Duration;
+
+ fn now() -> Self::Instant {
+ Self::Instant::from_ticks(T::Backend::timer_queue().now())
+ }
+
+ async fn delay(duration: Self::Duration) {
+ T::Backend::timer_queue().delay(duration.ticks()).await
+ }
+
+ async fn delay_until(instant: Self::Instant) {
+ T::Backend::timer_queue().delay_until(instant.ticks()).await
+ }
+
+ async fn timeout_at<F: core::future::Future>(
+ instant: Self::Instant,
+ future: F,
+ ) -> Result<F::Output, TimeoutError> {
+ T::Backend::timer_queue()
+ .timeout_at(instant.ticks(), future)
+ .await
+ }
+
+ async fn timeout_after<F: core::future::Future>(
+ duration: Self::Duration,
+ future: F,
+ ) -> Result<F::Output, TimeoutError> {
+ T::Backend::timer_queue()
+ .timeout_after(duration.ticks(), future)
+ .await
+ }
+}
+
+/// An instant that can be used in [`TimerQueueBasedMonotonic`].
+pub trait TimerQueueBasedInstant: Ord + Copy {
+ /// The internal type of the instant
+ type Ticks;
+ /// Convert from ticks to the instant
+ fn from_ticks(ticks: Self::Ticks) -> Self;
+ /// Convert the instant to ticks
+ fn ticks(self) -> Self::Ticks;
+}
+
+/// A duration that can be used in [`TimerQueueBasedMonotonic`].
+pub trait TimerQueueBasedDuration: Copy {
+ /// The internal type of the duration
+ type Ticks;
+ /// Convert the duration to ticks
+ fn ticks(self) -> Self::Ticks;
+}
+
+impl<const NOM: u32, const DENOM: u32> TimerQueueBasedInstant for fugit::Instant<u64, NOM, DENOM> {
+ type Ticks = u64;
+ fn from_ticks(ticks: Self::Ticks) -> Self {
+ Self::from_ticks(ticks)
+ }
+ fn ticks(self) -> Self::Ticks {
+ Self::ticks(&self)
+ }
+}
+
+impl<const NOM: u32, const DENOM: u32> TimerQueueBasedInstant for fugit::Instant<u32, NOM, DENOM> {
+ type Ticks = u32;
+ fn from_ticks(ticks: Self::Ticks) -> Self {
+ Self::from_ticks(ticks)
+ }
+ fn ticks(self) -> Self::Ticks {
+ Self::ticks(&self)
+ }
+}
+
+impl<const NOM: u32, const DENOM: u32> TimerQueueBasedDuration
+ for fugit::Duration<u64, NOM, DENOM>
+{
+ type Ticks = u64;
+ fn ticks(self) -> Self::Ticks {
+ Self::ticks(&self)
+ }
+}
+
+impl<const NOM: u32, const DENOM: u32> TimerQueueBasedDuration
+ for fugit::Duration<u32, NOM, DENOM>
+{
+ type Ticks = u32;
+ fn ticks(self) -> Self::Ticks {
+ Self::ticks(&self)
+ }
+}
diff --git a/rtic-time/src/timer_queue.rs b/rtic-time/src/timer_queue.rs
new file mode 100644
index 0000000..ea2c806
--- /dev/null
+++ b/rtic-time/src/timer_queue.rs
@@ -0,0 +1,281 @@
+//! A generic timer queue for async executors.
+
+use crate::linked_list::{self, Link, LinkedList};
+use crate::TimeoutError;
+
+use core::future::{poll_fn, Future};
+use core::pin::Pin;
+use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
+use core::task::{Poll, Waker};
+use futures_util::{
+ future::{select, Either},
+ pin_mut,
+};
+use rtic_common::dropper::OnDrop;
+
+mod backend;
+mod tick_type;
+pub use backend::TimerQueueBackend;
+pub use tick_type::TimerQueueTicks;
+
+/// Holds a waker and at which time instant this waker shall be awoken.
+struct WaitingWaker<Backend: TimerQueueBackend> {
+ waker: Waker,
+ release_at: Backend::Ticks,
+ was_popped: AtomicBool,
+}
+
+impl<Backend: TimerQueueBackend> Clone for WaitingWaker<Backend> {
+ fn clone(&self) -> Self {
+ Self {
+ waker: self.waker.clone(),
+ release_at: self.release_at,
+ was_popped: AtomicBool::new(self.was_popped.load(Ordering::Relaxed)),
+ }
+ }
+}
+
+impl<Backend: TimerQueueBackend> PartialEq for WaitingWaker<Backend> {
+ fn eq(&self, other: &Self) -> bool {
+ self.release_at == other.release_at
+ }
+}
+
+impl<Backend: TimerQueueBackend> PartialOrd for WaitingWaker<Backend> {
+ fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+ Some(self.release_at.compare(other.release_at))
+ }
+}
+
+/// A generic timer queue for async executors.
+///
+/// # Blocking
+///
+/// The internal priority queue uses global critical sections to manage access. This means that
+/// `await`ing a delay will cause a lock of the entire system for O(n) time. In practice the lock
+/// duration is ~10 clock cycles per element in the queue.
+///
+/// # Safety
+///
+/// This timer queue is based on an intrusive linked list, and by extension the links are stored
+/// on the async stacks of callers. The links are deallocated on `drop` or when the wait is
+/// complete.
+///
+/// Do not call `mem::forget` on an awaited future, or there will be dragons!
+pub struct TimerQueue<Backend: TimerQueueBackend> {
+ queue: LinkedList<WaitingWaker<Backend>>,
+ initialized: AtomicBool,
+}
+
+/// This is needed to make the async closure in `delay_until` accept that we "share"
+/// the link possible between threads.
+struct LinkPtr<Backend: TimerQueueBackend>(*mut Option<linked_list::Link<WaitingWaker<Backend>>>);
+
+impl<Backend: TimerQueueBackend> Clone for LinkPtr<Backend> {
+ fn clone(&self) -> Self {
+ LinkPtr(self.0)
+ }
+}
+
+impl<Backend: TimerQueueBackend> LinkPtr<Backend> {
+ /// This will dereference the pointer stored within and give out an `&mut`.
+ unsafe fn get(&mut self) -> &mut Option<linked_list::Link<WaitingWaker<Backend>>> {
+ &mut *self.0
+ }
+}
+
+unsafe impl<Backend: TimerQueueBackend> Send for LinkPtr<Backend> {}
+unsafe impl<Backend: TimerQueueBackend> Sync for LinkPtr<Backend> {}
+
+impl<Backend: TimerQueueBackend> TimerQueue<Backend> {
+ /// Make a new queue.
+ pub const fn new() -> Self {
+ Self {
+ queue: LinkedList::new(),
+ initialized: AtomicBool::new(false),
+ }
+ }
+
+ /// Forwards the `Monotonic::now()` method.
+ #[inline(always)]
+ pub fn now(&self) -> Backend::Ticks {
+ Backend::now()
+ }
+
+ /// Takes the initialized monotonic to initialize the TimerQueue.
+ pub fn initialize(&self, backend: Backend) {
+ self.initialized.store(true, Ordering::SeqCst);
+
+ // Don't run drop on `Mono`
+ core::mem::forget(backend);
+ }
+
+ /// Call this in the interrupt handler of the hardware timer supporting the `Monotonic`
+ ///
+ /// # Safety
+ ///
+ /// It's always safe to call, but it must only be called from the interrupt of the
+ /// monotonic timer for correct operation.
+ pub unsafe fn on_monotonic_interrupt(&self) {
+ Backend::clear_compare_flag();
+ Backend::on_interrupt();
+
+ loop {
+ let mut release_at = None;
+ let head = self.queue.pop_if(|head| {
+ release_at = Some(head.release_at);
+
+ let should_pop = Backend::now().is_at_least(head.release_at);
+ head.was_popped.store(should_pop, Ordering::Relaxed);
+
+ should_pop
+ });
+
+ match (head, release_at) {
+ (Some(link), _) => {
+ link.waker.wake();
+ }
+ (None, Some(instant)) => {
+ Backend::enable_timer();
+ Backend::set_compare(instant);
+
+ if Backend::now().is_at_least(instant) {
+ // The time for the next instant passed while handling it,
+ // continue dequeueing
+ continue;
+ }
+
+ break;
+ }
+ (None, None) => {
+ // Queue is empty
+ Backend::disable_timer();
+
+ break;
+ }
+ }
+ }
+ }
+
+ /// Timeout at a specific time.
+ pub async fn timeout_at<F: Future>(
+ &self,
+ instant: Backend::Ticks,
+ future: F,
+ ) -> Result<F::Output, TimeoutError> {
+ let delay = self.delay_until(instant);
+
+ pin_mut!(future);
+ pin_mut!(delay);
+
+ match select(future, delay).await {
+ Either::Left((r, _)) => Ok(r),
+ Either::Right(_) => Err(TimeoutError),
+ }
+ }
+
+ /// Timeout after at least a specific duration.
+ #[inline]
+ pub async fn timeout_after<F: Future>(
+ &self,
+ duration: Backend::Ticks,
+ future: F,
+ ) -> Result<F::Output, TimeoutError> {
+ let now = Backend::now();
+ let mut timeout = now.wrapping_add(duration);
+ if now != timeout {
+ timeout = timeout.wrapping_add(Backend::Ticks::ONE_TICK);
+ }
+
+ // Wait for one period longer, because by definition timers have an uncertainty
+ // of one period, so waiting for 'at least' needs to compensate for that.
+ self.timeout_at(timeout, future).await
+ }
+
+ /// Delay for at least some duration of time.
+ #[inline]
+ pub async fn delay(&self, duration: Backend::Ticks) {
+ let now = Backend::now();
+ let mut timeout = now.wrapping_add(duration);
+ if now != timeout {
+ timeout = timeout.wrapping_add(Backend::Ticks::ONE_TICK);
+ }
+
+ // Wait for one period longer, because by definition timers have an uncertainty
+ // of one period, so waiting for 'at least' needs to compensate for that.
+ self.delay_until(timeout).await;
+ }
+
+ /// Delay to some specific time instant.
+ pub async fn delay_until(&self, instant: Backend::Ticks) {
+ if !self.initialized.load(Ordering::Relaxed) {
+ panic!(
+ "The timer queue is not initialized with a monotonic, you need to run `initialize`"
+ );
+ }
+
+ let mut link_ptr: Option<linked_list::Link<WaitingWaker<Backend>>> = None;
+
+ // Make this future `Drop`-safe
+ // SAFETY(link_ptr): Shadow the original definition of `link_ptr` so we can't abuse it.
+ let mut link_ptr =
+ LinkPtr(&mut link_ptr as *mut Option<linked_list::Link<WaitingWaker<Backend>>>);
+ let mut link_ptr2 = link_ptr.clone();
+
+ let queue = &self.queue;
+ let marker = &AtomicUsize::new(0);
+
+ let dropper = OnDrop::new(|| {
+ queue.delete(marker.load(Ordering::Relaxed));
+ });
+
+ poll_fn(|cx| {
+ if Backend::now().is_at_least(instant) {
+ return Poll::Ready(());
+ }
+
+ // SAFETY: This pointer is only dereferenced here and on drop of the future
+ // which happens outside this `poll_fn`'s stack frame, so this mutable access cannot
+ // happen at the same time as `dropper` runs.
+ let link = unsafe { link_ptr2.get() };
+ if link.is_none() {
+ let link_ref = link.insert(Link::new(WaitingWaker {
+ waker: cx.waker().clone(),
+ release_at: instant,
+ was_popped: AtomicBool::new(false),
+ }));
+
+ // SAFETY(new_unchecked): The address to the link is stable as it is defined
+ //outside this stack frame.
+ // SAFETY(insert): `link_ref` lifetime comes from `link_ptr` that is shadowed, and
+ // we make sure in `dropper` that the link is removed from the queue before
+ // dropping `link_ptr` AND `dropper` makes sure that the shadowed `link_ptr` lives
+ // until the end of the stack frame.
+ let (head_updated, addr) = unsafe { queue.insert(Pin::new_unchecked(link_ref)) };
+
+ marker.store(addr, Ordering::Relaxed);
+
+ if head_updated {
+ // Pend the monotonic handler if the queue head was updated.
+ Backend::pend_interrupt()
+ }
+ }
+
+ Poll::Pending
+ })
+ .await;
+
+ // SAFETY: We only run this and dereference the pointer if we have
+ // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference
+ // of this pointer is in the `poll_fn`.
+ if let Some(link) = unsafe { link_ptr.get() } {
+ if link.val.was_popped.load(Ordering::Relaxed) {
+ // If it was popped from the queue there is no need to run delete
+ dropper.defuse();
+ }
+ } else {
+ // Make sure that our link is deleted from the list before we drop this stack
+ drop(dropper);
+ }
+ }
+}
diff --git a/rtic-time/src/timer_queue/backend.rs b/rtic-time/src/timer_queue/backend.rs
new file mode 100644
index 0000000..5b71b7c
--- /dev/null
+++ b/rtic-time/src/timer_queue/backend.rs
@@ -0,0 +1,44 @@
+use super::{TimerQueue, TimerQueueTicks};
+
+/// A backend definition for a monotonic clock/counter.
+pub trait TimerQueueBackend: 'static + Sized {
+ /// The type for ticks.
+ type Ticks: TimerQueueTicks;
+
+ /// Get the current time.
+ fn now() -> Self::Ticks;
+
+ /// Set the compare value of the timer interrupt.
+ ///
+ /// **Note:** This method does not need to handle race conditions of the monotonic, the timer
+ /// queue in RTIC checks this.
+ fn set_compare(instant: Self::Ticks);
+
+ /// Clear the compare interrupt flag.
+ fn clear_compare_flag();
+
+ /// Pend the timer's interrupt.
+ fn pend_interrupt();
+
+ /// Optional. Runs on interrupt before any timer queue handling.
+ fn on_interrupt() {}
+
+ /// Optional. This is used to save power, this is called when the timer queue is not empty.
+ ///
+ /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
+ /// based of `now()` is still valid.
+ ///
+ /// NOTE: This may be called more than once.
+ fn enable_timer() {}
+
+ /// Optional. This is used to save power, this is called when the timer queue is empty.
+ ///
+ /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
+ /// based of `now()` is still valid.
+ ///
+ /// NOTE: This may be called more than once.
+ fn disable_timer() {}
+
+ /// Returns a reference to the underlying timer queue.
+ fn timer_queue() -> &'static TimerQueue<Self>;
+}
diff --git a/rtic-time/src/timer_queue/tick_type.rs b/rtic-time/src/timer_queue/tick_type.rs
new file mode 100644
index 0000000..571dfc2
--- /dev/null
+++ b/rtic-time/src/timer_queue/tick_type.rs
@@ -0,0 +1,49 @@
+use core::cmp;
+
+/// The ticks of a timer.
+pub trait TimerQueueTicks: Copy + PartialEq + Eq {
+ /// Represents a single tick.
+ const ONE_TICK: Self;
+
+ /// Compares to another tick count.
+ ///
+ /// Takes into account timer wrapping; if the difference is more than
+ /// half the value range, the result will be flipped.
+ fn compare(self, other: Self) -> cmp::Ordering;
+
+ /// True if `self` is at the same time as `other` or later.
+ ///
+ /// Takes into account timer wrapping; if the difference is more than
+ /// half the value range, the result will be negated.
+ fn is_at_least(self, other: Self) -> bool {
+ match self.compare(other) {
+ cmp::Ordering::Less => false,
+ cmp::Ordering::Equal => true,
+ cmp::Ordering::Greater => true,
+ }
+ }
+
+ /// Wrapping addition.
+ fn wrapping_add(self, other: Self) -> Self;
+}
+
+impl TimerQueueTicks for u32 {
+ const ONE_TICK: Self = 1;
+
+ fn compare(self, other: Self) -> cmp::Ordering {
+ (self.wrapping_sub(other) as i32).cmp(&0)
+ }
+ fn wrapping_add(self, other: Self) -> Self {
+ u32::wrapping_add(self, other)
+ }
+}
+impl TimerQueueTicks for u64 {
+ const ONE_TICK: Self = 1;
+
+ fn compare(self, other: Self) -> cmp::Ordering {
+ (self.wrapping_sub(other) as i64).cmp(&0)
+ }
+ fn wrapping_add(self, other: Self) -> Self {
+ u64::wrapping_add(self, other)
+ }
+}
diff --git a/rtic-time/tests/delay_precision_subtick.rs b/rtic-time/tests/delay_precision_subtick.rs
index a4ef4bb..a415b65 100644
--- a/rtic-time/tests/delay_precision_subtick.rs
+++ b/rtic-time/tests/delay_precision_subtick.rs
@@ -15,46 +15,31 @@ use std::{
time::Duration,
};
-use ::fugit::ExtU64Ceil;
use cooked_waker::{IntoWaker, WakeRef};
+use fugit::ExtU64Ceil;
use parking_lot::Mutex;
-use rtic_time::{Monotonic, TimeoutError, TimerQueue};
+use rtic_time::{
+ monotonic::TimerQueueBasedMonotonic,
+ timer_queue::{TimerQueue, TimerQueueBackend},
+ Monotonic,
+};
const SUBTICKS_PER_TICK: u32 = 10;
struct SubtickTestTimer;
-static TIMER_QUEUE: TimerQueue<SubtickTestTimer> = TimerQueue::new();
+struct SubtickTestTimerBackend;
+static TIMER_QUEUE: TimerQueue<SubtickTestTimerBackend> = TimerQueue::new();
static NOW_SUBTICKS: AtomicU64 = AtomicU64::new(0);
static COMPARE_TICKS: Mutex<Option<u64>> = Mutex::new(None);
-impl Monotonic for SubtickTestTimer {
- const ZERO: Self::Instant = Self::Instant::from_ticks(0);
- const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
-
- type Instant = fugit::Instant<u64, SUBTICKS_PER_TICK, 1000>;
- type Duration = fugit::Duration<u64, SUBTICKS_PER_TICK, 1000>;
-
- fn now() -> Self::Instant {
- Self::Instant::from_ticks(
- NOW_SUBTICKS.load(Ordering::Relaxed) / u64::from(SUBTICKS_PER_TICK),
- )
- }
-
- fn set_compare(instant: Self::Instant) {
- *COMPARE_TICKS.lock() = Some(instant.ticks());
- }
-
- fn clear_compare_flag() {}
-
- fn pend_interrupt() {
- unsafe {
- Self::__tq().on_monotonic_interrupt();
- }
+impl SubtickTestTimer {
+ pub fn init() {
+ SubtickTestTimerBackend::init();
}
}
-impl SubtickTestTimer {
- pub fn init() {
- Self::__tq().initialize(Self)
+impl SubtickTestTimerBackend {
+ fn init() {
+ Self::timer_queue().initialize(Self)
}
pub fn tick() -> u64 {
@@ -70,7 +55,7 @@ impl SubtickTestTimer {
// );
if subticks == 0 && Some(ticks) == *compare {
unsafe {
- Self::__tq().on_monotonic_interrupt();
+ Self::timer_queue().on_monotonic_interrupt();
}
}
@@ -85,29 +70,41 @@ impl SubtickTestTimer {
pub fn now_subticks() -> u64 {
NOW_SUBTICKS.load(Ordering::Relaxed)
}
+}
- fn __tq() -> &'static TimerQueue<Self> {
- &TIMER_QUEUE
+impl TimerQueueBackend for SubtickTestTimerBackend {
+ type Ticks = u64;
+
+ fn now() -> Self::Ticks {
+ NOW_SUBTICKS.load(Ordering::Relaxed) / u64::from(SUBTICKS_PER_TICK)
}
- /// Delay for some duration of time.
- #[inline]
- pub async fn delay(duration: <Self as Monotonic>::Duration) {
- Self::__tq().delay(duration).await;
+ fn set_compare(instant: Self::Ticks) {
+ *COMPARE_TICKS.lock() = Some(instant);
}
- /// 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> {
- Self::__tq().timeout_after(duration, future).await
+ fn clear_compare_flag() {}
+
+ fn pend_interrupt() {
+ unsafe {
+ Self::timer_queue().on_monotonic_interrupt();
+ }
}
+
+ fn timer_queue() -> &'static TimerQueue<Self> {
+ &TIMER_QUEUE
+ }
+}
+
+impl TimerQueueBasedMonotonic for SubtickTestTimer {
+ type Backend = SubtickTestTimerBackend;
+
+ type Instant = fugit::Instant<u64, SUBTICKS_PER_TICK, 1000>;
+ type Duration = fugit::Duration<u64, SUBTICKS_PER_TICK, 1000>;
}
-rtic_time::embedded_hal_delay_impl_fugit64!(SubtickTestTimer);
-rtic_time::embedded_hal_async_delay_impl_fugit64!(SubtickTestTimer);
+rtic_time::impl_embedded_hal_delay_fugit!(SubtickTestTimer);
+rtic_time::impl_embedded_hal_async_delay_fugit!(SubtickTestTimer);
// A simple struct that counts the number of times it is awoken. Can't
// be awoken by value (because that would discard the counter), so we
@@ -144,7 +141,7 @@ impl<F: FnOnce()> Drop for OnDrop<F> {
macro_rules! subtick_test {
(@run $start:expr, $actual_duration:expr, $delay_fn:expr) => {{
// forward clock to $start
- SubtickTestTimer::forward_to_subtick($start);
+ SubtickTestTimerBackend::forward_to_subtick($start);
// call wait function
let delay_fn = $delay_fn;
@@ -164,7 +161,7 @@ macro_rules! subtick_test {
};
assert_eq!(wakecounter.get(), 0);
- SubtickTestTimer::tick();
+ SubtickTestTimerBackend::tick();
}
let expected_wakeups = {
@@ -177,7 +174,7 @@ macro_rules! subtick_test {
assert_eq!(wakecounter.get(), expected_wakeups);
// Tick again to test that we don't get a second wake
- SubtickTestTimer::tick();
+ SubtickTestTimerBackend::tick();
assert_eq!(wakecounter.get(), expected_wakeups);
assert_eq!(
@@ -191,9 +188,9 @@ macro_rules! subtick_test {
(@run_blocking $start:expr, $actual_duration:expr, $delay_fn:expr) => {{
// forward clock to $start
- SubtickTestTimer::forward_to_subtick($start);
+ SubtickTestTimerBackend::forward_to_subtick($start);
- let t_start = SubtickTestTimer::now_subticks();
+ let t_start = SubtickTestTimerBackend::now_subticks();
let finished = AtomicBool::new(false);
std::thread::scope(|s|{
@@ -204,13 +201,13 @@ macro_rules! subtick_test {
s.spawn(||{
sleep(Duration::from_millis(10));
while !finished.load(Ordering::Relaxed) {
- SubtickTestTimer::tick();
+ SubtickTestTimerBackend::tick();
sleep(Duration::from_millis(10));
}
});
});
- let t_end = SubtickTestTimer::now_subticks();
+ let t_end = SubtickTestTimerBackend::now_subticks();
let measured_duration = t_end - t_start;
assert_eq!(
$actual_duration,
diff --git a/rtic-time/tests/timer_queue.rs b/rtic-time/tests/timer_queue.rs
index 8bae385..2ef7ce3 100644
--- a/rtic-time/tests/timer_queue.rs
+++ b/rtic-time/tests/timer_queue.rs
@@ -2,63 +2,24 @@
//!
//! To run this test, you need to activate the `critical-section/std` feature.
-use std::{
- fmt::Debug,
- task::{Poll, Waker},
-};
-
use cassette::Cassette;
use parking_lot::Mutex;
-use rtic_time::{Monotonic, TimerQueue};
-
-static NOW: Mutex<Option<Instant>> = Mutex::new(None);
-
-#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
-pub struct Duration(u64);
-
-impl Duration {
- pub const fn from_ticks(millis: u64) -> Self {
- Self(millis)
- }
-
- pub fn as_ticks(&self) -> u64 {
- self.0
- }
-}
-
-impl core::ops::Add<Duration> for Duration {
- type Output = Duration;
-
- fn add(self, rhs: Duration) -> Self::Output {
- Self(self.0 + rhs.0)
- }
-}
+use rtic_time::timer_queue::{TimerQueue, TimerQueueBackend};
-impl From<Duration> for Instant {
- fn from(value: Duration) -> Self {
- Instant(value.0)
- }
-}
-
-static WAKERS: Mutex<Vec<Waker>> = Mutex::new(Vec::new());
+mod peripheral {
+ use parking_lot::Mutex;
+ use std::{
+ sync::atomic::{AtomicU64, Ordering},
+ task::{Poll, Waker},
+ };
-#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
-pub struct Instant(u64);
+ use super::TestMonoBackend;
-impl Instant {
- const ZERO: Self = Self(0);
+ static NOW: AtomicU64 = AtomicU64::new(0);
+ static WAKERS: Mutex<Vec<Waker>> = Mutex::new(Vec::new());
pub fn tick() -> bool {
- // If we've never ticked before, initialize the clock.
- if NOW.lock().is_none() {
- *NOW.lock() = Some(Instant::ZERO);
- }
- // We've ticked before, add one to the clock
- else {
- let now = Instant::now();
- let new_time = now + Duration(1);
- *NOW.lock() = Some(new_time);
- }
+ NOW.fetch_add(1, Ordering::Release);
let had_wakers = !WAKERS.lock().is_empty();
// Wake up all things waiting for a specific time to happen.
@@ -66,22 +27,18 @@ impl Instant {
waker.wake_by_ref();
}
- let had_interrupt = TestMono::tick(false);
+ let had_interrupt = TestMonoBackend::tick(false);
had_interrupt || had_wakers
}
- pub fn now() -> Self {
- NOW.lock().clone().unwrap_or(Instant::ZERO)
+ pub fn now() -> u64 {
+ NOW.load(Ordering::Acquire)
}
- pub fn elapsed(&self) -> Duration {
- Duration(Self::now().0 - self.0)
- }
-
- pub async fn wait_until(time: Instant) {
+ pub async fn wait_until(time: u64) {
core::future::poll_fn(|ctx| {
- if Instant::now() >= time {
+ if now() >= time {
Poll::Ready(())
} else {
WAKERS.lock().push(ctx.waker().clone());
@@ -92,51 +49,21 @@ impl Instant {
}
}
-impl From<u64> for Instant {
- fn from(value: u64) -> Self {
- Self(value)
- }
-}
+static COMPARE: Mutex<Option<u64>> = Mutex::new(None);
+static TIMER_QUEUE: TimerQueue<TestMonoBackend> = TimerQueue::new();
-impl core::ops::Add<Duration> for Instant {
- type Output = Instant;
+pub struct TestMonoBackend;
- fn add(self, rhs: Duration) -> Self::Output {
- Self(self.0 + rhs.0)
- }
-}
-
-impl core::ops::Sub<Duration> for Instant {
- type Output = Instant;
-
- fn sub(self, rhs: Duration) -> Self::Output {
- Self(self.0 - rhs.0)
- }
-}
-
-impl core::ops::Sub<Instant> for Instant {
- type Output = Duration;
-
- fn sub(self, rhs: Instant) -> Self::Output {
- Duration(self.0 - rhs.0)
- }
-}
-
-static COMPARE: Mutex<Option<Instant>> = Mutex::new(None);
-static TIMER_QUEUE: TimerQueue<TestMono> = TimerQueue::new();
-
-pub struct TestMono;
-
-impl TestMono {
+impl TestMonoBackend {
pub fn tick(force_interrupt: bool) -> bool {
- let now = Instant::now();
+ let now = peripheral::now();
let compare_reached = Some(now) == Self::compare();
let interrupt = compare_reached || force_interrupt;
if interrupt {
unsafe {
- TestMono::queue().on_monotonic_interrupt();
+ TestMonoBackend::timer_queue().on_monotonic_interrupt();
}
true
} else {
@@ -144,35 +71,26 @@ impl TestMono {
}
}
- /// Initialize the monotonic.
- pub fn init() {
- Self::queue().initialize(Self);
- }
-
- /// Used to access the underlying timer queue
- pub fn queue() -> &'static TimerQueue<TestMono> {
- &TIMER_QUEUE
- }
-
- pub fn compare() -> Option<Instant> {
+ pub fn compare() -> Option<u64> {
COMPARE.lock().clone()
}
}
-impl Monotonic for TestMono {
- const ZERO: Self::Instant = Instant::ZERO;
- const TICK_PERIOD: Self::Duration = Duration::from_ticks(1);
-
- type Instant = Instant;
+impl TestMonoBackend {
+ fn init() {
+ Self::timer_queue().initialize(Self);
+ }
+}
- type Duration = Duration;
+impl TimerQueueBackend for TestMonoBackend {
+ type Ticks = u64;
- fn now() -> Self::Instant {
- Instant::now()
+ fn now() -> Self::Ticks {
+ peripheral::now()
}
- fn set_compare(instant: Self::Instant) {
- let _ = COMPARE.lock().insert(instant);
+ fn set_compare(instant: Self::Ticks) {
+ *COMPARE.lock() = Some(instant);
}
fn clear_compare_flag() {}
@@ -180,42 +98,40 @@ impl Monotonic for TestMono {
fn pend_interrupt() {
Self::tick(true);
}
+
+ fn timer_queue() -> &'static TimerQueue<Self> {
+ &TIMER_QUEUE
+ }
}
#[test]
fn timer_queue() {
- TestMono::init();
- let start = Instant::ZERO;
+ TestMonoBackend::init();
+ let start = 0;
let build_delay_test = |pre_delay: Option<u64>, delay: u64| {
- let delay = Duration::from_ticks(delay);
- let pre_delay = pre_delay.map(Duration::from_ticks);
-
let total = if let Some(pre_delay) = pre_delay {
pre_delay + delay
} else {
delay
};
- let total_millis = total.as_ticks();
async move {
// A `pre_delay` simulates a delay in scheduling,
// without the `pre_delay` being present in the timer
// queue
if let Some(pre_delay) = pre_delay {
- Instant::wait_until(start + pre_delay).await;
+ peripheral::wait_until(start + pre_delay).await;
}
- TestMono::queue().delay(delay).await;
+ TestMonoBackend::timer_queue().delay(delay).await;
- let elapsed = start.elapsed().as_ticks();
- println!("{total_millis} ticks delay reached after {elapsed} ticks");
+ let elapsed = peripheral::now() - start;
+ println!("{total} ticks delay reached after {elapsed} ticks");
// Expect a delay of one longer, to compensate for timer uncertainty
- if elapsed != total_millis + 1 {
- panic!(
- "{total_millis} ticks delay was not on time ({elapsed} ticks passed instead)"
- );
+ if elapsed != total + 1 {
+ panic!("{total} ticks delay was not on time ({elapsed} ticks passed instead)");
}
}
};
@@ -259,31 +175,31 @@ fn timer_queue() {
// We only poll the waiting futures if an
// interrupt occured or if an artificial delay
// has passed.
- if Instant::tick() {
+ if peripheral::tick() {
poll!(d1, d2, d3);
}
- if Instant::now() == 0.into() {
+ if peripheral::now() == 0 {
// First, we want to be waiting for our 300 tick delay
- assert_eq!(TestMono::compare(), Some(301.into()));
+ assert_eq!(TestMonoBackend::compare(), Some(301));
}
- if Instant::now() == 100.into() {
+ if peripheral::now() == 100 {
// After 100 ticks, we enqueue a new delay that is supposed to last
// until the 200-tick-mark
- assert_eq!(TestMono::compare(), Some(201.into()));
+ assert_eq!(TestMonoBackend::compare(), Some(201));
}
- if Instant::now() == 201.into() {
+ if peripheral::now() == 201 {
// After 200 ticks, we dequeue the 200-tick-mark delay and
// requeue the 300 tick delay
- assert_eq!(TestMono::compare(), Some(301.into()));
+ assert_eq!(TestMonoBackend::compare(), Some(301));
}
- if Instant::now() == 301.into() {
+ if peripheral::now() == 301 {
// After 300 ticks, we dequeue the 300-tick-mark delay and
// go to the 400 tick delay that is already enqueued
- assert_eq!(TestMono::compare(), Some(401.into()));
+ assert_eq!(TestMonoBackend::compare(), Some(401));
}
}
diff --git a/rtic/CHANGELOG.md b/rtic/CHANGELOG.md
index c2cd678..bca258f 100644
--- a/rtic/CHANGELOG.md
+++ b/rtic/CHANGELOG.md
@@ -7,6 +7,10 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## [Unreleased]
+### Changed
+
+- Remove unused dependency `rtic-monotonics`
+
## [v2.1.1] - 2024-03-13
### Fixed
diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml
index 9a67dd1..e58acb1 100644
--- a/rtic/Cargo.toml
+++ b/rtic/Cargo.toml
@@ -32,7 +32,6 @@ cortex-m = { version = "0.7.0", optional = true }
bare-metal = "1.0.0"
# portable-atomic = { version = "0.3.19" }
atomic-polyfill = "1"
-rtic-monotonics = { path = "../rtic-monotonics", version = "1.4.0", optional = true }
rtic-macros = { path = "../rtic-macros", version = "=2.1.0" }
rtic-core = "1"
critical-section = "1"
@@ -69,7 +68,4 @@ riscv-clint-backend = [
]
# needed for testing
-test-critical-section = [
- "cortex-m/critical-section-single-core",
- "rtic-monotonics/systick-100hz",
-]
+test-critical-section = ["cortex-m/critical-section-single-core"]
diff --git a/rtic/src/lib.rs b/rtic/src/lib.rs
index 2932c22..5b0971f 100644
--- a/rtic/src/lib.rs
+++ b/rtic/src/lib.rs
@@ -34,7 +34,6 @@
pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex};
pub use rtic_macros::app;
-// pub use rtic_monotonic::{self, Monotonic};
/// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude`
pub mod mutex {
diff --git a/xtask/src/argument_parsing.rs b/xtask/src/argument_parsing.rs
index f72be77..c327af8 100644
--- a/xtask/src/argument_parsing.rs
+++ b/xtask/src/argument_parsing.rs
@@ -64,25 +64,28 @@ impl Package {
}
Package::RticMonotonics => {
let features = if partial {
- &["cortex-m-systick", "rp2040", "nrf52840"][..]
+ &[
+ "cortex-m-systick",
+ "rp2040",
+ "nrf52840",
+ "imxrt_gpt1,imxrt-ral/imxrt1062",
+ "stm32_tim2,stm32h725ag",
+ ][..]
} else {
&[
- "cortex-m-systick,embedded-hal-async",
- "cortex-m-systick,systick-100hz,embedded-hal-async",
- "cortex-m-systick,systick-10khz,embedded-hal-async",
- "cortex-m-systick,embedded-hal-async,systick-64bit",
- "cortex-m-systick,systick-100hz,embedded-hal-async,systick-64bit",
- "cortex-m-systick,systick-10khz,embedded-hal-async,systick-64bit",
- "rp2040,embedded-hal-async",
- "nrf52810,embedded-hal-async",
- "nrf52811,embedded-hal-async",
- "nrf52832,embedded-hal-async",
- "nrf52833,embedded-hal-async",
- "nrf52840,embedded-hal-async",
- "nrf5340-app,embedded-hal-async",
- "nrf5340-net,embedded-hal-async",
- "nrf9160,embedded-hal-async",
- "imxrt_gpt1,imxrt-ral/imxrt1062,embedded-hal-async",
+ "cortex-m-systick",
+ "cortex-m-systick,systick-64bit",
+ "rp2040",
+ "nrf52810",
+ "nrf52811",
+ "nrf52832",
+ "nrf52833",
+ "nrf52840",
+ "nrf5340-app",
+ "nrf5340-net",
+ "nrf9160",
+ "imxrt_gpt1,imxrt_gpt2,imxrt-ral/imxrt1062",
+ "stm32_tim2,stm32_tim3,stm32_tim4,stm32_tim5,stm32_tim15,stm32h725ag",
][..]
};
diff --git a/xtask/src/run.rs b/xtask/src/run.rs
index ff81e6a..a36c98c 100644
--- a/xtask/src/run.rs
+++ b/xtask/src/run.rs
@@ -299,7 +299,26 @@ pub fn cargo_doc<'c>(
backend: Backends,
arguments: &'c Option<ExtraArguments>,
) -> Vec<FinalRunResult<'c>> {
- let features = Some(backend.to_target().and_features(backend.to_rtic_feature()));
+ let extra_doc_features = [
+ "rtic-monotonics/cortex-m-systick",
+ "rtic-monotonics/rp2040",
+ "rtic-monotonics/nrf52840",
+ "imxrt-ral/imxrt1011",
+ "rtic-monotonics/imxrt_gpt1",
+ "rtic-monotonics/imxrt_gpt2",
+ "rtic-monotonics/stm32h725ag",
+ "rtic-monotonics/stm32_tim2",
+ "rtic-monotonics/stm32_tim3",
+ "rtic-monotonics/stm32_tim4",
+ "rtic-monotonics/stm32_tim5",
+ "rtic-monotonics/stm32_tim15",
+ ];
+
+ let features = Some(format!(
+ "{},{}",
+ backend.to_target().and_features(backend.to_rtic_feature()),
+ extra_doc_features.join(",")
+ ));
let command = CargoCommand::Doc {
cargoarg,