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>
This commit is contained in:
Finomnis 2024-04-11 00:00:38 +02:00 committed by GitHub
parent e4cc5fd17b
commit 8c23e178f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 2637 additions and 1676 deletions

View file

@ -463,7 +463,7 @@ jobs:
which qemu-system-riscv32 which qemu-system-riscv32
- name: Run-pass tests - name: Run-pass tests
run: cargo xtask -vvv --platform esp32-c3 qemu run: cargo xtask -v --platform esp32-c3 qemu
# Run test suite # Run test suite
tests: tests:

View file

@ -22,10 +22,10 @@ entry 0x403cc710
I (1) boot: 1 phy_init RF data 01 01 0000f000 00001000 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: 2 factory factory app 00 00 00010000 003f0000
I (1) boot: End of partition table I (1) boot: End of partition table
I (1) esp_image: segment 0: paddr=00010020 vaddr=3c010020 size=022e4h ( 8932) map I (1) esp_image: REDACTED
I (3) esp_image: segment 1: paddr=0001230c vaddr=40380000 size=01250h ( 4688) load I (3) esp_image: REDACTED
I (3) esp_image: segment 2: paddr=00013564 vaddr=00000000 size=0cab4h ( 51892)  I (3) esp_image: REDACTED
I (8) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=05db4h ( 23988) map I (8) esp_image: REDACTED
I (11) boot: Loaded app from partition at offset 0x10000 I (11) boot: Loaded app from partition at offset 0x10000
I (11) boot: Disabling RNG early entropy source... I (11) boot: Disabling RNG early entropy source...
init init

View file

@ -5,9 +5,11 @@
use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::gpio::{Level, Output, Speed};
use rtic::app; use rtic::app;
use rtic_monotonics::systick::*; use rtic_monotonics::systick::prelude::*;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
systick_monotonic!(Mono, 1_000);
pub mod pac { pub mod pac {
pub use embassy_stm32::pac::Interrupt as interrupt; pub use embassy_stm32::pac::Interrupt as interrupt;
pub use embassy_stm32::pac::*; pub use embassy_stm32::pac::*;
@ -26,8 +28,7 @@ mod app {
#[init] #[init]
fn init(cx: init::Context) -> (Shared, Local) { fn init(cx: init::Context) -> (Shared, Local) {
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
let systick_mono_token = rtic_monotonics::create_systick_token!(); Mono::start(cx.core.SYST, 25_000_000);
Systick::start(cx.core.SYST, 25_000_000, systick_mono_token);
let p = embassy_stm32::init(Default::default()); let p = embassy_stm32::init(Default::default());
defmt::info!("Hello World!"); defmt::info!("Hello World!");
@ -53,7 +54,7 @@ mod app {
led.set_low(); led.set_low();
} }
state = !state; state = !state;
Systick::delay(1000.millis()).await; Mono::delay(1000.millis()).await;
} }
} }
} }

View file

@ -26,4 +26,6 @@ sleep 3s
# Kill QEMU nicely by sending 'q' (quit) over tcp # Kill QEMU nicely by sending 'q' (quit) over tcp
echo q | nc -N 127.0.0.1 55555 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/'

View file

@ -362,7 +362,6 @@ dependencies = [
"critical-section", "critical-section",
"rtic-core", "rtic-core",
"rtic-macros", "rtic-macros",
"rtic-monotonics",
] ]
[[package]] [[package]]
@ -392,12 +391,11 @@ dependencies = [
[[package]] [[package]]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "1.5.0" version = "2.0.0"
dependencies = [ dependencies = [
"atomic-polyfill", "atomic-polyfill",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"embedded-hal 1.0.0",
"fugit", "fugit",
"rtic-time", "rtic-time",
] ]
@ -417,9 +415,12 @@ dependencies = [
[[package]] [[package]]
name = "rtic-time" name = "rtic-time"
version = "1.3.0" version = "2.0.0"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"embedded-hal 1.0.0",
"embedded-hal-async",
"fugit",
"futures-util", "futures-util",
"rtic-common", "rtic-common",
] ]

View file

