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 OLDOLDOLDSTABLE_VERSION: 0.4
QEMU_VERSION: 8.2.0 QEMU_VERSION: 8.2.0
QEMU_URL: https://download.qemu.org/qemu-8.2.0.tar.xz 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: jobs:
# Run cargo xtask format-check # Run cargo xtask format-check
@ -257,7 +259,7 @@ jobs:
run: | run: |
sudo apt update sudo apt update
sudo apt install -y qemu-system-arm qemu-system-riscv32 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' }} - if: ${{ steps.cache-qemu.outputs.cache-hit != 'true' }}
name: Download QEMU name: Download QEMU
@ -277,7 +279,15 @@ jobs:
name: Build QEMU name: Build QEMU
run: | run: |
cd qemu-${{ env.QEMU_VERSION }} 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 - name: Archive QEMU build
run: | run: |
@ -413,6 +423,75 @@ jobs:
if: ${{ matrix.backend != 'riscv32-imc-clint' }} if: ${{ matrix.backend != 'riscv32-imc-clint' }}
run: cargo xtask --deny-warnings --platform hifive1 --backend ${{ matrix.backend }} qemu 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 # Run test suite
tests: tests:
name: tests name: tests
@ -720,7 +799,7 @@ jobs:
tool: mdbook-mermaid tool: mdbook-mermaid
- name: mdBook Action - name: mdBook Action
uses: peaceiris/actions-mdbook@v1 uses: peaceiris/actions-mdbook@v2
with: with:
mdbook-version: 'latest' mdbook-version: 'latest'
@ -831,7 +910,7 @@ jobs:
tar -xf bookstodeploy.tar tar -xf bookstodeploy.tar
- name: Deploy to GH-pages - name: Deploy to GH-pages
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@v4
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./bookstodeploy publish_dir: ./bookstodeploy
@ -853,6 +932,7 @@ jobs:
- checkexamplesesp32c6 - checkexamplesesp32c6
- testexampleslm3s6965 - testexampleslm3s6965
- testexampleshifive1 - testexampleshifive1
- testexamplesesp32c3
- tests - tests
- docs - docs
- mdbook - mdbook

1
.gitignore vendored
View file

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

View file

