Merge remote-tracking branch 'origin/master' into feature/esp32c6

This commit is contained in:
Wouter Geraedts 2024-04-17 09:44:01 +02:00
commit 7e03812494
57 changed files with 2788 additions and 1679 deletions

View file

@ -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,7 +279,15 @@ 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: |
@ -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

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ book-target/
.DS_Store
.vscode/
qemu.log

View file

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

View file

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

View file

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

31
examples/esp32c3/runner.sh Executable file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,24 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output)
runner = "probe-run --chip STM32G030F6Px"
rustflags = [
"-C", "linker=flip-link",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
]
[build]
# TODO(3) Adjust the compilation target.
# (`thumbv6m-*` is compatible with all ARM Cortex-M chips but using the right
# target improves performance)
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
[alias]
rb = "run --bin"
rrb = "run --release --bin"

View file

@ -0,0 +1,9 @@
{
// override the default setting (`cargo check --all-targets`) which produces the following error
// "can't find crate for `test`" when the default compilation target is a no_std target
// with these changes RA will call `cargo check --bins` on save
"rust-analyzer.checkOnSave.allTargets": false,
"rust-analyzer.checkOnSave.extraArgs": [
"--bins"
]
}

View file

@ -0,0 +1,511 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
[[package]]
name = "bare-metal"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
dependencies = [
"rustc_version",
]
[[package]]
name = "bare-metal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
[[package]]
name = "bitfield"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cortex-m"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
dependencies = [
"bare-metal 0.2.5",
"bitfield",
"critical-section",
"embedded-hal 0.2.7",
"volatile-register",
]
[[package]]
name = "cortex-m-rt"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1"
dependencies = [
"cortex-m-rt-macros",
]
[[package]]
name = "cortex-m-rt-macros"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "critical-section"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "defmt"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3939552907426de152b3c2c6f51ed53f98f448babd26f28694c95f5906194595"
dependencies = [
"bitflags",
"defmt-macros",
]
[[package]]
name = "defmt-macros"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18bdc7a7b92ac413e19e95240e75d3a73a8d8e78aa24a594c22cbb4d44b4bbda"
dependencies = [
"defmt-parser",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.51",
]
[[package]]
name = "defmt-parser"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
dependencies = [
"thiserror",
]
[[package]]
name = "defmt-rtt"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "609923761264dd99ed9c7d209718cda4631c5fe84668e0f0960124cbb844c49f"
dependencies = [
"critical-section",
"defmt",
]
[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
]
[[package]]
name = "embedded-hal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
[[package]]
name = "embedded-hal-async"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
dependencies = [
"embedded-hal 1.0.0",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fugit"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
dependencies = [
"gcd",
]
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "gcd"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.1.0",
]
[[package]]
name = "nb"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
[[package]]
name = "panic-probe"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa6fa5645ef5a760cd340eaa92af9c1ce131c8c09e7f8926d8a24b59d26652b9"
dependencies = [
"cortex-m",
"defmt",
]
[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rtic"
version = "2.0.1"
dependencies = [
"atomic-polyfill",
"bare-metal 1.0.0",
"cortex-m",
"critical-section",
"rtic-core",
"rtic-macros",
]
[[package]]
name = "rtic-common"
version = "1.0.1"
dependencies = [
"critical-section",
"portable-atomic",
]
[[package]]
name = "rtic-core"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
[[package]]
name = "rtic-macros"
version = "2.0.1"
dependencies = [
"indexmap",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.51",
]
[[package]]
name = "rtic-monotonics"
version = "2.0.0"
dependencies = [
"atomic-polyfill",
"cfg-if",
"cortex-m",
"embedded-hal 1.0.0",
"fugit",
"proc-macro2",
"quote",
"rtic-time",
"stm32-metapac",
]
[[package]]
name = "rtic-time"
version = "2.0.0"
dependencies = [
"critical-section",
"embedded-hal 1.0.0",
"embedded-hal-async",
"fugit",
"futures-util",
"rtic-common",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "stm32-metapac"
version = "15.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deabea56a8821dcea05d0109f3ab3135f31eb572444e5da203d06149c594c8c6"
dependencies = [
"cortex-m",
]
[[package]]
name = "stm32g0"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc2ac544cea741c92a501bfd027d197354cd22ee92b439aea26d2ee0b55bcd7"
dependencies = [
"bare-metal 1.0.0",
"cortex-m",
"cortex-m-rt",
"vcell",
]
[[package]]
name = "stm32g030f6_periodic_prints"
version = "0.1.0"
dependencies = [
"cortex-m",
"cortex-m-rt",
"defmt",
"defmt-rtt",
"fugit",
"panic-probe",
"rtic",
"rtic-monotonics",
"stm32g0xx-hal",
]
[[package]]
name = "stm32g0xx-hal"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fae457e81f9601121c5b92dca20e3612c80ea957898f8e0e68efcaab6b242067"
dependencies = [
"bare-metal 1.0.0",
"cortex-m",
"embedded-hal 0.2.7",
"fugit",
"nb 1.1.0",
"stm32g0",
"void",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.51",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "vcell"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "volatile-register"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
dependencies = [
"vcell",
]

View file

@ -0,0 +1,48 @@
[package]
authors = ["Finomnis <finomnis@gmail.com>"]
name = "stm32g030f6_periodic_prints"
edition = "2021"
version = "0.1.0"
[workspace]
[dependencies.rtic]
path = "../../rtic"
version = "2.0.1"
features = ["thumbv6-backend"]
[dependencies.rtic-monotonics]
path = "../../rtic-monotonics"
version = "2.0.0"
features = ["stm32g030f6", "stm32_tim3"]
[dependencies]
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7.3"
defmt = "0.3.4"
defmt-rtt = "0.4.0"
fugit = "0.3.7"
panic-probe = { version = "0.3.1", features = ["print-defmt"] }
stm32g0xx-hal = { version = "0.2.0", features = ["rt", "stm32g030"] }
# cargo build/run
[profile.dev]
codegen-units = 1
debug = 2
debug-assertions = true # <-
incremental = false
opt-level = 3 # <-
overflow-checks = true # <-
# cargo build/run --release
[profile.release]
codegen-units = 1
debug = 2
debug-assertions = false # <-
incremental = false
lto = 'fat'
opt-level = 3 # <-
overflow-checks = false # <-

View file

@ -0,0 +1,31 @@
# `stm32g030f6_periodic_prints`
An RTIC periodic print example intended for the stm32g030f6 chip.
## Dependencies
#### 1. `flip-link`:
```console
$ cargo install flip-link
```
#### 2. `probe-rs`:
``` console
$ # make sure to install v0.2.0 or later
$ cargo install probe-rs --features cli
```
## Run
The stm32g030f6 chip needs to be connected to the computer via an SWD probe, like a [J-Link EDU Mini].
Then, run:
```
cargo run --release
```
[J-Link EDU Mini]: https://www.segger.com/products/debug-probes/j-link/models/j-link-edu-mini/

View file

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

View file

@ -0,0 +1,57 @@
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use defmt_rtt as _; // global logger
pub use stm32g0xx_hal as hal; // memory layout
use panic_probe as _;
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
#[defmt::panic_handler]
fn panic() -> ! {
cortex_m::asm::udf()
}
use rtic_monotonics::stm32::prelude::*;
stm32_tim3_monotonic!(Mono, 1_000_000);
#[rtic::app(device = hal::stm32, peripherals = true, dispatchers = [USART1, USART2])]
mod app {
use super::*;
#[local]
struct LocalResources {}
#[shared]
struct SharedResources {}
#[init]
fn init(ctx: init::Context) -> (SharedResources, LocalResources) {
// enable dma clock during sleep, otherwise defmt doesn't work
ctx.device.RCC.ahbenr.modify(|_, w| w.dmaen().set_bit());
defmt::println!("TIM Monotonic blinker example!");
// Start the monotonic
Mono::start(16_000_000);
print_messages::spawn().unwrap();
(SharedResources {}, LocalResources {})
}
#[task(priority = 2)]
async fn print_messages(_cx: print_messages::Context) {
let mut next_update = <Mono as Monotonic>::Instant::from_ticks(0u64);
loop {
defmt::println!("Time: {} ticks", Mono::now().ticks());
next_update += 1000u64.millis();
Mono::delay_until(next_update).await;
}
}
}

View file

@ -133,9 +133,18 @@ dependencies = [
[[package]]
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",
]

View file

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

View file

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

View file

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

View file

@ -242,8 +242,7 @@ mod esp32c3 {
stmts
}
pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}
}
}

View file

@ -5,10 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## 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`

View file

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

View file

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

View file

