diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5470a63e9e..2880415ac0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,8 @@ env: OLDOLDOLDSTABLE_VERSION: 0.4 QEMU_VERSION: 8.2.0 QEMU_URL: https://download.qemu.org/qemu-8.2.0.tar.xz + QEMU_ESP: qemu_esp + QEMU_ESP_URL: https://github.com/espressif/qemu/releases/download/esp-develop-8.2.0-20240122/qemu-riscv32-softmmu-esp_develop_8.2.0_20240122-x86_64-linux-gnu.tar.xz jobs: # Run cargo xtask format-check @@ -257,7 +259,7 @@ jobs: run: | sudo apt update sudo apt install -y qemu-system-arm qemu-system-riscv32 - sudo apt-get install git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build + sudo apt install -y git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build - if: ${{ steps.cache-qemu.outputs.cache-hit != 'true' }} name: Download QEMU @@ -277,8 +279,16 @@ jobs: name: Build QEMU run: | cd qemu-${{ env.QEMU_VERSION }} - make -j$(nproc) + ninja -C build + - name: Download ESP32 QEMU + run: wget "${{ env.QEMU_ESP_URL }}" --output-document=${{ env.QEMU_ESP}}.tar.xz + + - name: Extract ESP32 QEMU + run: | + mkdir -p qemu-${{ env.QEMU_VERSION }}/build/esp32 + tar --strip-components=1 -xvJf ${{ env.QEMU_ESP }}.tar.xz -C qemu-${{ env.QEMU_VERSION }}/build/esp32 qemu + - name: Archive QEMU build run: | cd qemu-${{ env.QEMU_VERSION }}/build @@ -413,6 +423,75 @@ jobs: if: ${{ matrix.backend != 'riscv32-imc-clint' }} run: cargo xtask --deny-warnings --platform hifive1 --backend ${{ matrix.backend }} qemu + # Platform esp32c3: verify the example output with run-pass tests + testexamplesesp32c3: + name: QEMU run (esp32c3) + needs: buildqemu + runs-on: ubuntu-22.04 + strategy: + matrix: + toolchain: + - stable + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust ${{ matrix.toolchain }} + run: | + rustup set profile minimal + rustup override set ${{ matrix.toolchain }} + + - name: Configure Rust target + run: | + rustup target add riscv32imac-unknown-none-elf + rustup target add riscv32imc-unknown-none-elf + + - name: Add Rust component llvm-tools-preview + run: rustup component add llvm-tools-preview + + - name: Install libudev espflash dependency + run: | + sudo apt update + sudo apt install -y libudev-dev + + # Use precompiled binutils + - name: Install cargo-binutils + uses: taiki-e/install-action@v2 + with: + tool: cargo-binutils + + # Use precompiled if possible + - name: Install cargo-binutils + uses: taiki-e/install-action@v2 + with: + tool: espflash + + - name: Install esptool.py + run: pip install esptool + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: Install QEMU to get dependencies + run: | + sudo apt update + sudo apt install -y qemu-system-riscv32 + + - name: Download built QEMU + uses: actions/download-artifact@v4 + with: + name: qemu + + - name: Extract ESP32 QEMU into local path + run: sudo tar --strip-components=1 -xf qemu.tar -C /usr/local/ esp32/ + + - name: Check which QEMU is used + run: | + which qemu-system-riscv32 + + - name: Run-pass tests + run: cargo xtask -v --platform esp32-c3 qemu + # Run test suite tests: name: tests @@ -720,7 +799,7 @@ jobs: tool: mdbook-mermaid - name: mdBook Action - uses: peaceiris/actions-mdbook@v1 + uses: peaceiris/actions-mdbook@v2 with: mdbook-version: 'latest' @@ -831,7 +910,7 @@ jobs: tar -xf bookstodeploy.tar - name: Deploy to GH-pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./bookstodeploy @@ -853,6 +932,7 @@ jobs: - checkexamplesesp32c6 - testexampleslm3s6965 - testexampleshifive1 + - testexamplesesp32c3 - tests - docs - mdbook diff --git a/.gitignore b/.gitignore index 88b19b544e..10d2d78f98 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ book-target/ .DS_Store .vscode/ +qemu.log diff --git a/ci/expected/esp32c3/sw_and_hw.run b/ci/expected/esp32c3/sw_and_hw.run new file mode 100644 index 0000000000..9421a9c6d9 --- /dev/null +++ b/ci/expected/esp32c3/sw_and_hw.run @@ -0,0 +1,36 @@ +QEMU 8.2.0 monitor - type 'help' for more information +(qemu) q +ESP-ROM:esp32c3-api1-20210207 +Build:Feb 7 2021 +rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT) +SPIWP:0xee +mode:DIO, clock div:2 +load:0x3fcd5820,len:0x1714 +load:0x403cc710,len:0x968 +load:0x403ce710,len:0x2f9c +entry 0x403cc710 +I (0) boot: ESP-IDF v5.1.2-342-gbcf1645e44 2nd stage bootloader +I (0) boot: compile time Dec 12 2023 10:50:58 +I (0) boot: chip revision: v0.3 +I (0) boot.esp32c3: SPI Speed : 40MHz +I (0) boot.esp32c3: SPI Mode : SLOW READ +I (0) boot.esp32c3: SPI Flash Size : 4MB +I (0) boot: Enabling RNG early entropy source... +I (1) boot: Partition Table: +I (1) boot: ## Label Usage Type ST Offset Length +I (1) boot: 0 nvs WiFi data 01 02 00009000 00006000 +I (1) boot: 1 phy_init RF data 01 01 0000f000 00001000 +I (1) boot: 2 factory factory app 00 00 00010000 003f0000 +I (1) boot: End of partition table +I (1) esp_image: REDACTED +I (3) esp_image: REDACTED +I (3) esp_image: REDACTED +I (8) esp_image: REDACTED +I (11) boot: Loaded app from partition at offset 0x10000 +I (11) boot: Disabling RNG early entropy source... +init +Inside high prio task, press button now! +Leaving high prio task. +idle +Inside low prio task, press button now! +Leaving low prio task. diff --git a/examples/embassy-stm32g4/src/bin/blinky.rs b/examples/embassy-stm32g4/src/bin/blinky.rs index 6855b1edd1..617aa66511 100644 --- a/examples/embassy-stm32g4/src/bin/blinky.rs +++ b/examples/embassy-stm32g4/src/bin/blinky.rs @@ -5,9 +5,11 @@ use embassy_stm32::gpio::{Level, Output, Speed}; use rtic::app; -use rtic_monotonics::systick::*; +use rtic_monotonics::systick::prelude::*; use {defmt_rtt as _, panic_probe as _}; +systick_monotonic!(Mono, 1_000); + pub mod pac { pub use embassy_stm32::pac::Interrupt as interrupt; pub use embassy_stm32::pac::*; @@ -26,8 +28,7 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local) { // Initialize the systick interrupt & obtain the token to prove that we did - let systick_mono_token = rtic_monotonics::create_systick_token!(); - Systick::start(cx.core.SYST, 25_000_000, systick_mono_token); + Mono::start(cx.core.SYST, 25_000_000); let p = embassy_stm32::init(Default::default()); defmt::info!("Hello World!"); @@ -53,7 +54,7 @@ mod app { led.set_low(); } state = !state; - Systick::delay(1000.millis()).await; + Mono::delay(1000.millis()).await; } } } diff --git a/examples/esp32c3/.cargo/config.toml b/examples/esp32c3/.cargo/config.toml index fb52d86782..9ea4ecb95c 100644 --- a/examples/esp32c3/.cargo/config.toml +++ b/examples/esp32c3/.cargo/config.toml @@ -1,5 +1,9 @@ [target.riscv32imc-unknown-none-elf] -runner = "espflash flash --monitor" +# Real hardware +#runner = "espflash flash --monitor" + +# QEMU emulator +runner = "./runner.sh" [build] rustflags = [ diff --git a/examples/esp32c3/runner.sh b/examples/esp32c3/runner.sh new file mode 100755 index 0000000000..3310acaf94 --- /dev/null +++ b/examples/esp32c3/runner.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +if [ $# -eq 0 ] + then + echo "No arguments supplied! Provide path to ELF as argument" +fi + +outputfilenamecargo=$1 +outputfilename="$outputfilenamecargo".bin + +logfile=qemu.log + +qemuexec=qemu-system-riscv32 + +# Building ESP32-C3 image +espflash save-image --chip esp32c3 --merge "$outputfilenamecargo" "$outputfilename" 1>&2 + +# Get stats +esptool.py image_info --version 2 "$outputfilename" 1>&2 + +# Run in QEMU +$qemuexec -nographic -monitor tcp:127.0.0.1:55555,server,nowait -icount 3 -machine esp32c3 -drive file="$outputfilename",if=mtd,format=raw -serial file:"$logfile" & + +# Let it run +sleep 3s + +# Kill QEMU nicely by sending 'q' (quit) over tcp +echo q | nc -N 127.0.0.1 55555 +# Output that will be compared, remove the esp_image segments as they change +# between runs +cat "$logfile" | sed 's/esp_image: .*$/esp_image: REDACTED/' diff --git a/examples/lm3s6965/Cargo.lock b/examples/lm3s6965/Cargo.lock index 076a156ea8..848a06ff5a 100644 --- a/examples/lm3s6965/Cargo.lock +++ b/examples/lm3s6965/Cargo.lock @@ -362,7 +362,6 @@ dependencies = [ "critical-section", "rtic-core", "rtic-macros", - "rtic-monotonics", ] [[package]] @@ -392,12 +391,11 @@ dependencies = [ [[package]] name = "rtic-monotonics" -version = "1.5.0" +version = "2.0.0" dependencies = [ "atomic-polyfill", "cfg-if", "cortex-m", - "embedded-hal 1.0.0", "fugit", "rtic-time", ] @@ -417,9 +415,12 @@ dependencies = [ [[package]] name = "rtic-time" -version = "1.3.0" +version = "2.0.0" dependencies = [ "critical-section", + "embedded-hal 1.0.0", + "embedded-hal-async", + "fugit", "futures-util", "rtic-common", ] diff --git a/examples/lm3s6965/examples/async-delay.rs b/examples/lm3s6965/examples/async-delay.rs index 9ccfc02aba..0f151cbf4d 100644 --- a/examples/lm3s6965/examples/async-delay.rs +++ b/examples/lm3s6965/examples/async-delay.rs @@ -11,7 +11,9 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] mod app { use cortex_m_semihosting::{debug, hprintln}; - use rtic_monotonics::systick::*; + use rtic_monotonics::systick::prelude::*; + + systick_monotonic!(Mono, 100); #[shared] struct Shared {} @@ -23,8 +25,7 @@ mod app { fn init(cx: init::Context) -> (Shared, Local) { hprintln!("init"); - let systick_token = rtic_monotonics::create_systick_token!(); - Systick::start(cx.core.SYST, 12_000_000, systick_token); + Mono::start(cx.core.SYST, 12_000_000); foo::spawn().ok(); bar::spawn().ok(); @@ -36,21 +37,21 @@ mod app { #[task] async fn foo(_cx: foo::Context) { hprintln!("hello from foo"); - Systick::delay(100.millis()).await; + Mono::delay(100.millis()).await; hprintln!("bye from foo"); } #[task] async fn bar(_cx: bar::Context) { hprintln!("hello from bar"); - Systick::delay(200.millis()).await; + Mono::delay(200.millis()).await; hprintln!("bye from bar"); } #[task] async fn baz(_cx: baz::Context) { hprintln!("hello from baz"); - Systick::delay(300.millis()).await; + Mono::delay(300.millis()).await; hprintln!("bye from baz"); debug::exit(debug::EXIT_SUCCESS); diff --git a/examples/lm3s6965/examples/async-timeout.rs b/examples/lm3s6965/examples/async-timeout.rs index e5e129f405..169b13226d 100644 --- a/examples/lm3s6965/examples/async-timeout.rs +++ b/examples/lm3s6965/examples/async-timeout.rs @@ -8,13 +8,13 @@ use cortex_m_semihosting::{debug, hprintln}; use panic_semihosting as _; -use rtic_monotonics::systick::*; +use rtic_monotonics::systick::prelude::*; +systick_monotonic!(Mono, 100); #[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] mod app { use super::*; use futures::{future::FutureExt, select_biased}; - use rtic_monotonics::Monotonic; #[shared] struct Shared {} @@ -27,8 +27,7 @@ mod app { fn init(cx: init::Context) -> (Shared, Local) { hprintln!("init"); - let systick_token = rtic_monotonics::create_systick_token!(); - Systick::start(cx.core.SYST, 12_000_000, systick_token); + Mono::start(cx.core.SYST, 12_000_000); // ANCHOR_END: init foo::spawn().ok(); @@ -42,19 +41,19 @@ mod app { // Call hal with short relative timeout using `select_biased` select_biased! { v = hal_get(1).fuse() => hprintln!("hal returned {}", v), - _ = Systick::delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first + _ = Mono::delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first } // Call hal with long relative timeout using `select_biased` select_biased! { v = hal_get(1).fuse() => hprintln!("hal returned {}", v), // hal finish first - _ = Systick::delay(1000.millis()).fuse() => hprintln!("timeout", ), + _ = Mono::delay(1000.millis()).fuse() => hprintln!("timeout", ), } // ANCHOR_END: select_biased // ANCHOR: timeout_after_basic // Call hal with long relative timeout using monotonic `timeout_after` - match Systick::timeout_after(1000.millis(), hal_get(1)).await { + match Mono::timeout_after(1000.millis(), hal_get(1)).await { Ok(v) => hprintln!("hal returned {}", v), _ => hprintln!("timeout"), } @@ -62,20 +61,20 @@ mod app { // ANCHOR: timeout_at_basic // get the current time instance - let mut instant = Systick::now(); + let mut instant = Mono::now(); // do this 3 times for n in 0..3 { // absolute point in time without drift instant += 1000.millis(); - Systick::delay_until(instant).await; + Mono::delay_until(instant).await; // absolute point in time for timeout let timeout = instant + 500.millis(); - hprintln!("now is {:?}, timeout at {:?}", Systick::now(), timeout); + hprintln!("now is {:?}, timeout at {:?}", Mono::now(), timeout); - match Systick::timeout_at(timeout, hal_get(n)).await { - Ok(v) => hprintln!("hal returned {} at time {:?}", v, Systick::now()), + match Mono::timeout_at(timeout, hal_get(n)).await { + Ok(v) => hprintln!("hal returned {} at time {:?}", v, Mono::now()), _ => hprintln!("timeout"), } } @@ -90,7 +89,7 @@ async fn hal_get(n: u32) -> u32 { // emulate some delay time dependent on n let d = 350.millis() + n * 100.millis(); hprintln!("the hal takes a duration of {:?}", d); - Systick::delay(d).await; + Mono::delay(d).await; // emulate some return value 5 } diff --git a/examples/nrf52840_blinky/Cargo.lock b/examples/nrf52840_blinky/Cargo.lock index a3d754273d..ec5f4f9f10 100644 --- a/examples/nrf52840_blinky/Cargo.lock +++ b/examples/nrf52840_blinky/Cargo.lock @@ -136,7 +136,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.51", ] [[package]] @@ -179,9 +179,18 @@ dependencies = [ [[package]] name = "embedded-hal" -version = "1.0.0-rc.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "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]] name = "embedded-storage" @@ -412,18 +421,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -468,18 +477,18 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.51", ] [[package]] name = "rtic-monotonics" -version = "1.4.0" +version = "2.0.0" dependencies = [ "atomic-polyfill", "cfg-if", "cortex-m", "critical-section", - "embedded-hal 1.0.0-rc.2", + "embedded-hal 1.0.0", "fugit", "nrf52840-pac", "rtic-time", @@ -487,9 +496,12 @@ dependencies = [ [[package]] name = "rtic-time" -version = "1.1.0" +version = "2.0.0" dependencies = [ "critical-section", + "embedded-hal 1.0.0", + "embedded-hal-async", + "fugit", "futures-util", "rtic-common", ] @@ -537,9 +549,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" dependencies = [ "proc-macro2", "quote", @@ -563,7 +575,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.51", ] [[package]] diff --git a/examples/nrf52840_blinky/Cargo.toml b/examples/nrf52840_blinky/Cargo.toml index 8b13649321..556901948f 100644 --- a/examples/nrf52840_blinky/Cargo.toml +++ b/examples/nrf52840_blinky/Cargo.toml @@ -26,7 +26,7 @@ features = ["thumbv7-backend"] [dependencies.rtic-monotonics] path = "../../rtic-monotonics" -version = "1.4.0" +version = "2.0.0" features = ["nrf52840"] # cargo build/run diff --git a/examples/nrf52840_blinky/src/bin/blinky_rtc.rs b/examples/nrf52840_blinky/src/bin/blinky_rtc.rs index dbdd0b0fa4..fa654b8fa0 100644 --- a/examples/nrf52840_blinky/src/bin/blinky_rtc.rs +++ b/examples/nrf52840_blinky/src/bin/blinky_rtc.rs @@ -5,6 +5,9 @@ use nrf52840_blinky::hal; +use rtic_monotonics::nrf::rtc::prelude::*; +nrf_rtc0_monotonic!(Mono); + #[rtic::app(device = hal::pac, dispatchers = [SWI0_EGU0])] mod app { use super::*; @@ -12,10 +15,6 @@ mod app { use hal::gpio::{Level, Output, Pin, PushPull}; use hal::prelude::*; - use rtic_monotonics::nrf::rtc::Rtc0 as Mono; - use rtic_monotonics::nrf::rtc::*; - use rtic_monotonics::Monotonic; - #[shared] struct Shared {} @@ -30,8 +29,7 @@ mod app { hal::clocks::Clocks::new(cx.device.CLOCK).start_lfclk(); // Initialize Monotonic - let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!(); - Mono::start(cx.device.RTC0, token); + Mono::start(cx.device.RTC0); // Setup LED let port0 = hal::gpio::p0::Parts::new(cx.device.P0); diff --git a/examples/nrf52840_blinky/src/bin/blinky_timer.rs b/examples/nrf52840_blinky/src/bin/blinky_timer.rs index c1342f479b..ebb20fc4af 100644 --- a/examples/nrf52840_blinky/src/bin/blinky_timer.rs +++ b/examples/nrf52840_blinky/src/bin/blinky_timer.rs @@ -5,6 +5,9 @@ use nrf52840_blinky::hal; +use rtic_monotonics::nrf::timer::prelude::*; +nrf_timer0_monotonic!(Mono, 8_000_000); + #[rtic::app(device = hal::pac, dispatchers = [SWI0_EGU0])] mod app { use super::*; @@ -12,10 +15,6 @@ mod app { use hal::gpio::{Level, Output, Pin, PushPull}; use hal::prelude::*; - use rtic_monotonics::nrf::timer::Timer0 as Mono; - use rtic_monotonics::nrf::timer::*; - use rtic_monotonics::Monotonic; - #[shared] struct Shared {} @@ -27,8 +26,7 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local) { // Initialize Monotonic - let token = rtic_monotonics::create_nrf_timer0_monotonic_token!(); - Mono::start(cx.device.TIMER0, token); + Mono::start(cx.device.TIMER0); // Setup LED let port0 = hal::gpio::p0::Parts::new(cx.device.P0); diff --git a/examples/rp2040_local_i2c_init/Cargo.lock b/examples/rp2040_local_i2c_init/Cargo.lock index d40e616a63..d4c01558aa 100644 --- a/examples/rp2040_local_i2c_init/Cargo.lock +++ b/examples/rp2040_local_i2c_init/Cargo.lock @@ -10,9 +10,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "atomic-polyfill" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c314e70d181aa6053b26e3f7fbf86d1dfff84f816a6175b967666b3506ef7289" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" dependencies = [ "critical-section", ] @@ -52,7 +52,7 @@ checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" dependencies = [ "bare-metal 0.2.5", "bitfield", - "embedded-hal", + "embedded-hal 0.2.7", "volatile-register", ] @@ -73,23 +73,23 @@ checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "crc-any" -version = "2.4.3" +version = "2.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774646b687f63643eb0f4bf13dc263cb581c8c9e57973b6ddf78bda3994d88df" +checksum = "c01a5e1f881f6fb6099a7bdf949e946719fd4f1fefa56264890574febf0eb6d0" dependencies = [ "debug-helper", ] [[package]] name = "critical-section" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "debug-helper" @@ -99,9 +99,9 @@ checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" [[package]] name = "either" -version = "1.8.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "embedded-dma" @@ -123,10 +123,64 @@ dependencies = [ ] [[package]] -name = "equivalent" +name = "embedded-hal" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "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]] name = "fugit" @@ -139,21 +193,21 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-task", @@ -169,15 +223,15 @@ checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown", @@ -224,7 +278,7 @@ checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -238,15 +292,15 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -265,6 +319,12 @@ dependencies = [ "paste", ] +[[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" @@ -274,7 +334,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -291,18 +351,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.29" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -315,11 +375,10 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "rp-pico" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab28f6f4e19cec2d61b64cdd685e69794b81c579fd3b765579c46018fe616d0" +checksum = "6341771e6f8e5d130b2b3cbc23435b7847761adf198af09f4b2a60407d43bd56" dependencies = [ - "cortex-m", "cortex-m-rt", "fugit", "rp2040-boot2", @@ -329,23 +388,24 @@ dependencies = [ [[package]] name = "rp2040-boot2" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c773ec49b836077aa144b58dc7654a243e1eecdb6cf0d25361ae7c7600fabd8" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" dependencies = [ "crc-any", ] [[package]] name = "rp2040-hal" -version = "0.8.2" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1369bb84862d7f69391a96606b2f29a00bfce7f29a749e23d5f01fc3f607ada0" +checksum = "1ff2b9ae7e6dd6720fd9f64250c9087260e50fe98b6b032ccca65be3581167ca" dependencies = [ "cortex-m", "critical-section", "embedded-dma", - "embedded-hal", + "embedded-hal 0.2.7", + "frunk", "fugit", "itertools", "nb 1.1.0", @@ -368,17 +428,18 @@ dependencies = [ "cortex-m-rt", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "rp2040-pac" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9192cafbb40d717c9e0ddf767aaf9c69fee1b4e48d22ed853b57b11f6d9f3d7e" +checksum = "12d9d8375815f543f54835d01160d4e47f9e2cae75f17ff8f1ec19ce1da96e4c" dependencies = [ "cortex-m", "cortex-m-rt", + "critical-section", "vcell", ] @@ -387,7 +448,7 @@ name = "rp2040_local_i2c_init" version = "0.1.0" dependencies = [ "cortex-m", - "embedded-hal", + "embedded-hal 0.2.7", "fugit", "panic-probe", "rp-pico", @@ -397,7 +458,7 @@ dependencies = [ [[package]] name = "rtic" -version = "2.0.0" +version = "2.0.1" dependencies = [ "atomic-polyfill", "bare-metal 1.0.0", @@ -409,9 +470,10 @@ dependencies = [ [[package]] name = "rtic-common" -version = "1.0.0" +version = "1.0.1" dependencies = [ "critical-section", + "portable-atomic", ] [[package]] @@ -422,22 +484,23 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42" [[package]] name = "rtic-macros" -version = "2.0.0" +version = "2.0.1" dependencies = [ "indexmap", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.51", ] [[package]] name = "rtic-monotonics" -version = "1.0.0" +version = "2.0.0" dependencies = [ "atomic-polyfill", "cfg-if", "cortex-m", + "embedded-hal 1.0.0", "fugit", "rp2040-pac", "rtic-time", @@ -445,9 +508,12 @@ dependencies = [ [[package]] name = "rtic-time" -version = "1.0.0" +version = "2.0.0" dependencies = [ "critical-section", + "embedded-hal 1.0.0", + "embedded-hal-async", + "fugit", "futures-util", "rtic-common", ] @@ -494,10 +560,21 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.9" +name = "syn" +version = "2.0.51" 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]] name = "usb-device" @@ -525,9 +602,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "volatile-register" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" dependencies = [ "vcell", ] diff --git a/examples/rp2040_local_i2c_init/Cargo.toml b/examples/rp2040_local_i2c_init/Cargo.toml index 6e501b685b..300bedee08 100644 --- a/examples/rp2040_local_i2c_init/Cargo.toml +++ b/examples/rp2040_local_i2c_init/Cargo.toml @@ -15,14 +15,14 @@ features = ["thumbv6-backend"] [dependencies.rtic-monotonics] path = "../../rtic-monotonics" -version = "1.0.0" +version = "2.0.0" features = ["rp2040"] [dependencies] cortex-m = "0.7" embedded-hal = { version = "0.2.7", features = ["unproven"] } fugit = "0.3" -rp-pico = "0.7.0" +rp-pico = "0.8.0" panic-probe = "0.3" [profile.dev] diff --git a/examples/rp2040_local_i2c_init/src/main.rs b/examples/rp2040_local_i2c_init/src/main.rs index 97049dce2f..799376c214 100644 --- a/examples/rp2040_local_i2c_init/src/main.rs +++ b/examples/rp2040_local_i2c_init/src/main.rs @@ -1,15 +1,21 @@ #![no_std] #![no_main] -#[rtic::app( - device = rp_pico::hal::pac, - dispatchers = [TIMER_IRQ_1] -)] +use rtic_monotonics::rp2040::prelude::*; + +rp2040_timer_monotonic!(Mono); + +#[rtic::app(device = rp_pico::hal::pac)] mod app { + use super::*; + use rp_pico::hal::{ - clocks, gpio, - gpio::pin::bank0::{Gpio2, Gpio25, Gpio3}, - gpio::pin::PushPullOutput, + clocks, + gpio::{ + self, + bank0::{Gpio2, Gpio25, Gpio3}, + FunctionSioOutput, PullNone, PullUp, + }, pac, sio::Sio, watchdog::Watchdog, @@ -20,15 +26,13 @@ mod app { use core::mem::MaybeUninit; use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin}; use fugit::RateExtU32; - use rtic_monotonics::rp2040::*; - use panic_probe as _; type I2CBus = I2C< pac::I2C1, ( - gpio::Pin, - gpio::Pin, + gpio::Pin, + gpio::Pin, ), >; @@ -37,7 +41,7 @@ mod app { #[local] struct Local { - led: gpio::Pin, + led: gpio::Pin, i2c: &'static mut I2CBus, } @@ -48,11 +52,8 @@ mod app { i2c_ctx: MaybeUninit = MaybeUninit::uninit() ])] fn init(mut ctx: init::Context) -> (Shared, Local) { - // Initialize the interrupt for the RP2040 timer and obtain the token - // proving that we have. - let rp2040_timer_token = rtic_monotonics::create_rp2040_monotonic_token!(); // Configure the clocks, watchdog - The default is to generate a 125 MHz system clock - Timer::start(ctx.device.TIMER, &mut ctx.device.RESETS, rp2040_timer_token); // default rp2040 clock-rate is 125MHz + Mono::start(ctx.device.TIMER, &mut ctx.device.RESETS); // default rp2040 clock-rate is 125MHz let mut watchdog = Watchdog::new(ctx.device.WATCHDOG); let clocks = clocks::init_clocks_and_plls( XOSC_CRYSTAL_FREQ, @@ -74,12 +75,21 @@ mod app { sio.gpio_bank0, &mut ctx.device.RESETS, ); - let mut led = gpioa.led.into_push_pull_output(); + let mut led = gpioa + .led + .into_pull_type::() + .into_push_pull_output(); led.set_low().unwrap(); // Init I2C pins - let sda_pin = gpioa.gpio2.into_mode::(); - let scl_pin = gpioa.gpio3.into_mode::(); + let sda_pin = gpioa + .gpio2 + .into_pull_up_disabled() + .into_function::(); + let scl_pin = gpioa + .gpio3 + .into_pull_up_disabled() + .into_function::(); // Init I2C itself, using MaybeUninit to overwrite the previously // uninitialized i2c_ctx variable without dropping its value @@ -118,7 +128,7 @@ mod app { // now to do something with it! // Delay for 1 second - Timer::delay(1000.millis()).await; + Mono::delay(1000.millis()).await; } } } diff --git a/examples/stm32f3_blinky/Cargo.lock b/examples/stm32f3_blinky/Cargo.lock index 3fbec9e575..527d4eb0ab 100644 --- a/examples/stm32f3_blinky/Cargo.lock +++ b/examples/stm32f3_blinky/Cargo.lock @@ -52,11 +52,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bxcan" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b13b4b2ea9ab2ba924063ebb86ad895cb79f4a79bf90f27949eb20c335b30f9" +checksum = "40ac3d0c0a542d0ab5521211f873f62706a7136df415676f676d347e5a41dd80" dependencies = [ "bitflags", + "embedded-hal 0.2.7", "nb 1.1.0", "vcell", ] @@ -86,7 +87,7 @@ dependencies = [ "bare-metal 0.2.5", "bitfield", "critical-section", - "embedded-hal", + "embedded-hal 0.2.7", "volatile-register", ] @@ -112,9 +113,9 @@ dependencies = [ [[package]] name = "critical-section" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "darling" @@ -136,7 +137,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.50", ] [[package]] @@ -147,7 +148,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 2.0.22", + "syn 2.0.50", ] [[package]] @@ -169,6 +170,21 @@ dependencies = [ "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 = "embedded-time" version = "0.12.1" @@ -180,9 +196,9 @@ dependencies = [ [[package]] name = "enumset" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" dependencies = [ "enumset_derive", ] @@ -196,7 +212,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.22", + "syn 2.0.50", ] [[package]] @@ -343,28 +359,28 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "panic-rtt-target" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ab67bc881453e4c90f958c657c1303670ea87bc1a16e87fd71a40f656dce9" +checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe" dependencies = [ - "cortex-m", - "rtt-target 0.3.1", + "critical-section", + "rtt-target", ] [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pin-project-lite" @@ -378,6 +394,12 @@ 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" @@ -404,18 +426,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.29" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -431,7 +453,7 @@ dependencies = [ [[package]] name = "rtic" -version = "2.0.0" +version = "2.0.1" dependencies = [ "atomic-polyfill", "bare-metal 1.0.0", @@ -443,9 +465,10 @@ dependencies = [ [[package]] name = "rtic-common" -version = "1.0.0" +version = "1.0.1" dependencies = [ "critical-section", + "portable-atomic", ] [[package]] @@ -456,49 +479,44 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42" [[package]] name = "rtic-macros" -version = "2.0.0" +version = "2.0.1" dependencies = [ "indexmap", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.50", ] [[package]] name = "rtic-monotonics" -version = "1.0.0" +version = "2.0.0" dependencies = [ "atomic-polyfill", "cfg-if", "cortex-m", + "embedded-hal 1.0.0", "fugit", "rtic-time", ] [[package]] name = "rtic-time" -version = "1.0.0" +version = "2.0.0" dependencies = [ "critical-section", + "embedded-hal 1.0.0", + "embedded-hal-async", + "fugit", "futures-util", "rtic-common", ] [[package]] name = "rtt-target" -version = "0.3.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065d6058bb1204f51a562a67209e1817cf714759d5cf845aa45c75fa7b0b9d9b" -dependencies = [ - "ufmt-write", -] - -[[package]] -name = "rtt-target" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afa12c77ba1b9bf560e4039a9b9a08bb9cde0e9e6923955eeb917dd8d5cf303" +checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0" dependencies = [ "critical-section", "ufmt-write", @@ -553,9 +571,9 @@ dependencies = [ [[package]] name = "stm32f3" -version = "0.14.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "265cda62ac13307414de4aca58dbbbd8038ddba85cffbb335823aa216f2e3200" +checksum = "b28b37228ef3fa47956af38c6abd756e912f244c1657f14e66d42fc8d74ea96f" dependencies = [ "bare-metal 1.0.0", "cortex-m", @@ -567,30 +585,32 @@ dependencies = [ name = "stm32f3-blinky" version = "0.1.0" dependencies = [ - "embedded-hal", + "cortex-m", + "embedded-hal 0.2.7", "panic-rtt-target", "rtic", "rtic-monotonics", - "rtt-target 0.4.0", + "rtt-target", "stm32f3xx-hal", ] [[package]] name = "stm32f3xx-hal" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9d827f02df3826371c897404dfbea8a1abd544eed9d6cdc3e5f6e9f04b9e06" +checksum = "4c73e8b6e63435b75198d2fe2b27cd7f5c8e0b07bd5da9f82cffddf23210f77f" dependencies = [ - "bare-metal 1.0.0", "bxcan", "cfg-if", "cortex-m", "cortex-m-rt", + "critical-section", "embedded-dma", - "embedded-hal", + "embedded-hal 0.2.7", "embedded-time", "enumset", "nb 1.1.0", + "num-traits", "paste", "rtcc", "slice-group-by", @@ -612,9 +632,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.22" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", diff --git a/examples/stm32f3_blinky/Cargo.toml b/examples/stm32f3_blinky/Cargo.toml index 3773f0e581..5e2818842c 100644 --- a/examples/stm32f3_blinky/Cargo.toml +++ b/examples/stm32f3_blinky/Cargo.toml @@ -14,17 +14,21 @@ features = ["thumbv7-backend"] [dependencies.rtic-monotonics] path = "../../rtic-monotonics" -version = "1.0.0" +version = "2.0.0" features = ["cortex-m-systick"] +[dependencies.cortex-m] +version = "0.7.7" +features = ["critical-section-single-core"] + [dependencies] embedded-hal = "0.2.7" -panic-rtt-target = { version = "0.1.2", features = ["cortex-m"] } -rtt-target = { version = "0.4.0" } +panic-rtt-target = { version = "0.1.3" } +rtt-target = { version = "0.5.0" } [dependencies.stm32f3xx-hal] features = ["stm32f303xc", "rt"] -version = "0.9.2" +version = "0.10.0" # this lets you use `cargo fix`! [[bin]] diff --git a/examples/stm32f3_blinky/src/main.rs b/examples/stm32f3_blinky/src/main.rs index 28dc7c205b..e8c2e50599 100644 --- a/examples/stm32f3_blinky/src/main.rs +++ b/examples/stm32f3_blinky/src/main.rs @@ -5,11 +5,13 @@ use panic_rtt_target as _; use rtic::app; -use rtic_monotonics::systick::*; +use rtic_monotonics::systick::prelude::*; use rtt_target::{rprintln, rtt_init_print}; use stm32f3xx_hal::gpio::{Output, PushPull, PA5}; use stm32f3xx_hal::prelude::*; +systick_monotonic!(Mono, 1000); + #[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [SPI1])] mod app { use super::*; @@ -30,8 +32,7 @@ mod app { let mut rcc = cx.device.RCC.constrain(); // Initialize the systick interrupt & obtain the token to prove that we did - let systick_mono_token = rtic_monotonics::create_systick_token!(); - Systick::start(cx.core.SYST, 36_000_000, systick_mono_token); // default STM32F303 clock-rate is 36MHz + Mono::start(cx.core.SYST, 36_000_000); // default STM32F303 clock-rate is 36MHz rtt_init_print!(); rprintln!("init"); @@ -67,7 +68,7 @@ mod app { cx.local.led.set_low().unwrap(); *cx.local.state = true; } - Systick::delay(1000.millis()).await; + Mono::delay(1000.millis()).await; } } } diff --git a/examples/stm32g030f6_periodic_prints/.cargo/config.toml b/examples/stm32g030f6_periodic_prints/.cargo/config.toml new file mode 100644 index 0000000000..15ddd2a716 --- /dev/null +++ b/examples/stm32g030f6_periodic_prints/.cargo/config.toml @@ -0,0 +1,24 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output) +runner = "probe-run --chip STM32G030F6Px" +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", +] + +[build] +# TODO(3) Adjust the compilation target. +# (`thumbv6m-*` is compatible with all ARM Cortex-M chips but using the right +# target improves performance) +target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ +# target = "thumbv7m-none-eabi" # Cortex-M3 +# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) +# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + +[alias] +rb = "run --bin" +rrb = "run --release --bin" diff --git a/examples/stm32g030f6_periodic_prints/.vscode/settings.json b/examples/stm32g030f6_periodic_prints/.vscode/settings.json new file mode 100644 index 0000000000..c684c2425b --- /dev/null +++ b/examples/stm32g030f6_periodic_prints/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + // override the default setting (`cargo check --all-targets`) which produces the following error + // "can't find crate for `test`" when the default compilation target is a no_std target + // with these changes RA will call `cargo check --bins` on save + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.extraArgs": [ + "--bins" + ] +} diff --git a/examples/stm32g030f6_periodic_prints/Cargo.lock b/examples/stm32g030f6_periodic_prints/Cargo.lock new file mode 100644 index 0000000000..65dbf61f02 --- /dev/null +++ b/examples/stm32g030f6_periodic_prints/Cargo.lock @@ -0,0 +1,511 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal 0.2.5", + "bitfield", + "critical-section", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "defmt" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3939552907426de152b3c2c6f51ed53f98f448babd26f28694c95f5906194595" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18bdc7a7b92ac413e19e95240e75d3a73a8d8e78aa24a594c22cbb4d44b4bbda" +dependencies = [ + "defmt-parser", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "defmt-parser" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "609923761264dd99ed9c7d209718cda4631c5fe84668e0f0960124cbb844c49f" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "gcd", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "indexmap" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "panic-probe" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa6fa5645ef5a760cd340eaa92af9c1ce131c8c09e7f8926d8a24b59d26652b9" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rtic" +version = "2.0.1" +dependencies = [ + "atomic-polyfill", + "bare-metal 1.0.0", + "cortex-m", + "critical-section", + "rtic-core", + "rtic-macros", +] + +[[package]] +name = "rtic-common" +version = "1.0.1" +dependencies = [ + "critical-section", + "portable-atomic", +] + +[[package]] +name = "rtic-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42" + +[[package]] +name = "rtic-macros" +version = "2.0.1" +dependencies = [ + "indexmap", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "rtic-monotonics" +version = "2.0.0" +dependencies = [ + "atomic-polyfill", + "cfg-if", + "cortex-m", + "embedded-hal 1.0.0", + "fugit", + "proc-macro2", + "quote", + "rtic-time", + "stm32-metapac", +] + +[[package]] +name = "rtic-time" +version = "2.0.0" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", + "embedded-hal-async", + "fugit", + "futures-util", + "rtic-common", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "stm32-metapac" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deabea56a8821dcea05d0109f3ab3135f31eb572444e5da203d06149c594c8c6" +dependencies = [ + "cortex-m", +] + +[[package]] +name = "stm32g0" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc2ac544cea741c92a501bfd027d197354cd22ee92b439aea26d2ee0b55bcd7" +dependencies = [ + "bare-metal 1.0.0", + "cortex-m", + "cortex-m-rt", + "vcell", +] + +[[package]] +name = "stm32g030f6_periodic_prints" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "fugit", + "panic-probe", + "rtic", + "rtic-monotonics", + "stm32g0xx-hal", +] + +[[package]] +name = "stm32g0xx-hal" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fae457e81f9601121c5b92dca20e3612c80ea957898f8e0e68efcaab6b242067" +dependencies = [ + "bare-metal 1.0.0", + "cortex-m", + "embedded-hal 0.2.7", + "fugit", + "nb 1.1.0", + "stm32g0", + "void", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/examples/stm32g030f6_periodic_prints/Cargo.toml b/examples/stm32g030f6_periodic_prints/Cargo.toml new file mode 100644 index 0000000000..d8d8e352a2 --- /dev/null +++ b/examples/stm32g030f6_periodic_prints/Cargo.toml @@ -0,0 +1,48 @@ +[package] +authors = ["Finomnis "] +name = "stm32g030f6_periodic_prints" +edition = "2021" +version = "0.1.0" + +[workspace] + + +[dependencies.rtic] +path = "../../rtic" +version = "2.0.1" +features = ["thumbv6-backend"] + +[dependencies.rtic-monotonics] +path = "../../rtic-monotonics" +version = "2.0.0" +features = ["stm32g030f6", "stm32_tim3"] + + +[dependencies] +cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.3" +defmt = "0.3.4" +defmt-rtt = "0.4.0" +fugit = "0.3.7" +panic-probe = { version = "0.3.1", features = ["print-defmt"] } + +stm32g0xx-hal = { version = "0.2.0", features = ["rt", "stm32g030"] } + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- diff --git a/examples/stm32g030f6_periodic_prints/README.md b/examples/stm32g030f6_periodic_prints/README.md new file mode 100644 index 0000000000..e6693789eb --- /dev/null +++ b/examples/stm32g030f6_periodic_prints/README.md @@ -0,0 +1,31 @@ +# `stm32g030f6_periodic_prints` + +An RTIC periodic print example intended for the stm32g030f6 chip. + +## Dependencies + +#### 1. `flip-link`: + +```console +$ cargo install flip-link +``` + +#### 2. `probe-rs`: + +``` console +$ # make sure to install v0.2.0 or later +$ cargo install probe-rs --features cli +``` + + +## Run + +The stm32g030f6 chip needs to be connected to the computer via an SWD probe, like a [J-Link EDU Mini]. + +Then, run: + +``` +cargo run --release +``` + +[J-Link EDU Mini]: https://www.segger.com/products/debug-probes/j-link/models/j-link-edu-mini/ diff --git a/examples/stm32g030f6_periodic_prints/memory.x b/examples/stm32g030f6_periodic_prints/memory.x new file mode 100644 index 0000000000..3f89120c60 --- /dev/null +++ b/examples/stm32g030f6_periodic_prints/memory.x @@ -0,0 +1,6 @@ +/* Linker script for the STM32G030F6 */ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 32K + RAM : ORIGIN = 0x20000000, LENGTH = 8K +} diff --git a/examples/stm32g030f6_periodic_prints/src/main.rs b/examples/stm32g030f6_periodic_prints/src/main.rs new file mode 100644 index 0000000000..00fa802907 --- /dev/null +++ b/examples/stm32g030f6_periodic_prints/src/main.rs @@ -0,0 +1,57 @@ +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use defmt_rtt as _; // global logger + +pub use stm32g0xx_hal as hal; // memory layout + +use panic_probe as _; + +// same panicking *behavior* as `panic-probe` but doesn't print a panic message +// this prevents the panic message being printed *twice* when `defmt::panic` is invoked +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} + +use rtic_monotonics::stm32::prelude::*; +stm32_tim3_monotonic!(Mono, 1_000_000); + +#[rtic::app(device = hal::stm32, peripherals = true, dispatchers = [USART1, USART2])] +mod app { + use super::*; + + #[local] + struct LocalResources {} + + #[shared] + struct SharedResources {} + + #[init] + fn init(ctx: init::Context) -> (SharedResources, LocalResources) { + // enable dma clock during sleep, otherwise defmt doesn't work + ctx.device.RCC.ahbenr.modify(|_, w| w.dmaen().set_bit()); + + defmt::println!("TIM Monotonic blinker example!"); + + // Start the monotonic + Mono::start(16_000_000); + + print_messages::spawn().unwrap(); + + (SharedResources {}, LocalResources {}) + } + + #[task(priority = 2)] + async fn print_messages(_cx: print_messages::Context) { + let mut next_update = ::Instant::from_ticks(0u64); + + loop { + defmt::println!("Time: {} ticks", Mono::now().ticks()); + next_update += 1000u64.millis(); + Mono::delay_until(next_update).await; + } + } +} diff --git a/examples/teensy4_blinky/Cargo.lock b/examples/teensy4_blinky/Cargo.lock index 314eae0629..8ae5b958c7 100644 --- a/examples/teensy4_blinky/Cargo.lock +++ b/examples/teensy4_blinky/Cargo.lock @@ -133,9 +133,18 @@ dependencies = [ [[package]] name = "embedded-hal" -version = "1.0.0-rc.2" +version = "1.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e57ec6ad0bc8eb967cf9c9f144177f5e8f2f6f02dad0b8b683f9f05f6b22def" +checksum = "bc402f79e1fd22731ca945b4f97b5ff37e7b3f379312595c42bb2e8811c29920" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa1fba2ef2ffb35d614acc6fb323ddf7facc45c069f24544d49ea54e5043626d" +dependencies = [ + "embedded-hal 1.0.0-rc.3", +] [[package]] name = "equivalent" @@ -440,12 +449,12 @@ dependencies = [ [[package]] name = "rtic-monotonics" -version = "1.4.0" +version = "2.0.0" dependencies = [ "atomic-polyfill", "cfg-if", "cortex-m", - "embedded-hal 1.0.0-rc.2", + "embedded-hal 1.0.0-rc.3", "fugit", "imxrt-ral", "rtic-time", @@ -453,9 +462,12 @@ dependencies = [ [[package]] name = "rtic-time" -version = "1.1.0" +version = "2.0.0" dependencies = [ "critical-section", + "embedded-hal 1.0.0-rc.3", + "embedded-hal-async", + "fugit", "futures-util", "rtic-common", ] diff --git a/examples/teensy4_blinky/Cargo.toml b/examples/teensy4_blinky/Cargo.toml index a81a155acd..e050213635 100644 --- a/examples/teensy4_blinky/Cargo.toml +++ b/examples/teensy4_blinky/Cargo.toml @@ -14,7 +14,7 @@ features = ["thumbv7-backend"] [dependencies.rtic-monotonics] path = "../../rtic-monotonics" -version = "1.2.1" +version = "2.0.0" features = ["imxrt_gpt1"] [dependencies] diff --git a/examples/teensy4_blinky/examples/with_logs.rs b/examples/teensy4_blinky/examples/with_logs.rs index 82c7154690..111e21e838 100644 --- a/examples/teensy4_blinky/examples/with_logs.rs +++ b/examples/teensy4_blinky/examples/with_logs.rs @@ -13,9 +13,8 @@ use bsp::logging; use embedded_hal::serial::Write; -use rtic_monotonics::imxrt::Gpt1 as Mono; -use rtic_monotonics::imxrt::*; -use rtic_monotonics::Monotonic; +use rtic_monotonics::imxrt::prelude::*; +imxrt_gpt1_monotonic!(Mono, board::PERCLK_FREQUENCY); #[rtic::app(device = teensy4_bsp, dispatchers = [LPSPI1])] mod app { @@ -61,8 +60,7 @@ mod app { // Initialize Monotonic gpt1.set_clock_source(hal::gpt::ClockSource::PeripheralClock); - let gpt1_mono_token = rtic_monotonics::create_imxrt_gpt1_token!(); - Mono::start(board::PERCLK_FREQUENCY, gpt1.release(), gpt1_mono_token); + Mono::start(gpt1.release()); // Setup LED let led = board::led(&mut gpio2, pins.p13); diff --git a/examples/teensy4_blinky/src/main.rs b/examples/teensy4_blinky/src/main.rs index 443e6059a6..b3b1773577 100644 --- a/examples/teensy4_blinky/src/main.rs +++ b/examples/teensy4_blinky/src/main.rs @@ -10,8 +10,8 @@ fn panic(_: &::core::panic::PanicInfo) -> ! { use teensy4_bsp::{board, hal}; -use rtic_monotonics::imxrt::Gpt1 as Mono; -use rtic_monotonics::imxrt::*; +use rtic_monotonics::imxrt::prelude::*; +imxrt_gpt1_monotonic!(Mono, board::PERCLK_FREQUENCY); #[rtic::app(device = teensy4_bsp, dispatchers = [LPSPI1])] mod app { @@ -36,8 +36,7 @@ mod app { // Initialize Monotonic gpt1.set_clock_source(hal::gpt::ClockSource::PeripheralClock); - let gpt1_mono_token = rtic_monotonics::create_imxrt_gpt1_token!(); - Mono::start(board::PERCLK_FREQUENCY, gpt1.release(), gpt1_mono_token); + Mono::start(gpt1.release()); // Setup LED let led = board::led(&mut gpio2, pins.p13); diff --git a/rtic-macros/src/codegen/bindings/esp32c3.rs b/rtic-macros/src/codegen/bindings/esp32c3.rs index 26617e079c..254b0f5e28 100644 --- a/rtic-macros/src/codegen/bindings/esp32c3.rs +++ b/rtic-macros/src/codegen/bindings/esp32c3.rs @@ -242,8 +242,7 @@ mod esp32c3 { stmts } -pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec { - vec![] + pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec { + vec![] + } } -} - diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md index ef5840fd63..f114a18384 100644 --- a/rtic-monotonics/CHANGELOG.md +++ b/rtic-monotonics/CHANGELOG.md @@ -5,10 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). For each category, *Added*, *Changed*, *Fixed* add new entries at the top! -## Unreleased +## Unreleased - v2.0.0 ### Changed +- Rework all timers based on `rtic-time 2.0.0` +- Most timer tick rates are now configurable - Tweak `build.rs` to avoid warnings in Nightly 1.78+ - Removed unused `rust-toolchain.toml` diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 5860653b55..81df2402b8 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rtic-monotonics" -version = "1.5.0" +version = "2.0.0" edition = "2021" authors = [ @@ -24,13 +24,17 @@ features = [ "imxrt_gpt1", "imxrt_gpt2", "imxrt-ral/imxrt1011", + "stm32h725ag", + "stm32_tim2", + "stm32_tim3", + "stm32_tim4", + "stm32_tim5", + "stm32_tim15", ] rustdoc-flags = ["--cfg", "docsrs"] [dependencies] -rtic-time = { version = "1.1.0", path = "../rtic-time" } -embedded-hal = { version = "1.0" } -embedded-hal-async = { version = "1.0", optional = true } +rtic-time = { version = "2.0.0", path = "../rtic-time" } fugit = { version = "0.3.6" } atomic-polyfill = "1" cfg-if = "1.0.0" @@ -69,8 +73,6 @@ defmt = ["fugit/defmt"] # Systick on Cortex-M, default 1 kHz cortex-m-systick = ["dep:cortex-m"] -systick-100hz = [] -systick-10khz = [] # Use 64-bit wide backing storage for the Instant systick-64bit = [] @@ -99,7 +101,6 @@ stm32_tim2 = [] stm32_tim3 = [] stm32_tim4 = [] stm32_tim5 = [] -stm32_tim12 = [] stm32_tim15 = [] stm32-metapac = ["dep:stm32-metapac", "dep:quote", "dep:proc-macro2"] diff --git a/rtic-monotonics/src/imxrt.rs b/rtic-monotonics/src/imxrt.rs index 2299beac46..6d07be67d4 100644 --- a/rtic-monotonics/src/imxrt.rs +++ b/rtic-monotonics/src/imxrt.rs @@ -1,112 +1,158 @@ -//! [`Monotonic`] implementations for i.MX RT's GPT peripherals. +//! [`Monotonic`](rtic_time::Monotonic) implementations for i.MX RT's GPT peripherals. //! //! # Example //! //! ``` -//! use rtic_monotonics::imxrt::*; -//! use rtic_monotonics::imxrt::Gpt1 as Mono; +//! use rtic_monotonics::imxrt::prelude::*; +//! imxrt_gpt1_monotonic!(Mono, 1_000_000); //! //! fn init() { -//! // Obtain ownership of the timer register block +//! // Obtain ownership of the timer register block. //! let gpt1 = unsafe { imxrt_ral::gpt::GPT1::instance() }; //! -//! // Configure the timer clock source and determine its tick rate -//! let timer_tickrate_hz = 1_000_000; -//! -//! // Generate timer token to ensure correct timer interrupt handler is used -//! let token = rtic_monotonics::create_imxrt_gpt1_token!(); +//! // Configure the timer tick rate as specified earlier +//! todo!("Configure the gpt1 peripheral to a tick rate of 1_000_000"); //! //! // Start the monotonic -//! Mono::start(timer_tickrate_hz, gpt1, token); +//! Mono::start(gpt1); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! let timestamp = Mono::now().ticks(); +//! let timestamp = Mono::now(); //! Mono::delay(100.millis()).await; //! } //! } //! ``` -use crate::{Monotonic, TimeoutError, TimerQueue}; use atomic_polyfill::{AtomicU32, Ordering}; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rtic_time::half_period_counter::calculate_now; +use rtic_time::{ + 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)] #[macro_export] macro_rules! __internal_create_imxrt_timer_interrupt { - ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ + ($mono_backend:ident, $timer:ident) => { #[no_mangle] #[allow(non_snake_case)] unsafe extern "C" fn $timer() { - $crate::imxrt::$mono_timer::__tq().on_monotonic_interrupt(); + use $crate::TimerQueueBackend; + $crate::imxrt::$mono_backend::timer_queue().on_monotonic_interrupt(); + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __internal_create_imxrt_timer_struct { + ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => { + /// A `Monotonic` based on the GPT peripheral. + struct $name; + + impl $name { + /// Starts the `Monotonic`. + /// + /// This method must be called only once. + pub fn start(gpt: $crate::imxrt::ral::gpt::$timer) { + $crate::__internal_create_imxrt_timer_interrupt!($mono_backend, $timer); + + $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< + ::Ticks, + 1, + { $tick_rate_hz }, + >; + type Duration = $crate::fugit::Duration< + ::Ticks, + 1, + { $tick_rate_hz }, + >; + } - unsafe impl $crate::InterruptToken<$crate::imxrt::$mono_timer> for $timer_token {} - - $timer_token - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } -/// Register the GPT1 interrupt for the monotonic. +/// Create a GPT1 based monotonic and register the GPT1 interrupt for it. +/// +/// See [`crate::imxrt`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. It's the user's responsibility +/// to configure the peripheral to the given frequency before starting the +/// monotonic. #[cfg(feature = "imxrt_gpt1")] #[macro_export] -macro_rules! create_imxrt_gpt1_token { - () => {{ - $crate::__internal_create_imxrt_timer_interrupt!(Gpt1, GPT1, Gpt1Token) - }}; +macro_rules! imxrt_gpt1_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_imxrt_timer_struct!($name, Gpt1Backend, GPT1, $tick_rate_hz); + }; } -/// Register the GPT2 interrupt for the monotonic. +/// Create a GPT2 based monotonic and register the GPT2 interrupt for it. +/// +/// See [`crate::imxrt`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. It's the user's responsibility +/// to configure the peripheral to the given frequency before starting the +/// monotonic. #[cfg(feature = "imxrt_gpt2")] #[macro_export] -macro_rules! create_imxrt_gpt2_token { - () => {{ - $crate::__internal_create_imxrt_timer_interrupt!(Gpt2, GPT2, Gpt2Token) - }}; +macro_rules! imxrt_gpt2_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_imxrt_timer_struct!($name, Gpt2Backend, GPT2, $tick_rate_hz); + }; } macro_rules! make_timer { - ($mono_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { - /// Timer implementing [`Monotonic`] which runs at 1 MHz. + ($mono_name:ident, $backend_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { + /// GPT based [`TimerQueueBackend`]. $( #[cfg_attr(docsrs, doc(cfg($($doc)*)))] )? - pub struct $mono_name; + pub struct $backend_name; use ral::gpt::$timer; /// Number of 2^31 periods elapsed since boot. static $period: AtomicU32 = AtomicU32::new(0); - static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + static $tq: TimerQueue<$backend_name> = TimerQueue::new(); - impl $mono_name { - /// Starts the monotonic timer. + impl $backend_name { + /// Starts the timer. /// - /// - `tick_freq_hz`: The tick frequency of the given timer. - /// - `gpt`: The GPT timer register block instance. - /// - `_interrupt_token`: Required for correct timer interrupt handling. + /// **Do not use this function directly.** /// - /// This method must be called only once. - pub fn start(tick_freq_hz: u32, gpt: $timer, _interrupt_token: impl crate::InterruptToken) { - // Find a prescaler that creates our desired tick frequency - let previous_prescaler = ral::read_reg!(ral::gpt, gpt, PR, PRESCALER) + 1; - let previous_clock_freq = tick_freq_hz * previous_prescaler; - assert!((previous_clock_freq % TIMER_HZ) == 0, - "Unable to find a fitting prescaler value!\n Input: {}/{}\n Desired: {}", - previous_clock_freq, previous_prescaler, TIMER_HZ); - let prescaler = previous_clock_freq / TIMER_HZ; - assert!(prescaler > 0); - assert!(prescaler <= 4096); + /// Use the prelude macros instead. + pub fn _start(gpt: $timer) { // Disable the timer. ral::modify_reg!(ral::gpt, gpt, CR, EN: 0); @@ -122,11 +168,6 @@ macro_rules! make_timer { // Reset period $period.store(0, Ordering::SeqCst); - // Prescaler - ral::modify_reg!(ral::gpt, gpt, PR, - PRESCALER: (prescaler - 1), // Scale to our desired clock rate - ); - // Enable interrupts ral::write_reg!(ral::gpt, gpt, IR, ROVIE: 1, // Rollover interrupt @@ -150,7 +191,6 @@ macro_rules! make_timer { ENMOD: 0, // Keep state when disabled ); - // SAFETY: We take full ownership of the peripheral and interrupt vector, // plus we are not using any external shared resources so we won't impact // basepri/source masking based critical sections. @@ -159,65 +199,21 @@ macro_rules! make_timer { cortex_m::peripheral::NVIC::unmask(ral::Interrupt::$timer); } } - - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<$mono_name> { - &$tq - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: ::Duration) { - $tq.delay(duration).await; - } - - /// Timeout at a specific time. - pub async fn timeout_at( - instant: ::Instant, - future: F, - ) -> Result { - $tq.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - duration: ::Duration, - future: F, - ) -> Result { - $tq.timeout_after(duration, future).await - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: ::Instant) { - $tq.delay_until(instant).await; - } } - rtic_time::embedded_hal_delay_impl_fugit64!($mono_name); + impl TimerQueueBackend for $backend_name { + type Ticks = u64; - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name); - - impl Monotonic for $mono_name { - type Instant = fugit::TimerInstantU64; - type Duration = fugit::TimerDurationU64; - - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - fn now() -> Self::Instant { + fn now() -> Self::Ticks { let gpt = unsafe{ $timer::instance() }; - Self::Instant::from_ticks(calculate_now( + calculate_now( || $period.load(Ordering::Relaxed), || ral::read_reg!(ral::gpt, gpt, CNT) - )) + ) } - fn set_compare(instant: Self::Instant) { + fn set_compare(instant: Self::Ticks) { let gpt = unsafe{ $timer::instance() }; // Set the timer regardless of whether it is multiple periods in the future, @@ -225,8 +221,7 @@ macro_rules! make_timer { // The worst thing that can happen is a spurious wakeup, and with a timer // period of half an hour, this is hardly a problem. - let ticks = instant.duration_since_epoch().ticks(); - let ticks_wrapped = ticks as u32; + let ticks_wrapped = instant as u32; ral::write_reg!(ral::gpt, gpt, OCR[1], ticks_wrapped); } @@ -257,12 +252,16 @@ macro_rules! make_timer { assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!"); } } + + fn timer_queue() -> &'static TimerQueue { + &$tq + } } }; } #[cfg(feature = "imxrt_gpt1")] -make_timer!(Gpt1, GPT1, GPT1_HALFPERIODS, GPT1_TQ); +make_timer!(Gpt1, Gpt1Backend, GPT1, GPT1_HALFPERIODS, GPT1_TQ); #[cfg(feature = "imxrt_gpt2")] -make_timer!(Gpt2, GPT2, GPT2_HALFPERIODS, GPT2_TQ); +make_timer!(Gpt2, Gpt2Backend, GPT2, GPT2_HALFPERIODS, GPT2_TQ); diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index 6dc703eb61..65180b1b8f 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -21,14 +21,18 @@ //! `Available on crate features X only` tag are available on any `nrf52*` feature. //! // To build these docs correctly: -// RUSTFLAGS="--cfg docsrs" cargo doc --featuers cortex-m-systick,rp2040,nrf52840 +// RUSTFLAGS="--cfg docsrs" cargo +nightly doc --features thumbv7-backend,cortex-m-systick,rp2040,nrf52840,imxrt_gpt1,imxrt_gpt2,imxrt-ral/imxrt1011,stm32h725ag,stm32_tim2,stm32_tim3,stm32_tim4,stm32_tim5,stm32_tim15 #![no_std] #![deny(missing_docs)] #![allow(incomplete_features)] #![cfg_attr(docsrs, feature(doc_cfg))] -pub use rtic_time::{Monotonic, TimeoutError, TimerQueue}; +pub use fugit; +pub use rtic_time::{ + self, monotonic::TimerQueueBasedMonotonic, timer_queue::TimerQueueBackend, Monotonic, + TimeoutError, +}; #[cfg(feature = "cortex-m-systick")] pub mod systick; @@ -92,13 +96,3 @@ pub(crate) unsafe fn set_monotonic_prio( nvic.set_priority(interrupt, hw_prio); } - -/// This marker is implemented on an interrupt token to enforce that the right tokens -/// are given to the correct monotonic implementation. -/// -/// This trait is implemented by this crate and not intended for user implementation. -/// -/// # Safety -/// -/// This is only safely implemented by this crate. -pub unsafe trait InterruptToken {} diff --git a/rtic-monotonics/src/nrf/rtc.rs b/rtic-monotonics/src/nrf/rtc.rs index d425b11481..39b26c4d75 100644 --- a/rtic-monotonics/src/nrf/rtc.rs +++ b/rtic-monotonics/src/nrf/rtc.rs @@ -1,96 +1,152 @@ -//! [`Monotonic`] implementation for the nRF Real Time Clocks (RTC). +//! [`Monotonic`](rtic_time::Monotonic) implementation for the nRF Real Time Clocks (RTC). //! //! # Example //! //! ``` -//! use rtic_monotonics::nrf::rtc::*; +//! use rtic_monotonics::nrf::rtc::prelude::*; +//! nrf_rtc0_monotonic!(Mono); //! //! fn init() { //! # // This is normally provided by the selected PAC //! # let rtc = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!(); -//! //! // Start the monotonic -//! Rtc0::start(rtc, token); +//! Mono::start(rtc); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! Rtc0::delay(100.millis()).await; +//! let timestamp = Mono::now(); +//! Mono::delay(100.millis()).await; //! } //! } //! ``` -#[cfg(feature = "nrf52810")] -use nrf52810_pac::{self as pac, Interrupt, RTC0, RTC1}; -#[cfg(feature = "nrf52811")] -use nrf52811_pac::{self as pac, Interrupt, RTC0, RTC1}; -#[cfg(feature = "nrf52832")] -use nrf52832_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; -#[cfg(feature = "nrf52833")] -use nrf52833_pac::{self as pac, Interrupt, RTC0, RTC1, RTC2}; -#[cfg(feature = "nrf52840")] -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 = "nrf5340-net")] -use nrf5340_net_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; -#[cfg(feature = "nrf9160")] -use nrf9160_pac::{self as pac, Interrupt, RTC0_NS as RTC0, RTC1_NS as RTC1}; +/// Common definitions and traits for using the nRF RTC monotonics +pub mod prelude { + pub use crate::nrf_rtc0_monotonic; + pub use crate::nrf_rtc1_monotonic; + #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] + pub use crate::nrf_rtc2_monotonic; + + pub use crate::Monotonic; + pub use fugit::{self, ExtU64, ExtU64Ceil}; +} + +#[cfg(feature = "nrf52810")] +#[doc(hidden)] +pub use nrf52810_pac::{self as pac, RTC0, RTC1}; +#[cfg(feature = "nrf52811")] +#[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 core::future::Future; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rtic_time::half_period_counter::calculate_now; +use rtic_time::{ + half_period_counter::calculate_now, + timer_queue::{TimerQueue, TimerQueueBackend}, +}; #[doc(hidden)] #[macro_export] macro_rules! __internal_create_nrf_rtc_interrupt { - ($mono_timer:ident, $rtc:ident, $rtc_token:ident) => {{ + ($mono_backend:ident, $rtc:ident) => { #[no_mangle] #[allow(non_snake_case)] unsafe extern "C" fn $rtc() { - $crate::nrf::rtc::$mono_timer::__tq().on_monotonic_interrupt(); + use $crate::TimerQueueBackend; + $crate::nrf::rtc::$mono_backend::timer_queue().on_monotonic_interrupt(); + } + }; +} + +#[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< + ::Ticks, + 1, + 32_768, + >; + type Duration = $crate::fugit::Duration< + ::Ticks, + 1, + 32_768, + >; + } - unsafe impl $crate::InterruptToken<$crate::nrf::rtc::$mono_timer> for $rtc_token {} - - $rtc_token - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } -/// Register the Rtc0 interrupt for the monotonic. +/// Create an RTC0 based monotonic and register the RTC0 interrupt for it. +/// +/// See [`crate::nrf::rtc`] for more details. #[macro_export] -macro_rules! create_nrf_rtc0_monotonic_token { - () => {{ - $crate::__internal_create_nrf_rtc_interrupt!(Rtc0, RTC0, Rtc0Token) - }}; +macro_rules! nrf_rtc0_monotonic { + ($name:ident) => { + $crate::__internal_create_nrf_rtc_struct!($name, Rtc0Backend, RTC0); + }; } -/// Register the Rtc1 interrupt for the monotonic. +/// Create an RTC1 based monotonic and register the RTC1 interrupt for it. +/// +/// See [`crate::nrf::rtc`] for more details. #[macro_export] -macro_rules! create_nrf_rtc1_monotonic_token { - () => {{ - $crate::__internal_create_nrf_rtc_interrupt!(Rtc1, RTC1, Rtc1Token) - }}; +macro_rules! nrf_rtc1_monotonic { + ($name:ident) => { + $crate::__internal_create_nrf_rtc_struct!($name, Rtc1Backend, RTC1); + }; } -/// Register the Rtc2 interrupt for the monotonic. +/// Create an RTC2 based monotonic and register the RTC2 interrupt for it. +/// +/// See [`crate::nrf::rtc`] for more details. #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) )] #[macro_export] -macro_rules! create_nrf_rtc2_monotonic_token { - () => {{ - $crate::__internal_create_nrf_rtc_interrupt!(Rtc2, RTC2, Rtc2Token) - }}; +macro_rules! nrf_rtc2_monotonic { + ($name:ident) => { + $crate::__internal_create_nrf_rtc_struct!($name, Rtc2Backend, RTC2); + }; } struct TimerValueU24(u32); @@ -104,19 +160,23 @@ impl From for u64 { } macro_rules! make_rtc { - ($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { - /// Monotonic timer queue implementation. + ($backend_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { + /// RTC based [`TimerQueueBackend`]. $( #[cfg_attr(docsrs, doc(cfg($($doc)*)))] )? - pub struct $mono_name; + pub struct $backend_name; static $overflow: AtomicU32 = AtomicU32::new(0); - static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + static $tq: TimerQueue<$backend_name> = TimerQueue::new(); - impl $mono_name { - /// Start the timer monotonic. - pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken) { + impl $backend_name { + /// Starts the timer. + /// + /// **Do not use this function directly.** + /// + /// Use the prelude macros instead. + pub fn _start(rtc: $rtc) { unsafe { rtc.prescaler.write(|w| w.bits(0)) }; // Disable interrupts, as preparation @@ -166,67 +226,21 @@ macro_rules! make_rtc { // plus we are not using any external shared resources so we won't impact // basepri/source masking based critical sections. unsafe { - crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$rtc); - pac::NVIC::unmask(Interrupt::$rtc); + crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$rtc); + pac::NVIC::unmask(pac::Interrupt::$rtc); } } - - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<$mono_name> { - &$tq - } - - /// Timeout at a specific time. - #[inline] - pub async fn timeout_at( - instant: ::Instant, - future: F, - ) -> Result { - $tq.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - duration: ::Duration, - future: F, - ) -> Result { - $tq.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: ::Duration) { - $tq.delay(duration).await; - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: ::Instant) { - $tq.delay_until(instant).await; - } } + impl TimerQueueBackend for $backend_name { + type Ticks = u64; - rtic_time::embedded_hal_delay_impl_fugit64!($mono_name); - - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name); - - impl Monotonic for $mono_name { - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - type Instant = fugit::TimerInstantU64<32_768>; - type Duration = fugit::TimerDurationU64<32_768>; - - fn now() -> Self::Instant { + fn now() -> Self::Ticks { let rtc = unsafe { &*$rtc::PTR }; - Self::Instant::from_ticks(calculate_now( + calculate_now( || $overflow.load(Ordering::Relaxed), || TimerValueU24(rtc.counter.read().bits()) - )) + ) } fn on_interrupt() { @@ -243,28 +257,35 @@ macro_rules! make_rtc { } } - fn enable_timer() {} - - fn disable_timer() {} - - fn set_compare(mut instant: Self::Instant) { + fn set_compare(mut instant: Self::Ticks) { let rtc = unsafe { &*$rtc::PTR }; + const MAX: u64 = 0xff_ffff; + // Disable interrupts because this section is timing critical. // We rely on the fact that this entire section runs within one // RTC clock tick. (which it will do easily if it doesn't get // interrupted) critical_section::with(|_|{ let now = Self::now(); - if let Some(diff) = instant.checked_duration_since(now) { + // wrapping_sub deals with the u64 overflow corner case + let diff = instant.wrapping_sub(now); + let val = if diff <= MAX { + // Now we know `instant` whill happen within one `MAX` time duration. + // Errata: Timer interrupts don't fire if they are scheduled less than // two ticks in the future. Make it three, because the timer could // tick right now. - if diff.ticks() < 3 { - instant = Self::Instant::from_ticks(now.ticks().wrapping_add(3)); + if diff < 3 { + instant = now.wrapping_add(3); } - unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xff_ffff)) }; - } + + (instant & MAX) as u32 + } else { + 0 + }; + + unsafe { rtc.cc[0].write(|w| w.bits(val)) }; }); } @@ -274,13 +295,17 @@ macro_rules! make_rtc { } fn pend_interrupt() { - pac::NVIC::pend(Interrupt::$rtc); + pac::NVIC::pend(pac::Interrupt::$rtc); + } + + fn timer_queue() -> &'static TimerQueue { + &$tq } } }; } -make_rtc!(Rtc0, RTC0, RTC0_OVERFLOWS, RTC0_TQ); -make_rtc!(Rtc1, RTC1, RTC1_OVERFLOWS, RTC1_TQ); +make_rtc!(Rtc0Backend, RTC0, RTC0_OVERFLOWS, RTC0_TQ); +make_rtc!(Rtc1Backend, RTC1, RTC1_OVERFLOWS, RTC1_TQ); #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] -make_rtc!(Rtc2, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); +make_rtc!(Rtc2Backend, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); diff --git a/rtic-monotonics/src/nrf/timer.rs b/rtic-monotonics/src/nrf/timer.rs index 7b760e4c2b..60ee7dc603 100644 --- a/rtic-monotonics/src/nrf/timer.rs +++ b/rtic-monotonics/src/nrf/timer.rs @@ -1,4 +1,4 @@ -//! [`Monotonic`] impl for the 32-bit timers of the nRF series. +//! [`Monotonic`](rtic_time::Monotonic) implementation for the 32-bit timers of the nRF series. //! //! Not all timers are available on all parts. Ensure that only the available //! timers are exposed by having the correct `nrf52*` feature enabled for `rtic-monotonics`. @@ -6,139 +6,217 @@ //! # Example //! //! ``` -//! use rtic_monotonics::nrf::timer::*; +//! use rtic_monotonics::nrf::timer::prelude::*; +//! nrf_timer0_monotonic!(Mono); //! //! fn init() { //! # // This is normally provided by the selected PAC //! # let timer = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let token = rtic_monotonics::create_nrf_timer0_monotonic_token!(); -//! //! // Start the monotonic -//! Timer0::start(timer, token); +//! Mono::start(timer); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! Timer0::delay(100.millis()).await; +//! let timestamp = Mono::now(); +//! Mono::delay(100.millis()).await; //! } //! } //! ``` -use crate::{Monotonic, TimeoutError, TimerQueue}; -use atomic_polyfill::{AtomicU32, Ordering}; -use core::future::Future; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rtic_time::half_period_counter::calculate_now; +/// Common definitions and traits for using the nRF Timer monotonics +pub mod prelude { + pub use crate::nrf_timer0_monotonic; + pub use crate::nrf_timer1_monotonic; + pub use crate::nrf_timer2_monotonic; + #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] + pub use crate::nrf_timer3_monotonic; + #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] + pub use crate::nrf_timer4_monotonic; + + pub use crate::Monotonic; + pub use fugit::{self, ExtU64, ExtU64Ceil}; +} #[cfg(feature = "nrf52810")] -use nrf52810_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; +#[doc(hidden)] +pub use nrf52810_pac::{self as pac, TIMER0, TIMER1, TIMER2}; #[cfg(feature = "nrf52811")] -use nrf52811_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; +#[doc(hidden)] +pub use nrf52811_pac::{self as pac, TIMER0, TIMER1, TIMER2}; #[cfg(feature = "nrf52832")] -use nrf52832_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[doc(hidden)] +pub use nrf52832_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; #[cfg(feature = "nrf52833")] -use nrf52833_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[doc(hidden)] +pub use nrf52833_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; #[cfg(feature = "nrf52840")] -use nrf52840_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +#[doc(hidden)] +pub use nrf52840_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; #[cfg(feature = "nrf5340-app")] -use nrf5340_app_pac::{ - self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +#[doc(hidden)] +pub use nrf5340_app_pac::{ + self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, }; #[cfg(feature = "nrf5340-net")] -use nrf5340_net_pac::{ - self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +#[doc(hidden)] +pub use nrf5340_net_pac::{ + self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, }; #[cfg(feature = "nrf9160")] -use nrf9160_pac::{ - self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, +#[doc(hidden)] +pub use nrf9160_pac::{self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2}; + +use atomic_polyfill::{AtomicU32, Ordering}; +use rtic_time::{ + half_period_counter::calculate_now, + timer_queue::{TimerQueue, TimerQueueBackend}, }; #[doc(hidden)] #[macro_export] macro_rules! __internal_create_nrf_timer_interrupt { - ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ + ($mono_backend:ident, $timer:ident) => { #[no_mangle] #[allow(non_snake_case)] unsafe extern "C" fn $timer() { - $crate::nrf::timer::$mono_timer::__tq().on_monotonic_interrupt(); + use $crate::TimerQueueBackend; + $crate::nrf::timer::$mono_backend::timer_queue().on_monotonic_interrupt(); + } + }; +} + +#[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< + ::Ticks, + 1, + { $tick_rate_hz }, + >; + type Duration = $crate::fugit::Duration< + ::Ticks, + 1, + { $tick_rate_hz }, + >; + } - unsafe impl $crate::InterruptToken<$crate::nrf::timer::$mono_timer> for $timer_token {} - - $timer_token - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } -/// Register the Timer0 interrupt for the monotonic. +/// Create an Timer0 based monotonic and register the TIMER0 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[macro_export] -macro_rules! create_nrf_timer0_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer0, TIMER0, Timer0Token) - }}; +macro_rules! nrf_timer0_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer0Backend, TIMER0, $tick_rate_hz); + }; } -/// Register the Timer1 interrupt for the monotonic. +/// Create an Timer1 based monotonic and register the TIMER1 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[macro_export] -macro_rules! create_nrf_timer1_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer1, TIMER1, Timer1Token) - }}; +macro_rules! nrf_timer1_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer1Backend, TIMER1, $tick_rate_hz); + }; } -/// Register the Timer2 interrupt for the monotonic. +/// Create an Timer2 based monotonic and register the TIMER2 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[macro_export] -macro_rules! create_nrf_timer2_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer2, TIMER2, Timer2Token) - }}; +macro_rules! nrf_timer2_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer2Backend, TIMER2, $tick_rate_hz); + }; } -/// Register the Timer3 interrupt for the monotonic. +/// Create an Timer3 based monotonic and register the TIMER3 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[cfg_attr( docsrs, doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) )] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[macro_export] -macro_rules! create_nrf_timer3_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer3, TIMER3, Timer3Token) - }}; +macro_rules! nrf_timer3_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer3Backend, TIMER3, $tick_rate_hz); + }; } -/// Register the Timer4 interrupt for the monotonic. +/// Create an Timer4 based monotonic and register the TIMER4 interrupt for it. +/// +/// See [`crate::nrf::timer`] for more details. #[cfg_attr( docsrs, doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) )] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[macro_export] -macro_rules! create_nrf_timer4_monotonic_token { - () => {{ - $crate::__internal_create_nrf_timer_interrupt!(Timer4, TIMER4, Timer4Token) - }}; +macro_rules! nrf_timer4_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_nrf_timer_struct!($name, Timer4Backend, TIMER4, $tick_rate_hz); + }; } macro_rules! make_timer { - ($mono_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { - /// Monotonic timer queue implementation. + ($backend_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { + /// Timer peripheral based [`TimerQueueBackend`]. $( #[cfg_attr(docsrs, doc(cfg($($doc)*)))] )? - pub struct $mono_name; + pub struct $backend_name; static $overflow: AtomicU32 = AtomicU32::new(0); - static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + static $tq: TimerQueue<$backend_name> = TimerQueue::new(); - impl $mono_name { - /// Start the timer monotonic. - pub fn start(timer: $timer, _interrupt_token: impl crate::InterruptToken) { - // 1 MHz - timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) }); + impl $backend_name { + /// Starts the timer. + /// + /// **Do not use this function directly.** + /// + /// Use the prelude macros instead. + pub fn _start(timer: $timer, prescaler: u8) { + timer.prescaler.write(|w| unsafe { w.prescaler().bits(prescaler) }); timer.bitmode.write(|w| w.bitmode()._32bit()); // Disable interrupts, as preparation @@ -184,70 +262,25 @@ macro_rules! make_timer { // plus we are not using any external shared resources so we won't impact // basepri/source masking based critical sections. unsafe { - crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$timer); - pac::NVIC::unmask(Interrupt::$timer); + crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$timer); + pac::NVIC::unmask(pac::Interrupt::$timer); } } - - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<$mono_name> { - &$tq - } - - /// Timeout at a specific time. - #[inline] - pub async fn timeout_at( - instant: ::Instant, - future: F, - ) -> Result { - $tq.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - duration: ::Duration, - future: F, - ) -> Result { - $tq.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: ::Duration) { - $tq.delay(duration).await; - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: ::Instant) { - $tq.delay_until(instant).await; - } } - rtic_time::embedded_hal_delay_impl_fugit64!($mono_name); + impl TimerQueueBackend for $backend_name { + type Ticks = u64; - #[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 { + fn now() -> Self::Ticks { let timer = unsafe { &*$timer::PTR }; - Self::Instant::from_ticks(calculate_now( + calculate_now( || $overflow.load(Ordering::Relaxed), || { timer.tasks_capture[3].write(|w| unsafe { w.bits(1) }); timer.cc[3].read().bits() } - )) + ) } fn on_interrupt() { @@ -268,9 +301,9 @@ macro_rules! make_timer { } } - fn set_compare(instant: Self::Instant) { + fn set_compare(instant: Self::Ticks) { let timer = unsafe { &*$timer::PTR }; - timer.cc[0].write(|w| unsafe { w.cc().bits(instant.ticks() as u32) }); + timer.cc[0].write(|w| unsafe { w.cc().bits(instant as u32) }); } fn clear_compare_flag() { @@ -279,16 +312,20 @@ macro_rules! make_timer { } fn pend_interrupt() { - pac::NVIC::pend(Interrupt::$timer); + pac::NVIC::pend(pac::Interrupt::$timer); + } + + fn timer_queue() -> &'static TimerQueue<$backend_name> { + &$tq } } }; } -make_timer!(Timer0, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ); -make_timer!(Timer1, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ); -make_timer!(Timer2, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ); +make_timer!(Timer0Backend, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ); +make_timer!(Timer1Backend, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ); +make_timer!(Timer2Backend, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ); #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] -make_timer!(Timer3, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); +make_timer!(Timer3Backend, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] -make_timer!(Timer4, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); +make_timer!(Timer4Backend, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); diff --git a/rtic-monotonics/src/rp2040.rs b/rtic-monotonics/src/rp2040.rs index 998b53251e..d4f590fedc 100644 --- a/rtic-monotonics/src/rp2040.rs +++ b/rtic-monotonics/src/rp2040.rs @@ -1,46 +1,56 @@ -//! [`Monotonic`] implementation for RP2040's Timer peripheral. +//! [`Monotonic`](rtic_time::Monotonic) implementation for RP2040's Timer peripheral. +//! +//! Always runs at a fixed rate of 1 MHz. //! //! # Example //! //! ``` -//! use rtic_monotonics::rp2040::*; +//! use rtic_monotonics::rp2040::prelude::*; +//! +//! rp2040_timer_monotonic!(Mono); //! //! fn init() { //! # // This is normally provided by the selected PAC //! # let timer = unsafe { core::mem::transmute(()) }; //! # let mut resets = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let token = rtic_monotonics::create_rp2040_monotonic_token!(); -//! +//! # //! // Start the monotonic -//! Timer::start(timer, &mut resets, token); +//! Mono::start(timer, &mut resets); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! Timer::delay(100.millis()).await; +//! let timestamp = Mono::now(); +//! Mono::delay(100.millis()).await; //! } //! } //! ``` -use super::Monotonic; +/// Common definitions and traits for using the RP2040 timer monotonic +pub mod prelude { + pub use crate::rp2040_timer_monotonic; -pub use super::{TimeoutError, TimerQueue}; -use core::future::Future; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rp2040_pac::{timer, Interrupt, NVIC, RESETS, TIMER}; + pub use crate::Monotonic; -/// Timer implementing [`Monotonic`] which runs at 1 MHz. -pub struct Timer; + pub use fugit::{self, ExtU64, ExtU64Ceil}; +} -impl Timer { - /// Start a `Monotonic` based on RP2040's Timer. - pub fn start( - timer: TIMER, - resets: &RESETS, - _interrupt_token: impl crate::InterruptToken, - ) { +use crate::TimerQueueBackend; +use rp2040_pac::{timer, Interrupt, NVIC}; +pub use rp2040_pac::{RESETS, TIMER}; +use rtic_time::timer_queue::TimerQueue; + +/// Timer implementing [`TimerQueueBackend`]. +pub struct TimerBackend; + +impl TimerBackend { + /// Starts the monotonic timer. + /// + /// **Do not use this function directly.** + /// + /// Use the prelude macros instead. + pub fn _start(timer: TIMER, resets: &RESETS) { resets.reset.modify(|_, w| w.timer().clear_bit()); while resets.reset_done.read().timer().bit_is_clear() {} timer.inte.modify(|_, w| w.alarm_0().bit(true)); @@ -58,55 +68,12 @@ impl Timer { } } -static TIMER_QUEUE: TimerQueue = TimerQueue::new(); +static TIMER_QUEUE: TimerQueue = TimerQueue::new(); -// Forward timerqueue interface -impl Timer { - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue { - &TIMER_QUEUE - } +impl TimerQueueBackend for TimerBackend { + type Ticks = u64; - /// Timeout at a specific time. - #[inline] - pub async fn timeout_at( - instant: ::Instant, - future: F, - ) -> Result { - TIMER_QUEUE.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - duration: ::Duration, - future: F, - ) -> Result { - TIMER_QUEUE.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: ::Duration) { - TIMER_QUEUE.delay(duration).await; - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: ::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 { + fn now() -> Self::Ticks { let timer = Self::timer(); let mut hi0 = timer.timerawh.read().bits(); @@ -114,22 +81,24 @@ impl Monotonic for Timer { let low = timer.timerawl.read().bits(); let hi1 = timer.timerawh.read().bits(); if hi0 == hi1 { - break Self::Instant::from_ticks((u64::from(hi0) << 32) | u64::from(low)); + break ((u64::from(hi0) << 32) | u64::from(low)); } hi0 = hi1; } } - fn set_compare(instant: Self::Instant) { + fn set_compare(instant: Self::Ticks) { let now = Self::now(); - let max = u32::MAX as u64; + const MAX: u64 = u32::MAX as u64; // Since the timer may or may not overflow based on the requested compare val, we check // how many ticks are left. - let val = match instant.checked_duration_since(now) { - Some(x) if x.ticks() <= max => instant.duration_since_epoch().ticks() & max, // Will not overflow - _ => 0, // Will overflow or in the past, set the same value as after overflow to not get extra interrupts + // `wrapping_sup` takes care of the u64 integer overflow special case. + let val = if instant.wrapping_sub(now) <= MAX { + instant & MAX + } else { + 0 }; Self::timer() @@ -145,32 +114,55 @@ impl Monotonic for Timer { rp2040_pac::NVIC::pend(Interrupt::TIMER_IRQ_0); } - fn on_interrupt() {} - - fn enable_timer() {} - - fn disable_timer() {} + fn timer_queue() -> &'static TimerQueue { + &TIMER_QUEUE + } } -rtic_time::embedded_hal_delay_impl_fugit64!(Timer); - -#[cfg(feature = "embedded-hal-async")] -rtic_time::embedded_hal_async_delay_impl_fugit64!(Timer); - -/// Register the Timer interrupt for the monotonic. +/// Create an RP2040 timer based monotonic and register the necessary interrupt for it. +/// +/// See [`crate::rp2040`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. #[macro_export] -macro_rules! create_rp2040_monotonic_token { - () => {{ - #[no_mangle] - #[allow(non_snake_case)] - unsafe extern "C" fn TIMER_IRQ_0() { - $crate::rp2040::Timer::__tq().on_monotonic_interrupt(); +macro_rules! rp2040_timer_monotonic { + ($name:ident) => { + /// A `Monotonic` based on the RP2040 Timer peripheral. + struct $name; + + impl $name { + /// Starts the `Monotonic`. + /// + /// This method must be called only once. + pub fn start(timer: $crate::rp2040::TIMER, resets: &$crate::rp2040::RESETS) { + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn TIMER_IRQ_0() { + use $crate::TimerQueueBackend; + $crate::rp2040::TimerBackend::timer_queue().on_monotonic_interrupt(); + } + + $crate::rp2040::TimerBackend::_start(timer, resets); + } } - pub struct Rp2040Token; + impl $crate::TimerQueueBasedMonotonic for $name { + type Backend = $crate::rp2040::TimerBackend; + type Instant = $crate::fugit::Instant< + ::Ticks, + 1, + 1_000_000, + >; + type Duration = $crate::fugit::Duration< + ::Ticks, + 1, + 1_000_000, + >; + } - unsafe impl $crate::InterruptToken<$crate::rp2040::Timer> for Rp2040Token {} - - Rp2040Token - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } diff --git a/rtic-monotonics/src/stm32.rs b/rtic-monotonics/src/stm32.rs index 68f95a25d3..92800c7416 100644 --- a/rtic-monotonics/src/stm32.rs +++ b/rtic-monotonics/src/stm32.rs @@ -1,4 +1,4 @@ -//! [`Monotonic`] impl for the STM32. +//! [`Monotonic`](rtic_time::Monotonic) implementations for STM32 chips. //! //! Not all timers are available on all parts. Ensure that only available //! timers are exposed by having the correct `stm32*` feature enabled for `rtic-monotonics`. @@ -6,38 +6,56 @@ //! # Example //! //! ``` -//! use rtic_monotonics::stm32::*; -//! use rtic_monotonics::stm32::Tim2 as Mono; -//! use rtic_monotonics::Monotonic; -//! use embassy_stm32::peripherals::TIM2; -//! use embassy_stm32::rcc::low_level::RccPeripheral; +//! use rtic_monotonics::stm32::prelude::*; +//! +//! // Define the monotonic and set it to 1MHz tick rate +//! stm32_tim2_monotonic!(Mono, 1_000_000); //! //! 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: -//! let timer_clock_hz = TIM2::frequency(); +//! let timer_clock_hz = embassy_stm32::peripherals::TIM2::frequency(); //! // Or define it manually if you are using other HAL or know correct frequency: //! let timer_clock_hz = 64_000_000; //! //! // Start the monotonic -//! Mono::start(timer_clock_hz, token); +//! Mono::start(timer_clock_hz); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic -//! let timestamp = Mono::now().ticks(); +//! let timestamp = Mono::now(); //! Mono::delay(100.millis()).await; //! } //! } //! ``` -use crate::{Monotonic, TimeoutError, TimerQueue}; +/// Common definitions and traits for using the STM32 monotonics +pub mod prelude { + #[cfg(feature = "stm32_tim2")] + pub use crate::stm32_tim2_monotonic; + + #[cfg(feature = "stm32_tim3")] + pub use crate::stm32_tim3_monotonic; + + #[cfg(feature = "stm32_tim4")] + pub use crate::stm32_tim4_monotonic; + + #[cfg(feature = "stm32_tim5")] + pub use crate::stm32_tim5_monotonic; + + #[cfg(feature = "stm32_tim15")] + pub use crate::stm32_tim15_monotonic; + + pub use crate::Monotonic; + pub use fugit::{self, ExtU64, ExtU64Ceil}; +} + use atomic_polyfill::{AtomicU64, Ordering}; -pub use fugit::{self, ExtU64, ExtU64Ceil}; -use rtic_time::half_period_counter::calculate_now; +use rtic_time::{ + half_period_counter::calculate_now, + timer_queue::{TimerQueue, TimerQueueBackend}, +}; use stm32_metapac as pac; mod _generated { @@ -48,116 +66,180 @@ mod _generated { include!(concat!(env!("OUT_DIR"), "/_generated.rs")); } -const TIMER_HZ: u32 = 1_000_000; - #[doc(hidden)] #[macro_export] macro_rules! __internal_create_stm32_timer_interrupt { - ($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ + ($mono_backend:ident, $interrupt_name:ident) => { #[no_mangle] #[allow(non_snake_case)] - unsafe extern "C" fn $timer() { - $crate::stm32::$mono_timer::__tq().on_monotonic_interrupt(); + unsafe extern "C" fn $interrupt_name() { + use $crate::TimerQueueBackend; + $crate::stm32::$mono_backend::timer_queue().on_monotonic_interrupt(); + } + }; +} + +#[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< + ::Ticks, + 1, + { $tick_rate_hz }, + >; + type Duration = $crate::fugit::Duration< + ::Ticks, + 1, + { $tick_rate_hz }, + >; + } - unsafe impl $crate::InterruptToken<$crate::stm32::$mono_timer> for $timer_token {} - - $timer_token - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } -/// Register TIM2 interrupt for the monotonic. +/// Create a TIM2 based monotonic and register the TIM2 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim2")] #[macro_export] -macro_rules! create_stm32_tim2_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim2, TIM2, Tim2Token) - }}; +macro_rules! stm32_tim2_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim2Backend, TIM2, $tick_rate_hz); + }; } -/// Register TIM3 interrupt for the monotonic. +/// Create a TIM3 based monotonic and register the TIM3 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim3")] #[macro_export] -macro_rules! create_stm32_tim3_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim3, TIM3, Tim3Token) - }}; +macro_rules! stm32_tim3_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim3Backend, TIM3, $tick_rate_hz); + }; } -/// Register TIM4 interrupt for the monotonic. +/// Create a TIM4 based monotonic and register the TIM4 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim4")] #[macro_export] -macro_rules! create_stm32_tim4_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim4, TIM4, Tim4Token) - }}; +macro_rules! stm32_tim4_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim4Backend, TIM4, $tick_rate_hz); + }; } -/// Register TIM5 interrupt for the monotonic. +/// Create a TIM5 based monotonic and register the TIM5 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim5")] #[macro_export] -macro_rules! create_stm32_tim5_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim5, TIM5, Tim5Token) - }}; +macro_rules! stm32_tim5_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim5Backend, TIM5, $tick_rate_hz); + }; } -/// Register TIM12 interrupt for the monotonic. -#[cfg(feature = "stm32_tim12")] -#[macro_export] -macro_rules! create_stm32_tim12_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim12, TIM12, Tim12Token) - }}; -} - -/// Register TIM15 interrupt for the monotonic. +/// Create a TIM15 based monotonic and register the TIM15 interrupt for it. +/// +/// See [`crate::stm32`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// #[cfg(feature = "stm32_tim15")] #[macro_export] -macro_rules! create_stm32_tim15_monotonic_token { - () => {{ - $crate::__internal_create_stm32_timer_interrupt!(Tim15, TIM15, Tim15Token) - }}; +macro_rules! stm32_tim15_monotonic { + ($name:ident, $tick_rate_hz:expr) => { + $crate::__internal_create_stm32_timer_struct!($name, Tim15Backend, TIM15, $tick_rate_hz); + }; } macro_rules! make_timer { - ($mono_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { - /// Monotonic timer queue implementation. + ($backend_name:ident, $timer:ident, $bits:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { + /// Monotonic timer backend implementation. $( #[cfg_attr(docsrs, doc(cfg($($doc)*)))] )? - pub struct $mono_name; + pub struct $backend_name; use pac::$timer; static $overflow: AtomicU64 = AtomicU64::new(0); - static $tq: TimerQueue<$mono_name> = TimerQueue::new(); + static $tq: TimerQueue<$backend_name> = TimerQueue::new(); - impl $mono_name { - /// Starts the monotonic timer. + impl $backend_name { + /// Starts the timer. /// - /// - `tim_clock_hz`: `TIMx` peripheral clock frequency. - /// - `_interrupt_token`: Required for correct timer interrupt handling. + /// **Do not use this function directly.** /// - /// This method must be called only once. - pub fn start(tim_clock_hz: u32, _interrupt_token: impl crate::InterruptToken) { + /// Use the prelude macros instead. + pub fn _start(tim_clock_hz: u32, timer_hz: u32) { _generated::$timer::enable(); _generated::$timer::reset(); $timer.cr1().modify(|r| r.set_cen(false)); - assert!((tim_clock_hz % TIMER_HZ) == 0, "Unable to find suitable timer prescaler value!"); - let psc = tim_clock_hz / TIMER_HZ - 1; + assert!((tim_clock_hz % timer_hz) == 0, "Unable to find suitable timer prescaler value!"); + let psc = tim_clock_hz / timer_hz - 1; $timer.psc().write(|r| r.set_psc(psc as u16)); // Enable full-period interrupt. $timer.dier().modify(|r| r.set_uie(true)); // Configure and enable half-period interrupt - $timer.ccr(2).write(|r| r.set_ccr($bits::MAX - ($bits::MAX >> 1))); + $timer.ccr(2).write(|r| r.set_ccr(($bits::MAX - ($bits::MAX >> 1)).into())); $timer.dier().modify(|r| r.set_ccie(2, true)); // Trigger an update event to load the prescaler value to the clock. @@ -183,73 +265,31 @@ macro_rules! make_timer { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::$timer); } } - - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue<$mono_name> { - &$tq - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: ::Duration) { - $tq.delay(duration).await; - } - - /// Timeout at a specific time. - pub async fn timeout_at( - instant: ::Instant, - future: F, - ) -> Result { - $tq.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - duration: ::Duration, - future: F, - ) -> Result { - $tq.timeout_after(duration, future).await - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: ::Instant) { - $tq.delay_until(instant).await; - } } - rtic_time::embedded_hal_delay_impl_fugit64!($mono_name); + impl TimerQueueBackend for $backend_name { + type Ticks = u64; - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name); - - impl Monotonic for $mono_name { - type Instant = fugit::TimerInstantU64; - type Duration = fugit::TimerDurationU64; - - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - fn now() -> Self::Instant { - Self::Instant::from_ticks(calculate_now( + fn now() -> Self::Ticks { + calculate_now( || $overflow.load(Ordering::Relaxed), || $timer.cnt().read().cnt() - )) + ) } - fn set_compare(instant: Self::Instant) { + fn set_compare(instant: Self::Ticks) { let now = Self::now(); // Since the timer may or may not overflow based on the requested compare val, we check how many ticks are left. - let val = match instant.checked_duration_since(now) { - None => 0, // In the past - Some(x) if x.ticks() <= ($bits::MAX as u64) => instant.duration_since_epoch().ticks() as $bits, // Will not overflow - Some(_x) => 0, // Will overflow + // `wrapping_sup` takes care of the u64 integer overflow special case. + let val = if instant.wrapping_sub(now) <= ($bits::MAX as u64) { + instant as $bits + } else { + // In the past or will overflow + 0 }; - $timer.ccr(1).write(|r| r.set_ccr(val)); + $timer.ccr(1).write(|r| r.set_ccr(val.into())); } fn clear_compare_flag() { @@ -282,24 +322,25 @@ macro_rules! make_timer { assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!"); } } + + fn timer_queue() -> &'static TimerQueue<$backend_name> { + &$tq + } } }; } #[cfg(feature = "stm32_tim2")] -make_timer!(Tim2, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ); +make_timer!(Tim2Backend, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ); #[cfg(feature = "stm32_tim3")] -make_timer!(Tim3, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ); +make_timer!(Tim3Backend, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ); #[cfg(feature = "stm32_tim4")] -make_timer!(Tim4, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ); +make_timer!(Tim4Backend, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ); #[cfg(feature = "stm32_tim5")] -make_timer!(Tim5, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ); - -#[cfg(feature = "stm32_tim12")] -make_timer!(Tim12, TIM12, u16, TIMER12_OVERFLOWS, TIMER12_TQ); +make_timer!(Tim5Backend, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ); #[cfg(feature = "stm32_tim15")] -make_timer!(Tim15, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ); +make_timer!(Tim15Backend, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ); diff --git a/rtic-monotonics/src/systick.rs b/rtic-monotonics/src/systick.rs index 9bd056ce97..cc6ea3e0e6 100644 --- a/rtic-monotonics/src/systick.rs +++ b/rtic-monotonics/src/systick.rs @@ -1,93 +1,79 @@ -//! [`Monotonic`] based on Cortex-M SysTick. Note: this implementation is inefficient as it +//! [`Monotonic`](rtic_time::Monotonic) based on Cortex-M SysTick. +//! Note: this implementation is inefficient as it //! ticks and generates interrupts at a constant rate. //! -//! Currently, the following tick rates are supported: -//! -//! | Feature | Tick rate | Precision | -//! |:----------------:|----------:|----------:| -//! | (none / default) | 1 kHz | 1 ms | -//! | systick-100hz | 100 Hz | 10 ms | -//! | systick-10khz | 10 kHz | 0.1 ms | - //! # Example //! //! ``` -//! use rtic_monotonics::systick::*; +//! use rtic_monotonics::systick::prelude::*; +//! systick_monotonic!(Mono, 1_000); //! //! fn init() { //! # // This is normally provided by the selected PAC //! # let systick = unsafe { core::mem::transmute(()) }; -//! // Generate the required token -//! let systick_token = rtic_monotonics::create_systick_token!(); -//! +//! # //! // Start the monotonic -//! Systick::start(systick, 12_000_000, systick_token); +//! Mono::start(systick, 12_000_000); //! } //! //! async fn usage() { //! loop { //! // Use the monotonic +//! let timestamp = Mono::now(); //! Systick::delay(100.millis()).await; //! } //! } //! ``` -use super::Monotonic; -pub use super::{TimeoutError, TimerQueue}; +/// Common definitions and traits for using the systick monotonic +pub mod prelude { + pub use crate::systick_monotonic; + + pub use crate::Monotonic; + + cfg_if::cfg_if! { + if #[cfg(feature = "systick-64bit")] { + pub use fugit::{self, ExtU64, ExtU64Ceil}; + } else { + pub use fugit::{self, ExtU32, ExtU32Ceil}; + } + } +} + +pub use cortex_m::peripheral::SYST; + use atomic_polyfill::Ordering; -use core::future::Future; -use cortex_m::peripheral::SYST; -pub use fugit; +use rtic_time::timer_queue::TimerQueue; + +use crate::TimerQueueBackend; + cfg_if::cfg_if! { if #[cfg(feature = "systick-64bit")] { - pub use fugit::{ExtU64, ExtU64Ceil}; use atomic_polyfill::AtomicU64; static SYSTICK_CNT: AtomicU64 = AtomicU64::new(0); } else { - pub use fugit::{ExtU32, ExtU32Ceil}; use atomic_polyfill::AtomicU32; static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); } } -static SYSTICK_TIMER_QUEUE: TimerQueue = TimerQueue::new(); -// Features should be additive, here systick-100hz gets picked if both -// `systick-100hz` and `systick-10khz` are enabled. +static SYSTICK_TIMER_QUEUE: TimerQueue = TimerQueue::new(); -cfg_if::cfg_if! { - if #[cfg(feature = "systick-100hz")] - { - const TIMER_HZ: u32 = 100; - } else if #[cfg(feature = "systick-10khz")] - { - const TIMER_HZ: u32 = 10_000; - } else { - // Default case is 1 kHz - const TIMER_HZ: u32 = 1_000; - } -} +/// Systick based [`TimerQueueBackend`]. +pub struct SystickBackend; -/// Systick implementing [`Monotonic`] which runs at 1 kHz, 100Hz or 10 kHz. -pub struct Systick; - -impl Systick { - /// Start a `Monotonic` based on SysTick. +impl SystickBackend { + /// Starts the monotonic timer. /// - /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from - /// the clock generation function of the used HAL. + /// **Do not use this function directly.** /// - /// Notice that the actual rate of the timer is a best approximation based on the given - /// `sysclk` and `TIMER_HZ`. - /// - /// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue. - pub fn start( - mut systick: cortex_m::peripheral::SYST, - sysclk: u32, - _interrupt_token: impl crate::InterruptToken, - ) { - // + TIMER_HZ / 2 provides round to nearest instead of round to 0. - // - 1 as the counter range is inclusive [0, reload] - let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1; + /// Use the prelude macros instead. + pub fn _start(mut systick: SYST, sysclk: u32, timer_hz: u32) { + assert!( + (sysclk % timer_hz) == 0, + "timer_hz cannot evenly divide sysclk! Please adjust the timer or sysclk frequency." + ); + let reload = sysclk / timer_hz - 1; assert!(reload <= 0x00ff_ffff); assert!(reload > 0); @@ -98,7 +84,7 @@ impl Systick { systick.enable_interrupt(); systick.enable_counter(); - SYSTICK_TIMER_QUEUE.initialize(Systick {}); + SYSTICK_TIMER_QUEUE.initialize(SystickBackend {}); } fn systick() -> SYST { @@ -106,67 +92,24 @@ impl Systick { } } -// Forward timerqueue interface -impl Systick { - /// Used to access the underlying timer queue - #[doc(hidden)] - pub fn __tq() -> &'static TimerQueue { - &SYSTICK_TIMER_QUEUE - } - - /// Timeout at a specific time. - pub async fn timeout_at( - instant: ::Instant, - future: F, - ) -> Result { - SYSTICK_TIMER_QUEUE.timeout_at(instant, future).await - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - duration: ::Duration, - future: F, - ) -> Result { - SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await - } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: ::Duration) { - SYSTICK_TIMER_QUEUE.delay(duration).await; - } - - /// Delay to some specific time instant. - #[inline] - pub async fn delay_until(instant: ::Instant) { - SYSTICK_TIMER_QUEUE.delay_until(instant).await; - } -} - -impl Monotonic for Systick { +impl TimerQueueBackend for SystickBackend { cfg_if::cfg_if! { if #[cfg(feature = "systick-64bit")] { - type Instant = fugit::TimerInstantU64; - type Duration = fugit::TimerDurationU64; + type Ticks = u64; } else { - type Instant = fugit::TimerInstantU32; - type Duration = fugit::TimerDurationU32; + type Ticks = u32; } } - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - fn now() -> Self::Instant { + fn now() -> Self::Ticks { if Self::systick().has_wrapped() { SYSTICK_CNT.fetch_add(1, Ordering::AcqRel); } - Self::Instant::from_ticks(SYSTICK_CNT.load(Ordering::Relaxed)) + SYSTICK_CNT.load(Ordering::Relaxed) } - fn set_compare(_: Self::Instant) { + fn set_compare(_: Self::Ticks) { // No need to do something here, we get interrupts anyway. } @@ -184,39 +127,66 @@ impl Monotonic for Systick { } } - fn enable_timer() {} - - fn disable_timer() {} -} - -cfg_if::cfg_if! { - if #[cfg(feature = "systick-64bit")] { - rtic_time::embedded_hal_delay_impl_fugit64!(Systick); - - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit64!(Systick); - } else { - rtic_time::embedded_hal_delay_impl_fugit32!(Systick); - - #[cfg(feature = "embedded-hal-async")] - rtic_time::embedded_hal_async_delay_impl_fugit32!(Systick); + fn timer_queue() -> &'static TimerQueue { + &SYSTICK_TIMER_QUEUE } } -/// Register the Systick interrupt for the monotonic. +/// Create a Systick based monotonic and register the Systick interrupt for it. +/// +/// See [`crate::systick`] for more details. +/// +/// # Arguments +/// +/// * `name` - The name that the monotonic type will have. +/// * `tick_rate_hz` - The tick rate of the timer peripheral. +/// Can be omitted; defaults to 1kHz. #[macro_export] -macro_rules! create_systick_token { - () => {{ - #[no_mangle] - #[allow(non_snake_case)] - unsafe extern "C" fn SysTick() { - $crate::systick::Systick::__tq().on_monotonic_interrupt(); +macro_rules! systick_monotonic { + ($name:ident) => { + $crate::systick_monotonic($name, 1_000); + }; + ($name:ident, $tick_rate_hz:expr) => { + /// A `Monotonic` based on SysTick. + struct $name; + + impl $name { + /// Starts the `Monotonic`. + /// + /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from + /// the clock generation function of the used HAL. + /// + /// Panics if it is impossible to achieve the desired monotonic tick rate based + /// on the given `sysclk` parameter. If that happens, adjust the desired monotonic tick rate. + /// + /// This method must be called only once. + pub fn start(systick: $crate::systick::SYST, sysclk: u32) { + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn SysTick() { + use $crate::TimerQueueBackend; + $crate::systick::SystickBackend::timer_queue().on_monotonic_interrupt(); + } + + $crate::systick::SystickBackend::_start(systick, sysclk, $tick_rate_hz); + } } - pub struct SystickToken; + impl $crate::TimerQueueBasedMonotonic for $name { + type Backend = $crate::systick::SystickBackend; + type Instant = $crate::fugit::Instant< + ::Ticks, + 1, + { $tick_rate_hz }, + >; + type Duration = $crate::fugit::Duration< + ::Ticks, + 1, + { $tick_rate_hz }, + >; + } - unsafe impl $crate::InterruptToken<$crate::systick::Systick> for SystickToken {} - - SystickToken - }}; + $crate::rtic_time::impl_embedded_hal_delay_fugit!($name); + $crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name); + }; } diff --git a/rtic-time/CHANGELOG.md b/rtic-time/CHANGELOG.md index f3c9792d67..197c426328 100644 --- a/rtic-time/CHANGELOG.md +++ b/rtic-time/CHANGELOG.md @@ -5,11 +5,18 @@ This project adheres to [Semantic Versioning](http://semver.org/). For each category, *Added*, *Changed*, *Fixed* add new entries at the top! -## Unreleased +## Unreleased - v2.0.0 + ### Added ### Changed +- Full rewrite of the `Monotonic` API. + - Now split into multiple traits: + - `Monotonic` - A user-facing trait that defines what the functionality of a monotonic is. + - `TimerQueueBackend` - The set of functionality a backend must provide in order to be used with the `TimerQueue`. + - `TimerQueue` is now purely based on ticks and has no concept of real time. + - The `TimerQueueBasedMonotonic` trait implements a `Monotonic` based on a `TimerQueueBackend`, translating ticks into `Instant` and `Duration`. ### Fixed diff --git a/rtic-time/Cargo.toml b/rtic-time/Cargo.toml index 4ad91cde31..93d3224a7a 100644 --- a/rtic-time/Cargo.toml +++ b/rtic-time/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rtic-time" -version = "1.3.0" +version = "2.0.0" edition = "2021" authors = [ @@ -11,7 +11,7 @@ authors = [ "Per Lindgren ", ] categories = ["concurrency", "embedded", "no-std", "asynchronous"] -description = "rtic-time lib TODO" +description = "Basic definitions and utilities that can be used to keep track of time" license = "MIT OR Apache-2.0" repository = "https://github.com/rtic-rs/rtic" @@ -21,11 +21,11 @@ repository = "https://github.com/rtic-rs/rtic" critical-section = "1" futures-util = { version = "0.3.25", default-features = false } rtic-common = { version = "1.0.0", path = "../rtic-common" } +embedded-hal = { version = "1.0.0" } +embedded-hal-async = { version = "1.0.0" } +fugit = "0.3.7" [dev-dependencies] -embedded-hal = { version = "1.0" } -embedded-hal-async = { version = "1.0" } -fugit = "0.3.7" parking_lot = "0.12" cassette = "0.2" cooked-waker = "5.0.0" diff --git a/rtic-time/README.md b/rtic-time/README.md new file mode 100644 index 0000000000..53c2944119 --- /dev/null +++ b/rtic-time/README.md @@ -0,0 +1,29 @@ +# rtic-time + +Basic definitions and utilities that can be used to keep track of time. + +[![crates.io](https://img.shields.io/crates/v/rtic-time)](https://crates.io/crates/rtic-time) +[![docs.rs](https://docs.rs/rtic-time/badge.svg)](https://docs.rs/rtic-time) +[![matrix](https://img.shields.io/matrix/rtic:matrix.org)](https://matrix.to/#/#rtic:matrix.org) + + +## Content + +The main contribution of this crate is to define the [`Monotonic`](https://docs.rs/rtic-time/latest/rtic_time/trait.Monotonic.html) trait. It serves as a standardized interface for libraries to interact with the system's monotonic timers. + +Additionally, this crate provides tools and utilities that help with implementing monotonic timers. + +## Implementations of the `Monotonic` trait + +For implementations of [`Monotonic`](https://docs.rs/rtic-time/latest/rtic_time/trait.Monotonic.html) +on various hardware, see [`rtic-monotonics`](https://docs.rs/rtic-monotonics/). + + +## Chat + +Join us and talk about RTIC in the [Matrix room][matrix-room]. + +Weekly meeting minutes can be found over at [RTIC HackMD][hackmd]. + +[matrix-room]: https://matrix.to/#/#rtic:matrix.org +[hackmd]: https://rtic.rs/meeting diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index 9cd20d5f22..7b051dabb8 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -5,285 +5,60 @@ #![no_std] #![deny(missing_docs)] -#![allow(incomplete_features)] - -use core::future::{poll_fn, Future}; -use core::pin::Pin; -use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use core::task::{Poll, Waker}; -use futures_util::{ - future::{select, Either}, - pin_mut, -}; -use linked_list::{Link, LinkedList}; -pub use monotonic::Monotonic; -use rtic_common::dropper::OnDrop; +#![allow(async_fn_in_trait)] pub mod half_period_counter; mod linked_list; -mod monotonic; - -/// Holds a waker and at which time instant this waker shall be awoken. -struct WaitingWaker { - waker: Waker, - release_at: Mono::Instant, - was_popped: AtomicBool, -} - -impl Clone for WaitingWaker { - 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 PartialEq for WaitingWaker { - fn eq(&self, other: &Self) -> bool { - self.release_at == other.release_at - } -} - -impl PartialOrd for WaitingWaker { - fn partial_cmp(&self, other: &Self) -> Option { - 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 { - queue: LinkedList>, - initialized: AtomicBool, -} +pub mod monotonic; +pub mod timer_queue; /// This indicates that there was a timeout. pub struct TimeoutError; -/// This is needed to make the async closure in `delay_until` accept that we "share" -/// the link possible between threads. -struct LinkPtr(*mut Option>>); +/// Re-export for macros +pub use embedded_hal; +/// Re-export for macros +pub use embedded_hal_async; -impl Clone for LinkPtr { - fn clone(&self) -> Self { - LinkPtr(self.0) - } -} - -impl LinkPtr { - /// This will dereference the pointer stored within and give out an `&mut`. - unsafe fn get(&mut self) -> &mut Option>> { - &mut *self.0 - } -} - -unsafe impl Send for LinkPtr {} -unsafe impl Sync for LinkPtr {} - -impl TimerQueue { - /// 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` +/// # A monotonic clock / counter definition. +/// +/// ## Correctness +/// +/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This +/// is a requirement on the time library that the user chooses to use. +pub trait Monotonic { + /// The type for instant, defining an instant in time. /// - /// # Safety + /// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used. + type Instant: Ord + + Copy + + core::ops::Add + + core::ops::Sub + + core::ops::Sub; + + /// The type for duration, defining a duration of time. /// - /// It's always safe to call, but it must only be called from the interrupt of the - /// monotonic timer for correct operation. - pub unsafe fn on_monotonic_interrupt(&self) { - Mono::clear_compare_flag(); - Mono::on_interrupt(); + /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used. + type Duration: Copy; - loop { - let mut release_at = None; - let head = self.queue.pop_if(|head| { - release_at = Some(head.release_at); + /// Get the current time. + fn now() -> Self::Instant; - let should_pop = Mono::now() >= 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)) => { - 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( - &self, - instant: Mono::Instant, - future: F, - ) -> Result { - 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( - &self, - duration: Mono::Duration, - future: F, - ) -> Result { - 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 for some duration of time. + async fn delay(duration: Self::Duration); /// Delay to some specific time instant. - pub async fn delay_until(&self, instant: Mono::Instant) { - if !self.initialized.load(Ordering::Relaxed) { - panic!( - "The timer queue is not initialized with a monotonic, you need to run `initialize`" - ); - } + async fn delay_until(instant: Self::Instant); - let mut link_ptr: Option>> = None; + /// Timeout at a specific time. + async fn timeout_at( + instant: Self::Instant, + future: F, + ) -> Result; - // 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>>); - let mut link_ptr2 = link_ptr.clone(); - - let queue = &self.queue; - let marker = &AtomicUsize::new(0); - - let dropper = OnDrop::new(|| { - queue.delete(marker.load(Ordering::Relaxed)); - }); - - poll_fn(|cx| { - if Mono::now() >= instant { - return Poll::Ready(()); - } - - // SAFETY: This pointer is only dereferenced here and on drop of the future - // which happens outside this `poll_fn`'s stack frame, so this mutable access cannot - // happen at the same time as `dropper` runs. - let link = unsafe { link_ptr2.get() }; - if link.is_none() { - let link_ref = link.insert(Link::new(WaitingWaker { - waker: cx.waker().clone(), - release_at: instant, - was_popped: AtomicBool::new(false), - })); - - // SAFETY(new_unchecked): The address to the link is stable as it is defined - //outside this stack frame. - // SAFETY(insert): `link_ref` lifetime comes from `link_ptr` that is shadowed, and - // we make sure in `dropper` that the link is removed from the queue before - // dropping `link_ptr` AND `dropper` makes sure that the shadowed `link_ptr` lives - // until the end of the stack frame. - let (head_updated, addr) = unsafe { queue.insert(Pin::new_unchecked(link_ref)) }; - - marker.store(addr, Ordering::Relaxed); - - if head_updated { - // Pend the monotonic handler if the queue head was updated. - Mono::pend_interrupt() - } - } - - Poll::Pending - }) - .await; - - // SAFETY: We only run this and dereference the pointer if we have - // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference - // of this pointer is in the `poll_fn`. - if let Some(link) = unsafe { link_ptr.get() } { - if link.val.was_popped.load(Ordering::Relaxed) { - // If it was popped from the queue there is no need to run delete - dropper.defuse(); - } - } else { - // Make sure that our link is deleted from the list before we drop this stack - drop(dropper); - } - } + /// Timeout after a specific duration. + async fn timeout_after( + duration: Self::Duration, + future: F, + ) -> Result; } diff --git a/rtic-time/src/monotonic.rs b/rtic-time/src/monotonic.rs index e6a160d9d6..7c9d915e54 100644 --- a/rtic-time/src/monotonic.rs +++ b/rtic-time/src/monotonic.rs @@ -1,236 +1,8 @@ -//! A monotonic clock / counter definition. +//! Structs and traits surrounding the [`Monotonic`](crate::Monotonic) trait. -/// # A monotonic clock / counter definition. -/// -/// ## Correctness -/// -/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This -/// is a requirement on the time library that the user chooses to use. -pub trait Monotonic { - /// The time at time zero. - const ZERO: Self::Instant; +pub use timer_queue_based_monotonic::{ + TimerQueueBasedDuration, TimerQueueBasedInstant, TimerQueueBasedMonotonic, +}; - /// The duration between two timer ticks. - const TICK_PERIOD: Self::Duration; - - /// The type for instant, defining an instant in time. - /// - /// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used. - type Instant: Ord - + Copy - + core::ops::Add - + core::ops::Sub - + core::ops::Sub; - - /// The type for duration, defining an duration of time. - /// - /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used. - type Duration; - - /// Get the current time. - fn now() -> Self::Instant; - - /// Set the compare value of the timer interrupt. - /// - /// **Note:** This method does not need to handle race conditions of the monotonic, the timer - /// queue in RTIC checks this. - fn set_compare(instant: Self::Instant); - - /// This method used to be required by an errata workaround - /// for the nrf52 family, but it has been disabled as the - /// workaround was erroneous. - #[deprecated( - since = "1.2.0", - note = "this method is erroneous and has been disabled" - )] - fn should_dequeue_check(_: Self::Instant) -> bool { - panic!("This method should not be used as it is erroneous.") - } - - /// Clear the compare interrupt flag. - fn clear_compare_flag(); - - /// Pend the timer's interrupt. - fn pend_interrupt(); - - /// Optional. Runs on interrupt before any timer queue handling. - fn on_interrupt() {} - - /// Optional. This is used to save power, this is called when the timer queue is not empty. - /// - /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant - /// based of `now()` is still valid. - /// - /// NOTE: This may be called more than once. - fn enable_timer() {} - - /// Optional. This is used to save power, this is called when the timer queue is empty. - /// - /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant - /// based of `now()` is still valid. - /// - /// NOTE: This may be called more than once. - fn disable_timer() {} -} - -/// Creates impl blocks for [`embedded_hal::delay::DelayNs`][DelayNs], -/// based on [`fugit::ExtU64Ceil`][ExtU64Ceil]. -/// -/// [DelayNs]: https://docs.rs/embedded-hal/latest/embedded_hal/delay/trait.DelayNs.html -/// [ExtU64Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU64Ceil.html -#[macro_export] -macro_rules! embedded_hal_delay_impl_fugit64 { - ($t:ty) => { - impl ::embedded_hal::delay::DelayNs for $t { - fn delay_ns(&mut self, ns: u32) { - use ::fugit::ExtU64Ceil; - - let now = Self::now(); - let mut done = now + u64::from(ns).nanos_at_least(); - if now != done { - // Compensate for sub-tick uncertainty - done += Self::TICK_PERIOD; - } - - while Self::now() < done {} - } - - fn delay_us(&mut self, us: u32) { - use ::fugit::ExtU64Ceil; - - let now = Self::now(); - let mut done = now + u64::from(us).micros_at_least(); - if now != done { - // Compensate for sub-tick uncertainty - done += Self::TICK_PERIOD; - } - - while Self::now() < done {} - } - - fn delay_ms(&mut self, ms: u32) { - use ::fugit::ExtU64Ceil; - - let now = Self::now(); - let mut done = now + u64::from(ms).millis_at_least(); - if now != done { - // Compensate for sub-tick uncertainty - done += Self::TICK_PERIOD; - } - - while Self::now() < done {} - } - } - }; -} - -/// Creates impl blocks for [`embedded_hal_async::delay::DelayNs`][DelayNs], -/// based on [`fugit::ExtU64Ceil`][ExtU64Ceil]. -/// -/// [DelayNs]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/trait.DelayNs.html -/// [ExtU64Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU64Ceil.html -#[macro_export] -macro_rules! embedded_hal_async_delay_impl_fugit64 { - ($t:ty) => { - impl ::embedded_hal_async::delay::DelayNs for $t { - #[inline] - async fn delay_ns(&mut self, ns: u32) { - use ::fugit::ExtU64Ceil; - Self::delay(u64::from(ns).nanos_at_least()).await; - } - - #[inline] - async fn delay_us(&mut self, us: u32) { - use ::fugit::ExtU64Ceil; - Self::delay(u64::from(us).micros_at_least()).await; - } - - #[inline] - async fn delay_ms(&mut self, ms: u32) { - use ::fugit::ExtU64Ceil; - Self::delay(u64::from(ms).millis_at_least()).await; - } - } - }; -} - -/// Creates impl blocks for [`embedded_hal::delay::DelayNs`][DelayNs], -/// based on [`fugit::ExtU32Ceil`][ExtU32Ceil]. -/// -/// [DelayNs]: https://docs.rs/embedded-hal/latest/embedded_hal/delay/trait.DelayNs.html -/// [ExtU32Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU32Ceil.html -#[macro_export] -macro_rules! embedded_hal_delay_impl_fugit32 { - ($t:ty) => { - impl ::embedded_hal::delay::DelayNs for $t { - fn delay_ns(&mut self, ns: u32) { - use ::fugit::ExtU32Ceil; - - let now = Self::now(); - let mut done = now + ns.nanos_at_least(); - if now != done { - // Compensate for sub-tick uncertainty - done += Self::TICK_PERIOD; - } - - while Self::now() < done {} - } - - fn delay_us(&mut self, us: u32) { - use ::fugit::ExtU32Ceil; - - let now = Self::now(); - let mut done = now + us.micros_at_least(); - if now != done { - // Compensate for sub-tick uncertainty - done += Self::TICK_PERIOD; - } - - while Self::now() < done {} - } - - fn delay_ms(&mut self, ms: u32) { - use ::fugit::ExtU32Ceil; - - let now = Self::now(); - let mut done = now + ms.millis_at_least(); - if now != done { - // Compensate for sub-tick uncertainty - done += Self::TICK_PERIOD; - } - - while Self::now() < done {} - } - } - }; -} - -/// Creates impl blocks for [`embedded_hal_async::delay::DelayNs`][DelayNs], -/// based on [`fugit::ExtU32Ceil`][ExtU32Ceil]. -/// -/// [DelayNs]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/trait.DelayNs.html -/// [ExtU32Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU32Ceil.html -#[macro_export] -macro_rules! embedded_hal_async_delay_impl_fugit32 { - ($t:ty) => { - impl ::embedded_hal_async::delay::DelayNs for $t { - #[inline] - async fn delay_ns(&mut self, ns: u32) { - use ::fugit::ExtU32Ceil; - Self::delay(ns.nanos_at_least()).await; - } - - #[inline] - async fn delay_us(&mut self, us: u32) { - use ::fugit::ExtU32Ceil; - Self::delay(us.micros_at_least()).await; - } - - #[inline] - async fn delay_ms(&mut self, ms: u32) { - use ::fugit::ExtU32Ceil; - Self::delay(ms.millis_at_least()).await; - } - } - }; -} +mod embedded_hal_macros; +mod timer_queue_based_monotonic; diff --git a/rtic-time/src/monotonic/embedded_hal_macros.rs b/rtic-time/src/monotonic/embedded_hal_macros.rs new file mode 100644 index 0000000000..25ac791739 --- /dev/null +++ b/rtic-time/src/monotonic/embedded_hal_macros.rs @@ -0,0 +1,77 @@ +//! Macros that implement embedded-hal traits for Monotonics + +/// Implements [`embedded_hal::delay::DelayNs`] for a given monotonic. +#[macro_export] +macro_rules! impl_embedded_hal_delay_fugit { + ($t:ty) => { + impl $crate::embedded_hal::delay::DelayNs for $t { + fn delay_ns(&mut self, ns: u32) { + let now = ::now(); + let mut done = + now + ::Duration::nanos_at_least(ns.into()); + if now != done { + // Compensate for sub-tick uncertainty + done = done + ::Duration::from_ticks(1); + } + + while ::now() < done {} + } + + fn delay_us(&mut self, us: u32) { + let now = ::now(); + let mut done = + now + ::Duration::micros_at_least(us.into()); + if now != done { + // Compensate for sub-tick uncertainty + done = done + ::Duration::from_ticks(1); + } + + while ::now() < done {} + } + + fn delay_ms(&mut self, ms: u32) { + let now = ::now(); + let mut done = + now + ::Duration::millis_at_least(ms.into()); + if now != done { + // Compensate for sub-tick uncertainty + done = done + ::Duration::from_ticks(1); + } + + while ::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) { + ::delay( + ::Duration::nanos_at_least(ns.into()), + ) + .await; + } + + #[inline] + async fn delay_us(&mut self, us: u32) { + ::delay( + ::Duration::micros_at_least(us.into()), + ) + .await; + } + + #[inline] + async fn delay_ms(&mut self, ms: u32) { + ::delay( + ::Duration::millis_at_least(ms.into()), + ) + .await; + } + } + }; +} diff --git a/rtic-time/src/monotonic/timer_queue_based_monotonic.rs b/rtic-time/src/monotonic/timer_queue_based_monotonic.rs new file mode 100644 index 0000000000..699958bccb --- /dev/null +++ b/rtic-time/src/monotonic/timer_queue_based_monotonic.rs @@ -0,0 +1,113 @@ +use crate::{timer_queue::TimerQueueBackend, TimeoutError}; + +use crate::Monotonic; + +/// A [`Monotonic`] that is backed by the [`TimerQueue`](crate::timer_queue::TimerQueue). +pub trait TimerQueueBasedMonotonic { + /// The backend for the timer queue + type Backend: TimerQueueBackend; + + /// The type for instant, defining an instant in time. + /// + /// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used. + type Instant: TimerQueueBasedInstant::Ticks> + + core::ops::Add + + core::ops::Sub + + core::ops::Sub; + + /// 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>; +} + +impl 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( + instant: Self::Instant, + future: F, + ) -> Result { + T::Backend::timer_queue() + .timeout_at(instant.ticks(), future) + .await + } + + async fn timeout_after( + duration: Self::Duration, + future: F, + ) -> Result { + 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 TimerQueueBasedInstant for fugit::Instant { + type Ticks = u64; + fn from_ticks(ticks: Self::Ticks) -> Self { + Self::from_ticks(ticks) + } + fn ticks(self) -> Self::Ticks { + Self::ticks(&self) + } +} + +impl TimerQueueBasedInstant for fugit::Instant { + type Ticks = u32; + fn from_ticks(ticks: Self::Ticks) -> Self { + Self::from_ticks(ticks) + } + fn ticks(self) -> Self::Ticks { + Self::ticks(&self) + } +} + +impl TimerQueueBasedDuration + for fugit::Duration +{ + type Ticks = u64; + fn ticks(self) -> Self::Ticks { + Self::ticks(&self) + } +} + +impl TimerQueueBasedDuration + for fugit::Duration +{ + type Ticks = u32; + fn ticks(self) -> Self::Ticks { + Self::ticks(&self) + } +} diff --git a/rtic-time/src/timer_queue.rs b/rtic-time/src/timer_queue.rs new file mode 100644 index 0000000000..ea2c806a1c --- /dev/null +++ b/rtic-time/src/timer_queue.rs @@ -0,0 +1,281 @@ +//! A generic timer queue for async executors. + +use crate::linked_list::{self, Link, LinkedList}; +use crate::TimeoutError; + +use core::future::{poll_fn, Future}; +use core::pin::Pin; +use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use core::task::{Poll, Waker}; +use futures_util::{ + future::{select, Either}, + pin_mut, +}; +use rtic_common::dropper::OnDrop; + +mod backend; +mod tick_type; +pub use backend::TimerQueueBackend; +pub use tick_type::TimerQueueTicks; + +/// Holds a waker and at which time instant this waker shall be awoken. +struct WaitingWaker { + waker: Waker, + release_at: Backend::Ticks, + was_popped: AtomicBool, +} + +impl Clone for WaitingWaker { + 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 PartialEq for WaitingWaker { + fn eq(&self, other: &Self) -> bool { + self.release_at == other.release_at + } +} + +impl PartialOrd for WaitingWaker { + fn partial_cmp(&self, other: &Self) -> Option { + 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 { + queue: LinkedList>, + initialized: AtomicBool, +} + +/// This is needed to make the async closure in `delay_until` accept that we "share" +/// the link possible between threads. +struct LinkPtr(*mut Option>>); + +impl Clone for LinkPtr { + fn clone(&self) -> Self { + LinkPtr(self.0) + } +} + +impl LinkPtr { + /// This will dereference the pointer stored within and give out an `&mut`. + unsafe fn get(&mut self) -> &mut Option>> { + &mut *self.0 + } +} + +unsafe impl Send for LinkPtr {} +unsafe impl Sync for LinkPtr {} + +impl TimerQueue { + /// 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( + &self, + instant: Backend::Ticks, + future: F, + ) -> Result { + 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( + &self, + duration: Backend::Ticks, + future: F, + ) -> Result { + 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>> = 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>>); + let mut link_ptr2 = link_ptr.clone(); + + let queue = &self.queue; + let marker = &AtomicUsize::new(0); + + let dropper = OnDrop::new(|| { + queue.delete(marker.load(Ordering::Relaxed)); + }); + + poll_fn(|cx| { + if Backend::now().is_at_least(instant) { + return Poll::Ready(()); + } + + // SAFETY: This pointer is only dereferenced here and on drop of the future + // which happens outside this `poll_fn`'s stack frame, so this mutable access cannot + // happen at the same time as `dropper` runs. + let link = unsafe { link_ptr2.get() }; + if link.is_none() { + let link_ref = link.insert(Link::new(WaitingWaker { + waker: cx.waker().clone(), + release_at: instant, + was_popped: AtomicBool::new(false), + })); + + // SAFETY(new_unchecked): The address to the link is stable as it is defined + //outside this stack frame. + // SAFETY(insert): `link_ref` lifetime comes from `link_ptr` that is shadowed, and + // we make sure in `dropper` that the link is removed from the queue before + // dropping `link_ptr` AND `dropper` makes sure that the shadowed `link_ptr` lives + // until the end of the stack frame. + let (head_updated, addr) = unsafe { queue.insert(Pin::new_unchecked(link_ref)) }; + + marker.store(addr, Ordering::Relaxed); + + if head_updated { + // Pend the monotonic handler if the queue head was updated. + Backend::pend_interrupt() + } + } + + Poll::Pending + }) + .await; + + // SAFETY: We only run this and dereference the pointer if we have + // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference + // of this pointer is in the `poll_fn`. + if let Some(link) = unsafe { link_ptr.get() } { + if link.val.was_popped.load(Ordering::Relaxed) { + // If it was popped from the queue there is no need to run delete + dropper.defuse(); + } + } else { + // Make sure that our link is deleted from the list before we drop this stack + drop(dropper); + } + } +} diff --git a/rtic-time/src/timer_queue/backend.rs b/rtic-time/src/timer_queue/backend.rs new file mode 100644 index 0000000000..5b71b7c3b4 --- /dev/null +++ b/rtic-time/src/timer_queue/backend.rs @@ -0,0 +1,44 @@ +use super::{TimerQueue, TimerQueueTicks}; + +/// A backend definition for a monotonic clock/counter. +pub trait TimerQueueBackend: 'static + Sized { + /// The type for ticks. + type Ticks: TimerQueueTicks; + + /// Get the current time. + fn now() -> Self::Ticks; + + /// Set the compare value of the timer interrupt. + /// + /// **Note:** This method does not need to handle race conditions of the monotonic, the timer + /// queue in RTIC checks this. + fn set_compare(instant: Self::Ticks); + + /// Clear the compare interrupt flag. + fn clear_compare_flag(); + + /// Pend the timer's interrupt. + fn pend_interrupt(); + + /// Optional. Runs on interrupt before any timer queue handling. + fn on_interrupt() {} + + /// Optional. This is used to save power, this is called when the timer queue is not empty. + /// + /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant + /// based of `now()` is still valid. + /// + /// NOTE: This may be called more than once. + fn enable_timer() {} + + /// Optional. This is used to save power, this is called when the timer queue is empty. + /// + /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant + /// based of `now()` is still valid. + /// + /// NOTE: This may be called more than once. + fn disable_timer() {} + + /// Returns a reference to the underlying timer queue. + fn timer_queue() -> &'static TimerQueue; +} diff --git a/rtic-time/src/timer_queue/tick_type.rs b/rtic-time/src/timer_queue/tick_type.rs new file mode 100644 index 0000000000..571dfc24a3 --- /dev/null +++ b/rtic-time/src/timer_queue/tick_type.rs @@ -0,0 +1,49 @@ +use core::cmp; + +/// The ticks of a timer. +pub trait TimerQueueTicks: Copy + PartialEq + Eq { + /// Represents a single tick. + const ONE_TICK: Self; + + /// Compares to another tick count. + /// + /// Takes into account timer wrapping; if the difference is more than + /// half the value range, the result will be flipped. + fn compare(self, other: Self) -> cmp::Ordering; + + /// True if `self` is at the same time as `other` or later. + /// + /// Takes into account timer wrapping; if the difference is more than + /// half the value range, the result will be negated. + fn is_at_least(self, other: Self) -> bool { + match self.compare(other) { + cmp::Ordering::Less => false, + cmp::Ordering::Equal => true, + cmp::Ordering::Greater => true, + } + } + + /// Wrapping addition. + fn wrapping_add(self, other: Self) -> Self; +} + +impl TimerQueueTicks for u32 { + const ONE_TICK: Self = 1; + + fn compare(self, other: Self) -> cmp::Ordering { + (self.wrapping_sub(other) as i32).cmp(&0) + } + fn wrapping_add(self, other: Self) -> Self { + u32::wrapping_add(self, other) + } +} +impl TimerQueueTicks for u64 { + const ONE_TICK: Self = 1; + + fn compare(self, other: Self) -> cmp::Ordering { + (self.wrapping_sub(other) as i64).cmp(&0) + } + fn wrapping_add(self, other: Self) -> Self { + u64::wrapping_add(self, other) + } +} diff --git a/rtic-time/tests/delay_precision_subtick.rs b/rtic-time/tests/delay_precision_subtick.rs index a4ef4bbe91..a415b65216 100644 --- a/rtic-time/tests/delay_precision_subtick.rs +++ b/rtic-time/tests/delay_precision_subtick.rs @@ -15,46 +15,31 @@ use std::{ time::Duration, }; -use ::fugit::ExtU64Ceil; use cooked_waker::{IntoWaker, WakeRef}; +use fugit::ExtU64Ceil; use parking_lot::Mutex; -use rtic_time::{Monotonic, TimeoutError, TimerQueue}; +use rtic_time::{ + monotonic::TimerQueueBasedMonotonic, + timer_queue::{TimerQueue, TimerQueueBackend}, + Monotonic, +}; const SUBTICKS_PER_TICK: u32 = 10; struct SubtickTestTimer; -static TIMER_QUEUE: TimerQueue = TimerQueue::new(); +struct SubtickTestTimerBackend; +static TIMER_QUEUE: TimerQueue = TimerQueue::new(); static NOW_SUBTICKS: AtomicU64 = AtomicU64::new(0); static COMPARE_TICKS: Mutex> = Mutex::new(None); -impl Monotonic for SubtickTestTimer { - const ZERO: Self::Instant = Self::Instant::from_ticks(0); - const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1); - - type Instant = fugit::Instant; - type Duration = fugit::Duration; - - fn now() -> Self::Instant { - Self::Instant::from_ticks( - NOW_SUBTICKS.load(Ordering::Relaxed) / u64::from(SUBTICKS_PER_TICK), - ) - } - - fn set_compare(instant: Self::Instant) { - *COMPARE_TICKS.lock() = Some(instant.ticks()); - } - - fn clear_compare_flag() {} - - fn pend_interrupt() { - unsafe { - Self::__tq().on_monotonic_interrupt(); - } +impl SubtickTestTimer { + pub fn init() { + SubtickTestTimerBackend::init(); } } -impl SubtickTestTimer { - pub fn init() { - Self::__tq().initialize(Self) +impl SubtickTestTimerBackend { + fn init() { + Self::timer_queue().initialize(Self) } pub fn tick() -> u64 { @@ -70,7 +55,7 @@ impl SubtickTestTimer { // ); if subticks == 0 && Some(ticks) == *compare { unsafe { - Self::__tq().on_monotonic_interrupt(); + Self::timer_queue().on_monotonic_interrupt(); } } @@ -85,29 +70,41 @@ impl SubtickTestTimer { pub fn now_subticks() -> u64 { NOW_SUBTICKS.load(Ordering::Relaxed) } +} - fn __tq() -> &'static TimerQueue { +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 { &TIMER_QUEUE } - - /// Delay for some duration of time. - #[inline] - pub async fn delay(duration: ::Duration) { - Self::__tq().delay(duration).await; - } - - /// Timeout after a specific duration. - #[inline] - pub async fn timeout_after( - duration: ::Duration, - future: F, - ) -> Result { - Self::__tq().timeout_after(duration, future).await - } } -rtic_time::embedded_hal_delay_impl_fugit64!(SubtickTestTimer); -rtic_time::embedded_hal_async_delay_impl_fugit64!(SubtickTestTimer); +impl TimerQueueBasedMonotonic for SubtickTestTimer { + type Backend = SubtickTestTimerBackend; + + type Instant = fugit::Instant; + type Duration = fugit::Duration; +} + +rtic_time::impl_embedded_hal_delay_fugit!(SubtickTestTimer); +rtic_time::impl_embedded_hal_async_delay_fugit!(SubtickTestTimer); // A simple struct that counts the number of times it is awoken. Can't // be awoken by value (because that would discard the counter), so we @@ -144,7 +141,7 @@ impl Drop for OnDrop { macro_rules! subtick_test { (@run $start:expr, $actual_duration:expr, $delay_fn:expr) => {{ // forward clock to $start - SubtickTestTimer::forward_to_subtick($start); + SubtickTestTimerBackend::forward_to_subtick($start); // call wait function let delay_fn = $delay_fn; @@ -164,7 +161,7 @@ macro_rules! subtick_test { }; assert_eq!(wakecounter.get(), 0); - SubtickTestTimer::tick(); + SubtickTestTimerBackend::tick(); } let expected_wakeups = { @@ -177,7 +174,7 @@ macro_rules! subtick_test { assert_eq!(wakecounter.get(), expected_wakeups); // Tick again to test that we don't get a second wake - SubtickTestTimer::tick(); + SubtickTestTimerBackend::tick(); assert_eq!(wakecounter.get(), expected_wakeups); assert_eq!( @@ -191,9 +188,9 @@ macro_rules! subtick_test { (@run_blocking $start:expr, $actual_duration:expr, $delay_fn:expr) => {{ // forward clock to $start - SubtickTestTimer::forward_to_subtick($start); + SubtickTestTimerBackend::forward_to_subtick($start); - let t_start = SubtickTestTimer::now_subticks(); + let t_start = SubtickTestTimerBackend::now_subticks(); let finished = AtomicBool::new(false); std::thread::scope(|s|{ @@ -204,13 +201,13 @@ macro_rules! subtick_test { s.spawn(||{ sleep(Duration::from_millis(10)); while !finished.load(Ordering::Relaxed) { - SubtickTestTimer::tick(); + SubtickTestTimerBackend::tick(); sleep(Duration::from_millis(10)); } }); }); - let t_end = SubtickTestTimer::now_subticks(); + let t_end = SubtickTestTimerBackend::now_subticks(); let measured_duration = t_end - t_start; assert_eq!( $actual_duration, diff --git a/rtic-time/tests/timer_queue.rs b/rtic-time/tests/timer_queue.rs index 8bae385355..2ef7ce363c 100644 --- a/rtic-time/tests/timer_queue.rs +++ b/rtic-time/tests/timer_queue.rs @@ -2,63 +2,24 @@ //! //! To run this test, you need to activate the `critical-section/std` feature. -use std::{ - fmt::Debug, - task::{Poll, Waker}, -}; - use cassette::Cassette; use parking_lot::Mutex; -use rtic_time::{Monotonic, TimerQueue}; +use rtic_time::timer_queue::{TimerQueue, TimerQueueBackend}; -static NOW: Mutex> = 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)] -pub struct Duration(u64); + use super::TestMonoBackend; -impl Duration { - pub const fn from_ticks(millis: u64) -> Self { - Self(millis) - } - - pub fn as_ticks(&self) -> u64 { - self.0 - } -} - -impl core::ops::Add for Duration { - type Output = Duration; - - fn add(self, rhs: Duration) -> Self::Output { - Self(self.0 + rhs.0) - } -} - -impl From for Instant { - fn from(value: Duration) -> Self { - Instant(value.0) - } -} - -static WAKERS: Mutex> = Mutex::new(Vec::new()); - -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] -pub struct Instant(u64); - -impl Instant { - const ZERO: Self = Self(0); + static NOW: AtomicU64 = AtomicU64::new(0); + static WAKERS: Mutex> = Mutex::new(Vec::new()); pub fn tick() -> bool { - // If we've never ticked before, initialize the clock. - if NOW.lock().is_none() { - *NOW.lock() = Some(Instant::ZERO); - } - // We've ticked before, add one to the clock - else { - let now = Instant::now(); - let new_time = now + Duration(1); - *NOW.lock() = Some(new_time); - } + NOW.fetch_add(1, Ordering::Release); let had_wakers = !WAKERS.lock().is_empty(); // Wake up all things waiting for a specific time to happen. @@ -66,22 +27,18 @@ impl Instant { waker.wake_by_ref(); } - let had_interrupt = TestMono::tick(false); + let had_interrupt = TestMonoBackend::tick(false); had_interrupt || had_wakers } - pub fn now() -> Self { - NOW.lock().clone().unwrap_or(Instant::ZERO) + pub fn now() -> u64 { + NOW.load(Ordering::Acquire) } - pub fn elapsed(&self) -> Duration { - Duration(Self::now().0 - self.0) - } - - pub async fn wait_until(time: Instant) { + pub async fn wait_until(time: u64) { core::future::poll_fn(|ctx| { - if Instant::now() >= time { + if now() >= time { Poll::Ready(()) } else { WAKERS.lock().push(ctx.waker().clone()); @@ -92,51 +49,21 @@ impl Instant { } } -impl From for Instant { - fn from(value: u64) -> Self { - Self(value) - } -} +static COMPARE: Mutex> = Mutex::new(None); +static TIMER_QUEUE: TimerQueue = TimerQueue::new(); -impl core::ops::Add for Instant { - type Output = Instant; +pub struct TestMonoBackend; - fn add(self, rhs: Duration) -> Self::Output { - Self(self.0 + rhs.0) - } -} - -impl core::ops::Sub for Instant { - type Output = Instant; - - fn sub(self, rhs: Duration) -> Self::Output { - Self(self.0 - rhs.0) - } -} - -impl core::ops::Sub for Instant { - type Output = Duration; - - fn sub(self, rhs: Instant) -> Self::Output { - Duration(self.0 - rhs.0) - } -} - -static COMPARE: Mutex> = Mutex::new(None); -static TIMER_QUEUE: TimerQueue = TimerQueue::new(); - -pub struct TestMono; - -impl TestMono { +impl TestMonoBackend { pub fn tick(force_interrupt: bool) -> bool { - let now = Instant::now(); + let now = peripheral::now(); let compare_reached = Some(now) == Self::compare(); let interrupt = compare_reached || force_interrupt; if interrupt { unsafe { - TestMono::queue().on_monotonic_interrupt(); + TestMonoBackend::timer_queue().on_monotonic_interrupt(); } true } else { @@ -144,35 +71,26 @@ impl TestMono { } } - /// Initialize the monotonic. - pub fn init() { - Self::queue().initialize(Self); - } - - /// Used to access the underlying timer queue - pub fn queue() -> &'static TimerQueue { - &TIMER_QUEUE - } - - pub fn compare() -> Option { + pub fn compare() -> Option { COMPARE.lock().clone() } } -impl Monotonic for TestMono { - const ZERO: Self::Instant = Instant::ZERO; - const TICK_PERIOD: Self::Duration = Duration::from_ticks(1); +impl TestMonoBackend { + fn init() { + Self::timer_queue().initialize(Self); + } +} - type Instant = Instant; +impl TimerQueueBackend for TestMonoBackend { + type Ticks = u64; - type Duration = Duration; - - fn now() -> Self::Instant { - Instant::now() + fn now() -> Self::Ticks { + peripheral::now() } - fn set_compare(instant: Self::Instant) { - let _ = COMPARE.lock().insert(instant); + fn set_compare(instant: Self::Ticks) { + *COMPARE.lock() = Some(instant); } fn clear_compare_flag() {} @@ -180,42 +98,40 @@ impl Monotonic for TestMono { fn pend_interrupt() { Self::tick(true); } + + fn timer_queue() -> &'static TimerQueue { + &TIMER_QUEUE + } } #[test] fn timer_queue() { - TestMono::init(); - let start = Instant::ZERO; + TestMonoBackend::init(); + let start = 0; let build_delay_test = |pre_delay: Option, delay: u64| { - let delay = Duration::from_ticks(delay); - let pre_delay = pre_delay.map(Duration::from_ticks); - let total = if let Some(pre_delay) = pre_delay { pre_delay + delay } else { delay }; - let total_millis = total.as_ticks(); async move { // A `pre_delay` simulates a delay in scheduling, // without the `pre_delay` being present in the timer // queue if let Some(pre_delay) = pre_delay { - Instant::wait_until(start + pre_delay).await; + peripheral::wait_until(start + pre_delay).await; } - TestMono::queue().delay(delay).await; + TestMonoBackend::timer_queue().delay(delay).await; - let elapsed = start.elapsed().as_ticks(); - println!("{total_millis} ticks delay reached after {elapsed} ticks"); + let elapsed = peripheral::now() - start; + println!("{total} ticks delay reached after {elapsed} ticks"); // Expect a delay of one longer, to compensate for timer uncertainty - if elapsed != total_millis + 1 { - panic!( - "{total_millis} ticks delay was not on time ({elapsed} ticks passed instead)" - ); + if elapsed != total + 1 { + panic!("{total} ticks delay was not on time ({elapsed} ticks passed instead)"); } } }; @@ -259,31 +175,31 @@ fn timer_queue() { // We only poll the waiting futures if an // interrupt occured or if an artificial delay // has passed. - if Instant::tick() { + if peripheral::tick() { poll!(d1, d2, d3); } - if Instant::now() == 0.into() { + if peripheral::now() == 0 { // First, we want to be waiting for our 300 tick delay - assert_eq!(TestMono::compare(), Some(301.into())); + assert_eq!(TestMonoBackend::compare(), Some(301)); } - if Instant::now() == 100.into() { + if peripheral::now() == 100 { // After 100 ticks, we enqueue a new delay that is supposed to last // until the 200-tick-mark - assert_eq!(TestMono::compare(), Some(201.into())); + assert_eq!(TestMonoBackend::compare(), Some(201)); } - if Instant::now() == 201.into() { + if peripheral::now() == 201 { // After 200 ticks, we dequeue the 200-tick-mark delay and // requeue the 300 tick delay - assert_eq!(TestMono::compare(), Some(301.into())); + assert_eq!(TestMonoBackend::compare(), Some(301)); } - if Instant::now() == 301.into() { + if peripheral::now() == 301 { // After 300 ticks, we dequeue the 300-tick-mark delay and // go to the 400 tick delay that is already enqueued - assert_eq!(TestMono::compare(), Some(401.into())); + assert_eq!(TestMonoBackend::compare(), Some(401)); } } diff --git a/rtic/CHANGELOG.md b/rtic/CHANGELOG.md index a371eb3663..be84bf7664 100644 --- a/rtic/CHANGELOG.md +++ b/rtic/CHANGELOG.md @@ -10,6 +10,9 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added - Unstable support for ESP32-C6 +### Changed + +- Remove unused dependency `rtic-monotonics` ## [v2.1.1] - 2024-03-13 diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml index b5be423053..54ebe551db 100644 --- a/rtic/Cargo.toml +++ b/rtic/Cargo.toml @@ -33,7 +33,6 @@ cortex-m = { version = "0.7.0", optional = true } bare-metal = "1.0.0" # portable-atomic = { version = "0.3.19" } atomic-polyfill = "1" -rtic-monotonics = { path = "../rtic-monotonics", version = "1.4.0", optional = true } rtic-macros = { path = "../rtic-macros", version = "=2.1.0" } rtic-core = "1" critical-section = "1" @@ -71,7 +70,4 @@ riscv-clint-backend = [ ] # needed for testing -test-critical-section = [ - "cortex-m/critical-section-single-core", - "rtic-monotonics/systick-100hz", -] +test-critical-section = ["cortex-m/critical-section-single-core"] diff --git a/rtic/src/lib.rs b/rtic/src/lib.rs index 2932c2284f..5b0971fd7b 100644 --- a/rtic/src/lib.rs +++ b/rtic/src/lib.rs @@ -34,7 +34,6 @@ pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex}; pub use rtic_macros::app; -// pub use rtic_monotonic::{self, Monotonic}; /// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude` pub mod mutex { diff --git a/xtask/src/argument_parsing.rs b/xtask/src/argument_parsing.rs index 9a0fd72e7c..925baea62f 100644 --- a/xtask/src/argument_parsing.rs +++ b/xtask/src/argument_parsing.rs @@ -64,25 +64,28 @@ impl Package { } Package::RticMonotonics => { let features = if partial { - &["cortex-m-systick", "rp2040", "nrf52840"][..] + &[ + "cortex-m-systick", + "rp2040", + "nrf52840", + "imxrt_gpt1,imxrt-ral/imxrt1062", + "stm32_tim2,stm32h725ag", + ][..] } else { &[ - "cortex-m-systick,embedded-hal-async", - "cortex-m-systick,systick-100hz,embedded-hal-async", - "cortex-m-systick,systick-10khz,embedded-hal-async", - "cortex-m-systick,embedded-hal-async,systick-64bit", - "cortex-m-systick,systick-100hz,embedded-hal-async,systick-64bit", - "cortex-m-systick,systick-10khz,embedded-hal-async,systick-64bit", - "rp2040,embedded-hal-async", - "nrf52810,embedded-hal-async", - "nrf52811,embedded-hal-async", - "nrf52832,embedded-hal-async", - "nrf52833,embedded-hal-async", - "nrf52840,embedded-hal-async", - "nrf5340-app,embedded-hal-async", - "nrf5340-net,embedded-hal-async", - "nrf9160,embedded-hal-async", - "imxrt_gpt1,imxrt-ral/imxrt1062,embedded-hal-async", + "cortex-m-systick", + "cortex-m-systick,systick-64bit", + "rp2040", + "nrf52810", + "nrf52811", + "nrf52832", + "nrf52833", + "nrf52840", + "nrf5340-app", + "nrf5340-net", + "nrf9160", + "imxrt_gpt1,imxrt_gpt2,imxrt-ral/imxrt1062", + "stm32_tim2,stm32_tim3,stm32_tim4,stm32_tim5,stm32_tim15,stm32h725ag", ][..] }; diff --git a/xtask/src/run.rs b/xtask/src/run.rs index ff81e6a256..a36c98c9e5 100644 --- a/xtask/src/run.rs +++ b/xtask/src/run.rs @@ -299,7 +299,26 @@ pub fn cargo_doc<'c>( backend: Backends, arguments: &'c Option, ) -> Vec> { - let features = Some(backend.to_target().and_features(backend.to_rtic_feature())); + let extra_doc_features = [ + "rtic-monotonics/cortex-m-systick", + "rtic-monotonics/rp2040", + "rtic-monotonics/nrf52840", + "imxrt-ral/imxrt1011", + "rtic-monotonics/imxrt_gpt1", + "rtic-monotonics/imxrt_gpt2", + "rtic-monotonics/stm32h725ag", + "rtic-monotonics/stm32_tim2", + "rtic-monotonics/stm32_tim3", + "rtic-monotonics/stm32_tim4", + "rtic-monotonics/stm32_tim5", + "rtic-monotonics/stm32_tim15", + ]; + + let features = Some(format!( + "{},{}", + backend.to_target().and_features(backend.to_rtic_feature()), + extra_doc_features.join(",") + )); let command = CargoCommand::Doc { cargoarg,