Unable to get sleep_for to work with microseconds

rtos::ThisThread::sleep_for(mbed::chrono::milliseconds_u32(100));
rtos::ThisThread::sleep_for(mbed::chrono::microseconds_u32(100));

Is there any reason that the microseconds sleep_for does not work, but the milliseconds one does?

Compile says

Compilation error: no matching function for call to 'sleep_for(mbed::chrono::microseconds_u32)'

Note this fails in 2 implementations of Mbed (Artemis Sparkfun, and Arduino Portenta)

Thanks for any help

Yes, this is because ThisThread::sleep_for() does not work with microseconds, it has a max resolution of one millisecond (which is equal to one RTOS scheduling period).

If you need to do a microsecond wait and don’t care about other threads running in the background, use wait_us() for that. If you need to do a microsecond wait and allow other threads to run in the background, I actually made a library for this, AccurateWaiter.

1 Like

Thanks, that is what I am looking for.

I tried bringing the files into the Arduino IDE, but have not been able to get it to compile.

Any chance to look into it? I am trying to get it to run on both a Portenta H7, as well as a Sparkfun Artemis - both of which use mbed as an RTOS

Here is the code

.h

//
// Created by jamie on 11/12/2020.
//

#ifndef LIGHTSPEEDRANGEFINDER_ACCURATEWAITER_H
#define LIGHTSPEEDRANGEFINDER_ACCURATEWAITER_H

#include <mbed.h>
#include<TimerEvent.h>
#include <TickerDataClock.h>

/**
 * --------------- AccurateWaiter ---------------
 * ...your order, sir?
 *
 * AccurateWaiter allows threaded applications to have threads wait
 * for a precise amount of time, without sending the CPU into
 * a permanent spinlock.
 *
 * In standard Mbed OS, certain types of threaded applications are limited
 * by the precision of ThisThread::sleep_for(), which only allows sleeping
 * for a whole number of milliseconds.  This limitation comes from
 * the underlying RTX RTOS, which uses a 1 ms scheduling interrupt.
 *
 * What a lot of people don't know is that there is actually a way around
 * this limitation, using the RTOS's EventFlags objects.  Unlike
 * most other RTOS objects, EventFlags are usable from interrupts.  By
 * configuring the Mbed us ticker interrupt to set EventFlags,
 * a waiting thread can be woken immediately after an exact number
 * of ticks on the us ticker.
 *
 * The result is something that combines the best features of wait_us()
 * and ThisThread::sleep_for().  Other threads may run during the wait period
 * (like ThisThread::sleep_for()), but the wait time can be specified to the
 * microsecond (like wait_us()).  The only downside is a bit more overhead
 * when starting the wait operation (to trigger the timer interrupt).
 *
 * I tested this code on my NUCLEO_F429ZI board, and found that it was able
 * to handle any time duration from 1us-1s accurately, with exactly 14us of
 * delay between the set time and the time the wait function returns.  Overhead
 * of calling the function also is around the 15-25us range.
 *
 * Note 1: Since it uses the Mbed us ticker, this class allows waiting for
 * >250,000 years before rollover.
 *
 * Note 2: Each AccurateWaiter object may only be safely used by one thread
 * at a time.
 */
 
class AccurateWaiter : private TimerEvent
{
	// event flags, used to signal thread to wake up from interrupt
	rtos::EventFlags flags;

	// called from timer interrupt
	void handler() override;

public:

	AccurateWaiter();

	/**
	 * Get Mbed's C++ Clock object representing the us ticker.
	 * Use this object to get time_points for wait_until()
	 * @return
	 */
	TickerDataClock & clock()
	{
		return _ticker_data;
	}

	/**
	 * Wait for an exact amount of time.
	 * Other threads will be allowed to run during the wait period.
	 * When the timer expires, the waiting thread will be run immediately, preempting the
	 * current running thread, as long as a few things are true:
	 * - The waiting thread has higher thread priority than the currently running thread
	 * - Interrupts are not disabled
	 *
	 */
	void wait_for(std::chrono::microseconds duration);

	/**
	 * Wait for an exact amount of time.
	 * Overload that casts to us.
	 *
	 * Other threads will be allowed to run during the wait period.
	 * When the timer expires, the waiting thread will be run immediately, preempting the
	 * current running thread, as long as a few things are true:
	 * - The waiting thread has higher thread priority than the currently running thread
	 * - Interrupts are not disabled
	 */
	template<typename _Rep, typename _Period>
	void wait_for(std::chrono::duration<_Rep, _Period> duration)
	{
		wait_for(std::chrono::duration_cast<std::chrono::microseconds>(duration));
	}