@ -11,7 +11,9 @@ use panic_semihosting as _;
#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] #[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)]
mod app { mod app {
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use rtic_monotonics::systick::*; use rtic_monotonics::systick::prelude::*;
systick_monotonic!(Mono, 100);
#[shared] #[shared]
struct Shared {} struct Shared {}
@ -23,8 +25,7 @@ mod app {
fn init(cx: init::Context) -> (Shared, Local) { fn init(cx: init::Context) -> (Shared, Local) {
hprintln!("init"); hprintln!("init");
let systick_token = rtic_monotonics::create_systick_token!(); Mono::start(cx.core.SYST, 12_000_000);
Systick::start(cx.core.SYST, 12_000_000, systick_token);
foo::spawn().ok(); foo::spawn().ok();
bar::spawn().ok(); bar::spawn().ok();
@ -36,21 +37,21 @@ mod app {
#[task] #[task]
async fn foo(_cx: foo::Context) { async fn foo(_cx: foo::Context) {
hprintln!("hello from foo"); hprintln!("hello from foo");
Systick::delay(100.millis()).await; Mono::delay(100.millis()).await;
hprintln!("bye from foo"); hprintln!("bye from foo");
} }
#[task] #[task]
async fn bar(_cx: bar::Context) { async fn bar(_cx: bar::Context) {
hprintln!("hello from bar"); hprintln!("hello from bar");
Systick::delay(200.millis()).await; Mono::delay(200.millis()).await;
hprintln!("bye from bar"); hprintln!("bye from bar");
} }
#[task] #[task]
async fn baz(_cx: baz::Context) { async fn baz(_cx: baz::Context) {
hprintln!("hello from baz"); hprintln!("hello from baz");
Systick::delay(300.millis()).await; Mono::delay(300.millis()).await;
hprintln!("bye from baz"); hprintln!("bye from baz");
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);

View file

@ -8,13 +8,13 @@
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use panic_semihosting as _; 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)] #[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)]
mod app { mod app {
use super::*; use super::*;
use futures::{future::FutureExt, select_biased}; use futures::{future::FutureExt, select_biased};
use rtic_monotonics::Monotonic;
#[shared] #[shared]
struct Shared {} struct Shared {}
@ -27,8 +27,7 @@ mod app {
fn init(cx: init::Context) -> (Shared, Local) { fn init(cx: init::Context) -> (Shared, Local) {
hprintln!("init"); hprintln!("init");
let systick_token = rtic_monotonics::create_systick_token!(); Mono::start(cx.core.SYST, 12_000_000);
Systick::start(cx.core.SYST, 12_000_000, systick_token);
// ANCHOR_END: init // ANCHOR_END: init
foo::spawn().ok(); foo::spawn().ok();
@ -42,19 +41,19 @@ mod app {
// Call hal with short relative timeout using `select_biased` // Call hal with short relative timeout using `select_biased`
select_biased! { select_biased! {
v = hal_get(1).fuse() => hprintln!("hal returned {}", v), 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` // Call hal with long relative timeout using `select_biased`
select_biased! { select_biased! {
v = hal_get(1).fuse() => hprintln!("hal returned {}", v), // hal finish first 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_END: select_biased
// ANCHOR: timeout_after_basic // ANCHOR: timeout_after_basic
// Call hal with long relative timeout using monotonic `timeout_after` // 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), Ok(v) => hprintln!("hal returned {}", v),
_ => hprintln!("timeout"), _ => hprintln!("timeout"),
} }
@ -62,20 +61,20 @@ mod app {
// ANCHOR: timeout_at_basic // ANCHOR: timeout_at_basic
// get the current time instance // get the current time instance
let mut instant = Systick::now(); let mut instant = Mono::now();
// do this 3 times // do this 3 times
for n in 0..3 { for n in 0..3 {
// absolute point in time without drift // absolute point in time without drift
instant += 1000.millis(); instant += 1000.millis();
Systick::delay_until(instant).await; Mono::delay_until(instant).await;
// absolute point in time for timeout // absolute point in time for timeout
let timeout = instant + 500.millis(); 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 { match Mono::timeout_at(timeout, hal_get(n)).await {
Ok(v) => hprintln!("hal returned {} at time {:?}", v, Systick::now()), Ok(v) => hprintln!("hal returned {} at time {:?}", v, Mono::now()),
_ => hprintln!("timeout"), _ => hprintln!("timeout"),
} }
} }
@ -90,7 +89,7 @@ async fn hal_get(n: u32) -> u32 {
// emulate some delay time dependent on n // emulate some delay time dependent on n
let d = 350.millis() + n * 100.millis(); let d = 350.millis() + n * 100.millis();
hprintln!("the hal takes a duration of {:?}", d); hprintln!("the hal takes a duration of {:?}", d);
Systick::delay(d).await; Mono::delay(d).await;
// emulate some return value // emulate some return value
5 5
} }

View file

@ -136,7 +136,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.51",
] ]
[[package]] [[package]]
@ -179,9 +179,18 @@ dependencies = [
[[package]] [[package]]
name = "embedded-hal" name = "embedded-hal"
version = "1.0.0-rc.2" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e57ec6ad0bc8eb967cf9c9f144177f5e8f2f6f02dad0b8b683f9f05f6b22def" 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]] [[package]]
name = "embedded-storage" name = "embedded-storage"
@ -412,18 +421,18 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.70" version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -468,18 +477,18 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.51",
] ]
[[package]] [[package]]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "1.4.0" version = "2.0.0"
dependencies = [ dependencies = [
"atomic-polyfill", "atomic-polyfill",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"critical-section", "critical-section",
"embedded-hal 1.0.0-rc.2", "embedded-hal 1.0.0",
"fugit", "fugit",
"nrf52840-pac", "nrf52840-pac",
"rtic-time", "rtic-time",
@ -487,9 +496,12 @@ dependencies = [
[[package]] [[package]]
name = "rtic-time" name = "rtic-time"
version = "1.1.0" version = "2.0.0"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"embedded-hal 1.0.0",
"embedded-hal-async",
"fugit",
"futures-util", "futures-util",
"rtic-common", "rtic-common",
] ]
@ -537,9 +549,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.39" version = "2.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -563,7 +575,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.51",
] ]
[[package]] [[package]]

View file

@ -26,7 +26,7 @@ features = ["thumbv7-backend"]
[dependencies.rtic-monotonics] [dependencies.rtic-monotonics]
path = "../../rtic-monotonics" path = "../../rtic-monotonics"
version = "1.4.0" version = "2.0.0"
features = ["nrf52840"] features = ["nrf52840"]
# cargo build/run # cargo build/run

View file

@ -5,6 +5,9 @@
use nrf52840_blinky::hal; use nrf52840_blinky::hal;
use rtic_monotonics::nrf::rtc::prelude::*;
nrf_rtc0_monotonic!(Mono);
#[rtic::app(device = hal::pac, dispatchers = [SWI0_EGU0])] #[rtic::app(device = hal::pac, dispatchers = [SWI0_EGU0])]
mod app { mod app {
use super::*; use super::*;
@ -12,10 +15,6 @@ mod app {
use hal::gpio::{Level, Output, Pin, PushPull}; use hal::gpio::{Level, Output, Pin, PushPull};
use hal::prelude::*; use hal::prelude::*;
use rtic_monotonics::nrf::rtc::Rtc0 as Mono;
use rtic_monotonics::nrf::rtc::*;
use rtic_monotonics::Monotonic;
#[shared] #[shared]
struct Shared {} struct Shared {}
@ -30,8 +29,7 @@ mod app {
hal::clocks::Clocks::new(cx.device.CLOCK).start_lfclk(); hal::clocks::Clocks::new(cx.device.CLOCK).start_lfclk();
// Initialize Monotonic // Initialize Monotonic
let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!(); Mono::start(cx.device.RTC0);
Mono::start(cx.device.RTC0, token);
// Setup LED // Setup LED
let port0 = hal::gpio::p0::Parts::new(cx.device.P0); let port0 = hal::gpio::p0::Parts::new(cx.device.P0);

View file

@ -5,6 +5,9 @@
use nrf52840_blinky::hal; 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])] #[rtic::app(device = hal::pac, dispatchers = [SWI0_EGU0])]
mod app { mod app {
use super::*; use super::*;
@ -12,10 +15,6 @@ mod app {
use hal::gpio::{Level, Output, Pin, PushPull}; use hal::gpio::{Level, Output, Pin, PushPull};
use hal::prelude::*; use hal::prelude::*;
use rtic_monotonics::nrf::timer::Timer0 as Mono;
use rtic_monotonics::nrf::timer::*;
use rtic_monotonics::Monotonic;
#[shared] #[shared]
struct Shared {} struct Shared {}
@ -27,8 +26,7 @@ mod app {
#[init] #[init]
fn init(cx: init::Context) -> (Shared, Local) { fn init(cx: init::Context) -> (Shared, Local) {
// Initialize Monotonic // Initialize Monotonic
let token = rtic_monotonics::create_nrf_timer0_monotonic_token!(); Mono::start(cx.device.TIMER0);
Mono::start(cx.device.TIMER0, token);
// Setup LED // Setup LED
let port0 = hal::gpio::p0::Parts::new(cx.device.P0); let port0 = hal::gpio::p0::Parts::new(cx.device.P0);

View file

@ -10,9 +10,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]] [[package]]
name = "atomic-polyfill" name = "atomic-polyfill"
version = "1.0.2" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c314e70d181aa6053b26e3f7fbf86d1dfff84f816a6175b967666b3506ef7289" checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [ dependencies = [
"critical-section", "critical-section",
] ]
@ -52,7 +52,7 @@ checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
dependencies = [ dependencies = [
"bare-metal 0.2.5", "bare-metal 0.2.5",
"bitfield", "bitfield",
"embedded-hal", "embedded-hal 0.2.7",
"volatile-register", "volatile-register",
] ]
@ -73,23 +73,23 @@ checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.109",
] ]
[[package]] [[package]]
name = "crc-any" name = "crc-any"
version = "2.4.3" version = "2.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774646b687f63643eb0f4bf13dc263cb581c8c9e57973b6ddf78bda3994d88df" checksum = "c01a5e1f881f6fb6099a7bdf949e946719fd4f1fefa56264890574febf0eb6d0"
dependencies = [ dependencies = [
"debug-helper", "debug-helper",
] ]
[[package]] [[package]]
name = "critical-section" name = "critical-section"
version = "1.1.1" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]] [[package]]
name = "debug-helper" name = "debug-helper"
@ -99,9 +99,9 @@ checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e"
[[package]] [[package]]
name = "either" name = "either"
version = "1.8.1" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
[[package]] [[package]]
name = "embedded-dma" name = "embedded-dma"
@ -123,10 +123,64 @@ dependencies = [
] ]
[[package]] [[package]]
name = "equivalent" name = "embedded-hal"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" 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 = "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]] [[package]]
name = "fugit" name = "fugit"
@ -139,21 +193,21 @@ dependencies = [
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.28" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.28" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.28" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-task", "futures-task",
@ -169,15 +223,15 @@ checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.0" version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.0.0" version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -224,7 +278,7 @@ checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.109",
] ]
[[package]] [[package]]
@ -238,15 +292,15 @@ dependencies = [
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.12" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.9" version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]] [[package]]
name = "pin-utils" name = "pin-utils"
@ -265,6 +319,12 @@ dependencies = [
"paste", "paste",
] ]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
version = "1.0.4" version = "1.0.4"
@ -274,7 +334,7 @@ dependencies = [
"proc-macro-error-attr", "proc-macro-error-attr",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.109",
"version_check", "version_check",
] ]
@ -291,18 +351,18 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.63" version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.29" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -315,11 +375,10 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]] [[package]]
name = "rp-pico" name = "rp-pico"
version = "0.7.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aab28f6f4e19cec2d61b64cdd685e69794b81c579fd3b765579c46018fe616d0" checksum = "6341771e6f8e5d130b2b3cbc23435b7847761adf198af09f4b2a60407d43bd56"
dependencies = [ dependencies = [
"cortex-m",
"cortex-m-rt", "cortex-m-rt",
"fugit", "fugit",
"rp2040-boot2", "rp2040-boot2",
@ -329,23 +388,24 @@ dependencies = [
[[package]] [[package]]
name = "rp2040-boot2" name = "rp2040-boot2"
version = "0.2.1" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c773ec49b836077aa144b58dc7654a243e1eecdb6cf0d25361ae7c7600fabd8" checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21"
dependencies = [ dependencies = [
"crc-any", "crc-any",
] ]
[[package]] [[package]]
name = "rp2040-hal" name = "rp2040-hal"
version = "0.8.2" version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1369bb84862d7f69391a96606b2f29a00bfce7f29a749e23d5f01fc3f607ada0" checksum = "1ff2b9ae7e6dd6720fd9f64250c9087260e50fe98b6b032ccca65be3581167ca"
dependencies = [ dependencies = [
"cortex-m", "cortex-m",
"critical-section", "critical-section",
"embedded-dma", "embedded-dma",
"embedded-hal", "embedded-hal 0.2.7",
"frunk",
"fugit", "fugit",
"itertools", "itertools",
"nb 1.1.0", "nb 1.1.0",
@ -368,17 +428,18 @@ dependencies = [
"cortex-m-rt", "cortex-m-rt",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.109",
] ]
[[package]] [[package]]
name = "rp2040-pac" name = "rp2040-pac"
version = "0.4.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9192cafbb40d717c9e0ddf767aaf9c69fee1b4e48d22ed853b57b11f6d9f3d7e" checksum = "12d9d8375815f543f54835d01160d4e47f9e2cae75f17ff8f1ec19ce1da96e4c"
dependencies = [ dependencies = [
"cortex-m", "cortex-m",
"cortex-m-rt", "cortex-m-rt",
"critical-section",
"vcell", "vcell",
] ]
@ -387,7 +448,7 @@ name = "rp2040_local_i2c_init"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-m", "cortex-m",
"embedded-hal", "embedded-hal 0.2.7",
"fugit", "fugit",
"panic-probe", "panic-probe",
"rp-pico", "rp-pico",
@ -397,7 +458,7 @@ dependencies = [
[[package]] [[package]]
name = "rtic" name = "rtic"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"atomic-polyfill", "atomic-polyfill",
"bare-metal 1.0.0", "bare-metal 1.0.0",
@ -409,9 +470,10 @@ dependencies = [
[[package]] [[package]]
name = "rtic-common" name = "rtic-common"
version = "1.0.0" version = "1.0.1"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"portable-atomic",
] ]
[[package]] [[package]]
@ -422,22 +484,23 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
[[package]] [[package]]
name = "rtic-macros" name = "rtic-macros"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.51",
] ]
[[package]] [[package]]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "1.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"atomic-polyfill", "atomic-polyfill",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"embedded-hal 1.0.0",
"fugit", "fugit",
"rp2040-pac", "rp2040-pac",
"rtic-time", "rtic-time",
@ -445,9 +508,12 @@ dependencies = [
[[package]] [[package]]
name = "rtic-time" name = "rtic-time"
version = "1.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"embedded-hal 1.0.0",
"embedded-hal-async",
"fugit",
"futures-util", "futures-util",
"rtic-common", "rtic-common",
] ]
@ -494,10 +560,21 @@ dependencies = [
] ]
[[package]] [[package]]
name = "unicode-ident" name = "syn"
version = "1.0.9" version = "2.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]] [[package]]
name = "usb-device" name = "usb-device"
@ -525,9 +602,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]] [[package]]
name = "volatile-register" name = "volatile-register"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
dependencies = [ dependencies = [
"vcell", "vcell",
] ]

View file

@ -15,14 +15,14 @@ features = ["thumbv6-backend"]
[dependencies.rtic-monotonics] [dependencies.rtic-monotonics]
path = "../../rtic-monotonics" path = "../../rtic-monotonics"
version = "1.0.0" version = "2.0.0"
features = ["rp2040"] features = ["rp2040"]
[dependencies] [dependencies]
cortex-m = "0.7" cortex-m = "0.7"
embedded-hal = { version = "0.2.7", features = ["unproven"] } embedded-hal = { version = "0.2.7", features = ["unproven"] }
fugit = "0.3" fugit = "0.3"
rp-pico = "0.7.0" rp-pico = "0.8.0"
panic-probe = "0.3" panic-probe = "0.3"
[profile.dev] [profile.dev]

View file

@ -1,15 +1,21 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#[rtic::app( use rtic_monotonics::rp2040::prelude::*;
device = rp_pico::hal::pac,
dispatchers = [TIMER_IRQ_1] rp2040_timer_monotonic!(Mono);
)]
#[rtic::app(device = rp_pico::hal::pac)]
mod app { mod app {
use super::*;
use rp_pico::hal::{ use rp_pico::hal::{
clocks, gpio, clocks,
gpio::pin::bank0::{Gpio2, Gpio25, Gpio3}, gpio::{
gpio::pin::PushPullOutput, self,
bank0::{Gpio2, Gpio25, Gpio3},
FunctionSioOutput, PullNone, PullUp,
},
pac, pac,
sio::Sio, sio::Sio,
watchdog::Watchdog, watchdog::Watchdog,
@ -20,15 +26,13 @@ mod app {
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin}; use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin};
use fugit::RateExtU32; use fugit::RateExtU32;
use rtic_monotonics::rp2040::*;
use panic_probe as _; use panic_probe as _;
type I2CBus = I2C< type I2CBus = I2C<
pac::I2C1, pac::I2C1,
( (
gpio::Pin<Gpio2, gpio::FunctionI2C>, gpio::Pin<Gpio2, gpio::FunctionI2C, PullUp>,
gpio::Pin<Gpio3, gpio::FunctionI2C>, gpio::Pin<Gpio3, gpio::FunctionI2C, PullUp>,
), ),
>; >;
@ -37,7 +41,7 @@ mod app {
#[local] #[local]
struct Local { struct Local {
led: gpio::Pin<Gpio25, PushPullOutput>, led: gpio::Pin<Gpio25, FunctionSioOutput, PullNone>,
i2c: &'static mut I2CBus, i2c: &'static mut I2CBus,
} }
@ -48,11 +52,8 @@ mod app {
i2c_ctx: MaybeUninit<I2CBus> = MaybeUninit::uninit() i2c_ctx: MaybeUninit<I2CBus> = MaybeUninit::uninit()
])] ])]
fn init(mut ctx: init::Context) -> (Shared, Local) { 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 // 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 mut watchdog = Watchdog::new(ctx.device.WATCHDOG);
let clocks = clocks::init_clocks_and_plls( let clocks = clocks::init_clocks_and_plls(
XOSC_CRYSTAL_FREQ, XOSC_CRYSTAL_FREQ,
@ -74,12 +75,21 @@ mod app {
sio.gpio_bank0, sio.gpio_bank0,
&mut ctx.device.RESETS, &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(); led.set_low().unwrap();
// Init I2C pins // Init I2C pins
let sda_pin = gpioa.gpio2.into_mode::<gpio::FunctionI2C>(); let sda_pin = gpioa
let scl_pin = gpioa.gpio3.into_mode::<gpio::FunctionI2C>(); .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 // Init I2C itself, using MaybeUninit to overwrite the previously
// uninitialized i2c_ctx variable without dropping its value // uninitialized i2c_ctx variable without dropping its value
@ -118,7 +128,7 @@ mod app {
// now to do something with it! // now to do something with it!
// Delay for 1 second // Delay for 1 second
Timer::delay(1000.millis()).await; Mono::delay(1000.millis()).await;
} }
} }
} }

View file

@ -52,11 +52,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bxcan" name = "bxcan"
version = "0.6.2" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b13b4b2ea9ab2ba924063ebb86ad895cb79f4a79bf90f27949eb20c335b30f9" checksum = "40ac3d0c0a542d0ab5521211f873f62706a7136df415676f676d347e5a41dd80"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"embedded-hal 0.2.7",
"nb 1.1.0", "nb 1.1.0",
"vcell", "vcell",
] ]
@ -86,7 +87,7 @@ dependencies = [
"bare-metal 0.2.5", "bare-metal 0.2.5",
"bitfield", "bitfield",
"critical-section", "critical-section",
"embedded-hal", "embedded-hal 0.2.7",
"volatile-register", "volatile-register",
] ]
@ -112,9 +113,9 @@ dependencies = [
[[package]] [[package]]
name = "critical-section" name = "critical-section"
version = "1.1.1" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]] [[package]]
name = "darling" name = "darling"
@ -136,7 +137,7 @@ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.22", "syn 2.0.50",
] ]
[[package]] [[package]]
@ -147,7 +148,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.22", "syn 2.0.50",
] ]
[[package]] [[package]]
@ -169,6 +170,21 @@ dependencies = [
"void", "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]] [[package]]
name = "embedded-time" name = "embedded-time"
version = "0.12.1" version = "0.12.1"
@ -180,9 +196,9 @@ dependencies = [
[[package]] [[package]]
name = "enumset" name = "enumset"
version = "1.1.2" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d"
dependencies = [ dependencies = [
"enumset_derive", "enumset_derive",
] ]
@ -196,7 +212,7 @@ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.22", "syn 2.0.50",
] ]
[[package]] [[package]]
@ -343,28 +359,28 @@ dependencies = [
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.15" version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
[[package]] [[package]]
name = "panic-rtt-target" name = "panic-rtt-target"
version = "0.1.2" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6ab67bc881453e4c90f958c657c1303670ea87bc1a16e87fd71a40f656dce9" checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe"
dependencies = [ dependencies = [
"cortex-m", "critical-section",
"rtt-target 0.3.1", "rtt-target",
] ]
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.12" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
@ -378,6 +394,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
version = "1.0.4" version = "1.0.4"
@ -404,18 +426,18 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.63" version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.29" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -431,7 +453,7 @@ dependencies = [
[[package]] [[package]]
name = "rtic" name = "rtic"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"atomic-polyfill", "atomic-polyfill",
"bare-metal 1.0.0", "bare-metal 1.0.0",
@ -443,9 +465,10 @@ dependencies = [
[[package]] [[package]]
name = "rtic-common" name = "rtic-common"
version = "1.0.0" version = "1.0.1"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"portable-atomic",
] ]
[[package]] [[package]]
@ -456,49 +479,44 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
[[package]] [[package]]
name = "rtic-macros" name = "rtic-macros"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.50",
] ]
[[package]] [[package]]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "1.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"atomic-polyfill", "atomic-polyfill",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"embedded-hal 1.0.0",
"fugit", "fugit",
"rtic-time", "rtic-time",
] ]
[[package]] [[package]]
name = "rtic-time" name = "rtic-time"
version = "1.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"embedded-hal 1.0.0",
"embedded-hal-async",
"fugit",
"futures-util", "futures-util",
"rtic-common", "rtic-common",
] ]
[[package]] [[package]]
name = "rtt-target" name = "rtt-target"
version = "0.3.1" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065d6058bb1204f51a562a67209e1817cf714759d5cf845aa45c75fa7b0b9d9b" checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0"
dependencies = [
"ufmt-write",
]
[[package]]
name = "rtt-target"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3afa12c77ba1b9bf560e4039a9b9a08bb9cde0e9e6923955eeb917dd8d5cf303"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"ufmt-write", "ufmt-write",
@ -553,9 +571,9 @@ dependencies = [
[[package]] [[package]]
name = "stm32f3" name = "stm32f3"
version = "0.14.0" version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "265cda62ac13307414de4aca58dbbbd8038ddba85cffbb335823aa216f2e3200" checksum = "b28b37228ef3fa47956af38c6abd756e912f244c1657f14e66d42fc8d74ea96f"
dependencies = [ dependencies = [
"bare-metal 1.0.0", "bare-metal 1.0.0",
"cortex-m", "cortex-m",
@ -567,30 +585,32 @@ dependencies = [
name = "stm32f3-blinky" name = "stm32f3-blinky"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"embedded-hal", "cortex-m",
"embedded-hal 0.2.7",
"panic-rtt-target", "panic-rtt-target",
"rtic", "rtic",
"rtic-monotonics", "rtic-monotonics",
"rtt-target 0.4.0", "rtt-target",
"stm32f3xx-hal", "stm32f3xx-hal",
] ]
[[package]] [[package]]
name = "stm32f3xx-hal" name = "stm32f3xx-hal"
version = "0.9.2" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c9d827f02df3826371c897404dfbea8a1abd544eed9d6cdc3e5f6e9f04b9e06" checksum = "4c73e8b6e63435b75198d2fe2b27cd7f5c8e0b07bd5da9f82cffddf23210f77f"
dependencies = [ dependencies = [
"bare-metal 1.0.0",
"bxcan", "bxcan",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"cortex-m-rt", "cortex-m-rt",
"critical-section",
"embedded-dma", "embedded-dma",
"embedded-hal", "embedded-hal 0.2.7",
"embedded-time", "embedded-time",
"enumset", "enumset",
"nb 1.1.0", "nb 1.1.0",
"num-traits",
"paste", "paste",
"rtcc", "rtcc",
"slice-group-by", "slice-group-by",
@ -612,9 +632,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.22" version = "2.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -14,17 +14,21 @@ features = ["thumbv7-backend"]
[dependencies.rtic-monotonics] [dependencies.rtic-monotonics]
path = "../../rtic-monotonics" path = "../../rtic-monotonics"
version = "1.0.0" version = "2.0.0"
features = ["cortex-m-systick"] features = ["cortex-m-systick"]
[dependencies.cortex-m]
version = "0.7.7"
features = ["critical-section-single-core"]
[dependencies] [dependencies]
embedded-hal = "0.2.7" embedded-hal = "0.2.7"
panic-rtt-target = { version = "0.1.2", features = ["cortex-m"] } panic-rtt-target = { version = "0.1.3" }
rtt-target = { version = "0.4.0" } rtt-target = { version = "0.5.0" }
[dependencies.stm32f3xx-hal] [dependencies.stm32f3xx-hal]
features = ["stm32f303xc", "rt"] features = ["stm32f303xc", "rt"]
version = "0.9.2" version = "0.10.0"
# this lets you use `cargo fix`! # this lets you use `cargo fix`!
[[bin]] [[bin]]

View file

@ -5,11 +5,13 @@
use panic_rtt_target as _; use panic_rtt_target as _;
use rtic::app; use rtic::app;
use rtic_monotonics::systick::*; use rtic_monotonics::systick::prelude::*;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use stm32f3xx_hal::gpio::{Output, PushPull, PA5}; use stm32f3xx_hal::gpio::{Output, PushPull, PA5};
use stm32f3xx_hal::prelude::*; use stm32f3xx_hal::prelude::*;
systick_monotonic!(Mono, 1000);
#[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [SPI1])] #[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [SPI1])]
mod app { mod app {
use super::*; use super::*;
@ -30,8 +32,7 @@ mod app {
let mut rcc = cx.device.RCC.constrain(); let mut rcc = cx.device.RCC.constrain();
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
let systick_mono_token = rtic_monotonics::create_systick_token!(); Mono::start(cx.core.SYST, 36_000_000); // default STM32F303 clock-rate is 36MHz
Systick::start(cx.core.SYST, 36_000_000, systick_mono_token); // default STM32F303 clock-rate is 36MHz
rtt_init_print!(); rtt_init_print!();
rprintln!("init"); rprintln!("init");
@ -67,7 +68,7 @@ mod app {
cx.local.led.set_low().unwrap(); cx.local.led.set_low().unwrap();
*cx.local.state = true; *cx.local.state = true;
} }
Systick::delay(1000.millis()).await; Mono::delay(1000.millis()).await;
} }
} }
} }

View file

@ -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"

View file

@ -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"
]
}

View file

@ -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",
]

View file

@ -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 # <-

View file

@ -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/

View file

@ -0,0 +1,6 @@
/* Linker script for the STM32G030F6 */
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 32K
RAM : ORIGIN = 0x20000000, LENGTH = 8K
}

View file

@ -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;
}
}
}

View file

@ -133,9 +133,18 @@ dependencies = [
[[package]] [[package]]
name = "embedded-hal" 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" 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]] [[package]]
name = "equivalent" name = "equivalent"
@ -440,12 +449,12 @@ dependencies = [
[[package]] [[package]]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "1.4.0" version = "2.0.0"
dependencies = [ dependencies = [
"atomic-polyfill", "atomic-polyfill",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"embedded-hal 1.0.0-rc.2", "embedded-hal 1.0.0-rc.3",
"fugit", "fugit",
"imxrt-ral", "imxrt-ral",
"rtic-time", "rtic-time",
@ -453,9 +462,12 @@ dependencies = [
[[package]] [[package]]
name = "rtic-time" name = "rtic-time"
version = "1.1.0" version = "2.0.0"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"embedded-hal 1.0.0-rc.3",
"embedded-hal-async",
"fugit",
"futures-util", "futures-util",
"rtic-common", "rtic-common",
] ]

View file

@ -14,7 +14,7 @@ features = ["thumbv7-backend"]
[dependencies.rtic-monotonics] [dependencies.rtic-monotonics]
path = "../../rtic-monotonics" path = "../../rtic-monotonics"
version = "1.2.1" version = "2.0.0"
features = ["imxrt_gpt1"] features = ["imxrt_gpt1"]
[dependencies] [dependencies]

View file

@ -13,9 +13,8 @@ use bsp::logging;
use embedded_hal::serial::Write; use embedded_hal::serial::Write;
use rtic_monotonics::imxrt::Gpt1 as Mono; use rtic_monotonics::imxrt::prelude::*;
use rtic_monotonics::imxrt::*; imxrt_gpt1_monotonic!(Mono, board::PERCLK_FREQUENCY);
use rtic_monotonics::Monotonic;
#[rtic::app(device = teensy4_bsp, dispatchers = [LPSPI1])] #[rtic::app(device = teensy4_bsp, dispatchers = [LPSPI1])]
mod app { mod app {
@ -61,8 +60,7 @@ mod app {
// Initialize Monotonic // Initialize Monotonic
gpt1.set_clock_source(hal::gpt::ClockSource::PeripheralClock); gpt1.set_clock_source(hal::gpt::ClockSource::PeripheralClock);
let gpt1_mono_token = rtic_monotonics::create_imxrt_gpt1_token!(); Mono::start(gpt1.release());
Mono::start(board::PERCLK_FREQUENCY, gpt1.release(), gpt1_mono_token);
// Setup LED // Setup LED
let led = board::led(&mut gpio2, pins.p13); let led = board::led(&mut gpio2, pins.p13);

View file

@ -10,8 +10,8 @@ fn panic(_: &::core::panic::PanicInfo) -> ! {
use teensy4_bsp::{board, hal}; use teensy4_bsp::{board, hal};
use rtic_monotonics::imxrt::Gpt1 as Mono; use rtic_monotonics::imxrt::prelude::*;
use rtic_monotonics::imxrt::*; imxrt_gpt1_monotonic!(Mono, board::PERCLK_FREQUENCY);
#[rtic::app(device = teensy4_bsp, dispatchers = [LPSPI1])] #[rtic::app(device = teensy4_bsp, dispatchers = [LPSPI1])]
mod app { mod app {
@ -36,8 +36,7 @@ mod app {
// Initialize Monotonic // Initialize Monotonic
gpt1.set_clock_source(hal::gpt::ClockSource::PeripheralClock); gpt1.set_clock_source(hal::gpt::ClockSource::PeripheralClock);
let gpt1_mono_token = rtic_monotonics::create_imxrt_gpt1_token!(); Mono::start(gpt1.release());
Mono::start(board::PERCLK_FREQUENCY, gpt1.release(), gpt1_mono_token);
// Setup LED // Setup LED
let led = board::led(&mut gpio2, pins.p13); let led = board::led(&mut gpio2, pins.p13);

View file

@ -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! For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## Unreleased ## Unreleased - v2.0.0
### Changed ### 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+ - Tweak `build.rs` to avoid warnings in Nightly 1.78+
- Removed unused `rust-toolchain.toml` - Removed unused `rust-toolchain.toml`

View file

@ -1,6 +1,6 @@
[package] [package]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "1.5.0" version = "2.0.0"
edition = "2021" edition = "2021"
authors = [ authors = [
@ -24,13 +24,17 @@ features = [
"imxrt_gpt1", "imxrt_gpt1",
"imxrt_gpt2", "imxrt_gpt2",
"imxrt-ral/imxrt1011", "imxrt-ral/imxrt1011",
"stm32h725ag",
"stm32_tim2",
"stm32_tim3",
"stm32_tim4",
"stm32_tim5",
"stm32_tim15",
] ]
rustdoc-flags = ["--cfg", "docsrs"] rustdoc-flags = ["--cfg", "docsrs"]
[dependencies] [dependencies]
rtic-time = { version = "1.1.0", path = "../rtic-time" } rtic-time = { version = "2.0.0", path = "../rtic-time" }
embedded-hal = { version = "1.0" }
embedded-hal-async = { version = "1.0", optional = true }
fugit = { version = "0.3.6" } fugit = { version = "0.3.6" }
atomic-polyfill = "1" atomic-polyfill = "1"
cfg-if = "1.0.0" cfg-if = "1.0.0"
@ -69,8 +73,6 @@ defmt = ["fugit/defmt"]
# Systick on Cortex-M, default 1 kHz # Systick on Cortex-M, default 1 kHz
cortex-m-systick = ["dep:cortex-m"] cortex-m-systick = ["dep:cortex-m"]
systick-100hz = []
systick-10khz = []
# Use 64-bit wide backing storage for the Instant # Use 64-bit wide backing storage for the Instant
systick-64bit = [] systick-64bit = []
@ -99,7 +101,6 @@ stm32_tim2 = []
stm32_tim3 = [] stm32_tim3 = []
stm32_tim4 = [] stm32_tim4 = []
stm32_tim5 = [] stm32_tim5 = []
stm32_tim12 = []
stm32_tim15 = [] stm32_tim15 = []
stm32-metapac = ["dep:stm32-metapac", "dep:quote", "dep:proc-macro2"] stm32-metapac = ["dep:stm32-metapac", "dep:quote", "dep:proc-macro2"]

View file

@ -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 //! # Example
//! //!
//! ``` //! ```
//! use rtic_monotonics::imxrt::*; //! use rtic_monotonics::imxrt::prelude::*;
//! use rtic_monotonics::imxrt::Gpt1 as Mono; //! imxrt_gpt1_monotonic!(Mono, 1_000_000);
//! //!
//! fn init() { //! fn init() {
//! // Obtain ownership of the timer register block //! // Obtain ownership of the timer register block.
//! let gpt1 = unsafe { imxrt_ral::gpt::GPT1::instance() }; //! let gpt1 = unsafe { imxrt_ral::gpt::GPT1::instance() };
//! //!
//! // Configure the timer clock source and determine its tick rate //! // Configure the timer tick rate as specified earlier
//! let timer_tickrate_hz = 1_000_000; //! todo!("Configure the gpt1 peripheral to a tick rate of 1_000_000");
//!
//! // Generate timer token to ensure correct timer interrupt handler is used
//! let token = rtic_monotonics::create_imxrt_gpt1_token!();
//! //!
//! // Start the monotonic //! // Start the monotonic
//! Mono::start(timer_tickrate_hz, gpt1, token); //! Mono::start(gpt1);
//! } //! }
//! //!
//! async fn usage() { //! async fn usage() {
//! loop { //! loop {
//! // Use the monotonic //! // Use the monotonic
//! let timestamp = Mono::now().ticks(); //! let timestamp = Mono::now();
//! Mono::delay(100.millis()).await; //! Mono::delay(100.millis()).await;
//! } //! }
//! } //! }
//! ``` //! ```
use crate::{Monotonic, TimeoutError, TimerQueue};
use atomic_polyfill::{AtomicU32, Ordering}; use atomic_polyfill::{AtomicU32, Ordering};
pub use fugit::{self, ExtU64, ExtU64Ceil}; use rtic_time::{
use rtic_time::half_period_counter::calculate_now; half_period_counter::calculate_now,
timer_queue::{TimerQueue, TimerQueueBackend},
};
use imxrt_ral as ral; pub use imxrt_ral as ral;
const TIMER_HZ: u32 = 1_000_000; /// 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)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! __internal_create_imxrt_timer_interrupt { macro_rules! __internal_create_imxrt_timer_interrupt {
($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ ($mono_backend:ident, $timer:ident) => {
#[no_mangle] #[no_mangle]
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern "C" fn $timer() { 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);
$crate::imxrt::$mono_backend::_start(gpt);
}
} }
pub struct $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 },
>;
}
unsafe impl $crate::InterruptToken<$crate::imxrt::$mono_timer> for $timer_token {} $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
$timer_token };
}};
} }
/// 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")] #[cfg(feature = "imxrt_gpt1")]
#[macro_export] #[macro_export]
macro_rules! create_imxrt_gpt1_token { macro_rules! imxrt_gpt1_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_imxrt_timer_interrupt!(Gpt1, GPT1, Gpt1Token) $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")] #[cfg(feature = "imxrt_gpt2")]
#[macro_export] #[macro_export]
macro_rules! create_imxrt_gpt2_token { macro_rules! imxrt_gpt2_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_imxrt_timer_interrupt!(Gpt2, GPT2, Gpt2Token) $crate::__internal_create_imxrt_timer_struct!($name, Gpt2Backend, GPT2, $tick_rate_hz);
}}; };
} }
macro_rules! make_timer { macro_rules! make_timer {
($mono_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { ($mono_name:ident, $backend_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
/// Timer implementing [`Monotonic`] which runs at 1 MHz. /// GPT based [`TimerQueueBackend`].
$( $(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))] #[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)? )?
pub struct $mono_name; pub struct $backend_name;
use ral::gpt::$timer; use ral::gpt::$timer;
/// Number of 2^31 periods elapsed since boot. /// Number of 2^31 periods elapsed since boot.
static $period: AtomicU32 = AtomicU32::new(0); static $period: AtomicU32 = AtomicU32::new(0);
static $tq: TimerQueue<$mono_name> = TimerQueue::new(); static $tq: TimerQueue<$backend_name> = TimerQueue::new();
impl $mono_name { impl $backend_name {
/// Starts the monotonic timer. /// Starts the timer.
/// ///
/// - `tick_freq_hz`: The tick frequency of the given timer. /// **Do not use this function directly.**
/// - `gpt`: The GPT timer register block instance.
/// - `_interrupt_token`: Required for correct timer interrupt handling.
/// ///
/// This method must be called only once. /// Use the prelude macros instead.
pub fn start(tick_freq_hz: u32, gpt: $timer, _interrupt_token: impl crate::InterruptToken<Self>) { pub fn _start(gpt: $timer) {
// 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);
// Disable the timer. // Disable the timer.
ral::modify_reg!(ral::gpt, gpt, CR, EN: 0); ral::modify_reg!(ral::gpt, gpt, CR, EN: 0);
@ -122,11 +168,6 @@ macro_rules! make_timer {
// Reset period // Reset period
$period.store(0, Ordering::SeqCst); $period.store(0, Ordering::SeqCst);
// Prescaler
ral::modify_reg!(ral::gpt, gpt, PR,
PRESCALER: (prescaler - 1), // Scale to our desired clock rate
);
// Enable interrupts // Enable interrupts
ral::write_reg!(ral::gpt, gpt, IR, ral::write_reg!(ral::gpt, gpt, IR,
ROVIE: 1, // Rollover interrupt ROVIE: 1, // Rollover interrupt
@ -150,7 +191,6 @@ macro_rules! make_timer {
ENMOD: 0, // Keep state when disabled ENMOD: 0, // Keep state when disabled
); );
// SAFETY: We take full ownership of the peripheral and interrupt vector, // 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 // plus we are not using any external shared resources so we won't impact
// basepri/source masking based critical sections. // basepri/source masking based critical sections.
@ -159,65 +199,21 @@ macro_rules! make_timer {
cortex_m::peripheral::NVIC::unmask(ral::Interrupt::$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. impl TimerQueueBackend for $backend_name {
#[inline] type Ticks = u64;
pub async fn delay(duration: <Self as Monotonic>::Duration) {
$tq.delay(duration).await;
}
/// Timeout at a specific time. fn now() -> Self::Ticks {
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>;
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
fn now() -> Self::Instant {
let gpt = unsafe{ $timer::instance() }; let gpt = unsafe{ $timer::instance() };
Self::Instant::from_ticks(calculate_now( calculate_now(
|| $period.load(Ordering::Relaxed), || $period.load(Ordering::Relaxed),
|| ral::read_reg!(ral::gpt, gpt, CNT) || ral::read_reg!(ral::gpt, gpt, CNT)
)) )
} }
fn set_compare(instant: Self::Instant) { fn set_compare(instant: Self::Ticks) {
let gpt = unsafe{ $timer::instance() }; let gpt = unsafe{ $timer::instance() };
// Set the timer regardless of whether it is multiple periods in the future, // 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 // The worst thing that can happen is a spurious wakeup, and with a timer
// period of half an hour, this is hardly a problem. // period of half an hour, this is hardly a problem.
let ticks = instant.duration_since_epoch().ticks(); let ticks_wrapped = instant as u32;
let ticks_wrapped = ticks as u32;
ral::write_reg!(ral::gpt, gpt, OCR[1], ticks_wrapped); 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!"); assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!");
} }
} }
fn timer_queue() -> &'static TimerQueue<Self> {
&$tq
}
} }
}; };
} }
#[cfg(feature = "imxrt_gpt1")] #[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")] #[cfg(feature = "imxrt_gpt2")]
make_timer!(Gpt2, GPT2, GPT2_HALFPERIODS, GPT2_TQ); make_timer!(Gpt2, Gpt2Backend, GPT2, GPT2_HALFPERIODS, GPT2_TQ);

View file

@ -21,14 +21,18 @@
//! `Available on crate features X only` tag are available on any `nrf52*` feature. //! `Available on crate features X only` tag are available on any `nrf52*` feature.
//! //!
// To build these docs correctly: // 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] #![no_std]
#![deny(missing_docs)] #![deny(missing_docs)]
#![allow(incomplete_features)] #![allow(incomplete_features)]
#![cfg_attr(docsrs, feature(doc_cfg))] #![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")] #[cfg(feature = "cortex-m-systick")]
pub mod systick; pub mod systick;
@ -92,13 +96,3 @@ pub(crate) unsafe fn set_monotonic_prio(
nvic.set_priority(interrupt, hw_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> {}

View file

@ -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 //! # Example
//! //!
//! ``` //! ```
//! use rtic_monotonics::nrf::rtc::*; //! use rtic_monotonics::nrf::rtc::prelude::*;
//! nrf_rtc0_monotonic!(Mono);
//! //!
//! fn init() { //! fn init() {
//! # // This is normally provided by the selected PAC //! # // This is normally provided by the selected PAC
//! # let rtc = unsafe { core::mem::transmute(()) }; //! # let rtc = unsafe { core::mem::transmute(()) };
//! // Generate the required token
//! let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!();
//!
//! // Start the monotonic //! // Start the monotonic
//! Rtc0::start(rtc, token); //! Mono::start(rtc);
//! } //! }
//! //!
//! async fn usage() { //! async fn usage() {
//! loop { //! loop {
//! // Use the monotonic //! // Use the monotonic
//! Rtc0::delay(100.millis()).await; //! let timestamp = Mono::now();
//! Mono::delay(100.millis()).await;
//! } //! }
//! } //! }
//! ``` //! ```
#[cfg(feature = "nrf52810")] /// Common definitions and traits for using the nRF RTC monotonics
use nrf52810_pac::{self as pac, Interrupt, RTC0, RTC1}; pub mod prelude {
#[cfg(feature = "nrf52811")] pub use crate::nrf_rtc0_monotonic;
use nrf52811_pac::{self as pac, Interrupt, RTC0, RTC1}; pub use crate::nrf_rtc1_monotonic;
#[cfg(feature = "nrf52832")] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
use nrf52832_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; pub use crate::nrf_rtc2_monotonic;
#[cfg(feature = "nrf52833")]
use nrf52833_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; pub use crate::Monotonic;
#[cfg(feature = "nrf52840")] pub use fugit::{self, ExtU64, ExtU64Ceil};
use nrf52840_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; }
#[cfg(feature = "nrf5340-app")]
use nrf5340_app_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; #[cfg(feature = "nrf52810")]
#[cfg(feature = "nrf5340-net")] #[doc(hidden)]
use nrf5340_net_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; pub use nrf52810_pac::{self as pac, RTC0, RTC1};
#[cfg(feature = "nrf9160")] #[cfg(feature = "nrf52811")]
use nrf9160_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; #[doc(hidden)]
pub use nrf52811_pac::{self as pac, RTC0, RTC1};
#[cfg(feature = "nrf52832")]
#[doc(hidden)]
pub use nrf52832_pac::{self as pac, RTC0, RTC1, RTC2};
#[cfg(feature = "nrf52833")]
#[doc(hidden)]
pub use nrf52833_pac::{self as pac, RTC0, RTC1, RTC2};
#[cfg(feature = "nrf52840")]
#[doc(hidden)]
pub use nrf52840_pac::{self as pac, RTC0, RTC1, RTC2};
#[cfg(feature = "nrf5340-app")]
#[doc(hidden)]
pub use nrf5340_app_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
#[cfg(feature = "nrf5340-net")]
#[doc(hidden)]
pub use nrf5340_net_pac::{self as pac, RTC0_NS as RTC0, RTC1_NS as RTC1};
#[cfg(feature = "nrf9160")]
#[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 atomic_polyfill::{AtomicU32, Ordering};
use core::future::Future; use rtic_time::{
pub use fugit::{self, ExtU64, ExtU64Ceil}; half_period_counter::calculate_now,
use rtic_time::half_period_counter::calculate_now; timer_queue::{TimerQueue, TimerQueueBackend},
};
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! __internal_create_nrf_rtc_interrupt { macro_rules! __internal_create_nrf_rtc_interrupt {
($mono_timer:ident, $rtc:ident, $rtc_token:ident) => {{ ($mono_backend:ident, $rtc:ident) => {
#[no_mangle] #[no_mangle]
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern "C" fn $rtc() { 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();
}
};
}
#[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);
}
} }
pub struct $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,
>;
}
unsafe impl $crate::InterruptToken<$crate::nrf::rtc::$mono_timer> for $rtc_token {} $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
$rtc_token };
}};
} }
/// 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_export]
macro_rules! create_nrf_rtc0_monotonic_token { macro_rules! nrf_rtc0_monotonic {
() => {{ ($name:ident) => {
$crate::__internal_create_nrf_rtc_interrupt!(Rtc0, RTC0, Rtc0Token) $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_export]
macro_rules! create_nrf_rtc1_monotonic_token { macro_rules! nrf_rtc1_monotonic {
() => {{ ($name:ident) => {
$crate::__internal_create_nrf_rtc_interrupt!(Rtc1, RTC1, Rtc1Token) $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(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
#[cfg_attr( #[cfg_attr(
docsrs, docsrs,
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
)] )]
#[macro_export] #[macro_export]
macro_rules! create_nrf_rtc2_monotonic_token { macro_rules! nrf_rtc2_monotonic {
() => {{ ($name:ident) => {
$crate::__internal_create_nrf_rtc_interrupt!(Rtc2, RTC2, Rtc2Token) $crate::__internal_create_nrf_rtc_struct!($name, Rtc2Backend, RTC2);
}}; };
} }
struct TimerValueU24(u32); struct TimerValueU24(u32);
@ -104,19 +160,23 @@ impl From<TimerValueU24> for u64 {
} }
macro_rules! make_rtc { macro_rules! make_rtc {
($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { ($backend_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
/// Monotonic timer queue implementation. /// RTC based [`TimerQueueBackend`].
$( $(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))] #[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)? )?
pub struct $mono_name; pub struct $backend_name;
static $overflow: AtomicU32 = AtomicU32::new(0); static $overflow: AtomicU32 = AtomicU32::new(0);
static $tq: TimerQueue<$mono_name> = TimerQueue::new(); static $tq: TimerQueue<$backend_name> = TimerQueue::new();
impl $mono_name { impl $backend_name {
/// Start the timer monotonic. /// Starts the timer.
pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken<Self>) { ///
/// **Do not use this function directly.**
///
/// Use the prelude macros instead.
pub fn _start(rtc: $rtc) {
unsafe { rtc.prescaler.write(|w| w.bits(0)) }; unsafe { rtc.prescaler.write(|w| w.bits(0)) };
// Disable interrupts, as preparation // 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 // plus we are not using any external shared resources so we won't impact
// basepri/source masking based critical sections. // basepri/source masking based critical sections.
unsafe { unsafe {
crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$rtc); crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$rtc);
pac::NVIC::unmask(Interrupt::$rtc); pac::NVIC::unmask(pac::Interrupt::$rtc);
}
} }
} }
/// Used to access the underlying timer queue impl TimerQueueBackend for $backend_name {
#[doc(hidden)] type Ticks = u64;
pub fn __tq() -> &'static TimerQueue<$mono_name> {
&$tq
}
/// Timeout at a specific time. fn now() -> Self::Ticks {
#[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 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 {
let rtc = unsafe { &*$rtc::PTR }; let rtc = unsafe { &*$rtc::PTR };
Self::Instant::from_ticks(calculate_now( calculate_now(
|| $overflow.load(Ordering::Relaxed), || $overflow.load(Ordering::Relaxed),
|| TimerValueU24(rtc.counter.read().bits()) || TimerValueU24(rtc.counter.read().bits())
)) )
} }
fn on_interrupt() { fn on_interrupt() {
@ -243,28 +257,35 @@ macro_rules! make_rtc {
} }
} }
fn enable_timer() {} fn set_compare(mut instant: Self::Ticks) {
fn disable_timer() {}
fn set_compare(mut instant: Self::Instant) {
let rtc = unsafe { &*$rtc::PTR }; let rtc = unsafe { &*$rtc::PTR };
const MAX: u64 = 0xff_ffff;
// Disable interrupts because this section is timing critical. // Disable interrupts because this section is timing critical.
// We rely on the fact that this entire section runs within one // 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 // RTC clock tick. (which it will do easily if it doesn't get
// interrupted) // interrupted)
critical_section::with(|_|{ critical_section::with(|_|{
let now = Self::now(); 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 // Errata: Timer interrupts don't fire if they are scheduled less than
// two ticks in the future. Make it three, because the timer could // two ticks in the future. Make it three, because the timer could
// tick right now. // tick right now.
if diff.ticks() < 3 { if diff < 3 {
instant = Self::Instant::from_ticks(now.ticks().wrapping_add(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() { 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!(Rtc0Backend, RTC0, RTC0_OVERFLOWS, RTC0_TQ);
make_rtc!(Rtc1, RTC1, RTC1_OVERFLOWS, RTC1_TQ); make_rtc!(Rtc1Backend, RTC1, RTC1_OVERFLOWS, RTC1_TQ);
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[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")));

View file

@ -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 //! 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`. //! timers are exposed by having the correct `nrf52*` feature enabled for `rtic-monotonics`.
@ -6,139 +6,217 @@
//! # Example //! # Example
//! //!
//! ``` //! ```
//! use rtic_monotonics::nrf::timer::*; //! use rtic_monotonics::nrf::timer::prelude::*;
//! nrf_timer0_monotonic!(Mono);
//! //!
//! fn init() { //! fn init() {
//! # // This is normally provided by the selected PAC //! # // This is normally provided by the selected PAC
//! # let timer = unsafe { core::mem::transmute(()) }; //! # let timer = unsafe { core::mem::transmute(()) };
//! // Generate the required token
//! let token = rtic_monotonics::create_nrf_timer0_monotonic_token!();
//!
//! // Start the monotonic //! // Start the monotonic
//! Timer0::start(timer, token); //! Mono::start(timer);
//! } //! }
//! //!
//! async fn usage() { //! async fn usage() {
//! loop { //! loop {
//! // Use the monotonic //! // Use the monotonic
//! Timer0::delay(100.millis()).await; //! let timestamp = Mono::now();
//! Mono::delay(100.millis()).await;
//! } //! }
//! } //! }
//! ``` //! ```
use crate::{Monotonic, TimeoutError, TimerQueue}; /// Common definitions and traits for using the nRF Timer monotonics
use atomic_polyfill::{AtomicU32, Ordering}; pub mod prelude {
use core::future::Future; pub use crate::nrf_timer0_monotonic;
pub use fugit::{self, ExtU64, ExtU64Ceil}; pub use crate::nrf_timer1_monotonic;
use rtic_time::half_period_counter::calculate_now; 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")] #[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")] #[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")] #[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")] #[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")] #[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")] #[cfg(feature = "nrf5340-app")]
use nrf5340_app_pac::{ #[doc(hidden)]
self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, pub use nrf5340_app_pac::{
self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2,
}; };
#[cfg(feature = "nrf5340-net")] #[cfg(feature = "nrf5340-net")]
use nrf5340_net_pac::{ #[doc(hidden)]
self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, pub use nrf5340_net_pac::{
self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2,
}; };
#[cfg(feature = "nrf9160")] #[cfg(feature = "nrf9160")]
use nrf9160_pac::{ #[doc(hidden)]
self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, 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)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! __internal_create_nrf_timer_interrupt { macro_rules! __internal_create_nrf_timer_interrupt {
($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ ($mono_backend:ident, $timer:ident) => {
#[no_mangle] #[no_mangle]
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern "C" fn $timer() { 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();
}
};
}
#[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);
}
} }
pub struct $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 },
>;
}
unsafe impl $crate::InterruptToken<$crate::nrf::timer::$mono_timer> for $timer_token {} $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
$timer_token };
}};
} }
/// 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_export]
macro_rules! create_nrf_timer0_monotonic_token { macro_rules! nrf_timer0_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer0, TIMER0, Timer0Token) $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_export]
macro_rules! create_nrf_timer1_monotonic_token { macro_rules! nrf_timer1_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer1, TIMER1, Timer1Token) $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_export]
macro_rules! create_nrf_timer2_monotonic_token { macro_rules! nrf_timer2_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer2, TIMER2, Timer2Token) $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( #[cfg_attr(
docsrs, docsrs,
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
)] )]
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
#[macro_export] #[macro_export]
macro_rules! create_nrf_timer3_monotonic_token { macro_rules! nrf_timer3_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer3, TIMER3, Timer3Token) $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( #[cfg_attr(
docsrs, docsrs,
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
)] )]
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
#[macro_export] #[macro_export]
macro_rules! create_nrf_timer4_monotonic_token { macro_rules! nrf_timer4_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer4, TIMER4, Timer4Token) $crate::__internal_create_nrf_timer_struct!($name, Timer4Backend, TIMER4, $tick_rate_hz);
}}; };
} }
macro_rules! make_timer { macro_rules! make_timer {
($mono_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { ($backend_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
/// Monotonic timer queue implementation. /// Timer peripheral based [`TimerQueueBackend`].
$( $(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))] #[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)? )?
pub struct $mono_name; pub struct $backend_name;
static $overflow: AtomicU32 = AtomicU32::new(0); static $overflow: AtomicU32 = AtomicU32::new(0);
static $tq: TimerQueue<$mono_name> = TimerQueue::new(); static $tq: TimerQueue<$backend_name> = TimerQueue::new();
impl $mono_name { impl $backend_name {
/// Start the timer monotonic. /// Starts the timer.
pub fn start(timer: $timer, _interrupt_token: impl crate::InterruptToken<Self>) { ///
// 1 MHz /// **Do not use this function directly.**
timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) }); ///
/// 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()); timer.bitmode.write(|w| w.bitmode()._32bit());
// Disable interrupts, as preparation // 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 // plus we are not using any external shared resources so we won't impact
// basepri/source masking based critical sections. // basepri/source masking based critical sections.
unsafe { unsafe {
crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$timer); crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$timer);
pac::NVIC::unmask(Interrupt::$timer); pac::NVIC::unmask(pac::Interrupt::$timer);
}
} }
} }
/// Used to access the underlying timer queue impl TimerQueueBackend for $backend_name {
#[doc(hidden)] type Ticks = u64;
pub fn __tq() -> &'static TimerQueue<$mono_name> {
&$tq
}
/// Timeout at a specific time. fn now() -> Self::Ticks {
#[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 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 {
let timer = unsafe { &*$timer::PTR }; let timer = unsafe { &*$timer::PTR };
Self::Instant::from_ticks(calculate_now( calculate_now(
|| $overflow.load(Ordering::Relaxed), || $overflow.load(Ordering::Relaxed),
|| { || {
timer.tasks_capture[3].write(|w| unsafe { w.bits(1) }); timer.tasks_capture[3].write(|w| unsafe { w.bits(1) });
timer.cc[3].read().bits() timer.cc[3].read().bits()
} }
)) )
} }
fn on_interrupt() { 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 }; 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() { fn clear_compare_flag() {
@ -279,16 +312,20 @@ macro_rules! make_timer {
} }
fn pend_interrupt() { 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!(Timer0Backend, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ);
make_timer!(Timer1, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ); make_timer!(Timer1Backend, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ);
make_timer!(Timer2, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ); make_timer!(Timer2Backend, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ);
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[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"))] #[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")));

