nrf-rs / nrf-hal

A Rust HAL for the nRF family of devices
Apache License 2.0
499 stars 139 forks source link

RTIC monotonic implementation #384

Open barafael opened 2 years ago

barafael commented 2 years ago

There is already a working implementation of RTIC monotonic timer for TIMER{0,1,2} here: https://github.com/kalkyl/nrf-play/blob/47f4410d4e39374c18ff58dc17c25159085fb526/src/mono.rs It would be nice for this to be part of the HAL so that the implementation does not need to be copied every time :) I'm trying to do this, but I'm not sure how.

ivajon commented 1 year ago

I realize that this might be stale but, It would be relatively simple to add this. I made a draft implementation making the abstraction agnostic to whether you are using the RTC or the TIMER. Using the RTC yields much better power efficiency.

Using this would look something like this:

use nrf5284_hal as hal;
use hal::monotonic::*;
use hal::rtc::RTC0;
use hal::timer::TIMER0;

#[rtic::app(device = nrf52840_hal::pac,dispatchers=[TIMER2,TIMER3])]
mod app {

    use super::*;
    #[monotonic(binds = RTC0, default = true)]
    type MyMono = MonotonicTimer<RTC0>;
    // Or this if using timer
    #[monotonic(binds = TIMER0, default = true)]
    type MyMono = MonotonicTimer<TIMER0>;

    #[shared]
    struct Shared {}

    #[local]
    struct Local {}

    #[init]
    fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
        let mono = MonotonicTimer::new(cx.device.RTC0);
        // Or this if using timer
        let mono = MonotonicTimer::new(cx.device.TIMER0);

        // Return shared, local and mono
        (
            Shared {},
            Local {},
            init::Monotonics(mono),
        )
   }
}

Making it trivial to change clocks when writing your app

ivajon commented 1 year ago

@barafael I think that I have finished an implementation for this

The API looks like this at the time of writing and it will not change much.

#![no_main]
#![no_std]
use core::panic::PanicInfo;
use nrf52840_hal as hal;
use hal::monotonic::MonotonicTimer;
use hal::pac;
#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
    loop {}
}
#[rtic::app(device = pac, dispatchers = [TIMER2, TIMER3])]
mod app {
    use super::*;
    #[monotonic(binds = RTC0, default = true)]
    type Mono = MonotonicTimer<pac::RTC0, 32_768u32>;
    // Conversely
    #[monotonic(binds = TIMER0, default = true)]
    type Mono = MonotonicTimer<pac::TIMER0, 1_000_000u32>;
    #[shared]
    struct Shared {}
    #[local]
    struct Local {}
    #[init]
    fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
        let clocks = hal::clocks::Clocks::new(cx.device.CLOCK)
        .set_lfclk_src_external(hal::clocks::LfOscConfiguration::NoExternalNoBypass)
        .start_lfclk()
        .enable_ext_hfosc();
        let mono = Mono::new(cx.device.RTC0, &clocks).unwrap();
        // Conversely
        let mono = Mono::new(cx.device.TIMER0, &clocks).unwrap();

        // Return shared, local and mono
        (Shared {}, Local {}, init::Monotonics(mono))
    }
    #[idle()]
    fn idle(_cx: idle::Context) -> ! {
        loop {}
    }
}

I am still to test it as I do not have any hardware at hand but feel free to try it.