142 lines
5.8 KiB
C++
142 lines
5.8 KiB
C++
#pragma once
|
|
|
|
#define LIB8STATIC __attribute__((unused)) static inline
|
|
#define GET_MILLIS millis
|
|
|
|
/// Return the current seconds since boot in a 16-bit value. 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;
|
|
}
|
|
|
|
/// Return the current minutes since boot in a 16-bit value. Used as part of the
|
|
/// "every N time-periods" mechanism
|
|
LIB8STATIC uint16_t minutes16()
|
|
{
|
|
uint32_t ms = GET_MILLIS();
|
|
uint16_t m16;
|
|
m16 = (ms / (60000L)) & 0xFFFF;
|
|
return m16;
|
|
}
|
|
|
|
/// Return the current hours since boot in an 8-bit value. Used as part of the
|
|
/// "every N time-periods" mechanism
|
|
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".
|
|
#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);
|
|
|
|
#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) |