diff options
author | Mark Kriegsman <kriegsman@tr.org> | 2014-12-09 09:35:32 +0300 |
---|---|---|
committer | Mark Kriegsman <kriegsman@tr.org> | 2014-12-09 09:35:32 +0300 |
commit | f97e22c5915948d5bfd14a95f5bf804ac0e1ce77 (patch) | |
tree | bfe28ae27d621981661ed4cbcf31cf16aed96162 | |
parent | 4d9c0c60d09e5e9256f8a16bbb62ed5bea295a85 (diff) |
Added EVERY_N_SECONDS and several variations. Some of it is preprocessor macros instead of templates because of C++03 template rules (which have subsequently been relaxed in C++11).
-rw-r--r-- | lib8tion.cpp | 3 | ||||
-rw-r--r-- | lib8tion.h | 167 |
2 files changed, 167 insertions, 3 deletions
diff --git a/lib8tion.cpp b/lib8tion.cpp index 557fa005..84bcafdb 100644 --- a/lib8tion.cpp +++ b/lib8tion.cpp @@ -121,6 +121,9 @@ void * memmove8 ( void * dst, void* src, uint16_t num ) #endif /* AVR */ + + + #if 0 // TEST / VERIFICATION CODE ONLY BELOW THIS POINT #include <Arduino.h> @@ -1815,10 +1815,10 @@ typedef q<uint16_t, 12,4> q124; #if defined(ARDUINO) && !defined(USE_GET_MILLISECOND_TIMER) // Forward declaration of Arduino function 'millis'. uint32_t millis(); -#define GET_MILLIS (millis()) +#define GET_MILLIS millis #else uint32_t get_millisecond_timer(); -#define GET_MILLIS (get_millisecond_timer()) +#define GET_MILLIS get_millisecond_timer #endif // beat16 generates a 16-bit 'sawtooth' wave at a given BPM, @@ -1836,7 +1836,7 @@ LIB8STATIC uint16_t beat88( accum88 beats_per_minute_88, uint32_t timebase = 0) // The ratio 65536:60000 is 279.620266667:256; we'll call it 280:256. // The conversion is accurate to about 0.05%, more or less, // e.g. if you ask for "120 BPM", you'll get about "119.93". - return (((GET_MILLIS) - timebase) * beats_per_minute_88 * 280) >> 16; + return (((GET_MILLIS()) - timebase) * beats_per_minute_88 * 280) >> 16; } // beat16 generates a 16-bit 'sawtooth' wave at a given BPM @@ -1897,4 +1897,165 @@ LIB8STATIC uint8_t beatsin8( accum88 beats_per_minute, uint8_t lowest = 0, uint8 } +// seconds16, minutes16, hours8 +// functions to return the current seconds, minutes, or hours +// since boot time, in the specified width. Used as part of +// the "every N time-periods" mechanism. + +LIB8STATIC uint16_t seconds16() +{ + uint32_t ms = GET_MILLIS(); + uint16_t s16; + s16 = ms / 1000; + return s16; +} + +LIB8STATIC uint16_t minutes16() +{ + uint32_t ms = GET_MILLIS(); + uint16_t m16; + m16 = (ms / (60000L)) & 0xFFFF; + return m16; +} + +LIB8STATIC uint8_t hours8() +{ + uint32_t ms = GET_MILLIS(); + uint8_t h8; + h8 = (ms / (3600000L)) & 0xFF; + return h8; +} + + +// Helper routine to divide a 32-bit value by 1024, returning +// only the low 16 bits. You'd think this would be just +// result = (in32 >> 10) & 0xFFFF; +// and on ARM, that's what you want and all is well. +// But on AVR that code turns into a loop that executes +// a four-byte shift ten times: 40 shifts in all, plus loop +// overhead. This routine gets exactly the same result with +// just six shifts (vs 40), and no loop overhead. +// Used to convert millis to 'binary seconds' aka bseconds: +// one bsecond == 1024 millis. +LIB8STATIC uint16_t div1024_32_16( uint32_t in32) +{ + uint16_t out16; +#if defined(__AVR__) + asm volatile ( + " lsr %D[in] \n\t" + " ror %C[in] \n\t" + " ror %B[in] \n\t" + " lsr %D[in] \n\t" + " ror %C[in] \n\t" + " ror %B[in] \n\t" + " mov %B[out],%C[in] \n\t" + " mov %A[out],%B[in] \n\t" + : [in] "+r" (in32), + [out] "=r" (out16) + ); +#else + out16 = (in32 >> 10) & 0xFFFF; +#endif + return out16; +} + +// bseconds16 returns the current time-since-boot in +// "binary seconds", which are actually 1024/1000 of a +// second long. +LIB8STATIC uint16_t bseconds16() +{ + uint32_t ms = GET_MILLIS(); + uint16_t s16; + s16 = div1024_32_16( ms); + return s16; +} + + +// Classes to implement "Every N Milliseconds", "Every N Seconds", +// "Every N Minutes", "Every N Hours", and "Every N BSeconds". +#if 1 +#define INSTANTIATE_EVERY_N_TIME_PERIODS(NAME,TIMETYPE,TIMEGETTER) \ +class NAME { \ +public: \ + TIMETYPE mPrevTrigger; \ + TIMETYPE mPeriod; \ + \ + NAME() { reset(); mPeriod = 1; }; \ + NAME(TIMETYPE period) { reset(); setPeriod(period); }; \ + void setPeriod( TIMETYPE period) { mPeriod = period; }; \ + TIMETYPE getTime() { return (TIMETYPE)(TIMEGETTER()); }; \ + TIMETYPE getPeriod() { return mPeriod; }; \ + TIMETYPE getElapsed() { return getTime() - mPrevTrigger; } \ + TIMETYPE getRemaining() { return mPeriod - getElapsed(); } \ + TIMETYPE getLastTriggerTime() { return mPrevTrigger; } \ + bool ready() { \ + bool isReady = (getElapsed() >= mPeriod); \ + if( isReady ) { reset(); } \ + return isReady; \ + } \ + void reset() { mPrevTrigger = getTime(); }; \ + void trigger() { mPrevTrigger = getTime() - mPeriod; }; \ + \ + operator bool() { return ready(); } \ +}; +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNMillis,uint32_t,GET_MILLIS); +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNSeconds,uint16_t,seconds16); +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNBSeconds,uint16_t,bseconds16); +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNMinutes,uint16_t,minutes16); +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNHours,uint8_t,hours8); +#else + +// Under C++11 rules, we would be allowed to use not-external +// -linkage-type symbols as template arguments, +// e.g., LIB8STATIC seconds16, and we'd be able to use these +// templates as shown below. +// However, under C++03 rules, we cannot do that, and thus we +// have to resort to the preprocessor to 'instantiate' 'templates', +// as handled above. +template<typename timeType,timeType (*timeGetter)()> +class CEveryNTimePeriods { +public: + timeType mPrevTrigger; + timeType mPeriod; + + CEveryNTimePeriods() { reset(); mPeriod = 1; }; + CEveryNTimePeriods(timeType period) { reset(); setPeriod(period); }; + void setPeriod( timeType period) { mPeriod = period; }; + timeType getTime() { return (timeType)(timeGetter()); }; + timeType getPeriod() { return mPeriod; }; + timeType getElapsed() { return getTime() - mPrevTrigger; } + timeType getRemaining() { return mPeriod - getElapsed(); } + timeType getLastTriggerTime() { return mPrevTrigger; } + bool ready() { + bool isReady = (getElapsed() >= mPeriod); + if( isReady ) { reset(); } + return isReady; + } + void reset() { mPrevTrigger = getTime(); }; + void trigger() { mPrevTrigger = getTime() - mPeriod; }; + + operator bool() { return ready(); } +}; +typedef CEveryNTimePeriods<uint16_t,seconds16> CEveryNSeconds; +typedef CEveryNTimePeriods<uint16_t,bseconds16> CEveryNBSeconds; +typedef CEveryNTimePeriods<uint32_t,millis> CEveryNMillis; +typedef CEveryNTimePeriods<uint16_t,minutes16> CEveryNMinutes; +typedef CEveryNTimePeriods<uint8_t,hours8> CEveryNHours; +#endif + + +#define CONCAT_HELPER( x, y ) x##y +#define CONCAT_MACRO( x, y ) CONCAT_HELPER( x, y ) +#define EVERY_N_MILLIS(N) EVERY_N_MILLIS_I(CONCAT_MACRO(PER, __COUNTER__ ),N) +#define EVERY_N_MILLIS_I(NAME,N) static CEveryNMillis NAME(N); if( NAME ) +#define EVERY_N_SECONDS(N) EVERY_N_SECONDS_I(CONCAT_MACRO(PER, __COUNTER__ ),N) +#define EVERY_N_SECONDS_I(NAME,N) static CEveryNSeconds NAME(N); if( NAME ) +#define EVERY_N_BSECONDS(N) EVERY_N_BSECONDS_I(CONCAT_MACRO(PER, __COUNTER__ ),N) +#define EVERY_N_BSECONDS_I(NAME,N) static CEveryNBSeconds NAME(N); if( NAME ) +#define EVERY_N_MINUTES(N) EVERY_N_MINUTES_I(CONCAT_MACRO(PER, __COUNTER__ ),N) +#define EVERY_N_MINUTES_I(NAME,N) static CEveryNMinutes NAME(N); if( NAME ) +#define EVERY_N_HOURS(N) EVERY_N_HOURS_I(CONCAT_MACRO(PER, __COUNTER__ ),N) +#define EVERY_N_HOURS_I(NAME,N) static CEveryNHours NAME(N); if( NAME ) + + #endif |