View file

@ -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 //! # Example
//! //!
//! ``` //! ```
//! use rtic_monotonics::rp2040::*; //! use rtic_monotonics::rp2040::prelude::*;
//!
//! rp2040_timer_monotonic!(Mono);
//! //!
//! fn init() { //! fn init() {
//! # // This is normally provided by the selected PAC //! # // This is normally provided by the selected PAC
//! # let timer = unsafe { core::mem::transmute(()) }; //! # let timer = unsafe { core::mem::transmute(()) };
//! # let mut resets = 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 //! // Start the monotonic
//! Timer::start(timer, &mut resets, token); //! Mono::start(timer, &mut resets);
//! } //! }
//! //!
//! async fn usage() { //! async fn usage() {
//! loop { //! loop {
//! // Use the monotonic //! // 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}; pub use crate::Monotonic;
use core::future::Future;
pub use fugit::{self, ExtU64, ExtU64Ceil};
use rp2040_pac::{timer, Interrupt, NVIC, RESETS, TIMER};
/// Timer implementing [`Monotonic`] which runs at 1 MHz. pub use fugit::{self, ExtU64, ExtU64Ceil};
pub struct Timer; }
impl Timer { use crate::TimerQueueBackend;
/// Start a `Monotonic` based on RP2040's Timer. use rp2040_pac::{timer, Interrupt, NVIC};
pub fn start( pub use rp2040_pac::{RESETS, TIMER};
timer: TIMER, use rtic_time::timer_queue::TimerQueue;
resets: &RESETS,
_interrupt_token: impl crate::InterruptToken<Self>, /// 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()); resets.reset.modify(|_, w| w.timer().clear_bit());
while resets.reset_done.read().timer().bit_is_clear() {} while resets.reset_done.read().timer().bit_is_clear() {}
timer.inte.modify(|_, w| w.alarm_0().bit(true)); timer.inte.modify(|_, w| w.alarm_0().bit(true));
@ -58,55 +68,12 @@ impl Timer {
} }
} }
static TIMER_QUEUE: TimerQueue<Timer> = TimerQueue::new(); static TIMER_QUEUE: TimerQueue<TimerBackend> = TimerQueue::new();
// Forward timerqueue interface impl TimerQueueBackend for TimerBackend {
impl Timer { type Ticks = u64;
/// Used to access the underlying timer queue
#[doc(hidden)]
pub fn __tq() -> &'static TimerQueue<Timer> {
&TIMER_QUEUE
}
/// Timeout at a specific time. fn now() -> Self::Ticks {
#[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>;
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
fn now() -> Self::Instant {
let timer = Self::timer(); let timer = Self::timer();
let mut hi0 = timer.timerawh.read().bits(); let mut hi0 = timer.timerawh.read().bits();
@ -114,22 +81,24 @@ impl Monotonic for Timer {
let low = timer.timerawl.read().bits(); let low = timer.timerawl.read().bits();
let hi1 = timer.timerawh.read().bits(); let hi1 = timer.timerawh.read().bits();
if hi0 == hi1 { 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; hi0 = hi1;
} }
} }
fn set_compare(instant: Self::Instant) { fn set_compare(instant: Self::Ticks) {
let now = Self::now(); 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 // Since the timer may or may not overflow based on the requested compare val, we check
// how many ticks are left. // how many ticks are left.
let val = match instant.checked_duration_since(now) { // `wrapping_sup` takes care of the u64 integer overflow special case.
Some(x) if x.ticks() <= max => instant.duration_since_epoch().ticks() & max, // Will not overflow let val = if instant.wrapping_sub(now) <= MAX {
_ => 0, // Will overflow or in the past, set the same value as after overflow to not get extra interrupts instant & MAX
} else {
0
}; };
Self::timer() Self::timer()
@ -145,32 +114,55 @@ impl Monotonic for Timer {
rp2040_pac::NVIC::pend(Interrupt::TIMER_IRQ_0); rp2040_pac::NVIC::pend(Interrupt::TIMER_IRQ_0);
} }
fn on_interrupt() {} fn timer_queue() -> &'static TimerQueue<Self> {
&TIMER_QUEUE
fn enable_timer() {} }
fn disable_timer() {}
} }
rtic_time::embedded_hal_delay_impl_fugit64!(Timer); /// Create an RP2040 timer based monotonic and register the necessary interrupt for it.
///
#[cfg(feature = "embedded-hal-async")] /// See [`crate::rp2040`] for more details.
rtic_time::embedded_hal_async_delay_impl_fugit64!(Timer); ///
/// # Arguments
/// Register the Timer interrupt for the monotonic. ///
/// * `name` - The name that the monotonic type will have.
#[macro_export] #[macro_export]
macro_rules! create_rp2040_monotonic_token { 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] #[no_mangle]
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern "C" fn TIMER_IRQ_0() { unsafe extern "C" fn TIMER_IRQ_0() {
$crate::rp2040::Timer::__tq().on_monotonic_interrupt(); use $crate::TimerQueueBackend;
$crate::rp2040::TimerBackend::timer_queue().on_monotonic_interrupt();
} }
pub struct Rp2040Token; $crate::rp2040::TimerBackend::_start(timer, resets);
}
}
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);
};
} }