@ -21,14 +21,18 @@
//! `Available on crate features X only` tag are available on any `nrf52*` feature.
//!
// To build these docs correctly:
// RUSTFLAGS="--cfg docsrs" cargo doc --featuers cortex-m-systick,rp2040,nrf52840
// RUSTFLAGS="--cfg docsrs" cargo +nightly doc --features thumbv7-backend,cortex-m-systick,rp2040,nrf52840,imxrt_gpt1,imxrt_gpt2,imxrt-ral/imxrt1011,stm32h725ag,stm32_tim2,stm32_tim3,stm32_tim4,stm32_tim5,stm32_tim15
#![no_std]
#![deny(missing_docs)]
#![allow(incomplete_features)]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub use rtic_time::{Monotonic, TimeoutError, TimerQueue};
pub use fugit;
pub use rtic_time::{
self, monotonic::TimerQueueBasedMonotonic, timer_queue::TimerQueueBackend, Monotonic,
TimeoutError,
};
#[cfg(feature = "cortex-m-systick")]
pub mod systick;
@ -92,13 +96,3 @@ pub(crate) unsafe fn set_monotonic_prio(
nvic.set_priority(interrupt, hw_prio);
}
/// This marker is implemented on an interrupt token to enforce that the right tokens
/// are given to the correct monotonic implementation.
///
/// This trait is implemented by this crate and not intended for user implementation.
///
/// # Safety
///
/// This is only safely implemented by this crate.
pub unsafe trait InterruptToken<Periperhal> {}

View file

@ -1,96 +1,152 @@
//! [`Monotonic`] implementation for the nRF Real Time Clocks (RTC).
//! [`Monotonic`](rtic_time::Monotonic) implementation for the nRF Real Time Clocks (RTC).
//!
//! # Example
//!
//! ```
//! 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<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
32_768,
>;
type Duration = $crate::fugit::Duration<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
32_768,
>;
}
unsafe impl $crate::InterruptToken<$crate::nrf::rtc::$mono_timer> for $rtc_token {}
$rtc_token
}};
$crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
};
}
/// Register the Rtc0 interrupt for the monotonic.
/// Create an RTC0 based monotonic and register the RTC0 interrupt for it.
///
/// See [`crate::nrf::rtc`] for more details.
#[macro_export]
macro_rules! create_nrf_rtc0_monotonic_token {
() => {{
$crate::__internal_create_nrf_rtc_interrupt!(Rtc0, RTC0, Rtc0Token)
}};
macro_rules! nrf_rtc0_monotonic {
($name:ident) => {
$crate::__internal_create_nrf_rtc_struct!($name, Rtc0Backend, RTC0);
};
}
/// Register the Rtc1 interrupt for the monotonic.
/// Create an RTC1 based monotonic and register the RTC1 interrupt for it.
///
/// See [`crate::nrf::rtc`] for more details.
#[macro_export]
macro_rules! create_nrf_rtc1_monotonic_token {
() => {{
$crate::__internal_create_nrf_rtc_interrupt!(Rtc1, RTC1, Rtc1Token)
}};
macro_rules! nrf_rtc1_monotonic {
($name:ident) => {
$crate::__internal_create_nrf_rtc_struct!($name, Rtc1Backend, RTC1);
};
}
/// Register the Rtc2 interrupt for the monotonic.
/// Create an RTC2 based monotonic and register the RTC2 interrupt for it.
///
/// See [`crate::nrf::rtc`] for more details.
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
)]
#[macro_export]
macro_rules! create_nrf_rtc2_monotonic_token {
() => {{
$crate::__internal_create_nrf_rtc_interrupt!(Rtc2, RTC2, Rtc2Token)
}};
macro_rules! nrf_rtc2_monotonic {
($name:ident) => {
$crate::__internal_create_nrf_rtc_struct!($name, Rtc2Backend, RTC2);
};
}
struct TimerValueU24(u32);
@ -104,19 +160,23 @@ impl From<TimerValueU24> for u64 {
}
macro_rules! make_rtc {
($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
/// Monotonic timer queue implementation.
($backend_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
/// RTC based [`TimerQueueBackend`].
$(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)?
pub struct $mono_name;
pub struct $backend_name;
static $overflow: AtomicU32 = AtomicU32::new(0);
static $tq: TimerQueue<$mono_name> = TimerQueue::new();
static $tq: TimerQueue<$backend_name> = TimerQueue::new();
impl $mono_name {
/// Start the timer monotonic.
pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken<Self>) {
impl $backend_name {
/// Starts the timer.
///
/// **Do not use this function directly.**
///
/// Use the prelude macros instead.
pub fn _start(rtc: $rtc) {
unsafe { rtc.prescaler.write(|w| w.bits(0)) };
// Disable interrupts, as preparation
@ -166,67 +226,21 @@ macro_rules! make_rtc {
// plus we are not using any external shared resources so we won't impact
// basepri/source masking based critical sections.
unsafe {
crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$rtc);
pac::NVIC::unmask(Interrupt::$rtc);
crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$rtc);
pac::NVIC::unmask(pac::Interrupt::$rtc);
}
}
/// Used to access the underlying timer queue
#[doc(hidden)]
pub fn __tq() -> &'static TimerQueue<$mono_name> {
&$tq
}
/// Timeout at a specific time.
#[inline]
pub async fn timeout_at<F: Future>(
instant: <Self as Monotonic>::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_at(instant, future).await
}
/// Timeout after a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
duration: <Self as Monotonic>::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_after(duration, future).await
}
/// Delay for some duration of time.
#[inline]
pub async fn delay(duration: <Self as Monotonic>::Duration) {
$tq.delay(duration).await;
}
/// Delay to some specific time instant.
#[inline]
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
$tq.delay_until(instant).await;
}
}
impl TimerQueueBackend for $backend_name {
type Ticks = u64;
rtic_time::embedded_hal_delay_impl_fugit64!($mono_name);
#[cfg(feature = "embedded-hal-async")]
rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name);
impl Monotonic for $mono_name {
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
type Instant = fugit::TimerInstantU64<32_768>;
type Duration = fugit::TimerDurationU64<32_768>;
fn now() -> Self::Instant {
fn now() -> Self::Ticks {
let rtc = unsafe { &*$rtc::PTR };
Self::Instant::from_ticks(calculate_now(
calculate_now(
|| $overflow.load(Ordering::Relaxed),
|| TimerValueU24(rtc.counter.read().bits())
))
)
}
fn on_interrupt() {
@ -243,28 +257,35 @@ macro_rules! make_rtc {
}
}
fn enable_timer() {}
fn disable_timer() {}
fn set_compare(mut instant: Self::Instant) {
fn set_compare(mut instant: Self::Ticks) {
let rtc = unsafe { &*$rtc::PTR };
const MAX: u64 = 0xff_ffff;
// Disable interrupts because this section is timing critical.
// We rely on the fact that this entire section runs within one
// RTC clock tick. (which it will do easily if it doesn't get
// interrupted)
critical_section::with(|_|{
let now = Self::now();
if let Some(diff) = instant.checked_duration_since(now) {
// wrapping_sub deals with the u64 overflow corner case
let diff = instant.wrapping_sub(now);
let val = if diff <= MAX {
// Now we know `instant` whill happen within one `MAX` time duration.
// Errata: Timer interrupts don't fire if they are scheduled less than
// two ticks in the future. Make it three, because the timer could
// tick right now.
if diff.ticks() < 3 {
instant = Self::Instant::from_ticks(now.ticks().wrapping_add(3));
if diff < 3 {
instant = now.wrapping_add(3);
}
unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xff_ffff)) };
}
(instant & MAX) as u32
} else {
0
};
unsafe { rtc.cc[0].write(|w| w.bits(val)) };
});
}
@ -274,13 +295,17 @@ macro_rules! make_rtc {
}
fn pend_interrupt() {
pac::NVIC::pend(Interrupt::$rtc);
pac::NVIC::pend(pac::Interrupt::$rtc);
}
fn timer_queue() -> &'static TimerQueue<Self> {
&$tq
}
}
};
}
make_rtc!(Rtc0, RTC0, RTC0_OVERFLOWS, RTC0_TQ);
make_rtc!(Rtc1, RTC1, RTC1_OVERFLOWS, RTC1_TQ);
make_rtc!(Rtc0Backend, RTC0, RTC0_OVERFLOWS, RTC0_TQ);
make_rtc!(Rtc1Backend, RTC1, RTC1_OVERFLOWS, RTC1_TQ);
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
make_rtc!(Rtc2, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
make_rtc!(Rtc2Backend, RTC2, RTC2_OVERFLOWS, RTC2_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));

View file

@ -1,4 +1,4 @@
//! [`Monotonic`] impl for the 32-bit timers of the nRF series.
//! [`Monotonic`](rtic_time::Monotonic) implementation for the 32-bit timers of the nRF series.
//!
//! Not all timers are available on all parts. Ensure that only the available
//! 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<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
type Duration = $crate::fugit::Duration<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
}
unsafe impl $crate::InterruptToken<$crate::nrf::timer::$mono_timer> for $timer_token {}
$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<Self>) {
// 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<F: Future>(
instant: <Self as Monotonic>::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_at(instant, future).await
}
/// Timeout after a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
duration: <Self as Monotonic>::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_after(duration, future).await
}
/// Delay for some duration of time.
#[inline]
pub async fn delay(duration: <Self as Monotonic>::Duration) {
$tq.delay(duration).await;
}
/// Delay to some specific time instant.
#[inline]
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
$tq.delay_until(instant).await;
}
}
rtic_time::embedded_hal_delay_impl_fugit64!($mono_name);
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")));