	/**
	 * Wait until a specific us timestamp.
	 *
	 * Other threads will be allowed to run during the wait period.
	 * When the timer expires, the waiting thread will be run immediately, preempting the
	 * current running thread, as long as a few things are true:
	 * - The waiting thread has higher thread priority than the currently running thread
	 * - Interrupts are not disabled
	 */
	void wait_until(TickerDataClock::time_point timePoint);
};


AccurateWaiter::AccurateWaiter():
TimerEvent(get_us_ticker_data())
{

}


void AccurateWaiter::handler()
{
	// This signals the RTOS that the waiting thread is ready to wake up.
	flags.set(1);
}

void AccurateWaiter::wait_for(std::chrono::microseconds duration)
{
	// set up timer event to occur
	insert(duration);

	// wait for event flag and then clear it
	flags.wait_all(1);
}

void AccurateWaiter::wait_until(TickerDataClock::time_point timePoint)
{
	// set up timer event to occur
	insert_absolute(timePoint);

	// wait for event flag and then clear it
	flags.wait_all(1);
}

#endif //LIGHTSPEEDRANGEFINDER_ACCURATEWAITER_H

.ino

#include "AccurateWaiter.h"

namespace chrono = std::chrono;

 
// Thread to perform a precise wait and print the results
void testWaiting()
{
    char s[200];
    AccurateWaiter waiter;
 
    while(true)
    {
        // choose a random wait time
        chrono::microseconds randomWaitTime(100);
 
        auto startTime = waiter.clock().now();
        waiter.wait_for(randomWaitTime);
        auto endTime = waiter.clock().now();
 
        uint64_t actualWaitTime = chrono::duration_cast<chrono::microseconds>(endTime - startTime).count();
        sprintf(s, "Expected time: %" PRIu64 " us, actual: %" PRIu64 " us, offset = %" PRIi64 "us\n", randomWaitTime.count(), actualWaitTime,
            static_cast<int64_t>(actualWaitTime) - static_cast<int64_t>(randomWaitTime.count()));
        Serial.print(s);
    }
}




void setup() {
  // put your setup code here, to run once:
    // print info
    Serial.print("---------------------------------------------\n");
    Serial.print("        Starting Accurate Waiter Test\n");
    Serial.print("---------------------------------------------\n");
 
    Serial.print("us Ticker Resolution: %.02f us\n", 1e6f/us_ticker_get_info()->frequency);
 
    // start timing thread
    Thread timingThread(osPriorityHigh);
    timingThread.start(callback(testWaiting));
}

void loop() {
  // put your main code here, to run repeatedly:
    int workload;
    while(true)
    {
        workload++;
    }
}





the errors I am getting are numerous and here is a partial list

In file included from Z:\Temp\TestAccurateWaiter\TestAccurateWaiter.ino:1:0:
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:51:1: error: expected class-name before '{' token
 {
 ^
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:67:2: error: 'TickerDataClock' does not name a type
  TickerDataClock & clock()
  ^~~~~~~~~~~~~~~
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:108:18: error: 'TickerDataClock' has not been declared
  void wait_until(TickerDataClock::time_point timePoint);
                  ^~~~~~~~~~~~~~~
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:108:46: error: expected ',' or '...' before 'timePoint'
  void wait_until(TickerDataClock::time_point timePoint);
                                              ^~~~~~~~~
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:56:7: error: 'void AccurateWaiter::handler()' marked 'override', but does not override
  void handler() override;
       ^~~~~~~
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h: In constructor 'AccurateWaiter::AccurateWaiter()':
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:113:1: error: class 'AccurateWaiter' does not have any field named 'TimerEvent'
 TimerEvent(get_us_ticker_data())
 ^~~~~~~~~~
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h: In member function 'void AccurateWaiter::wait_for(std::chrono::microseconds)':
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:128:2: error: 'insert' was not declared in this scope
  insert(duration);
  ^~~~~~
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:128:2: note: suggested alternative: 'assert'
  insert(duration);
  ^~~~~~
  assert
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h: At global scope:
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:134:33: error: variable or field 'wait_until' declared void
 void AccurateWaiter::wait_until(TickerDataClock::time_point timePoint)
                                 ^~~~~~~~~~~~~~~
Z:\Temp\TestAccurateWaiter\AccurateWaiter.h:134:33: error: 'TickerDataClock' has not been declared
Z:\Temp\TestAccurateWaiter\TestAccurateWaiter.ino: In function 'void testWaiting()':
Z:\Temp\TestAccurateWaiter\TestAccurateWaiter.ino:17:33: error: 'class AccurateWaiter' has no member named 'clock'
         auto startTime = waiter.clock().now();
                                 ^~~~~
Z:\Temp\TestAccurateWaiter\TestAccurateWaiter.ino:19:31: error: 'class AccurateWaiter' has no member named 'clock'
         auto endTime = waiter.clock().now();