View file

@ -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 //! 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`. //! timers are exposed by having the correct `stm32*` feature enabled for `rtic-monotonics`.
@ -6,38 +6,56 @@
//! # Example //! # Example
//! //!
//! ``` //! ```
//! use rtic_monotonics::stm32::*; //! use rtic_monotonics::stm32::prelude::*;
//! use rtic_monotonics::stm32::Tim2 as Mono; //!
//! use rtic_monotonics::Monotonic; //! // Define the monotonic and set it to 1MHz tick rate
//! use embassy_stm32::peripherals::TIM2; //! stm32_tim2_monotonic!(Mono, 1_000_000);
//! use embassy_stm32::rcc::low_level::RccPeripheral;
//! //!
//! fn init() { //! fn init() {
//! // Generate timer token to ensure correct timer interrupt handler is used.
//! let token = rtic_monotonics::create_stm32_tim2_monotonic_token!();
//!
//! // If using `embassy-stm32` HAL, timer clock can be read out like this: //! // 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: //! // Or define it manually if you are using other HAL or know correct frequency:
//! let timer_clock_hz = 64_000_000; //! let timer_clock_hz = 64_000_000;
//! //!
//! // Start the monotonic //! // Start the monotonic
//! Mono::start(timer_clock_hz, token); //! Mono::start(timer_clock_hz);
//! } //! }
//! //!
//! async fn usage() { //! async fn usage() {
//! loop { //! loop {
//! // Use the monotonic //! // Use the monotonic
//! let timestamp = Mono::now().ticks(); //! let timestamp = Mono::now();
//! Mono::delay(100.millis()).await; //! 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}; use atomic_polyfill::{AtomicU64, Ordering};
pub use fugit::{self, ExtU64, ExtU64Ceil}; use rtic_time::{
use rtic_time::half_period_counter::calculate_now; half_period_counter::calculate_now,
timer_queue::{TimerQueue, TimerQueueBackend},
};
use stm32_metapac as pac; use stm32_metapac as pac;
mod _generated { mod _generated {
@ -48,116 +66,180 @@ mod _generated {
include!(concat!(env!("OUT_DIR"), "/_generated.rs")); include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
} }
const TIMER_HZ: u32 = 1_000_000;
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! __internal_create_stm32_timer_interrupt { macro_rules! __internal_create_stm32_timer_interrupt {
($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ ($mono_backend:ident, $interrupt_name:ident) => {
#[no_mangle] #[no_mangle]
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern "C" fn $timer() { unsafe extern "C" fn $interrupt_name() {
$crate::stm32::$mono_timer::__tq().on_monotonic_interrupt(); use $crate::TimerQueueBackend;
$crate::stm32::$mono_backend::timer_queue().on_monotonic_interrupt();
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __internal_create_stm32_timer_struct {
($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => {
struct $name;
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);
$crate::stm32::$mono_backend::_start(tim_clock_hz, $tick_rate_hz);
}
} }
pub struct $timer_token; 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 },
>;
}
unsafe impl $crate::InterruptToken<$crate::stm32::$mono_timer> for $timer_token {} $crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
$timer_token };
}};
} }
/// 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")] #[cfg(feature = "stm32_tim2")]
#[macro_export] #[macro_export]
macro_rules! create_stm32_tim2_monotonic_token { macro_rules! stm32_tim2_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_interrupt!(Tim2, TIM2, Tim2Token) $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")] #[cfg(feature = "stm32_tim3")]
#[macro_export] #[macro_export]
macro_rules! create_stm32_tim3_monotonic_token { macro_rules! stm32_tim3_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_interrupt!(Tim3, TIM3, Tim3Token) $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")] #[cfg(feature = "stm32_tim4")]
#[macro_export] #[macro_export]
macro_rules! create_stm32_tim4_monotonic_token { macro_rules! stm32_tim4_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_interrupt!(Tim4, TIM4, Tim4Token) $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")] #[cfg(feature = "stm32_tim5")]
#[macro_export] #[macro_export]
macro_rules! create_stm32_tim5_monotonic_token { macro_rules! stm32_tim5_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_interrupt!(Tim5, TIM5, Tim5Token) $crate::__internal_create_stm32_timer_struct!($name, Tim5Backend, TIM5, $tick_rate_hz);
}}; };
} }
/// Register TIM12 interrupt for the monotonic. /// Create a TIM15 based monotonic and register the TIM15 interrupt for it.
#[cfg(feature = "stm32_tim12")] ///
#[macro_export] /// See [`crate::stm32`] for more details.
macro_rules! create_stm32_tim12_monotonic_token { ///
() => {{ /// # Arguments
$crate::__internal_create_stm32_timer_interrupt!(Tim12, TIM12, Tim12Token) ///
}}; /// * `name` - The name that the monotonic type will have.
} /// * `tick_rate_hz` - The tick rate of the timer peripheral.
///
/// Register TIM15 interrupt for the monotonic.
#[cfg(feature = "stm32_tim15")] #[cfg(feature = "stm32_tim15")]
#[macro_export] #[macro_export]
macro_rules! create_stm32_tim15_monotonic_token { macro_rules! stm32_tim15_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_stm32_timer_interrupt!(Tim15, TIM15, Tim15Token) $crate::__internal_create_stm32_timer_struct!($name, Tim15Backend, TIM15, $tick_rate_hz);
}}; };
} }
macro_rules! make_timer { macro_rules! make_timer {
($mono_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { ($backend_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
/// Monotonic timer queue implementation. /// Monotonic timer backend implementation.
$( $(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))] #[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)? )?
pub struct $mono_name; pub struct $backend_name;
use pac::$timer; use pac::$timer;
static $overflow: AtomicU64 = AtomicU64::new(0); static $overflow: AtomicU64 = AtomicU64::new(0);
static $tq: TimerQueue<$mono_name> = TimerQueue::new(); static $tq: TimerQueue<$backend_name> = TimerQueue::new();
impl $mono_name { impl $backend_name {
/// Starts the monotonic timer. /// Starts the timer.
/// ///
/// - `tim_clock_hz`: `TIMx` peripheral clock frequency. /// **Do not use this function directly.**
/// - `_interrupt_token`: Required for correct timer interrupt handling.
/// ///
/// This method must be called only once. /// Use the prelude macros instead.
pub fn start(tim_clock_hz: u32, _interrupt_token: impl crate::InterruptToken<Self>) { pub fn _start(tim_clock_hz: u32, timer_hz: u32) {
_generated::$timer::enable(); _generated::$timer::enable();
_generated::$timer::reset(); _generated::$timer::reset();
$timer.cr1().modify(|r| r.set_cen(false)); $timer.cr1().modify(|r| r.set_cen(false));
assert!((tim_clock_hz % TIMER_HZ) == 0, "Unable to find suitable timer prescaler value!"); assert!((tim_clock_hz % timer_hz) == 0, "Unable to find suitable timer prescaler value!");
let psc = tim_clock_hz / TIMER_HZ - 1; let psc = tim_clock_hz / timer_hz - 1;
$timer.psc().write(|r| r.set_psc(psc as u16)); $timer.psc().write(|r| r.set_psc(psc as u16));
// Enable full-period interrupt. // Enable full-period interrupt.
$timer.dier().modify(|r| r.set_uie(true)); $timer.dier().modify(|r| r.set_uie(true));
// Configure and enable half-period interrupt // 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)); $timer.dier().modify(|r| r.set_ccie(2, true));
// Trigger an update event to load the prescaler value to the clock. // 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); 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. impl TimerQueueBackend for $backend_name {
#[inline] type Ticks = u64;
pub async fn delay(duration: <Self as Monotonic>::Duration) {
$tq.delay(duration).await;
}
/// Timeout at a specific time. fn now() -> Self::Ticks {
pub async fn timeout_at<F: core::future::Future>( calculate_now(
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>;
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(
|| $overflow.load(Ordering::Relaxed), || $overflow.load(Ordering::Relaxed),
|| $timer.cnt().read().cnt() || $timer.cnt().read().cnt()
)) )
} }
fn set_compare(instant: Self::Instant) { fn set_compare(instant: Self::Ticks) {
let now = Self::now(); 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. // 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) { // `wrapping_sup` takes care of the u64 integer overflow special case.
None => 0, // In the past let val = if instant.wrapping_sub(now) <= ($bits::MAX as u64) {
Some(x) if x.ticks() <= ($bits::MAX as u64) => instant.duration_since_epoch().ticks() as $bits, // Will not overflow instant as $bits
Some(_x) => 0, // Will overflow } 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() { fn clear_compare_flag() {
@ -282,24 +322,25 @@ macro_rules! make_timer {
assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!"); assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!");
} }
} }
fn timer_queue() -> &'static TimerQueue<$backend_name> {
&$tq
}
} }
}; };
} }
#[cfg(feature = "stm32_tim2")] #[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")] #[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")] #[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")] #[cfg(feature = "stm32_tim5")]
make_timer!(Tim5, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ); make_timer!(Tim5Backend, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ);
#[cfg(feature = "stm32_tim12")]
make_timer!(Tim12, TIM12, u16, TIMER12_OVERFLOWS, TIMER12_TQ);
#[cfg(feature = "stm32_tim15")] #[cfg(feature = "stm32_tim15")]
make_timer!(Tim15, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ); make_timer!(Tim15Backend, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ);

