Embedded + RTFM + Timer Queue: Blocking wait?

#1

Hi,

I am trying to use the cortex-m-rtfm with the timer-queue feature, but at the same time I need to do a blocking wait inside the init function.

The reason for a blocking wait is that I am initializing the enc28j60 chip, which has a silicone bug that requires a 1ms sleep after reset. The driver takes a DelayMs implementer to do that sleep. No other blocking sleep is needed after initialization, so it doesn’t keep it.

If I wasn’t using RTFM, I would use the stm32f1xx_hal::delay::Delay, but that requires the SYST peripheral, which is not available with RTFM, because it is used for the timer queue.

I know that blocking wait should be generally avoided, but in this case it would be lot easier to just sleep for 1ms during initialization instead of trying to break it into two parts. The initialized enc28j60 ends up being a RTFM resource, so I need to have it ready at the end of the init function.

Does anyone know how to do a blocking wait in such situation?

#2

The optimal solution would be to to use the other general purpose timers for this, but I don’t think we implement delay for those yet…

I’m not sure how timer-queue works, but presumably you initialise syst and then hand it off to timer-queue, can you initialise your peripheral using syst before handing it of to the timer queue?

#3

The optimal solution would be to to use the other general purpose timers for this, but I don’t think we implement delay for those yet…

That sounds useful, I will look into it.

I’m not sure how timer-queue works, but presumably you initialise syst and then hand it off to timer-queue, can you initialise your peripheral using syst before handing it of to the timer queue?

The code which creates the timer-queue is generated by the RTFM macros. I am not sure if it is created before or after the init function. The init function is called with subset of peripherals that you can use and the SYST is not among them.

#4

Sounds good, if nothing else works, you can try to implement delay yourself using the timers. Something like

struct DelayTimer { timer: Timer };

impl Delay for DelayTimer {
    fn delay(time: u32) {
        // Convert time to hertz and wait
        let hertz = convert_to_hertz(time);
        self.timer.start(hertz);
        self.timer.wait();
    }
}

Unfortunately, the embedded HAL forces you to specify time in Hertz which makes it impossible to specify delays longer than 1 second. Though I suppose for your use case, that is fine.
#5

Thank you. I am using this now:

pub struct DelayTimer<Timer>
    where Timer: CountDown,
          Timer::Time: From<Hertz>
{
    timer: Timer
}

impl<Timer> DelayTimer<Timer>
    where Timer: CountDown,
          Timer::Time: From<Hertz>
{
    pub fn new(timer: Timer) -> Self {
        Self {
            timer
        }
    }
}

impl<Timer> DelayMs<u8> for DelayTimer<Timer>
    where Timer: CountDown,
        Timer::Time: From<Hertz>
{
    fn delay_ms(&mut self, ms: u8) {
        if ms == 0 {
            return;
        }

        // TODO: Improve precision? This division will round things down
        //       and delays will be longer.
        self.timer.start((1000u32 / ms as u32).hz());
        block!(self.timer.wait()).unwrap();
    }
}

Since the timers in the HAL are given value in Hz, some delays can not be expressed precisely and the actual waiting time will be longer, but that is fine for my use case.

I have also discovered that there is cortex_m::asm::delay, which simply spins for given amount of instructions. Since I want to use it in the init function, where interrupts are disabled, it would be perfect for me. I used formula clocks.sysclk().0 / 1000 * ms to get the amount of cycles to wait, but it does not seem to work correctly - the delays are visibly longer. So for now I am using the timer.