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
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
Generated on Tue Jul 26 2022 01:26:16 by
![doxygen](doxygen.png)