View file

@ -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. //! 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 //! # Example
//! //!
//! ``` //! ```
//! use rtic_monotonics::systick::*; //! use rtic_monotonics::systick::prelude::*;
//! systick_monotonic!(Mono, 1_000);
//! //!
//! fn init() { //! fn init() {
//! # // This is normally provided by the selected PAC //! # // This is normally provided by the selected PAC
//! # let systick = unsafe { core::mem::transmute(()) }; //! # let systick = unsafe { core::mem::transmute(()) };
//! // Generate the required token //! #
//! let systick_token = rtic_monotonics::create_systick_token!();
//!
//! // Start the monotonic //! // Start the monotonic
//! Systick::start(systick, 12_000_000, systick_token); //! Mono::start(systick, 12_000_000);
//! } //! }
//! //!
//! async fn usage() { //! async fn usage() {
//! loop { //! loop {
//! // Use the monotonic //! // Use the monotonic
//! let timestamp = Mono::now();
//! Systick::delay(100.millis()).await; //! Systick::delay(100.millis()).await;
//! } //! }
//! } //! }
//! ``` //! ```
use super::Monotonic; /// Common definitions and traits for using the systick monotonic
pub use super::{TimeoutError, TimerQueue}; 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 atomic_polyfill::Ordering;
use core::future::Future; use rtic_time::timer_queue::TimerQueue;
use cortex_m::peripheral::SYST;
pub use fugit; use crate::TimerQueueBackend;
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "systick-64bit")] { if #[cfg(feature = "systick-64bit")] {
pub use fugit::{ExtU64, ExtU64Ceil};
use atomic_polyfill::AtomicU64; use atomic_polyfill::AtomicU64;
static SYSTICK_CNT: AtomicU64 = AtomicU64::new(0); static SYSTICK_CNT: AtomicU64 = AtomicU64::new(0);
} else { } else {
pub use fugit::{ExtU32, ExtU32Ceil};
use atomic_polyfill::AtomicU32; use atomic_polyfill::AtomicU32;
static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); 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 static SYSTICK_TIMER_QUEUE: TimerQueue<SystickBackend> = TimerQueue::new();
// `systick-100hz` and `systick-10khz` are enabled.
cfg_if::cfg_if! { /// Systick based [`TimerQueueBackend`].
if #[cfg(feature = "systick-100hz")] pub struct SystickBackend;
{
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. impl SystickBackend {
pub struct Systick; /// Starts the monotonic timer.
impl Systick {
/// Start a `Monotonic` based on SysTick.
/// ///
/// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from /// **Do not use this function directly.**
/// the clock generation function of the used HAL.
/// ///
/// Notice that the actual rate of the timer is a best approximation based on the given /// Use the prelude macros instead.
/// `sysclk` and `TIMER_HZ`. pub fn _start(mut systick: SYST, sysclk: u32, timer_hz: u32) {
/// assert!(
/// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue. (sysclk % timer_hz) == 0,
pub fn start( "timer_hz cannot evenly divide sysclk! Please adjust the timer or sysclk frequency."
mut systick: cortex_m::peripheral::SYST, );
sysclk: u32, let reload = sysclk / timer_hz - 1;
_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;
assert!(reload <= 0x00ff_ffff); assert!(reload <= 0x00ff_ffff);
assert!(reload > 0); assert!(reload > 0);
@ -98,7 +84,7 @@ impl Systick {
systick.enable_interrupt(); systick.enable_interrupt();
systick.enable_counter(); systick.enable_counter();
SYSTICK_TIMER_QUEUE.initialize(Systick {}); SYSTICK_TIMER_QUEUE.initialize(SystickBackend {});
} }
fn systick() -> SYST { fn systick() -> SYST {
@ -106,67 +92,24 @@ impl Systick {
} }
} }
// Forward timerqueue interface impl TimerQueueBackend for SystickBackend {
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 {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "systick-64bit")] { if #[cfg(feature = "systick-64bit")] {
type Instant = fugit::TimerInstantU64<TIMER_HZ>; type Ticks = u64;
type Duration = fugit::TimerDurationU64<TIMER_HZ>;
} else { } else {
type Instant = fugit::TimerInstantU32<TIMER_HZ>; type Ticks = u32;
type Duration = fugit::TimerDurationU32<TIMER_HZ>;
} }
} }
const ZERO: Self::Instant = Self::Instant::from_ticks(0); fn now() -> Self::Ticks {
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
fn now() -> Self::Instant {
if Self::systick().has_wrapped() { if Self::systick().has_wrapped() {
SYSTICK_CNT.fetch_add(1, Ordering::AcqRel); 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. // No need to do something here, we get interrupts anyway.
} }
@ -184,39 +127,66 @@ impl Monotonic for Systick {
} }
} }
fn enable_timer() {} fn timer_queue() -> &'static TimerQueue<Self> {
&SYSTICK_TIMER_QUEUE
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);
} }
} }
/// 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_export]
macro_rules! create_systick_token { 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] #[no_mangle]
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern "C" fn SysTick() { unsafe extern "C" fn SysTick() {
$crate::systick::Systick::__tq().on_monotonic_interrupt(); use $crate::TimerQueueBackend;
$crate::systick::SystickBackend::timer_queue().on_monotonic_interrupt();
} }
pub struct SystickToken; $crate::systick::SystickBackend::_start(systick, sysclk, $tick_rate_hz);
}
}
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);
};
} }