@ -1,5 +1,9 @@
[target.riscv32imc-unknown-none-elf] [target.riscv32imc-unknown-none-elf]
runner = "espflash flash --monitor" # Real hardware
#runner = "espflash flash --monitor"
# QEMU emulator
runner = "./runner.sh"
[build] [build]
rustflags = [ 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", "critical-section",
"rtic-core", "rtic-core",
"rtic-macros", "rtic-macros",
"rtic-monotonics",
] ]
[[package]] [[package]]
@ -392,12 +391,11 @@ dependencies = [
[[package]] [[package]]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "1.5.0" version = "2.0.0"
dependencies = [ dependencies = [
"atomic-polyfill", "atomic-polyfill",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"embedded-hal 1.0.0",
"fugit", "fugit",
"rtic-time", "rtic-time",
] ]
@ -417,9 +415,12 @@ dependencies = [
[[package]] [[package]]
name = "rtic-time" name = "rtic-time"
version = "1.3.0" version = "2.0.0"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"embedded-hal 1.0.0",
"embedded-hal-async",
"fugit",
"futures-util", "futures-util",
"rtic-common", "rtic-common",
] ]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -133,9 +133,18 @@ dependencies = [
[[package]] [[package]]
name = "embedded-hal" name = "embedded-hal"
version = "1.0.0-rc.2" version = "1.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e57ec6ad0bc8eb967cf9c9f144177f5e8f2f6f02dad0b8b683f9f05f6b22def" checksum = "bc402f79e1fd22731ca945b4f97b5ff37e7b3f379312595c42bb2e8811c29920"
[[package]]
name = "embedded-hal-async"
version = "1.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa1fba2ef2ffb35d614acc6fb323ddf7facc45c069f24544d49ea54e5043626d"
dependencies = [
"embedded-hal 1.0.0-rc.3",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
@ -440,12 +449,12 @@ dependencies = [
[[package]] [[package]]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "1.4.0" version = "2.0.0"
dependencies = [ dependencies = [
"atomic-polyfill", "atomic-polyfill",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"embedded-hal 1.0.0-rc.2", "embedded-hal 1.0.0-rc.3",
"fugit", "fugit",
"imxrt-ral", "imxrt-ral",
"rtic-time", "rtic-time",
@ -453,9 +462,12 @@ dependencies = [
[[package]] [[package]]
name = "rtic-time" name = "rtic-time"
version = "1.1.0" version = "2.0.0"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"embedded-hal 1.0.0-rc.3",
"embedded-hal-async",
"fugit",
"futures-util", "futures-util",
"rtic-common", "rtic-common",
] ]

View file

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

View file

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

View file

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

View file

@ -246,4 +246,3 @@ pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2
vec![] 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! For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## Unreleased ## Unreleased - v2.0.0
### Changed ### Changed
- Rework all timers based on `rtic-time 2.0.0`
- Most timer tick rates are now configurable
- Tweak `build.rs` to avoid warnings in Nightly 1.78+ - Tweak `build.rs` to avoid warnings in Nightly 1.78+
- Removed unused `rust-toolchain.toml` - Removed unused `rust-toolchain.toml`

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
//! [`Monotonic`] impl for the 32-bit timers of the nRF series. //! [`Monotonic`](rtic_time::Monotonic) implementation for the 32-bit timers of the nRF series.
//! //!
//! Not all timers are available on all parts. Ensure that only the available //! Not all timers are available on all parts. Ensure that only the available
//! timers are exposed by having the correct `nrf52*` feature enabled for `rtic-monotonics`. //! timers are exposed by having the correct `nrf52*` feature enabled for `rtic-monotonics`.
@ -6,139 +6,217 @@
//! # Example //! # Example
//! //!
//! ``` //! ```
//! use rtic_monotonics::nrf::timer::*; //! use rtic_monotonics::nrf::timer::prelude::*;
//! nrf_timer0_monotonic!(Mono);
//! //!
//! fn init() { //! fn init() {
//! # // This is normally provided by the selected PAC //! # // This is normally provided by the selected PAC
//! # let timer = unsafe { core::mem::transmute(()) }; //! # let timer = unsafe { core::mem::transmute(()) };
//! // Generate the required token
//! let token = rtic_monotonics::create_nrf_timer0_monotonic_token!();
//!
//! // Start the monotonic //! // Start the monotonic
//! Timer0::start(timer, token); //! Mono::start(timer);
//! } //! }
//! //!
//! async fn usage() { //! async fn usage() {
//! loop { //! loop {
//! // Use the monotonic //! // Use the monotonic
//! Timer0::delay(100.millis()).await; //! let timestamp = Mono::now();
//! Mono::delay(100.millis()).await;
//! } //! }
//! } //! }
//! ``` //! ```
use crate::{Monotonic, TimeoutError, TimerQueue}; /// Common definitions and traits for using the nRF Timer monotonics
use atomic_polyfill::{AtomicU32, Ordering}; pub mod prelude {
use core::future::Future; pub use crate::nrf_timer0_monotonic;
pub use 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}; pub use fugit::{self, ExtU64, ExtU64Ceil};
use rtic_time::half_period_counter::calculate_now; }
#[cfg(feature = "nrf52810")] #[cfg(feature = "nrf52810")]
use nrf52810_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; #[doc(hidden)]
pub use nrf52810_pac::{self as pac, TIMER0, TIMER1, TIMER2};
#[cfg(feature = "nrf52811")] #[cfg(feature = "nrf52811")]
use nrf52811_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; #[doc(hidden)]
pub use nrf52811_pac::{self as pac, TIMER0, TIMER1, TIMER2};
#[cfg(feature = "nrf52832")] #[cfg(feature = "nrf52832")]
use nrf52832_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; #[doc(hidden)]
pub use nrf52832_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
#[cfg(feature = "nrf52833")] #[cfg(feature = "nrf52833")]
use nrf52833_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; #[doc(hidden)]
pub use nrf52833_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
#[cfg(feature = "nrf52840")] #[cfg(feature = "nrf52840")]
use nrf52840_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; #[doc(hidden)]
pub use nrf52840_pac::{self as pac, TIMER0, TIMER1, TIMER2, TIMER3, TIMER4};
#[cfg(feature = "nrf5340-app")] #[cfg(feature = "nrf5340-app")]
use nrf5340_app_pac::{ #[doc(hidden)]
self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, pub use nrf5340_app_pac::{
self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2,
}; };
#[cfg(feature = "nrf5340-net")] #[cfg(feature = "nrf5340-net")]
use nrf5340_net_pac::{ #[doc(hidden)]
self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, pub use nrf5340_net_pac::{
self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2,
}; };
#[cfg(feature = "nrf9160")] #[cfg(feature = "nrf9160")]
use nrf9160_pac::{ #[doc(hidden)]
self as pac, Interrupt, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, pub use nrf9160_pac::{self as pac, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2};
use atomic_polyfill::{AtomicU32, Ordering};
use rtic_time::{
half_period_counter::calculate_now,
timer_queue::{TimerQueue, TimerQueueBackend},
}; };
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! __internal_create_nrf_timer_interrupt { macro_rules! __internal_create_nrf_timer_interrupt {
($mono_timer:ident, $timer:ident, $timer_token:ident) => {{ ($mono_backend:ident, $timer:ident) => {
#[no_mangle] #[no_mangle]
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern "C" fn $timer() { unsafe extern "C" fn $timer() {
$crate::nrf::timer::$mono_timer::__tq().on_monotonic_interrupt(); use $crate::TimerQueueBackend;
$crate::nrf::timer::$mono_backend::timer_queue().on_monotonic_interrupt();
}
};
} }
pub struct $timer_token; #[doc(hidden)]
unsafe impl $crate::InterruptToken<$crate::nrf::timer::$mono_timer> for $timer_token {}
$timer_token
}};
}
/// Register the Timer0 interrupt for the monotonic.
#[macro_export] #[macro_export]
macro_rules! create_nrf_timer0_monotonic_token { macro_rules! __internal_create_nrf_timer_struct {
() => {{ ($name:ident, $mono_backend:ident, $timer:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer0, TIMER0, Timer0Token) /// 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);
}
} }
/// Register the Timer1 interrupt for the monotonic. 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 },
>;
}
$crate::rtic_time::impl_embedded_hal_delay_fugit!($name);
$crate::rtic_time::impl_embedded_hal_async_delay_fugit!($name);
};
}
/// Create an Timer0 based monotonic and register the TIMER0 interrupt for it.
///
/// See [`crate::nrf::timer`] for more details.
#[macro_export] #[macro_export]
macro_rules! create_nrf_timer1_monotonic_token { macro_rules! nrf_timer0_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer1, TIMER1, Timer1Token) $crate::__internal_create_nrf_timer_struct!($name, Timer0Backend, TIMER0, $tick_rate_hz);
}}; };
} }
/// Register the Timer2 interrupt for the monotonic. /// Create an Timer1 based monotonic and register the TIMER1 interrupt for it.
///
/// See [`crate::nrf::timer`] for more details.
#[macro_export] #[macro_export]
macro_rules! create_nrf_timer2_monotonic_token { macro_rules! nrf_timer1_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer2, TIMER2, Timer2Token) $crate::__internal_create_nrf_timer_struct!($name, Timer1Backend, TIMER1, $tick_rate_hz);
}}; };
} }
/// Register the Timer3 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! nrf_timer2_monotonic {
($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_struct!($name, Timer2Backend, TIMER2, $tick_rate_hz);
};
}
/// Create an Timer3 based monotonic and register the TIMER3 interrupt for it.
///
/// See [`crate::nrf::timer`] for more details.
#[cfg_attr( #[cfg_attr(
docsrs, docsrs,
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
)] )]
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
#[macro_export] #[macro_export]
macro_rules! create_nrf_timer3_monotonic_token { macro_rules! nrf_timer3_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer3, TIMER3, Timer3Token) $crate::__internal_create_nrf_timer_struct!($name, Timer3Backend, TIMER3, $tick_rate_hz);
}}; };
} }
/// Register the Timer4 interrupt for the monotonic. /// Create an Timer4 based monotonic and register the TIMER4 interrupt for it.
///
/// See [`crate::nrf::timer`] for more details.
#[cfg_attr( #[cfg_attr(
docsrs, docsrs,
doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))) doc(cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")))
)] )]
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
#[macro_export] #[macro_export]
macro_rules! create_nrf_timer4_monotonic_token { macro_rules! nrf_timer4_monotonic {
() => {{ ($name:ident, $tick_rate_hz:expr) => {
$crate::__internal_create_nrf_timer_interrupt!(Timer4, TIMER4, Timer4Token) $crate::__internal_create_nrf_timer_struct!($name, Timer4Backend, TIMER4, $tick_rate_hz);
}}; };
} }
macro_rules! make_timer { macro_rules! make_timer {
($mono_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { ($backend_name:ident, $timer:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
/// Monotonic timer queue implementation. /// Timer peripheral based [`TimerQueueBackend`].
$( $(
#[cfg_attr(docsrs, doc(cfg($($doc)*)))] #[cfg_attr(docsrs, doc(cfg($($doc)*)))]
)? )?
pub struct $mono_name; pub struct $backend_name;
static $overflow: AtomicU32 = AtomicU32::new(0); static $overflow: AtomicU32 = AtomicU32::new(0);
static $tq: TimerQueue<$mono_name> = TimerQueue::new(); static $tq: TimerQueue<$backend_name> = TimerQueue::new();
impl $mono_name { impl $backend_name {
/// Start the timer monotonic. /// Starts the timer.
pub fn start(timer: $timer, _interrupt_token: impl crate::InterruptToken<Self>) { ///
// 1 MHz /// **Do not use this function directly.**
timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) }); ///
/// Use the prelude macros instead.
pub fn _start(timer: $timer, prescaler: u8) {
timer.prescaler.write(|w| unsafe { w.prescaler().bits(prescaler) });
timer.bitmode.write(|w| w.bitmode()._32bit()); timer.bitmode.write(|w| w.bitmode()._32bit());
// Disable interrupts, as preparation // Disable interrupts, as preparation
@ -184,70 +262,25 @@ macro_rules! make_timer {
// plus we are not using any external shared resources so we won't impact // plus we are not using any external shared resources so we won't impact
// basepri/source masking based critical sections. // basepri/source masking based critical sections.
unsafe { unsafe {
crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, Interrupt::$timer); crate::set_monotonic_prio(pac::NVIC_PRIO_BITS, pac::Interrupt::$timer);
pac::NVIC::unmask(Interrupt::$timer); pac::NVIC::unmask(pac::Interrupt::$timer);
}
} }
} }
/// Used to access the underlying timer queue impl TimerQueueBackend for $backend_name {
#[doc(hidden)] type Ticks = u64;
pub fn __tq() -> &'static TimerQueue<$mono_name> {
&$tq
}
/// Timeout at a specific time. fn now() -> Self::Ticks {
#[inline]
pub async fn timeout_at<F: Future>(
instant: <Self as Monotonic>::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_at(instant, future).await
}
/// Timeout after a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
duration: <Self as Monotonic>::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
$tq.timeout_after(duration, future).await
}
/// Delay for some duration of time.
#[inline]
pub async fn delay(duration: <Self as Monotonic>::Duration) {
$tq.delay(duration).await;
}
/// Delay to some specific time instant.
#[inline]
pub async fn delay_until(instant: <Self as Monotonic>::Instant) {
$tq.delay_until(instant).await;
}
}
rtic_time::embedded_hal_delay_impl_fugit64!($mono_name);
#[cfg(feature = "embedded-hal-async")]
rtic_time::embedded_hal_async_delay_impl_fugit64!($mono_name);
impl Monotonic for $mono_name {
const ZERO: Self::Instant = Self::Instant::from_ticks(0);
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
type Instant = fugit::TimerInstantU64<1_000_000>;
type Duration = fugit::TimerDurationU64<1_000_000>;
fn now() -> Self::Instant {
let timer = unsafe { &*$timer::PTR }; let timer = unsafe { &*$timer::PTR };
Self::Instant::from_ticks(calculate_now( calculate_now(
|| $overflow.load(Ordering::Relaxed), || $overflow.load(Ordering::Relaxed),
|| { || {
timer.tasks_capture[3].write(|w| unsafe { w.bits(1) }); timer.tasks_capture[3].write(|w| unsafe { w.bits(1) });
timer.cc[3].read().bits() timer.cc[3].read().bits()
} }
)) )
} }
fn on_interrupt() { fn on_interrupt() {
@ -268,9 +301,9 @@ macro_rules! make_timer {
} }
} }
fn set_compare(instant: Self::Instant) { fn set_compare(instant: Self::Ticks) {
let timer = unsafe { &*$timer::PTR }; let timer = unsafe { &*$timer::PTR };
timer.cc[0].write(|w| unsafe { w.cc().bits(instant.ticks() as u32) }); timer.cc[0].write(|w| unsafe { w.cc().bits(instant as u32) });
} }
fn clear_compare_flag() { fn clear_compare_flag() {
@ -279,16 +312,20 @@ macro_rules! make_timer {
} }
fn pend_interrupt() { fn pend_interrupt() {
pac::NVIC::pend(Interrupt::$timer); pac::NVIC::pend(pac::Interrupt::$timer);
}
fn timer_queue() -> &'static TimerQueue<$backend_name> {
&$tq
} }
} }
}; };
} }
make_timer!(Timer0, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ); make_timer!(Timer0Backend, TIMER0, TIMER0_OVERFLOWS, TIMER0_TQ);
make_timer!(Timer1, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ); make_timer!(Timer1Backend, TIMER1, TIMER1_OVERFLOWS, TIMER1_TQ);
make_timer!(Timer2, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ); make_timer!(Timer2Backend, TIMER2, TIMER2_OVERFLOWS, TIMER2_TQ);
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
make_timer!(Timer3, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); make_timer!(Timer3Backend, TIMER3, TIMER3_OVERFLOWS, TIMER3_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
make_timer!(Timer4, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))); make_timer!(Timer4Backend, TIMER4, TIMER4_OVERFLOWS, TIMER4_TQ, doc: (any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840")));

View file

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

View file

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

View file

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

View file

@ -5,11 +5,18 @@ This project adheres to [Semantic Versioning](http://semver.org/).
For each category, *Added*, *Changed*, *Fixed* add new entries at the top! For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## Unreleased ## Unreleased - v2.0.0
### Added ### Added
### Changed ### Changed
- Full rewrite of the `Monotonic` API.
- Now split into multiple traits:
- `Monotonic` - A user-facing trait that defines what the functionality of a monotonic is.
- `TimerQueueBackend` - The set of functionality a backend must provide in order to be used with the `TimerQueue`.
- `TimerQueue` is now purely based on ticks and has no concept of real time.
- The `TimerQueueBasedMonotonic` trait implements a `Monotonic` based on a `TimerQueueBackend`, translating ticks into `Instant` and `Duration`.
### Fixed ### Fixed

View file

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

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

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

View file

@ -5,285 +5,60 @@
#![no_std] #![no_std]
#![deny(missing_docs)] #![deny(missing_docs)]
#![allow(incomplete_features)] #![allow(async_fn_in_trait)]
use core::future::{poll_fn, Future};
use core::pin::Pin;
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use core::task::{Poll, Waker};
use futures_util::{
future::{select, Either},
pin_mut,
};
use linked_list::{Link, LinkedList};
pub use monotonic::Monotonic;
use rtic_common::dropper::OnDrop;
pub mod half_period_counter; pub mod half_period_counter;
mod linked_list; mod linked_list;
mod monotonic; pub mod monotonic;
pub mod timer_queue;
/// Holds a waker and at which time instant this waker shall be awoken.
struct WaitingWaker<Mono: Monotonic> {
waker: Waker,
release_at: Mono::Instant,
was_popped: AtomicBool,
}
impl<Mono: Monotonic> Clone for WaitingWaker<Mono> {
fn clone(&self) -> Self {
Self {
waker: self.waker.clone(),
release_at: self.release_at,
was_popped: AtomicBool::new(self.was_popped.load(Ordering::Relaxed)),
}
}
}
impl<Mono: Monotonic> PartialEq for WaitingWaker<Mono> {
fn eq(&self, other: &Self) -> bool {
self.release_at == other.release_at
}
}
impl<Mono: Monotonic> PartialOrd for WaitingWaker<Mono> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.release_at.partial_cmp(&other.release_at)
}
}
/// A generic timer queue for async executors.
///
/// # Blocking
///
/// The internal priority queue uses global critical sections to manage access. This means that
/// `await`ing a delay will cause a lock of the entire system for O(n) time. In practice the lock
/// duration is ~10 clock cycles per element in the queue.
///
/// # Safety
///
/// This timer queue is based on an intrusive linked list, and by extension the links are strored
/// on the async stacks of callers. The links are deallocated on `drop` or when the wait is
/// complete.
///
/// Do not call `mem::forget` on an awaited future, or there will be dragons!
pub struct TimerQueue<Mono: Monotonic> {
queue: LinkedList<WaitingWaker<Mono>>,
initialized: AtomicBool,
}
/// This indicates that there was a timeout. /// This indicates that there was a timeout.
pub struct TimeoutError; pub struct TimeoutError;
/// This is needed to make the async closure in `delay_until` accept that we "share" /// Re-export for macros
/// the link possible between threads. pub use embedded_hal;
struct LinkPtr<Mono: Monotonic>(*mut Option<linked_list::Link<WaitingWaker<Mono>>>); /// Re-export for macros
pub use embedded_hal_async;
impl<Mono: Monotonic> Clone for LinkPtr<Mono> { /// # A monotonic clock / counter definition.
fn clone(&self) -> Self {
LinkPtr(self.0)
}
}
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`
/// ///
/// # Safety /// ## Correctness
/// ///
/// It's always safe to call, but it must only be called from the interrupt of the /// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This
/// monotonic timer for correct operation. /// is a requirement on the time library that the user chooses to use.
pub unsafe fn on_monotonic_interrupt(&self) { pub trait Monotonic {
Mono::clear_compare_flag(); /// The type for instant, defining an instant in time.
Mono::on_interrupt(); ///
/// **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>;
loop { /// The type for duration, defining a duration of time.
let mut release_at = None; ///
let head = self.queue.pop_if(|head| { /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used.
release_at = Some(head.release_at); type Duration: Copy;
let should_pop = Mono::now() >= head.release_at; /// Get the current time.
head.was_popped.store(should_pop, Ordering::Relaxed); fn now() -> Self::Instant;
should_pop /// Delay for some duration of time.
}); async fn delay(duration: Self::Duration);
match (head, release_at) {
(Some(link), _) => {
link.waker.wake();
}
(None, Some(instant)) => {
Mono::enable_timer();
Mono::set_compare(instant);
if Mono::now() >= instant {
// The time for the next instant passed while handling it,
// continue dequeueing
continue;
}
break;
}
(None, None) => {
// Queue is empty
Mono::disable_timer();
break;
}
}
}
}
/// Timeout at a specific time.
pub async fn timeout_at<F: Future>(
&self,
instant: Mono::Instant,
future: F,
) -> Result<F::Output, TimeoutError> {
let delay = self.delay_until(instant);
pin_mut!(future);
pin_mut!(delay);
match select(future, delay).await {
Either::Left((r, _)) => Ok(r),
Either::Right(_) => Err(TimeoutError),
}
}
/// Timeout after at least a specific duration.
#[inline]
pub async fn timeout_after<F: Future>(
&self,
duration: Mono::Duration,
future: F,
) -> Result<F::Output, TimeoutError> {
let now = Mono::now();
let mut timeout = now + duration;
if now != timeout {
timeout = timeout + Mono::TICK_PERIOD;
}
// Wait for one period longer, because by definition timers have an uncertainty
// of one period, so waiting for 'at least' needs to compensate for that.
self.timeout_at(timeout, future).await
}
/// Delay for at least some duration of time.
#[inline]
pub async fn delay(&self, duration: Mono::Duration) {
let now = Mono::now();
let mut timeout = now + duration;
if now != timeout {
timeout = timeout + Mono::TICK_PERIOD;
}
// Wait for one period longer, because by definition timers have an uncertainty
// of one period, so waiting for 'at least' needs to compensate for that.
self.delay_until(timeout).await;
}
/// Delay to some specific time instant. /// Delay to some specific time instant.
pub async fn delay_until(&self, instant: Mono::Instant) { async fn delay_until(instant: Self::Instant);
if !self.initialized.load(Ordering::Relaxed) {
panic!( /// Timeout at a specific time.
"The timer queue is not initialized with a monotonic, you need to run `initialize`" async fn timeout_at<F: core::future::Future>(
); instant: Self::Instant,
} future: F,
) -> Result<F::Output, TimeoutError>;
let mut link_ptr: Option<linked_list::Link<WaitingWaker<Mono>>> = None;
/// Timeout after a specific duration.
// Make this future `Drop`-safe async fn timeout_after<F: core::future::Future>(
// SAFETY(link_ptr): Shadow the original definition of `link_ptr` so we can't abuse it. duration: Self::Duration,
let mut link_ptr = future: F,
LinkPtr(&mut link_ptr as *mut Option<linked_list::Link<WaitingWaker<Mono>>>); ) -> Result<F::Output, TimeoutError>;
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);
}
}
} }

View file

@ -1,236 +1,8 @@
//! A monotonic clock / counter definition. //! Structs and traits surrounding the [`Monotonic`](crate::Monotonic) trait.
/// # A monotonic clock / counter definition. pub use timer_queue_based_monotonic::{
/// TimerQueueBasedDuration, TimerQueueBasedInstant, TimerQueueBasedMonotonic,
/// ## Correctness
///
/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This
/// is a requirement on the time library that the user chooses to use.
pub trait Monotonic {
/// The time at time zero.
const ZERO: Self::Instant;
/// The duration between two timer ticks.
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], mod embedded_hal_macros;
/// based on [`fugit::ExtU64Ceil`][ExtU64Ceil]. mod timer_queue_based_monotonic;
///
/// [DelayNs]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/trait.DelayNs.html
/// [ExtU64Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU64Ceil.html
#[macro_export]
macro_rules! embedded_hal_async_delay_impl_fugit64 {
($t:ty) => {
impl ::embedded_hal_async::delay::DelayNs for $t {
#[inline]
async fn delay_ns(&mut self, ns: u32) {
use ::fugit::ExtU64Ceil;
Self::delay(u64::from(ns).nanos_at_least()).await;
}
#[inline]
async fn delay_us(&mut self, us: u32) {
use ::fugit::ExtU64Ceil;
Self::delay(u64::from(us).micros_at_least()).await;
}
#[inline]
async fn delay_ms(&mut self, ms: u32) {
use ::fugit::ExtU64Ceil;
Self::delay(u64::from(ms).millis_at_least()).await;
}
}
};
}
/// Creates impl blocks for [`embedded_hal::delay::DelayNs`][DelayNs],
/// based on [`fugit::ExtU32Ceil`][ExtU32Ceil].
///
/// [DelayNs]: https://docs.rs/embedded-hal/latest/embedded_hal/delay/trait.DelayNs.html
/// [ExtU32Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU32Ceil.html
#[macro_export]
macro_rules! embedded_hal_delay_impl_fugit32 {
($t:ty) => {
impl ::embedded_hal::delay::DelayNs for $t {
fn delay_ns(&mut self, ns: u32) {
use ::fugit::ExtU32Ceil;
let now = Self::now();
let mut done = now + ns.nanos_at_least();
if now != done {
// Compensate for sub-tick uncertainty
done += Self::TICK_PERIOD;
}
while Self::now() < done {}
}
fn delay_us(&mut self, us: u32) {
use ::fugit::ExtU32Ceil;
let now = Self::now();
let mut done = now + us.micros_at_least();
if now != done {
// Compensate for sub-tick uncertainty
done += Self::TICK_PERIOD;
}
while Self::now() < done {}
}
fn delay_ms(&mut self, ms: u32) {
use ::fugit::ExtU32Ceil;
let now = Self::now();
let mut done = now + ms.millis_at_least();
if now != done {
// Compensate for sub-tick uncertainty
done += Self::TICK_PERIOD;
}
while Self::now() < done {}
}
}
};
}
/// Creates impl blocks for [`embedded_hal_async::delay::DelayNs`][DelayNs],
/// based on [`fugit::ExtU32Ceil`][ExtU32Ceil].
///
/// [DelayNs]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/trait.DelayNs.html
/// [ExtU32Ceil]: https://docs.rs/fugit/latest/fugit/trait.ExtU32Ceil.html
#[macro_export]
macro_rules! embedded_hal_async_delay_impl_fugit32 {
($t:ty) => {
impl ::embedded_hal_async::delay::DelayNs for $t {
#[inline]
async fn delay_ns(&mut self, ns: u32) {
use ::fugit::ExtU32Ceil;
Self::delay(ns.nanos_at_least()).await;
}
#[inline]
async fn delay_us(&mut self, us: u32) {
use ::fugit::ExtU32Ceil;
Self::delay(us.micros_at_least()).await;
}
#[inline]
async fn delay_ms(&mut self, ms: u32) {
use ::fugit::ExtU32Ceil;
Self::delay(ms.millis_at_least()).await;
}
}
};
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,6 +10,9 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
### Added ### Added
- Unstable support for ESP32-C6 - Unstable support for ESP32-C6
### Changed
- Remove unused dependency `rtic-monotonics`
## [v2.1.1] - 2024-03-13 ## [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" bare-metal = "1.0.0"
# portable-atomic = { version = "0.3.19" } # portable-atomic = { version = "0.3.19" }
atomic-polyfill = "1" atomic-polyfill = "1"
rtic-monotonics = { path = "../rtic-monotonics", version = "1.4.0", optional = true }
rtic-macros = { path = "../rtic-macros", version = "=2.1.0" } rtic-macros = { path = "../rtic-macros", version = "=2.1.0" }
rtic-core = "1" rtic-core = "1"
critical-section = "1" critical-section = "1"
@ -71,7 +70,4 @@ riscv-clint-backend = [
] ]
# needed for testing # needed for testing
test-critical-section = [ test-critical-section = ["cortex-m/critical-section-single-core"]
"cortex-m/critical-section-single-core",
"rtic-monotonics/systick-100hz",
]

View file

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

View file

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

View file

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