View file

@ -1,46 +1,56 @@
//! [`Monotonic`] implementation for RP2040's Timer peripheral.
//! [`Monotonic`](rtic_time::Monotonic) implementation for RP2040's Timer peripheral.
//!
//! Always runs at a fixed rate of 1 MHz.
//!
//! # Example
//!
//! ```
//! use rtic_monotonics::rp2040::*;
//! use rtic_monotonics::rp2040::prelude::*;
//!
//! rp2040_timer_monotonic!(Mono);
//!
//! fn init() {
//! # // This is normally provided by the selected PAC
//! # let timer = unsafe { core::mem::transmute(()) };
//! # let mut resets = unsafe { core::mem::transmute(()) };
//! // Generate the required token
//! let token = rtic_monotonics::create_rp2040_monotonic_token!();
//!
//! #
//! // Start the monotonic
//! Timer::start(timer, &mut resets, token);
//! Mono::start(timer, &mut resets);
//! }
//!
//! async fn usage() {
//! loop {
//! // Use the monotonic
//! Timer::delay(100.millis()).await;
//! let timestamp = Mono::now();
//! Mono::delay(100.millis()).await;
//! }
//! }
//! ```
use super::Monotonic;
/// Common definitions and traits for using the RP2040 timer monotonic
pub mod prelude {
pub use crate::rp2040_timer_monotonic;
pub use super::{TimeoutError, TimerQueue};
use core::future::Future;
pub use fugit::{self, ExtU64, ExtU64Ceil};
use rp2040_pac::{timer, Interrupt, NVIC, RESETS, TIMER};
pub use crate::Monotonic;
/// Timer implementing [`Monotonic`] which runs at 1 MHz.
pub struct Timer;
pub use fugit::{self, ExtU64, ExtU64Ceil};
}
impl Timer {
/// Start a `Monotonic` based on RP2040's Timer.
pub fn start(
timer: TIMER,
resets: &RESETS,
_interrupt_token: impl crate::InterruptToken<Self>,
) {
use crate::TimerQueueBackend;
use rp2040_pac::{timer, Interrupt, NVIC};
pub use rp2040_pac::{RESETS, TIMER};
use rtic_time::timer_queue::TimerQueue;
/// Timer implementing [`TimerQueueBackend`].
pub struct TimerBackend;
impl TimerBackend {
/// Starts the monotonic timer.
///
/// **Do not use this function directly.**
///
/// Use the prelude macros instead.
pub fn _start(timer: TIMER, resets: &RESETS) {
resets.reset.modify(|_, w| w.timer().clear_bit());
while resets.reset_done.read().timer().bit_is_clear() {}
timer.inte.modify(|_, w| w.alarm_0().bit(true));
@ -58,55 +68,12 @@ impl Timer {
}
}
static TIMER_QUEUE: TimerQueue<Timer> = TimerQueue::new();
static TIMER_QUEUE: TimerQueue<TimerBackend> = TimerQueue::new();
// Forward timerqueue interface
impl Timer {
/// Used to access the underlying timer queue
#[doc(hidden)]
pub fn __tq() -> &'static TimerQueue<Timer> {
&TIMER_QUEUE
}
impl TimerQueueBackend for TimerBackend {
type Ticks = u64;
/// Timeout at a specific time.
#[inline]
pub async fn timeout_at<F: Future>(
instant: <Self as Monotonic>::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
TIMER_QUEUE.timeout_at(instant, future).await
}
/// Timeout after a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
duration: <Self as Monotonic>::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
TIMER_QUEUE.timeout_after(duration, future).await
}
/// Delay for some duration of time.
#[inline]
pub async fn delay(duration: <Self as Monotonic>::Duration) {
TIMER_QUEUE.delay(duration).await;
}
/// Delay to some specific time instant.
#[inline]
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
TIMER_QUEUE.delay_until(instant).await;
}
}
impl Monotonic for Timer {
type Instant = fugit::TimerInstantU64<1_000_000>;
type Duration = fugit::TimerDurationU64<1_000_000>;
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<Self> {
&TIMER_QUEUE
}
}
rtic_time::embedded_hal_delay_impl_fugit64!(Timer);
#[cfg(feature = "embedded-hal-async")]
rtic_time::embedded_hal_async_delay_impl_fugit64!(Timer);
/// Register the Timer interrupt for the monotonic.
/// Create an RP2040 timer based monotonic and register the necessary interrupt for it.
///
/// See [`crate::rp2040`] for more details.
///
/// # Arguments
///
/// * `name` - The name that the monotonic type will have.
#[macro_export]
macro_rules! create_rp2040_monotonic_token {
() => {{
#[no_mangle]
#[allow(non_snake_case)]
unsafe extern "C" fn TIMER_IRQ_0() {
$crate::rp2040::Timer::__tq().on_monotonic_interrupt();
macro_rules! rp2040_timer_monotonic {
($name:ident) => {
/// A `Monotonic` based on the RP2040 Timer peripheral.
struct $name;
impl $name {
/// Starts the `Monotonic`.
///
/// This method must be called only once.
pub fn start(timer: $crate::rp2040::TIMER, resets: &$crate::rp2040::RESETS) {
#[no_mangle]
#[allow(non_snake_case)]
unsafe extern "C" fn TIMER_IRQ_0() {
use $crate::TimerQueueBackend;
$crate::rp2040::TimerBackend::timer_queue().on_monotonic_interrupt();
}
$crate::rp2040::TimerBackend::_start(timer, resets);
}
}
pub struct Rp2040Token;
impl $crate::TimerQueueBasedMonotonic for $name {
type Backend = $crate::rp2040::TimerBackend;
type Instant = $crate::fugit::Instant<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
1_000_000,
>;
type Duration = $crate::fugit::Duration<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
1_000_000,
>;
}
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);
};
}

View file

@ -1,4 +1,4 @@
//! [`Monotonic`] impl for the STM32.
//! [`Monotonic`](rtic_time::Monotonic) implementations for STM32 chips.
//!
//! Not all timers are available on all parts. Ensure that only available
//! 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<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
type Duration = $crate::fugit::Duration<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
}
unsafe impl $crate::InterruptToken<$crate::stm32::$mono_timer> for $timer_token {}
$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<Self>) {
/// Use the prelude macros instead.
pub fn _start(tim_clock_hz: u32, timer_hz: u32) {
_generated::$timer::enable();
_generated::$timer::reset();
$timer.cr1().modify(|r| r.set_cen(false));
assert!((tim_clock_hz % TIMER_HZ) == 0, "Unable to find suitable timer prescaler value!");
let psc = tim_clock_hz / TIMER_HZ - 1;
assert!((tim_clock_hz % timer_hz) == 0, "Unable to find suitable timer prescaler value!");
let psc = tim_clock_hz / timer_hz - 1;
$timer.psc().write(|r| r.set_psc(psc as u16));
// Enable full-period interrupt.
$timer.dier().modify(|r| r.set_uie(true));
// Configure and enable half-period interrupt
$timer.ccr(2).write(|r| r.set_ccr($bits::MAX - ($bits::MAX >> 1)));
$timer.ccr(2).write(|r| r.set_ccr(($bits::MAX - ($bits::MAX >> 1)).into()));
$timer.dier().modify(|r| r.set_ccie(2, true));
// Trigger an update event to load the prescaler value to the clock.
@ -183,73 +265,31 @@ macro_rules! make_timer {
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::$timer);
}
}
/// Used to access the underlying timer queue
#[doc(hidden)]
pub fn __tq() -> &'static TimerQueue<$mono_name> {
&$tq
}
/// Delay for some duration of time.
#[inline]
pub async fn delay(duration: <Self as Monotonic>::Duration) {
$tq.delay(duration).await;
}
/// Timeout at a specific time.
pub async fn timeout_at<F: core::future::Future>(
instant: <Self as rtic_time::Monotonic>::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_at(instant, future).await
}
/// Timeout after a specific duration.
#[inline]
pub async fn timeout_after<F: core::future::Future>(
duration: <Self as Monotonic>::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_after(duration, future).await
}
/// Delay to some specific time instant.
#[inline]
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
$tq.delay_until(instant).await;
}
}
rtic_time::embedded_hal_delay_impl_fugit64!($mono_name);
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<TIMER_HZ>;
type Duration = fugit::TimerDurationU64<TIMER_HZ>;
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
fn now() -> Self::Instant {
Self::Instant::from_ticks(calculate_now(
fn now() -> Self::Ticks {
calculate_now(
|| $overflow.load(Ordering::Relaxed),
|| $timer.cnt().read().cnt()
))
)
}
fn set_compare(instant: Self::Instant) {
fn set_compare(instant: Self::Ticks) {
let now = Self::now();
// Since the timer may or may not overflow based on the requested compare val, we check how many ticks are left.
let val = match instant.checked_duration_since(now) {
None => 0, // In the past
Some(x) if x.ticks() <= ($bits::MAX as u64) => instant.duration_since_epoch().ticks() as $bits, // Will not overflow
Some(_x) => 0, // Will overflow
// `wrapping_sup` takes care of the u64 integer overflow special case.
let val = if instant.wrapping_sub(now) <= ($bits::MAX as u64) {
instant as $bits
} else {
// In the past or will overflow
0
};
$timer.ccr(1).write(|r| r.set_ccr(val));
$timer.ccr(1).write(|r| r.set_ccr(val.into()));
}
fn clear_compare_flag() {
@ -282,24 +322,25 @@ macro_rules! make_timer {
assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!");
}
}
fn timer_queue() -> &'static TimerQueue<$backend_name> {
&$tq
}
}
};
}
#[cfg(feature = "stm32_tim2")]
make_timer!(Tim2, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ);
make_timer!(Tim2Backend, TIM2, u32, TIMER2_OVERFLOWS, TIMER2_TQ);
#[cfg(feature = "stm32_tim3")]
make_timer!(Tim3, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ);
make_timer!(Tim3Backend, TIM3, u16, TIMER3_OVERFLOWS, TIMER3_TQ);
#[cfg(feature = "stm32_tim4")]
make_timer!(Tim4, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ);
make_timer!(Tim4Backend, TIM4, u16, TIMER4_OVERFLOWS, TIMER4_TQ);
#[cfg(feature = "stm32_tim5")]
make_timer!(Tim5, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ);
#[cfg(feature = "stm32_tim12")]
make_timer!(Tim12, TIM12, u16, TIMER12_OVERFLOWS, TIMER12_TQ);
make_timer!(Tim5Backend, TIM5, u16, TIMER5_OVERFLOWS, TIMER5_TQ);
#[cfg(feature = "stm32_tim15")]
make_timer!(Tim15, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ);
make_timer!(Tim15Backend, TIM15, u16, TIMER15_OVERFLOWS, TIMER15_TQ);

