2014-08-20 07:42:53 +00:00
|
|
|
/*
|
|
|
|
* This file is subject to the terms of the GFX License. If a copy of
|
|
|
|
* the license was not distributed with this file, you can obtain one at:
|
|
|
|
*
|
2018-10-01 15:32:39 +00:00
|
|
|
* http://ugfx.io/license.html
|
2014-08-20 07:42:53 +00:00
|
|
|
*/
|
|
|
|
|
2015-11-21 09:27:08 +00:00
|
|
|
#include "../../gfx.h"
|
2014-08-20 07:42:53 +00:00
|
|
|
|
|
|
|
#if GFX_USE_GTIMER || defined(__DOXYGEN__)
|
|
|
|
|
|
|
|
#define GTIMER_FLG_PERIODIC 0x0001
|
|
|
|
#define GTIMER_FLG_INFINITE 0x0002
|
|
|
|
#define GTIMER_FLG_JABBED 0x0004
|
|
|
|
#define GTIMER_FLG_SCHEDULED 0x0008
|
|
|
|
|
|
|
|
/* Don't rework this macro to use a ternary operator - the gcc compiler stuffs it up */
|
|
|
|
#define TimeIsWithin(x, start, end) ((end >= start && x >= start && x <= end) || (end < start && (x >= start || x <= end)))
|
|
|
|
|
|
|
|
/* This mutex protects access to our tables */
|
2018-11-03 02:29:30 +00:00
|
|
|
static gMutex mutex;
|
2018-07-08 05:40:27 +00:00
|
|
|
static gThread hThread = 0;
|
2018-11-03 02:29:30 +00:00
|
|
|
static GTimer *pTimerHead = 0;
|
|
|
|
static gSem waitsem;
|
2018-07-08 04:30:31 +00:00
|
|
|
static gTicks ticks2ms;
|
2018-11-03 02:29:30 +00:00
|
|
|
static GFX_THREAD_STACK(waTimerThread, GTIMER_THREAD_WORKAREA_SIZE);
|
2014-08-20 07:42:53 +00:00
|
|
|
|
|
|
|
/*===========================================================================*/
|
|
|
|
/* Driver local functions. */
|
|
|
|
/*===========================================================================*/
|
|
|
|
|
2018-11-03 02:29:30 +00:00
|
|
|
static GFX_THREAD_FUNCTION(GTimerThreadHandler, arg) {
|
2014-08-20 07:42:53 +00:00
|
|
|
GTimer *pt;
|
2018-07-08 04:30:31 +00:00
|
|
|
gTicks tm;
|
|
|
|
gTicks nxtTimeout;
|
|
|
|
gTicks lastTime;
|
2014-08-20 07:42:53 +00:00
|
|
|
GTimerFunction fn;
|
|
|
|
void *param;
|
2015-10-23 08:23:57 +00:00
|
|
|
(void) arg;
|
2014-08-20 07:42:53 +00:00
|
|
|
|
2018-07-08 05:15:15 +00:00
|
|
|
nxtTimeout = gDelayForever;
|
2014-08-20 07:42:53 +00:00
|
|
|
lastTime = 0;
|
|
|
|
while(1) {
|
|
|
|
/* Wait for work to do. */
|
|
|
|
gfxYield(); // Give someone else a go no matter how busy we are
|
|
|
|
gfxSemWait(&waitsem, nxtTimeout);
|
|
|
|
|
|
|
|
restartTimerChecks:
|
|
|
|
|
|
|
|
// Our reference time
|
|
|
|
tm = gfxSystemTicks();
|
2018-07-08 05:15:15 +00:00
|
|
|
nxtTimeout = gDelayForever;
|
2014-08-20 07:42:53 +00:00
|
|
|
|
|
|
|
/* We need to obtain the mutex */
|
|
|
|
gfxMutexEnter(&mutex);
|
|
|
|
|
|
|
|
if (pTimerHead) {
|
|
|
|
pt = pTimerHead;
|
|
|
|
do {
|
|
|
|
// Do we have something to do for this timer?
|
|
|
|
if ((pt->flags & GTIMER_FLG_JABBED) || (!(pt->flags & GTIMER_FLG_INFINITE) && TimeIsWithin(pt->when, lastTime, tm))) {
|
|
|
|
|
|
|
|
// Is this timer periodic?
|
2018-07-08 05:15:15 +00:00
|
|
|
if ((pt->flags & GTIMER_FLG_PERIODIC) && pt->period != gDelayNone) {
|
2014-08-20 07:42:53 +00:00
|
|
|
// Yes - Update ready for the next period
|
|
|
|
if (!(pt->flags & GTIMER_FLG_INFINITE)) {
|
|
|
|
// We may have skipped a period.
|
|
|
|
// We use this complicated formulae rather than a loop
|
|
|
|
// because the gcc compiler stuffs up the loop so that it
|
|
|
|
// either loops forever or doesn't get executed at all.
|
|
|
|
pt->when += ((tm + pt->period - pt->when) / pt->period) * pt->period;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We are definitely no longer jabbed
|
|
|
|
pt->flags &= ~GTIMER_FLG_JABBED;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// No - get us off the timers list
|
2015-12-23 23:54:57 +00:00
|
|
|
if (pt->next == pt)
|
2014-08-20 07:42:53 +00:00
|
|
|
pTimerHead = 0;
|
|
|
|
else {
|
|
|
|
pt->next->prev = pt->prev;
|
|
|
|
pt->prev->next = pt->next;
|
|
|
|
if (pTimerHead == pt)
|
|
|
|
pTimerHead = pt->next;
|
|
|
|
}
|
|
|
|
pt->flags = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call the callback function
|
|
|
|
fn = pt->fn;
|
|
|
|
param = pt->param;
|
|
|
|
gfxMutexExit(&mutex);
|
|
|
|
fn(param);
|
|
|
|
|
|
|
|
// We no longer hold the mutex, the callback function may have taken a while
|
|
|
|
// and our list may have been altered so start again!
|
|
|
|
goto restartTimerChecks;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find when we next need to wake up
|
|
|
|
if (!(pt->flags & GTIMER_FLG_INFINITE) && pt->when - tm < nxtTimeout)
|
2015-11-05 07:08:00 +00:00
|
|
|
nxtTimeout = (pt->when - tm)/ticks2ms;
|
2014-08-20 07:42:53 +00:00
|
|
|
pt = pt->next;
|
|
|
|
} while(pt != pTimerHead);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ready for the next loop
|
|
|
|
lastTime = tm;
|
|
|
|
gfxMutexExit(&mutex);
|
|
|
|
}
|
2018-11-03 02:29:30 +00:00
|
|
|
gfxThreadReturn(0);
|
2014-08-20 07:42:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void _gtimerInit(void)
|
|
|
|
{
|
|
|
|
gfxSemInit(&waitsem, 0, 1);
|
|
|
|
gfxMutexInit(&mutex);
|
2015-11-05 07:08:00 +00:00
|
|
|
ticks2ms = gfxMillisecondsToTicks(1);
|
2014-08-20 07:42:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void _gtimerDeinit(void)
|
|
|
|
{
|
|
|
|
gfxSemDestroy(&waitsem);
|
|
|
|
gfxMutexDestroy(&mutex);
|
|
|
|
// Need to destroy GTimer thread here
|
|
|
|
}
|
|
|
|
|
|
|
|
void gtimerInit(GTimer* pt)
|
|
|
|
{
|
|
|
|
pt->flags = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gtimerDeinit(GTimer* pt)
|
|
|
|
{
|
|
|
|
gtimerStop(pt);
|
|
|
|
}
|
|
|
|
|
2018-07-08 04:30:31 +00:00
|
|
|
void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, gBool periodic, gDelay millisec) {
|
2014-08-20 07:42:53 +00:00
|
|
|
gfxMutexEnter(&mutex);
|
|
|
|
|
|
|
|
// Start our thread if not already going
|
|
|
|
if (!hThread) {
|
2015-10-26 14:13:34 +00:00
|
|
|
hThread = gfxThreadCreate(waTimerThread, GTIMER_THREAD_WORKAREA_SIZE, GTIMER_THREAD_PRIORITY, GTimerThreadHandler, 0);
|
2014-08-20 07:42:53 +00:00
|
|
|
if (hThread) {gfxThreadClose(hThread);} // We never really need the handle again
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is this already scheduled?
|
|
|
|
if (pt->flags & GTIMER_FLG_SCHEDULED) {
|
|
|
|
// Cancel it!
|
2015-12-23 23:54:57 +00:00
|
|
|
if (pt->next == pt)
|
2014-08-20 07:42:53 +00:00
|
|
|
pTimerHead = 0;
|
|
|
|
else {
|
|
|
|
pt->next->prev = pt->prev;
|
|
|
|
pt->prev->next = pt->next;
|
|
|
|
if (pTimerHead == pt)
|
|
|
|
pTimerHead = pt->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up the timer structure
|
|
|
|
pt->fn = fn;
|
|
|
|
pt->param = param;
|
|
|
|
pt->flags = GTIMER_FLG_SCHEDULED;
|
|
|
|
if (periodic)
|
|
|
|
pt->flags |= GTIMER_FLG_PERIODIC;
|
2018-07-08 05:15:15 +00:00
|
|
|
if (millisec == gDelayForever) {
|
2014-08-20 07:42:53 +00:00
|
|
|
pt->flags |= GTIMER_FLG_INFINITE;
|
2018-07-08 05:15:15 +00:00
|
|
|
pt->period = gDelayForever;
|
2014-08-20 07:42:53 +00:00
|
|
|
} else {
|
|
|
|
pt->period = gfxMillisecondsToTicks(millisec);
|
|
|
|
pt->when = gfxSystemTicks() + pt->period;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just pop it on the end of the queue
|
|
|
|
if (pTimerHead) {
|
|
|
|
pt->next = pTimerHead;
|
|
|
|
pt->prev = pTimerHead->prev;
|
|
|
|
pt->prev->next = pt;
|
|
|
|
pt->next->prev = pt;
|
|
|
|
} else
|
|
|
|
pt->next = pt->prev = pTimerHead = pt;
|
|
|
|
|
|
|
|
// Bump the thread
|
|
|
|
if (!(pt->flags & GTIMER_FLG_INFINITE))
|
|
|
|
gfxSemSignal(&waitsem);
|
|
|
|
gfxMutexExit(&mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gtimerStop(GTimer *pt) {
|
|
|
|
gfxMutexEnter(&mutex);
|
|
|
|
if (pt->flags & GTIMER_FLG_SCHEDULED) {
|
|
|
|
// Cancel it!
|
2015-01-03 08:41:38 +00:00
|
|
|
if (pt->next == pt)
|
2014-08-20 07:42:53 +00:00
|
|
|
pTimerHead = 0;
|
|
|
|
else {
|
|
|
|
pt->next->prev = pt->prev;
|
|
|
|
pt->prev->next = pt->next;
|
|
|
|
if (pTimerHead == pt)
|
|
|
|
pTimerHead = pt->next;
|
|
|
|
}
|
|
|
|
// Make sure we know the structure is dead!
|
|
|
|
pt->flags = 0;
|
|
|
|
}
|
|
|
|
gfxMutexExit(&mutex);
|
|
|
|
}
|
|
|
|
|
2018-06-23 03:02:07 +00:00
|
|
|
gBool gtimerIsActive(GTimer *pt) {
|
|
|
|
return (pt->flags & GTIMER_FLG_SCHEDULED) ? gTrue : gFalse;
|
2014-08-20 07:42:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gtimerJab(GTimer *pt) {
|
|
|
|
gfxMutexEnter(&mutex);
|
|
|
|
|
|
|
|
// Jab it!
|
|
|
|
pt->flags |= GTIMER_FLG_JABBED;
|
|
|
|
|
|
|
|
// Bump the thread
|
|
|
|
gfxSemSignal(&waitsem);
|
|
|
|
gfxMutexExit(&mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gtimerJabI(GTimer *pt) {
|
|
|
|
// Jab it!
|
|
|
|
pt->flags |= GTIMER_FLG_JABBED;
|
|
|
|
|
|
|
|
// Bump the thread
|
|
|
|
gfxSemSignalI(&waitsem);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* GFX_USE_GTIMER */
|