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();