View file

@ -1,93 +1,79 @@
//! [`Monotonic`] based on Cortex-M SysTick. Note: this implementation is inefficient as it
//! [`Monotonic`](rtic_time::Monotonic) based on Cortex-M SysTick.
//! Note: this implementation is inefficient as it
//! ticks and generates interrupts at a constant rate.
//!
//! Currently, the following tick rates are supported:
//!
//! | Feature | Tick rate | Precision |
//! |:----------------:|----------:|----------:|
//! | (none / default) | 1 kHz | 1 ms |
//! | systick-100hz | 100 Hz | 10 ms |
//! | systick-10khz | 10 kHz | 0.1 ms |
//! # Example
//!
//! ```
//! use rtic_monotonics::systick::*;
//! use rtic_monotonics::systick::prelude::*;
//! systick_monotonic!(Mono, 1_000);
//!
//! fn init() {
//! # // This is normally provided by the selected PAC
//! # let systick = unsafe { core::mem::transmute(()) };
//! // Generate the required token
//! let systick_token = rtic_monotonics::create_systick_token!();
//!
//! #
//! // Start the monotonic
//! Systick::start(systick, 12_000_000, systick_token);
//! Mono::start(systick, 12_000_000);
//! }
//!
//! async fn usage() {
//! loop {
//! // Use the monotonic
//! let timestamp = Mono::now();
//! Systick::delay(100.millis()).await;
//! }
//! }
//! ```
use super::Monotonic;
pub use super::{TimeoutError, TimerQueue};
/// Common definitions and traits for using the systick monotonic
pub mod prelude {
pub use crate::systick_monotonic;
pub use crate::Monotonic;
cfg_if::cfg_if! {
if #[cfg(feature = "systick-64bit")] {
pub use fugit::{self, ExtU64, ExtU64Ceil};
} else {
pub use fugit::{self, ExtU32, ExtU32Ceil};
}
}
}
pub use cortex_m::peripheral::SYST;
use atomic_polyfill::Ordering;
use core::future::Future;
use cortex_m::peripheral::SYST;
pub use fugit;
use rtic_time::timer_queue::TimerQueue;
use crate::TimerQueueBackend;
cfg_if::cfg_if! {
if #[cfg(feature = "systick-64bit")] {
pub use fugit::{ExtU64, ExtU64Ceil};
use atomic_polyfill::AtomicU64;
static SYSTICK_CNT: AtomicU64 = AtomicU64::new(0);
} else {
pub use fugit::{ExtU32, ExtU32Ceil};
use atomic_polyfill::AtomicU32;
static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0);
}
}
static SYSTICK_TIMER_QUEUE: TimerQueue<Systick> = TimerQueue::new();
// Features should be additive, here systick-100hz gets picked if both
// `systick-100hz` and `systick-10khz` are enabled.
static SYSTICK_TIMER_QUEUE: TimerQueue<SystickBackend> = TimerQueue::new();
cfg_if::cfg_if! {
if #[cfg(feature = "systick-100hz")]
{
const TIMER_HZ: u32 = 100;
} else if #[cfg(feature = "systick-10khz")]
{
const TIMER_HZ: u32 = 10_000;
} else {
// Default case is 1 kHz
const TIMER_HZ: u32 = 1_000;
}
}
/// Systick 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<Self>,
) {
// + TIMER_HZ / 2 provides round to nearest instead of round to 0.
// - 1 as the counter range is inclusive [0, reload]
let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1;
/// Use the prelude macros instead.
pub fn _start(mut systick: SYST, sysclk: u32, timer_hz: u32) {
assert!(
(sysclk % timer_hz) == 0,
"timer_hz cannot evenly divide sysclk! Please adjust the timer or sysclk frequency."
);
let reload = sysclk / timer_hz - 1;
assert!(reload <= 0x00ff_ffff);
assert!(reload > 0);
@ -98,7 +84,7 @@ impl Systick {
systick.enable_interrupt();
systick.enable_counter();
SYSTICK_TIMER_QUEUE.initialize(Systick {});
SYSTICK_TIMER_QUEUE.initialize(SystickBackend {});
}
fn systick() -> SYST {
@ -106,67 +92,24 @@ impl Systick {
}
}
// Forward timerqueue interface
impl Systick {
/// Used to access the underlying timer queue
#[doc(hidden)]
pub fn __tq() -> &'static TimerQueue<Systick> {
&SYSTICK_TIMER_QUEUE
}
/// Timeout at a specific time.
pub async fn timeout_at<F: Future>(
instant: <Self as Monotonic>::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
SYSTICK_TIMER_QUEUE.timeout_at(instant, future).await
}
/// Timeout after a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
duration: <Self as Monotonic>::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
SYSTICK_TIMER_QUEUE.timeout_after(duration, future).await
}
/// Delay for some duration of time.
#[inline]
pub async fn delay(duration: <Self as Monotonic>::Duration) {
SYSTICK_TIMER_QUEUE.delay(duration).await;
}
/// Delay to some specific time instant.
#[inline]
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
SYSTICK_TIMER_QUEUE.delay_until(instant).await;
}
}
impl Monotonic for Systick {
impl TimerQueueBackend for SystickBackend {
cfg_if::cfg_if! {
if #[cfg(feature = "systick-64bit")] {
type Instant = fugit::TimerInstantU64<TIMER_HZ>;
type Duration = fugit::TimerDurationU64<TIMER_HZ>;
type Ticks = u64;
} else {
type Instant = fugit::TimerInstantU32<TIMER_HZ>;
type Duration = fugit::TimerDurationU32<TIMER_HZ>;
type Ticks = u32;
}
}
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
fn now() -> Self::Instant {
fn now() -> Self::Ticks {
if Self::systick().has_wrapped() {
SYSTICK_CNT.fetch_add(1, Ordering::AcqRel);
}
Self::Instant::from_ticks(SYSTICK_CNT.load(Ordering::Relaxed))
SYSTICK_CNT.load(Ordering::Relaxed)
}
fn set_compare(_: Self::Instant) {
fn set_compare(_: Self::Ticks) {
// No need to do something here, we get interrupts anyway.
}
@ -184,39 +127,66 @@ impl Monotonic for Systick {
}
}
fn enable_timer() {}
fn disable_timer() {}
}
cfg_if::cfg_if! {
if #[cfg(feature = "systick-64bit")] {
rtic_time::embedded_hal_delay_impl_fugit64!(Systick);
#[cfg(feature = "embedded-hal-async")]
rtic_time::embedded_hal_async_delay_impl_fugit64!(Systick);
} else {
rtic_time::embedded_hal_delay_impl_fugit32!(Systick);
#[cfg(feature = "embedded-hal-async")]
rtic_time::embedded_hal_async_delay_impl_fugit32!(Systick);
fn timer_queue() -> &'static TimerQueue<Self> {
&SYSTICK_TIMER_QUEUE
}
}
/// Register the Systick interrupt for the monotonic.
/// Create a Systick based monotonic and register the Systick interrupt for it.
///
/// See [`crate::systick`] for more details.
///
/// # Arguments
///
/// * `name` - The name that the monotonic type will have.
/// * `tick_rate_hz` - The tick rate of the timer peripheral.
/// Can be omitted; defaults to 1kHz.
#[macro_export]
macro_rules! create_systick_token {
() => {{
#[no_mangle]
#[allow(non_snake_case)]
unsafe extern "C" fn SysTick() {
$crate::systick::Systick::__tq().on_monotonic_interrupt();
macro_rules! systick_monotonic {
($name:ident) => {
$crate::systick_monotonic($name, 1_000);
};
($name:ident, $tick_rate_hz:expr) => {
/// A `Monotonic` based on SysTick.
struct $name;
impl $name {
/// Starts the `Monotonic`.
///
/// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from
/// the clock generation function of the used HAL.
///
/// Panics if it is impossible to achieve the desired monotonic tick rate based
/// on the given `sysclk` parameter. If that happens, adjust the desired monotonic tick rate.
///
/// This method must be called only once.
pub fn start(systick: $crate::systick::SYST, sysclk: u32) {
#[no_mangle]
#[allow(non_snake_case)]
unsafe extern "C" fn SysTick() {
use $crate::TimerQueueBackend;
$crate::systick::SystickBackend::timer_queue().on_monotonic_interrupt();
}
$crate::systick::SystickBackend::_start(systick, sysclk, $tick_rate_hz);
}
}
pub struct SystickToken;
impl $crate::TimerQueueBasedMonotonic for $name {
type Backend = $crate::systick::SystickBackend;
type Instant = $crate::fugit::Instant<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
type Duration = $crate::fugit::Duration<
<Self::Backend as $crate::TimerQueueBackend>::Ticks,
1,
{ $tick_rate_hz },
>;
}
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);
};
}

View file

@ -5,11 +5,18 @@ This project adheres to [Semantic Versioning](http://semver.org/).
For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## 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

View file

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

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

@ -0,0 +1,29 @@
# rtic-time
Basic definitions and utilities that can be used to keep track of time.
[![crates.io](https://img.shields.io/crates/v/rtic-time)](https://crates.io/crates/rtic-time)
[![docs.rs](https://docs.rs/rtic-time/badge.svg)](https://docs.rs/rtic-time)
[![matrix](https://img.shields.io/matrix/rtic:matrix.org)](https://matrix.to/#/#rtic:matrix.org)
## Content
The main contribution of this crate is to define the [`Monotonic`](https://docs.rs/rtic-time/latest/rtic_time/trait.Monotonic.html) trait. It serves as a standardized interface for libraries to interact with the system's monotonic timers.
Additionally, this crate provides tools and utilities that help with implementing monotonic timers.
## Implementations of the `Monotonic` trait
For implementations of [`Monotonic`](https://docs.rs/rtic-time/latest/rtic_time/trait.Monotonic.html)
on various hardware, see [`rtic-monotonics`](https://docs.rs/rtic-monotonics/).
## Chat
Join us and talk about RTIC in the [Matrix room][matrix-room].
Weekly meeting minutes can be found over at [RTIC HackMD][hackmd].
[matrix-room]: https://matrix.to/#/#rtic:matrix.org
[hackmd]: https://rtic.rs/meeting

View file

@ -5,285 +5,60 @@
#![no_std]
#![deny(missing_docs)]
#![allow(incomplete_features)]
use core::future::{poll_fn, Future};
use core::pin::Pin;
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use core::task::{Poll, Waker};
use futures_util::{
future::{select, Either},
pin_mut,
};
use linked_list::{Link, LinkedList};
pub use monotonic::Monotonic;
use rtic_common::dropper::OnDrop;
#![allow(async_fn_in_trait)]
pub mod half_period_counter;
mod linked_list;
mod monotonic;
/// Holds a waker and at which time instant this waker shall be awoken.
struct WaitingWaker<Mono: Monotonic> {
waker: Waker,
release_at: Mono::Instant,
was_popped: AtomicBool,
}
impl<Mono: Monotonic> Clone for WaitingWaker<Mono> {
fn clone(&self) -> Self {
Self {
waker: self.waker.clone(),
release_at: self.release_at,
was_popped: AtomicBool::new(self.was_popped.load(Ordering::Relaxed)),
}
}
}
impl<Mono: Monotonic> PartialEq for WaitingWaker<Mono> {
fn eq(&self, other: &Self) -> bool {
self.release_at == other.release_at
}
}
impl<Mono: Monotonic> PartialOrd for WaitingWaker<Mono> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.release_at.partial_cmp(&other.release_at)
}
}
/// A generic timer queue for async executors.
///
/// # Blocking
///
/// The internal priority queue uses global critical sections to manage access. This means that
/// `await`ing a delay will cause a lock of the entire system for O(n) time. In practice the lock
/// duration is ~10 clock cycles per element in the queue.
///
/// # Safety
///
/// This timer queue is based on an intrusive linked list, and by extension the links are strored
/// on the async stacks of callers. The links are deallocated on `drop` or when the wait is
/// complete.
///
/// Do not call `mem::forget` on an awaited future, or there will be dragons!
pub struct TimerQueue<Mono: Monotonic> {
queue: LinkedList<WaitingWaker<Mono>>,
initialized: AtomicBool,
}
pub mod monotonic;
pub mod timer_queue;
/// This indicates that there was a timeout.
pub struct TimeoutError;
/// This is needed to make the async closure in `delay_until` accept that we "share"
/// the link possible between threads.
struct LinkPtr<Mono: Monotonic>(*mut Option<linked_list::Link<WaitingWaker<Mono>>>);
/// Re-export for macros
pub use embedded_hal;
/// Re-export for macros
pub use embedded_hal_async;
impl<Mono: Monotonic> Clone for LinkPtr<Mono> {
fn clone(&self) -> Self {
LinkPtr(self.0)
}
}
impl<Mono: Monotonic> LinkPtr<Mono> {
/// This will dereference the pointer stored within and give out an `&mut`.
unsafe fn get(&mut self) -> &mut Option<linked_list::Link<WaitingWaker<Mono>>> {
&mut *self.0
}
}
unsafe impl<Mono: Monotonic> Send for LinkPtr<Mono> {}
unsafe impl<Mono: Monotonic> Sync for LinkPtr<Mono> {}
impl<Mono: Monotonic> TimerQueue<Mono> {
/// Make a new queue.
pub const fn new() -> Self {
Self {
queue: LinkedList::new(),
initialized: AtomicBool::new(false),
}
}
/// Forwards the `Monotonic::now()` method.
#[inline(always)]
pub fn now(&self) -> Mono::Instant {
Mono::now()
}
/// Takes the initialized monotonic to initialize the TimerQueue.
pub fn initialize(&self, monotonic: Mono) {
self.initialized.store(true, Ordering::SeqCst);
// Don't run drop on `Mono`
core::mem::forget(monotonic);
}
/// Call this in the interrupt handler of the hardware timer supporting the `Monotonic`
/// # A monotonic clock / counter definition.
///
/// ## Correctness
///
/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This
/// is a requirement on the time library that the user chooses to use.
pub trait Monotonic {
/// The type for instant, defining an instant in time.
///
/// # Safety
/// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used.
type Instant: Ord
+ Copy
+ core::ops::Add<Self::Duration, Output = Self::Instant>
+ core::ops::Sub<Self::Duration, Output = Self::Instant>
+ core::ops::Sub<Self::Instant, Output = Self::Duration>;
/// The type for duration, defining a duration of time.
///
/// It's always safe to call, but it must only be called from the interrupt of the
/// monotonic timer for correct operation.
pub unsafe fn on_monotonic_interrupt(&self) {
Mono::clear_compare_flag();
Mono::on_interrupt();
/// **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<F: Future>(
&self,
instant: Mono::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
let delay = self.delay_until(instant);
pin_mut!(future);
pin_mut!(delay);
match select(future, delay).await {
Either::Left((r, _)) => Ok(r),
Either::Right(_) => Err(TimeoutError),
}
}
/// Timeout after at least a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
&self,
duration: Mono::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
let now = Mono::now();
let mut timeout = now + duration;
if now != timeout {
timeout = timeout + Mono::TICK_PERIOD;
}
// Wait for one period longer, because by definition timers have an uncertainty
// of one period, so waiting for 'at least' needs to compensate for that.
self.timeout_at(timeout, future).await
}
/// Delay for at least some duration of time.
#[inline]
pub async fn delay(&self, duration: Mono::Duration) {
let now = Mono::now();
let mut timeout = now + duration;
if now != timeout {
timeout = timeout + Mono::TICK_PERIOD;
}
// Wait for one period longer, because by definition timers have an uncertainty
// of one period, so waiting for 'at least' needs to compensate for that.
self.delay_until(timeout).await;
}
/// Delay 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<linked_list::Link<WaitingWaker<Mono>>> = None;
/// Timeout at a specific time.
async fn timeout_at<F: core::future::Future>(
instant: Self::Instant,
future: F,
) -> Result<F::Output, TimeoutError>;
// Make this future `Drop`-safe
// SAFETY(link_ptr): Shadow the original definition of `link_ptr` so we can't abuse it.
let mut link_ptr =
LinkPtr(&mut link_ptr as *mut Option<linked_list::Link<WaitingWaker<Mono>>>);
let mut link_ptr2 = link_ptr.clone();
let queue = &self.queue;
let marker = &AtomicUsize::new(0);
let dropper = OnDrop::new(|| {
queue.delete(marker.load(Ordering::Relaxed));
});
poll_fn(|cx| {
if Mono::now() >= instant {
return Poll::Ready(());
}
// SAFETY: This pointer is only dereferenced here and on drop of the future
// which happens outside this `poll_fn`'s stack frame, so this mutable access cannot
// happen at the same time as `dropper` runs.
let link = unsafe { link_ptr2.get() };
if link.is_none() {
let link_ref = link.insert(Link::new(WaitingWaker {
waker: cx.waker().clone(),
release_at: instant,
was_popped: AtomicBool::new(false),
}));
// SAFETY(new_unchecked): The address to the link is stable as it is defined
//outside this stack frame.
// SAFETY(insert): `link_ref` lifetime comes from `link_ptr` that is shadowed, and
// we make sure in `dropper` that the link is removed from the queue before
// dropping `link_ptr` AND `dropper` makes sure that the shadowed `link_ptr` lives
// until the end of the stack frame.
let (head_updated, addr) = unsafe { queue.insert(Pin::new_unchecked(link_ref)) };
marker.store(addr, Ordering::Relaxed);
if head_updated {
// Pend the monotonic handler if the queue head was updated.
Mono::pend_interrupt()
}
}
Poll::Pending
})
.await;
// SAFETY: We only run this and dereference the pointer if we have
// exited the `poll_fn` below in the `drop(dropper)` call. The other dereference
// of this pointer is in the `poll_fn`.
if let Some(link) = unsafe { link_ptr.get() } {
if link.val.was_popped.load(Ordering::Relaxed) {
// If it was popped from the queue there is no need to run delete
dropper.defuse();
}
} else {
// Make sure that our link is deleted from the list before we drop this stack
drop(dropper);
}
}
/// Timeout after a specific duration.
async fn timeout_after<F: core::future::Future>(
duration: Self::Duration,
future: F,
) -> Result<F::Output, TimeoutError>;
}

View file

@ -1,236 +1,8 @@
//! A monotonic clock / counter definition.
//! Structs and traits surrounding the [`Monotonic`](crate::Monotonic) trait.
/// # A monotonic clock / counter definition.
///
/// ## Correctness
///
/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This
/// is a requirement on the time library that the user chooses to use.
pub trait Monotonic {
/// The time at time zero.
const ZERO: Self::Instant;
pub use timer_queue_based_monotonic::{
TimerQueueBasedDuration, TimerQueueBasedInstant, TimerQueueBasedMonotonic,
};
/// The duration between two timer ticks.
const TICK_PERIOD: Self::Duration;
/// The type for instant, defining an instant in time.
///
/// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used.
type Instant: Ord
+ Copy
+ core::ops::Add<Self::Duration, Output = Self::Instant>
+ core::ops::Sub<Self::Duration, Output = Self::Instant>
+ core::ops::Sub<Self::Instant, Output = Self::Duration>;
/// The type for duration, defining an duration of time.
///
/// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used.
type Duration;
/// Get the current time.
fn now() -> Self::Instant;
/// Set the compare value of the timer interrupt.
///
/// **Note:** This method does not need to handle race conditions of the monotonic, the timer
/// queue in RTIC checks this.
fn set_compare(instant: Self::Instant);
/// This method used to be required by an errata workaround
/// for the nrf52 family, but it has been disabled as the
/// workaround was erroneous.
#[deprecated(
since = "1.2.0",
note = "this method is erroneous and has been disabled"
)]
fn should_dequeue_check(_: Self::Instant) -> bool {
panic!("This method should not be used as it is erroneous.")
}
/// Clear the compare interrupt flag.
fn clear_compare_flag();
/// Pend the timer's interrupt.
fn pend_interrupt();
/// Optional. Runs on interrupt before any timer queue handling.
fn on_interrupt() {}
/// Optional. This is used to save power, this is called when the timer queue is not empty.
///
/// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
/// based of `now()` is still valid.
///
/// NOTE: This may be called more than once.
fn enable_timer() {}
/// Optional. This is used to save power, this is called when the timer queue is empty.
///
/// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
/// based of `now()` is still valid.
///
/// NOTE: This may be called more than once.
fn disable_timer() {}
}
/// Creates impl blocks for [`embedded_hal::delay::DelayNs`][DelayNs],
/// based on [`fugit::ExtU64Ceil`][ExtU64Ceil].
///
/// [DelayNs]: https://docs.rs/embedded-hal/latest/embedded_hal/delay/trait.DelayNs.html
/// [ExtU64Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU64Ceil.html
#[macro_export]
macro_rules! embedded_hal_delay_impl_fugit64 {
($t:ty) => {
impl ::embedded_hal::delay::DelayNs for $t {
fn delay_ns(&mut self, ns: u32) {
use ::fugit::ExtU64Ceil;
let now = Self::now();
let mut done = now + u64::from(ns).nanos_at_least();
if now != done {
// Compensate for sub-tick uncertainty
done += Self::TICK_PERIOD;
}
while Self::now() < done {}
}
fn delay_us(&mut self, us: u32) {
use ::fugit::ExtU64Ceil;
let now = Self::now();
let mut done = now + u64::from(us).micros_at_least();
if now != done {
// Compensate for sub-tick uncertainty
done += Self::TICK_PERIOD;
}
while Self::now() < done {}
}
fn delay_ms(&mut self, ms: u32) {
use ::fugit::ExtU64Ceil;
let now = Self::now();
let mut done = now + u64::from(ms).millis_at_least();
if now != done {
// Compensate for sub-tick uncertainty
done += Self::TICK_PERIOD;
}
while Self::now() < done {}
}
}
};
}
/// Creates impl blocks for [`embedded_hal_async::delay::DelayNs`][DelayNs],
/// based on [`fugit::ExtU64Ceil`][ExtU64Ceil].
///
/// [DelayNs]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/trait.DelayNs.html
/// [ExtU64Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU64Ceil.html
#[macro_export]
macro_rules! embedded_hal_async_delay_impl_fugit64 {
($t:ty) => {
impl ::embedded_hal_async::delay::DelayNs for $t {
#[inline]
async fn delay_ns(&mut self, ns: u32) {
use ::fugit::ExtU64Ceil;
Self::delay(u64::from(ns).nanos_at_least()).await;
}
#[inline]
async fn delay_us(&mut self, us: u32) {
use ::fugit::ExtU64Ceil;
Self::delay(u64::from(us).micros_at_least()).await;
}
#[inline]
async fn delay_ms(&mut self, ms: u32) {
use ::fugit::ExtU64Ceil;
Self::delay(u64::from(ms).millis_at_least()).await;
}
}
};
}
/// Creates impl blocks for [`embedded_hal::delay::DelayNs`][DelayNs],
/// based on [`fugit::ExtU32Ceil`][ExtU32Ceil].
///
/// [DelayNs]: https://docs.rs/embedded-hal/latest/embedded_hal/delay/trait.DelayNs.html
/// [ExtU32Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU32Ceil.html
#[macro_export]
macro_rules! embedded_hal_delay_impl_fugit32 {
($t:ty) => {
impl ::embedded_hal::delay::DelayNs for $t {
fn delay_ns(&mut self, ns: u32) {
use ::fugit::ExtU32Ceil;
let now = Self::now();
let mut done = now + ns.nanos_at_least();
if now != done {
// Compensate for sub-tick uncertainty
done += Self::TICK_PERIOD;
}
while Self::now() < done {}
}
fn delay_us(&mut self, us: u32) {
use ::fugit::ExtU32Ceil;
let now = Self::now();
let mut done = now + us.micros_at_least();
if now != done {
// Compensate for sub-tick uncertainty
done += Self::TICK_PERIOD;
}
while Self::now() < done {}
}
fn delay_ms(&mut self, ms: u32) {
use ::fugit::ExtU32Ceil;
let now = Self::now();
let mut done = now + ms.millis_at_least();
if now != done {
// Compensate for sub-tick uncertainty
done += Self::TICK_PERIOD;
}
while Self::now() < done {}
}
}
};
}
/// Creates impl blocks for [`embedded_hal_async::delay::DelayNs`][DelayNs],
/// based on [`fugit::ExtU32Ceil`][ExtU32Ceil].
///
/// [DelayNs]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/trait.DelayNs.html
/// [ExtU32Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU32Ceil.html
#[macro_export]
macro_rules! embedded_hal_async_delay_impl_fugit32 {
($t:ty) => {
impl ::embedded_hal_async::delay::DelayNs for $t {
#[inline]
async fn delay_ns(&mut self, ns: u32) {
use ::fugit::ExtU32Ceil;
Self::delay(ns.nanos_at_least()).await;
}
#[inline]
async fn delay_us(&mut self, us: u32) {
use ::fugit::ExtU32Ceil;
Self::delay(us.micros_at_least()).await;
}
#[inline]
async fn delay_ms(&mut self, ms: u32) {
use ::fugit::ExtU32Ceil;
Self::delay(ms.millis_at_least()).await;
}
}
};
}
mod embedded_hal_macros;
mod timer_queue_based_monotonic;

View file

@ -0,0 +1,77 @@
//! Macros that implement embedded-hal traits for Monotonics
/// Implements [`embedded_hal::delay::DelayNs`] for a given monotonic.
#[macro_export]
macro_rules! impl_embedded_hal_delay_fugit {
($t:ty) => {
impl $crate::embedded_hal::delay::DelayNs for $t {
fn delay_ns(&mut self, ns: u32) {
let now = <Self as $crate::Monotonic>::now();
let mut done =
now + <Self as $crate::Monotonic>::Duration::nanos_at_least(ns.into());
if now != done {
// Compensate for sub-tick uncertainty
done = done + <Self as $crate::Monotonic>::Duration::from_ticks(1);
}
while <Self as $crate::Monotonic>::now() < done {}
}
fn delay_us(&mut self, us: u32) {
let now = <Self as $crate::Monotonic>::now();
let mut done =
now + <Self as $crate::Monotonic>::Duration::micros_at_least(us.into());
if now != done {
// Compensate for sub-tick uncertainty
done = done + <Self as $crate::Monotonic>::Duration::from_ticks(1);
}
while <Self as $crate::Monotonic>::now() < done {}
}
fn delay_ms(&mut self, ms: u32) {
let now = <Self as $crate::Monotonic>::now();
let mut done =
now + <Self as $crate::Monotonic>::Duration::millis_at_least(ms.into());
if now != done {
// Compensate for sub-tick uncertainty
done = done + <Self as $crate::Monotonic>::Duration::from_ticks(1);
}
while <Self as $crate::Monotonic>::now() < done {}
}
}
};
}
/// Implements [`embedded_hal_async::delay::DelayNs`] for a given monotonic.
#[macro_export]
macro_rules! impl_embedded_hal_async_delay_fugit {
($t:ty) => {
impl $crate::embedded_hal_async::delay::DelayNs for $t {
#[inline]
async fn delay_ns(&mut self, ns: u32) {
<Self as $crate::Monotonic>::delay(
<Self as $crate::Monotonic>::Duration::nanos_at_least(ns.into()),
)
.await;
}
#[inline]
async fn delay_us(&mut self, us: u32) {
<Self as $crate::Monotonic>::delay(
<Self as $crate::Monotonic>::Duration::micros_at_least(us.into()),
)
.await;
}
#[inline]
async fn delay_ms(&mut self, ms: u32) {
<Self as $crate::Monotonic>::delay(
<Self as $crate::Monotonic>::Duration::millis_at_least(ms.into()),
)
.await;
}
}
};
}

View file

@ -0,0 +1,113 @@
use crate::{timer_queue::TimerQueueBackend, TimeoutError};
use crate::Monotonic;
/// A [`Monotonic`] that is backed by the [`TimerQueue`](crate::timer_queue::TimerQueue).
pub trait TimerQueueBasedMonotonic {
/// The backend for the timer queue
type Backend: TimerQueueBackend;
/// The type for instant, defining an instant in time.
///
/// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used.
type Instant: TimerQueueBasedInstant<Ticks = <Self::Backend as TimerQueueBackend>::Ticks>
+ core::ops::Add<Self::Duration, Output = Self::Instant>
+ core::ops::Sub<Self::Duration, Output = Self::Instant>
+ core::ops::Sub<Self::Instant, Output = Self::Duration>;
/// The type for duration, defining a duration of time.
///
/// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used.
type Duration: TimerQueueBasedDuration<Ticks = <Self::Backend as TimerQueueBackend>::Ticks>;
}
impl<T: TimerQueueBasedMonotonic> Monotonic for T {
type Instant = T::Instant;
type Duration = T::Duration;
fn now() -> Self::Instant {
Self::Instant::from_ticks(T::Backend::timer_queue().now())
}
async fn delay(duration: Self::Duration) {
T::Backend::timer_queue().delay(duration.ticks()).await
}
async fn delay_until(instant: Self::Instant) {
T::Backend::timer_queue().delay_until(instant.ticks()).await
}
async fn timeout_at<F: core::future::Future>(
instant: Self::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
T::Backend::timer_queue()
.timeout_at(instant.ticks(), future)
.await
}
async fn timeout_after<F: core::future::Future>(
duration: Self::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
T::Backend::timer_queue()
.timeout_after(duration.ticks(), future)
.await
}
}
/// An instant that can be used in [`TimerQueueBasedMonotonic`].
pub trait TimerQueueBasedInstant: Ord + Copy {
/// The internal type of the instant
type Ticks;
/// Convert from ticks to the instant
fn from_ticks(ticks: Self::Ticks) -> Self;
/// Convert the instant to ticks
fn ticks(self) -> Self::Ticks;
}
/// A duration that can be used in [`TimerQueueBasedMonotonic`].
pub trait TimerQueueBasedDuration: Copy {
/// The internal type of the duration
type Ticks;
/// Convert the duration to ticks
fn ticks(self) -> Self::Ticks;
}
impl<const NOM: u32, const DENOM: u32> TimerQueueBasedInstant for fugit::Instant<u64, NOM, DENOM> {
type Ticks = u64;
fn from_ticks(ticks: Self::Ticks) -> Self {
Self::from_ticks(ticks)
}
fn ticks(self) -> Self::Ticks {
Self::ticks(&self)
}
}
impl<const NOM: u32, const DENOM: u32> TimerQueueBasedInstant for fugit::Instant<u32, NOM, DENOM> {
type Ticks = u32;
fn from_ticks(ticks: Self::Ticks) -> Self {
Self::from_ticks(ticks)
}
fn ticks(self) -> Self::Ticks {
Self::ticks(&self)
}
}
impl<const NOM: u32, const DENOM: u32> TimerQueueBasedDuration
for fugit::Duration<u64, NOM, DENOM>
{
type Ticks = u64;
fn ticks(self) -> Self::Ticks {
Self::ticks(&self)
}
}
impl<const NOM: u32, const DENOM: u32> TimerQueueBasedDuration
for fugit::Duration<u32, NOM, DENOM>
{
type Ticks = u32;
fn ticks(self) -> Self::Ticks {
Self::ticks(&self)
}
}

View file

@ -0,0 +1,281 @@
//! A generic timer queue for async executors.
use crate::linked_list::{self, Link, LinkedList};
use crate::TimeoutError;
use core::future::{poll_fn, Future};
use core::pin::Pin;
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use core::task::{Poll, Waker};
use futures_util::{
future::{select, Either},
pin_mut,
};
use rtic_common::dropper::OnDrop;
mod backend;
mod tick_type;
pub use backend::TimerQueueBackend;
pub use tick_type::TimerQueueTicks;
/// Holds a waker and at which time instant this waker shall be awoken.
struct WaitingWaker<Backend: TimerQueueBackend> {
waker: Waker,
release_at: Backend::Ticks,
was_popped: AtomicBool,
}
impl<Backend: TimerQueueBackend> Clone for WaitingWaker<Backend> {
fn clone(&self) -> Self {
Self {
waker: self.waker.clone(),
release_at: self.release_at,
was_popped: AtomicBool::new(self.was_popped.load(Ordering::Relaxed)),
}
}
}
impl<Backend: TimerQueueBackend> PartialEq for WaitingWaker<Backend> {
fn eq(&self, other: &Self) -> bool {
self.release_at == other.release_at
}
}
impl<Backend: TimerQueueBackend> PartialOrd for WaitingWaker<Backend> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.release_at.compare(other.release_at))
}
}
/// A generic timer queue for async executors.
///
/// # Blocking
///
/// The internal priority queue uses global critical sections to manage access. This means that
/// `await`ing a delay will cause a lock of the entire system for O(n) time. In practice the lock
/// duration is ~10 clock cycles per element in the queue.
///
/// # Safety
///
/// This timer queue is based on an intrusive linked list, and by extension the links are stored
/// on the async stacks of callers. The links are deallocated on `drop` or when the wait is
/// complete.
///
/// Do not call `mem::forget` on an awaited future, or there will be dragons!
pub struct TimerQueue<Backend: TimerQueueBackend> {
queue: LinkedList<WaitingWaker<Backend>>,
initialized: AtomicBool,
}
/// This is needed to make the async closure in `delay_until` accept that we "share"
/// the link possible between threads.
struct LinkPtr<Backend: TimerQueueBackend>(*mut Option<linked_list::Link<WaitingWaker<Backend>>>);
impl<Backend: TimerQueueBackend> Clone for LinkPtr<Backend> {
fn clone(&self) -> Self {
LinkPtr(self.0)
}
}
impl<Backend: TimerQueueBackend> LinkPtr<Backend> {
/// This will dereference the pointer stored within and give out an `&mut`.
unsafe fn get(&mut self) -> &mut Option<linked_list::Link<WaitingWaker<Backend>>> {
&mut *self.0
}
}
unsafe impl<Backend: TimerQueueBackend> Send for LinkPtr<Backend> {}
unsafe impl<Backend: TimerQueueBackend> Sync for LinkPtr<Backend> {}
impl<Backend: TimerQueueBackend> TimerQueue<Backend> {
/// Make a new queue.
pub const fn new() -> Self {
Self {
queue: LinkedList::new(),
initialized: AtomicBool::new(false),
}
}
/// Forwards the `Monotonic::now()` method.
#[inline(always)]
pub fn now(&self) -> Backend::Ticks {
Backend::now()
}
/// Takes the initialized monotonic to initialize the TimerQueue.
pub fn initialize(&self, backend: Backend) {
self.initialized.store(true, Ordering::SeqCst);
// Don't run drop on `Mono`
core::mem::forget(backend);
}
/// Call this in the interrupt handler of the hardware timer supporting the `Monotonic`
///
/// # Safety
///
/// It's always safe to call, but it must only be called from the interrupt of the
/// monotonic timer for correct operation.
pub unsafe fn on_monotonic_interrupt(&self) {
Backend::clear_compare_flag();
Backend::on_interrupt();
loop {
let mut release_at = None;
let head = self.queue.pop_if(|head| {
release_at = Some(head.release_at);
let should_pop = Backend::now().is_at_least(head.release_at);
head.was_popped.store(should_pop, Ordering::Relaxed);
should_pop
});
match (head, release_at) {
(Some(link), _) => {
link.waker.wake();
}
(None, Some(instant)) => {
Backend::enable_timer();
Backend::set_compare(instant);
if Backend::now().is_at_least(instant) {
// The time for the next instant passed while handling it,
// continue dequeueing
continue;
}
break;
}
(None, None) => {
// Queue is empty
Backend::disable_timer();
break;
}
}
}
}
/// Timeout at a specific time.
pub async fn timeout_at<F: Future>(
&self,
instant: Backend::Ticks,
future: F,
) -> Result<F::Output, TimeoutError> {
let delay = self.delay_until(instant);
pin_mut!(future);
pin_mut!(delay);
match select(future, delay).await {
Either::Left((r, _)) => Ok(r),
Either::Right(_) => Err(TimeoutError),
}
}
/// Timeout after at least a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
&self,
duration: Backend::Ticks,
future: F,
) -> Result<F::Output, TimeoutError> {
let now = Backend::now();
let mut timeout = now.wrapping_add(duration);
if now != timeout {
timeout = timeout.wrapping_add(Backend::Ticks::ONE_TICK);
}
// Wait for one period longer, because by definition timers have an uncertainty
// of one period, so waiting for 'at least' needs to compensate for that.
self.timeout_at(timeout, future).await
}
/// Delay for at least some duration of time.
#[inline]
pub async fn delay(&self, duration: Backend::Ticks) {
let now = Backend::now();
let mut timeout = now.wrapping_add(duration);
if now != timeout {
timeout = timeout.wrapping_add(Backend::Ticks::ONE_TICK);
}
// Wait for one period longer, because by definition timers have an uncertainty
// of one period, so waiting for 'at least' needs to compensate for that.
self.delay_until(timeout).await;
}
/// Delay to some specific time instant.
pub async fn delay_until(&self, instant: Backend::Ticks) {
if !self.initialized.load(Ordering::Relaxed) {
panic!(
"The timer queue is not initialized with a monotonic, you need to run `initialize`"
);
}
let mut link_ptr: Option<linked_list::Link<WaitingWaker<Backend>>> = None;
// Make this future `Drop`-safe
// SAFETY(link_ptr): Shadow the original definition of `link_ptr` so we can't abuse it.
let mut link_ptr =
LinkPtr(&mut link_ptr as *mut Option<linked_list::Link<WaitingWaker<Backend>>>);
let mut link_ptr2 = link_ptr.clone();
let queue = &self.queue;
let marker = &AtomicUsize::new(0);
let dropper = OnDrop::new(|| {
queue.delete(marker.load(Ordering::Relaxed));
});
poll_fn(|cx| {
if Backend::now().is_at_least(instant) {
return Poll::Ready(());
}
// SAFETY: This pointer is only dereferenced here and on drop of the future
// which happens outside this `poll_fn`'s stack frame, so this mutable access cannot
// happen at the same time as `dropper` runs.
let link = unsafe { link_ptr2.get() };
if link.is_none() {
let link_ref = link.insert(Link::new(WaitingWaker {
waker: cx.waker().clone(),
release_at: instant,
was_popped: AtomicBool::new(false),
}));
// SAFETY(new_unchecked): The address to the link is stable as it is defined
//outside this stack frame.
// SAFETY(insert): `link_ref` lifetime comes from `link_ptr` that is shadowed, and
// we make sure in `dropper` that the link is removed from the queue before
// dropping `link_ptr` AND `dropper` makes sure that the shadowed `link_ptr` lives
// until the end of the stack frame.
let (head_updated, addr) = unsafe { queue.insert(Pin::new_unchecked(link_ref)) };
marker.store(addr, Ordering::Relaxed);
if head_updated {
// Pend the monotonic handler if the queue head was updated.
Backend::pend_interrupt()
}
}
Poll::Pending
})
.await;
// SAFETY: We only run this and dereference the pointer if we have
// exited the `poll_fn` below in the `drop(dropper)` call. The other dereference
// of this pointer is in the `poll_fn`.
if let Some(link) = unsafe { link_ptr.get() } {
if link.val.was_popped.load(Ordering::Relaxed) {
// If it was popped from the queue there is no need to run delete
dropper.defuse();
}
} else {
// Make sure that our link is deleted from the list before we drop this stack
drop(dropper);
}
}
}

View file

@ -0,0 +1,44 @@
use super::{TimerQueue, TimerQueueTicks};
/// A backend definition for a monotonic clock/counter.
pub trait TimerQueueBackend: 'static + Sized {
/// The type for ticks.
type Ticks: TimerQueueTicks;
/// Get the current time.
fn now() -> Self::Ticks;
/// Set the compare value of the timer interrupt.
///
/// **Note:** This method does not need to handle race conditions of the monotonic, the timer
/// queue in RTIC checks this.
fn set_compare(instant: Self::Ticks);
/// Clear the compare interrupt flag.
fn clear_compare_flag();
/// Pend the timer's interrupt.
fn pend_interrupt();
/// Optional. Runs on interrupt before any timer queue handling.
fn on_interrupt() {}
/// Optional. This is used to save power, this is called when the timer queue is not empty.
///
/// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
/// based of `now()` is still valid.
///
/// NOTE: This may be called more than once.
fn enable_timer() {}
/// Optional. This is used to save power, this is called when the timer queue is empty.
///
/// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
/// based of `now()` is still valid.
///
/// NOTE: This may be called more than once.
fn disable_timer() {}
/// Returns a reference to the underlying timer queue.
fn timer_queue() -> &'static TimerQueue<Self>;
}

View file

@ -0,0 +1,49 @@
use core::cmp;
/// The ticks of a timer.
pub trait TimerQueueTicks: Copy + PartialEq + Eq {
/// Represents a single tick.
const ONE_TICK: Self;
/// Compares to another tick count.
///
/// Takes into account timer wrapping; if the difference is more than
/// half the value range, the result will be flipped.
fn compare(self, other: Self) -> cmp::Ordering;
/// True if `self` is at the same time as `other` or later.
///
/// Takes into account timer wrapping; if the difference is more than
/// half the value range, the result will be negated.
fn is_at_least(self, other: Self) -> bool {
match self.compare(other) {
cmp::Ordering::Less => false,
cmp::Ordering::Equal => true,
cmp::Ordering::Greater => true,
}
}
/// Wrapping addition.
fn wrapping_add(self, other: Self) -> Self;
}
impl TimerQueueTicks for u32 {
const ONE_TICK: Self = 1;
fn compare(self, other: Self) -> cmp::Ordering {
(self.wrapping_sub(other) as i32).cmp(&0)
}
fn wrapping_add(self, other: Self) -> Self {
u32::wrapping_add(self, other)
}
}
impl TimerQueueTicks for u64 {
const ONE_TICK: Self = 1;
fn compare(self, other: Self) -> cmp::Ordering {
(self.wrapping_sub(other) as i64).cmp(&0)
}
fn wrapping_add(self, other: Self) -> Self {
u64::wrapping_add(self, other)
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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",
][..]
};

View file

@ -299,7 +299,26 @@ pub fn cargo_doc<'c>(
backend: Backends,
arguments: &'c Option<ExtraArguments>,
) -> Vec<FinalRunResult<'c>> {
let features = Some(backend.to_target().and_features(backend.to_rtic_feature()));
let extra_doc_features = [
"rtic-monotonics/cortex-m-systick",
"rtic-monotonics/rp2040",
"rtic-monotonics/nrf52840",
"imxrt-ral/imxrt1011",
"rtic-monotonics/imxrt_gpt1",
"rtic-monotonics/imxrt_gpt2",
"rtic-monotonics/stm32h725ag",
"rtic-monotonics/stm32_tim2",
"rtic-monotonics/stm32_tim3",
"rtic-monotonics/stm32_tim4",
"rtic-monotonics/stm32_tim5",
"rtic-monotonics/stm32_tim15",
];
let features = Some(format!(
"{},{}",
backend.to_target().and_features(backend.to_rtic_feature()),
extra_doc_features.join(",")
));
let command = CargoCommand::Doc {
cargoarg,