View file

@ -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! For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## Unreleased ## Unreleased - v2.0.0
### Added ### Added
### Changed ### 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 ### Fixed

View file

@ -1,6 +1,6 @@
[package] [package]
name = "rtic-time" name = "rtic-time"
version = "1.3.0" version = "2.0.0"
edition = "2021" edition = "2021"
authors = [ authors = [
@ -11,7 +11,7 @@ authors = [
"Per Lindgren <per.lindgren@ltu.se>", "Per Lindgren <per.lindgren@ltu.se>",
] ]
categories = ["concurrency", "embedded", "no-std", "asynchronous"] 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" license = "MIT OR Apache-2.0"
repository = "https://github.com/rtic-rs/rtic" repository = "https://github.com/rtic-rs/rtic"
@ -21,11 +21,11 @@ repository = "https://github.com/rtic-rs/rtic"
critical-section = "1" critical-section = "1"
futures-util = { version = "0.3.25", default-features = false } futures-util = { version = "0.3.25", default-features = false }
rtic-common = { version = "1.0.0", path = "../rtic-common" } 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] [dev-dependencies]
embedded-hal = { version = "1.0" }
embedded-hal-async = { version = "1.0" }
fugit = "0.3.7"
parking_lot = "0.12" parking_lot = "0.12"
cassette = "0.2" cassette = "0.2"
cooked-waker = "5.0.0" cooked-waker = "5.0.0"

