David-OConnor / stm32-hal

This library provides access to STM32 peripherals in Rust.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Could you provide an example to implement rtic_monotonic::Monotonic trait?

seanybaggins opened this issue · comments

It would be nice if there was an example to implement the Monotonic trait using the hal api calls.

Here is an example using the peripheral access crate.
https://github.com/kalkyl/f411-rtic/blob/c1c74d887e3620bddcaf9217c93767e9f743d7b1/src/mono.rs

Good idea. I personally haven't used Monotomic (have been using RTIC just for the interrupt and shared-state functionality), but agree that would be a good addition. Perhaps instead of an example, actually implementing it for timers, although I'm not sure that's how it works.

In the latest release. I think there's a bug with it; can't get it to work properly. Hopefully an easy fix; LMK if you have any ideas, or if you're able to get it working as-is.

I did manage to implement my own timer. Here is the code. Might circle back and open up a PR for an example. Currently don't have the bandwidth to check the default implementation for the stm32. I might be able to take a look at it during a weekend during the holidays.

use rtic::Monotonic;
use stm32_hal2::clocks::Clocks;
use stm32_hal2::pac::TIM2;
use stm32_hal2::timer::{Alignment, CaptureCompareDma, CountDir, Timer, TimerConfig, UpdateReqSrc};

pub struct MonoTimer<T, const FREQ: u32>(Timer<T>);

impl<const FREQ: u32> MonoTimer<TIM2, FREQ> {
    pub fn new(timer: TIM2, clocks: &Clocks) -> Self {
        let timer_config = TimerConfig {
            one_pulse_mode: false,
            update_request_source: UpdateReqSrc::Any,
            auto_reload_preload: false,
            alignment: Alignment::Edge,
            capture_compare_dma: CaptureCompareDma::Update,
            direction: CountDir::Up,
        };
        let mut timer = Timer::new_tim2(timer, FREQ as f32, timer_config, &clocks);
        let prescaler = clocks.sysclk() / FREQ - 1;

        timer.set_auto_reload(u32::MAX);
        timer.set_prescaler(prescaler as u16);

        timer.regs.egr.write(|w| w.ug().set_bit());
        timer.regs.sr.write(|w| w.uif().clear_bit());
        timer.regs.cr1.write(|w| w.cen().set_bit().udis().set_bit());

        Self(timer)
    }

    pub fn counts(&self) -> u32 {
        self.0.read_count()
    }
}

impl<const FREQ: u32> Monotonic for MonoTimer<TIM2, FREQ> {
    type Instant = fugit::TimerInstantU32<FREQ>;
    type Duration = fugit::TimerDurationU32<FREQ>;

    #[inline(always)]
    fn now(&mut self) -> Self::Instant {
        let ticks = self.0.read_count();
        Self::Instant::from_ticks(ticks)
    }

    fn set_compare(&mut self, instant: Self::Instant) {
        // No idea. Taken from https://github.com/kalkyl/f411-rtic/blob/a696fce7d6d19fda2356c37642c4d53547982cca/src/mono.rs#L87
        self.0
            .regs
            .ccr1()
            .write(|w| w.ccr().bits(instant.duration_since_epoch().ticks()))
    }

    fn clear_compare_flag(&mut self) {
        self.0.regs.sr.write(|w| w.cc1if().clear_bit())
    }

    #[inline(always)]
    fn zero() -> Self::Instant {
        Self::Instant::from_ticks(0)
    }

    unsafe fn reset(&mut self) {
        self.0.regs.dier.write(|w| w.cc1ie().set_bit())
    }
}

Nice! Check the current impl here: https://github.com/David-OConnor/stm32-hal/blob/main/src/timer.rs#L791

Overall, same idea as what you posted, with the following exceptions:

  • Uses core::Duration, and a custom Instant type (see the newly-added instant module in this lib)
  • Delegates now to a non-trait-requiring time_elapsed fn: I think this function will be useful even without using Monotonic, for getting the time in seconds, ms, us, or ns since timer start.
  • Doesn't work when I try to spawn a Monotonic task in RTIC. Any ideas on that?
  • Has some untested wrapping logic, to keep a continuous time even after the timer resets and starts again.

Are you calling spawn_after or spawn? It could be the timer is hitting the auto_reload value and resetting before it hits the value you asked to wait for.

Looks like spawn works; tried increasing the timer timeout period, but something is still not working with spawn_after.

I'm dropping further integration due to the direction RTIC is moving.

Can you elaborate on due to the direction RTIC is moving?

Async for RTIC 2.

I wonder if it will play nice with embassy. It would be cool if you could use an RTIC 2 executor with embassy hals. Never used embassy's hals but have wanted to give them a try.

From what I gather from some chats, it should play nice with Embassy, although I don't know how the interactions will work. I think the intent is to keep the core RTIC functionality (Resource sharing and interrupts) the same, but to move everything else, which would include monotonic, to async.