AccurateWaiter allows threaded applications to have threads wait for a precise amount of time, without sending the CPU into a permanent spinlock.

Dependents:   AccurateWaiter-Test

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers AccurateWaiter.h Source File

AccurateWaiter.h

00001 //
00002 // Created by jamie on 11/12/2020.
00003 //
00004 
00005 #ifndef LIGHTSPEEDRANGEFINDER_ACCURATEWAITER_H
00006 #define LIGHTSPEEDRANGEFINDER_ACCURATEWAITER_H
00007 
00008 #include <mbed.h>
00009 
00010 /**
00011  * --------------- AccurateWaiter ---------------
00012  * ...your order, sir?
00013  *
00014  * AccurateWaiter allows threaded applications to have threads wait
00015  * for a precise amount of time, without sending the CPU into
00016  * a permanent spinlock.
00017  *
00018  * In standard Mbed OS, certain types of threaded applications are limited
00019  * by the precision of ThisThread::sleep_for(), which only allows sleeping
00020  * for a whole number of milliseconds.  This limitation comes from
00021  * the underlying RTX RTOS, which uses a 1 ms scheduling interrupt.
00022  *
00023  * What a lot of people don't know is that there is actually a way around
00024  * this limitation, using the RTOS's EventFlags objects.  Unlike
00025  * most other RTOS objects, EventFlags are usable from interrupts.  By
00026  * configuring the Mbed us ticker interrupt to set EventFlags,
00027  * a waiting thread can be woken immediately after an exact number
00028  * of ticks on the us ticker.
00029  *
00030  * The result is something that combines the best features of wait_us()
00031  * and ThisThread::sleep_for().  Other threads may run during the wait period
00032  * (like ThisThread::sleep_for()), but the wait time can be specified to the
00033  * microsecond (like wait_us()).  The only downside is a bit more overhead
00034  * when starting the wait operation (to trigger the timer interrupt).
00035  *
00036  * I tested this code on my NUCLEO_F429ZI board, and found that it was able
00037  * to handle any time duration from 1us-1s accurately, with exactly 14us of
00038  * delay between the set time and the time the wait function returns.  Overhead
00039  * of calling the function also is around the 15-25us range.
00040  *
00041  * Note 1: Since it uses the Mbed us ticker, this class allows waiting for
00042  * >250,000 years before rollover.
00043  *
00044  * Note 2: Each AccurateWaiter object may only be safely used by one thread
00045  * at a time.
00046  */
00047 class AccurateWaiter : private TimerEvent
00048 {
00049     // event flags, used to signal thread to wake up from interrupt
00050     rtos::EventFlags flags;
00051 
00052     // called from timer interrupt
00053     void handler() override;
00054 
00055 public:
00056 
00057     AccurateWaiter();
00058 
00059     /**
00060      * Get Mbed's C++ Clock object representing the us ticker.
00061      * Use this object to get time_points for wait_until()
00062      * @return
00063      */
00064     TickerDataClock & clock()
00065     {
00066         return _ticker_data;
00067     }
00068 
00069     /**
00070      * Wait for an exact amount of time.
00071      * Other threads will be allowed to run during the wait period.
00072      * When the timer expires, the waiting thread will be run immediately, preempting the
00073      * current running thread, as long as a few things are true:
00074      * - The waiting thread has higher thread priority than the currently running thread
00075      * - Interrupts are not disabled
00076      *
00077      */
00078     void wait_for(std::chrono::microseconds duration);
00079 
00080     /**
00081      * Wait for an exact amount of time.
00082      * Overload that casts to us.
00083      *
00084      * Other threads will be allowed to run during the wait period.
00085      * When the timer expires, the waiting thread will be run immediately, preempting the
00086      * current running thread, as long as a few things are true:
00087      * - The waiting thread has higher thread priority than the currently running thread
00088      * - Interrupts are not disabled
00089      */
00090     template<typename _Rep, typename _Period>
00091     void wait_for(std::chrono::duration<_Rep, _Period> duration)
00092     {
00093         wait_for(std::chrono::duration_cast<std::chrono::microseconds>(duration));
00094     }
00095 
00096     /**
00097      * Wait until a specific us timestamp.
00098      *
00099      * Other threads will be allowed to run during the wait period.
00100      * When the timer expires, the waiting thread will be run immediately, preempting the
00101      * current running thread, as long as a few things are true:
00102      * - The waiting thread has higher thread priority than the currently running thread
00103      * - Interrupts are not disabled
00104      */
00105     void wait_until(TickerDataClock::time_point timePoint);
00106 };
00107 
00108 #endif //LIGHTSPEEDRANGEFINDER_ACCURATEWAITER_H