29
rtic-time/README.md Normal file
View file

@ -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

View file

@ -5,285 +5,60 @@
#![no_std] #![no_std]
#![deny(missing_docs)] #![deny(missing_docs)]
#![allow(incomplete_features)] #![allow(async_fn_in_trait)]
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;
pub mod half_period_counter; pub mod half_period_counter;
mod linked_list; mod linked_list;
mod monotonic; pub mod monotonic;
pub mod timer_queue;
/// 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,
}
/// This indicates that there was a timeout. /// This indicates that there was a timeout.
pub struct TimeoutError; pub struct TimeoutError;
/// This is needed to make the async closure in `delay_until` accept that we "share" /// Re-export for macros
/// the link possible between threads. pub use embedded_hal;
struct LinkPtr<Mono: Monotonic>(*mut Option<linked_list::Link<WaitingWaker<Mono>>>); /// Re-export for macros
pub use embedded_hal_async;
impl<Mono: Monotonic> Clone for LinkPtr<Mono> { /// # A monotonic clock / counter definition.
fn clone(&self) -> Self { ///
LinkPtr(self.0) /// ## 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.
impl<Mono: Monotonic> LinkPtr<Mono> { pub trait Monotonic {
/// This will dereference the pointer stored within and give out an `&mut`. /// The type for instant, defining an instant in time.
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);
}
/// Call this in the interrupt handler of the hardware timer supporting the `Monotonic`
/// ///
/// # 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 /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used.
/// monotonic timer for correct operation. type Duration: Copy;
pub unsafe fn on_monotonic_interrupt(&self) {
Mono::clear_compare_flag();
Mono::on_interrupt();
loop { /// Get the current time.
let mut release_at = None; fn now() -> Self::Instant;
let head = self.queue.pop_if(|head| {
release_at = Some(head.release_at);
let should_pop = Mono::now() >= head.release_at; /// Delay for some duration of time.
head.was_popped.store(should_pop, Ordering::Relaxed); async fn delay(duration: Self::Duration);
should_pop
});
match (head, release_at) {
(Some(link), _) => {
link.waker.wake();
}
(None, Some(instant)) => {
Mono::enable_timer();
Mono::set_compare(instant);
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;
}
}
}
}
/// Timeout at a specific time.
pub async fn timeout_at<F: Future>(
&self,
instant: Mono::Instant,
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: Mono::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. /// Delay to some specific time instant.
pub async fn delay_until(&self, instant: Mono::Instant) { async fn delay_until(instant: Self::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; /// Timeout at a specific time.
async fn timeout_at<F: core::future::Future>(
instant: Self::Instant,
future: F,
) -> Result<F::Output, TimeoutError>;
// Make this future `Drop`-safe /// Timeout after a specific duration.
// SAFETY(link_ptr): Shadow the original definition of `link_ptr` so we can't abuse it. async fn timeout_after<F: core::future::Future>(
let mut link_ptr = duration: Self::Duration,
LinkPtr(&mut link_ptr as *mut Option<linked_list::Link<WaitingWaker<Mono>>>); future: F,
let mut link_ptr2 = link_ptr.clone(); ) -> Result<F::Output, TimeoutError>;
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);
}
}
} }

View file

@ -1,236 +1,8 @@
//! A monotonic clock / counter definition. //! Structs and traits surrounding the [`Monotonic`](crate::Monotonic) trait.
/// # A monotonic clock / counter definition. pub use timer_queue_based_monotonic::{
/// TimerQueueBasedDuration, TimerQueueBasedInstant, TimerQueueBasedMonotonic,
/// ## 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;
/// The duration between two timer ticks. mod embedded_hal_macros;
const TICK_PERIOD: Self::Duration; mod timer_queue_based_monotonic;
/// 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;
}
}
};
}

View file

@ -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;
}
}
};
}

View file

@ -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)
}
}

View file

@ -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);
}
}
}

View file

@ -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>;
}

View file

@ -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)
}
}

View file

@ -15,46 +15,31 @@ use std::{
time::Duration, time::Duration,
}; };
use ::fugit::ExtU64Ceil;
use cooked_waker::{IntoWaker, WakeRef}; use cooked_waker::{IntoWaker, WakeRef};
use fugit::ExtU64Ceil;
use parking_lot::Mutex; 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; const SUBTICKS_PER_TICK: u32 = 10;
struct SubtickTestTimer; 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 NOW_SUBTICKS: AtomicU64 = AtomicU64::new(0);
static COMPARE_TICKS: Mutex<Option<u64>> = Mutex::new(None); static COMPARE_TICKS: Mutex<Option<u64>> = Mutex::new(None);
impl Monotonic for SubtickTestTimer { impl SubtickTestTimer {
const ZERO: Self::Instant = Self::Instant::from_ticks(0); pub fn init() {
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); SubtickTestTimerBackend::init();
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 { impl SubtickTestTimerBackend {
pub fn init() { fn init() {
Self::__tq().initialize(Self) Self::timer_queue().initialize(Self)
} }
pub fn tick() -> u64 { pub fn tick() -> u64 {
@ -70,7 +55,7 @@ impl SubtickTestTimer {
// ); // );
if subticks == 0 && Some(ticks) == *compare { if subticks == 0 && Some(ticks) == *compare {
unsafe { unsafe {
Self::__tq().on_monotonic_interrupt(); Self::timer_queue().on_monotonic_interrupt();
} }
} }
@ -85,29 +70,41 @@ impl SubtickTestTimer {
pub fn now_subticks() -> u64 { pub fn now_subticks() -> u64 {
NOW_SUBTICKS.load(Ordering::Relaxed) NOW_SUBTICKS.load(Ordering::Relaxed)
} }
}
fn __tq() -> &'static TimerQueue<Self> { impl TimerQueueBackend for SubtickTestTimerBackend {
type Ticks = u64;
fn now() -> Self::Ticks {
NOW_SUBTICKS.load(Ordering::Relaxed) / u64::from(SUBTICKS_PER_TICK)
}
fn set_compare(instant: Self::Ticks) {
*COMPARE_TICKS.lock() = Some(instant);
}
fn clear_compare_flag() {}
fn pend_interrupt() {
unsafe {
Self::timer_queue().on_monotonic_interrupt();
}
}
fn timer_queue() -> &'static TimerQueue<Self> {
&TIMER_QUEUE &TIMER_QUEUE
} }
/// Delay for some duration of time.
#[inline]
pub async fn delay(duration: <Self as Monotonic>::Duration) {
Self::__tq().delay(duration).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> {
Self::__tq().timeout_after(duration, future).await
}
} }
rtic_time::embedded_hal_delay_impl_fugit64!(SubtickTestTimer); impl TimerQueueBasedMonotonic for SubtickTestTimer {
rtic_time::embedded_hal_async_delay_impl_fugit64!(SubtickTestTimer); type Backend = SubtickTestTimerBackend;
type Instant = fugit::Instant<u64, SUBTICKS_PER_TICK, 1000>;
type Duration = fugit::Duration<u64, SUBTICKS_PER_TICK, 1000>;
}
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 // 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 // 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 { macro_rules! subtick_test {
(@run $start:expr, $actual_duration:expr, $delay_fn:expr) => {{ (@run $start:expr, $actual_duration:expr, $delay_fn:expr) => {{
// forward clock to $start // forward clock to $start
SubtickTestTimer::forward_to_subtick($start); SubtickTestTimerBackend::forward_to_subtick($start);
// call wait function // call wait function
let delay_fn = $delay_fn; let delay_fn = $delay_fn;
@ -164,7 +161,7 @@ macro_rules! subtick_test {
}; };
assert_eq!(wakecounter.get(), 0); assert_eq!(wakecounter.get(), 0);
SubtickTestTimer::tick(); SubtickTestTimerBackend::tick();
} }
let expected_wakeups = { let expected_wakeups = {
@ -177,7 +174,7 @@ macro_rules! subtick_test {
assert_eq!(wakecounter.get(), expected_wakeups); assert_eq!(wakecounter.get(), expected_wakeups);
// Tick again to test that we don't get a second wake // Tick again to test that we don't get a second wake
SubtickTestTimer::tick(); SubtickTestTimerBackend::tick();
assert_eq!(wakecounter.get(), expected_wakeups); assert_eq!(wakecounter.get(), expected_wakeups);
assert_eq!( assert_eq!(
@ -191,9 +188,9 @@ macro_rules! subtick_test {
(@run_blocking $start:expr, $actual_duration:expr, $delay_fn:expr) => {{ (@run_blocking $start:expr, $actual_duration:expr, $delay_fn:expr) => {{
// forward clock to $start // 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); let finished = AtomicBool::new(false);
std::thread::scope(|s|{ std::thread::scope(|s|{
@ -204,13 +201,13 @@ macro_rules! subtick_test {
s.spawn(||{ s.spawn(||{
sleep(Duration::from_millis(10)); sleep(Duration::from_millis(10));
while !finished.load(Ordering::Relaxed) { while !finished.load(Ordering::Relaxed) {
SubtickTestTimer::tick(); SubtickTestTimerBackend::tick();
sleep(Duration::from_millis(10)); sleep(Duration::from_millis(10));
} }
}); });
}); });
let t_end = SubtickTestTimer::now_subticks(); let t_end = SubtickTestTimerBackend::now_subticks();
let measured_duration = t_end - t_start; let measured_duration = t_end - t_start;
assert_eq!( assert_eq!(
$actual_duration, $actual_duration,

View file

@ -2,63 +2,24 @@
//! //!
//! To run this test, you need to activate the `critical-section/std` feature. //! To run this test, you need to activate the `critical-section/std` feature.
use std::{
fmt::Debug,
task::{Poll, Waker},
};
use cassette::Cassette; use cassette::Cassette;
use parking_lot::Mutex; use parking_lot::Mutex;
use rtic_time::{Monotonic, TimerQueue}; use rtic_time::timer_queue::{TimerQueue, TimerQueueBackend};
static NOW: Mutex<Option<Instant>> = Mutex::new(None); mod peripheral {
use parking_lot::Mutex;
use std::{
sync::atomic::{AtomicU64, Ordering},
task::{Poll, Waker},
};
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] use super::TestMonoBackend;
pub struct Duration(u64);
impl Duration { static NOW: AtomicU64 = AtomicU64::new(0);
pub const fn from_ticks(millis: u64) -> Self { static WAKERS: Mutex<Vec<Waker>> = Mutex::new(Vec::new());
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)
}
}
impl From<Duration> for Instant {
fn from(value: Duration) -> Self {
Instant(value.0)
}
}
static WAKERS: Mutex<Vec<Waker>> = Mutex::new(Vec::new());
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub struct Instant(u64);
impl Instant {
const ZERO: Self = Self(0);
pub fn tick() -> bool { pub fn tick() -> bool {
// If we've never ticked before, initialize the clock. NOW.fetch_add(1, Ordering::Release);
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);
}
let had_wakers = !WAKERS.lock().is_empty(); let had_wakers = !WAKERS.lock().is_empty();
// Wake up all things waiting for a specific time to happen. // Wake up all things waiting for a specific time to happen.
@ -66,22 +27,18 @@ impl Instant {
waker.wake_by_ref(); waker.wake_by_ref();
} }
let had_interrupt = TestMono::tick(false); let had_interrupt = TestMonoBackend::tick(false);
had_interrupt || had_wakers had_interrupt || had_wakers
} }
pub fn now() -> Self { pub fn now() -> u64 {
NOW.lock().clone().unwrap_or(Instant::ZERO) NOW.load(Ordering::Acquire)
} }
pub fn elapsed(&self) -> Duration { pub async fn wait_until(time: u64) {
Duration(Self::now().0 - self.0)
}
pub async fn wait_until(time: Instant) {
core::future::poll_fn(|ctx| { core::future::poll_fn(|ctx| {
if Instant::now() >= time { if now() >= time {
Poll::Ready(()) Poll::Ready(())
} else { } else {
WAKERS.lock().push(ctx.waker().clone()); WAKERS.lock().push(ctx.waker().clone());
@ -92,51 +49,21 @@ impl Instant {
} }
} }
impl From<u64> for Instant { static COMPARE: Mutex<Option<u64>> = Mutex::new(None);
fn from(value: u64) -> Self { static TIMER_QUEUE: TimerQueue<TestMonoBackend> = TimerQueue::new();
Self(value)
}
}
impl core::ops::Add<Duration> for Instant { pub struct TestMonoBackend;
type Output = Instant;
fn add(self, rhs: Duration) -> Self::Output { impl TestMonoBackend {
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 {
pub fn tick(force_interrupt: bool) -> bool { pub fn tick(force_interrupt: bool) -> bool {
let now = Instant::now(); let now = peripheral::now();
let compare_reached = Some(now) == Self::compare(); let compare_reached = Some(now) == Self::compare();
let interrupt = compare_reached || force_interrupt; let interrupt = compare_reached || force_interrupt;
if interrupt { if interrupt {
unsafe { unsafe {
TestMono::queue().on_monotonic_interrupt(); TestMonoBackend::timer_queue().on_monotonic_interrupt();
} }
true true
} else { } else {
@ -144,35 +71,26 @@ impl TestMono {
} }
} }
/// Initialize the monotonic. pub fn compare() -> Option<u64> {
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> {
COMPARE.lock().clone() COMPARE.lock().clone()
} }
} }
impl Monotonic for TestMono { impl TestMonoBackend {
const ZERO: Self::Instant = Instant::ZERO; fn init() {
const TICK_PERIOD: Self::Duration = Duration::from_ticks(1); Self::timer_queue().initialize(Self);
}
}
type Instant = Instant; impl TimerQueueBackend for TestMonoBackend {
type Ticks = u64;
type Duration = Duration; fn now() -> Self::Ticks {
peripheral::now()
fn now() -> Self::Instant {
Instant::now()
} }
fn set_compare(instant: Self::Instant) { fn set_compare(instant: Self::Ticks) {
let _ = COMPARE.lock().insert(instant); *COMPARE.lock() = Some(instant);
} }
fn clear_compare_flag() {} fn clear_compare_flag() {}
@ -180,42 +98,40 @@ impl Monotonic for TestMono {
fn pend_interrupt() { fn pend_interrupt() {
Self::tick(true); Self::tick(true);
} }
fn timer_queue() -> &'static TimerQueue<Self> {
&TIMER_QUEUE
}
} }
#[test] #[test]
fn timer_queue() { fn timer_queue() {
TestMono::init(); TestMonoBackend::init();
let start = Instant::ZERO; let start = 0;
let build_delay_test = |pre_delay: Option<u64>, delay: u64| { 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 { let total = if let Some(pre_delay) = pre_delay {
pre_delay + delay pre_delay + delay
} else { } else {
delay delay
}; };
let total_millis = total.as_ticks();
async move { async move {
// A `pre_delay` simulates a delay in scheduling, // A `pre_delay` simulates a delay in scheduling,
// without the `pre_delay` being present in the timer // without the `pre_delay` being present in the timer
// queue // queue
if let Some(pre_delay) = pre_delay { 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(); let elapsed = peripheral::now() - start;
println!("{total_millis} ticks delay reached after {elapsed} ticks"); println!("{total} ticks delay reached after {elapsed} ticks");
// Expect a delay of one longer, to compensate for timer uncertainty // Expect a delay of one longer, to compensate for timer uncertainty
if elapsed != total_millis + 1 { if elapsed != total + 1 {
panic!( panic!("{total} ticks delay was not on time ({elapsed} ticks passed instead)");
"{total_millis} 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 // We only poll the waiting futures if an
// interrupt occured or if an artificial delay // interrupt occured or if an artificial delay
// has passed. // has passed.
if Instant::tick() { if peripheral::tick() {
poll!(d1, d2, d3); poll!(d1, d2, d3);
} }
if Instant::now() == 0.into() { if peripheral::now() == 0 {
// First, we want to be waiting for our 300 tick delay // 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 // After 100 ticks, we enqueue a new delay that is supposed to last
// until the 200-tick-mark // 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 // After 200 ticks, we dequeue the 200-tick-mark delay and
// requeue the 300 tick delay // 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 // After 300 ticks, we dequeue the 300-tick-mark delay and
// go to the 400 tick delay that is already enqueued // go to the 400 tick delay that is already enqueued
assert_eq!(TestMono::compare(), Some(401.into())); assert_eq!(TestMonoBackend::compare(), Some(401));
} }
} }

View file

@ -7,6 +7,10 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## [Unreleased] ## [Unreleased]
### Changed
- Remove unused dependency `rtic-monotonics`
## [v2.1.1] - 2024-03-13 ## [v2.1.1] - 2024-03-13
### Fixed ### Fixed

View file

@ -32,7 +32,6 @@ cortex-m = { version = "0.7.0", optional = true }
bare-metal = "1.0.0" bare-metal = "1.0.0"
# portable-atomic = { version = "0.3.19" } # portable-atomic = { version = "0.3.19" }
atomic-polyfill = "1" atomic-polyfill = "1"
rtic-monotonics = { path = "../rtic-monotonics", version = "1.4.0", optional = true }
rtic-macros = { path = "../rtic-macros", version = "=2.1.0" } rtic-macros = { path = "../rtic-macros", version = "=2.1.0" }
rtic-core = "1" rtic-core = "1"
critical-section = "1" critical-section = "1"
@ -69,7 +68,4 @@ riscv-clint-backend = [
] ]
# needed for testing # needed for testing
test-critical-section = [ test-critical-section = ["cortex-m/critical-section-single-core"]
"cortex-m/critical-section-single-core",
"rtic-monotonics/systick-100hz",
]

View file

@ -34,7 +34,6 @@
pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex}; pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex};
pub use rtic_macros::app; pub use rtic_macros::app;
// pub use rtic_monotonic::{self, Monotonic};
/// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude` /// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude`
pub mod mutex { pub mod mutex {

View file

@ -64,25 +64,28 @@ impl Package {
} }
Package::RticMonotonics => { Package::RticMonotonics => {
let features = if partial { let features = if partial {
&["cortex-m-systick", "rp2040", "nrf52840"][..] &[
"cortex-m-systick",
"rp2040",
"nrf52840",
"imxrt_gpt1,imxrt-ral/imxrt1062",
"stm32_tim2,stm32h725ag",
][..]
} else { } else {
&[ &[
"cortex-m-systick,embedded-hal-async", "cortex-m-systick",
"cortex-m-systick,systick-100hz,embedded-hal-async", "cortex-m-systick,systick-64bit",
"cortex-m-systick,systick-10khz,embedded-hal-async", "rp2040",
"cortex-m-systick,embedded-hal-async,systick-64bit", "nrf52810",
"cortex-m-systick,systick-100hz,embedded-hal-async,systick-64bit", "nrf52811",
"cortex-m-systick,systick-10khz,embedded-hal-async,systick-64bit", "nrf52832",
"rp2040,embedded-hal-async", "nrf52833",
"nrf52810,embedded-hal-async", "nrf52840",
"nrf52811,embedded-hal-async", "nrf5340-app",
"nrf52832,embedded-hal-async", "nrf5340-net",
"nrf52833,embedded-hal-async", "nrf9160",
"nrf52840,embedded-hal-async", "imxrt_gpt1,imxrt_gpt2,imxrt-ral/imxrt1062",
"nrf5340-app,embedded-hal-async", "stm32_tim2,stm32_tim3,stm32_tim4,stm32_tim5,stm32_tim15,stm32h725ag",
"nrf5340-net,embedded-hal-async",
"nrf9160,embedded-hal-async",
"imxrt_gpt1,imxrt-ral/imxrt1062,embedded-hal-async",
][..] ][..]
}; };

View file

@ -299,7 +299,26 @@ pub fn cargo_doc<'c>(
backend: Backends, backend: Backends,
arguments: &'c Option<ExtraArguments>, arguments: &'c Option<ExtraArguments>,
) -> Vec<FinalRunResult<'c>> { ) -> 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 { let command = CargoCommand::Doc {
cargoarg, cargoarg,