version 2.8
gtimer.c
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.org/license.html
6  */
7 
8 #include "../../gfx.h"
9 
10 #if GFX_USE_GTIMER || defined(__DOXYGEN__)
11 
12 #define GTIMER_FLG_PERIODIC 0x0001
13 #define GTIMER_FLG_INFINITE 0x0002
14 #define GTIMER_FLG_JABBED 0x0004
15 #define GTIMER_FLG_SCHEDULED 0x0008
16 
17 /* Don't rework this macro to use a ternary operator - the gcc compiler stuffs it up */
18 #define TimeIsWithin(x, start, end) ((end >= start && x >= start && x <= end) || (end < start && (x >= start || x <= end)))
19 
20 /* This mutex protects access to our tables */
21 static gfxMutex mutex;
22 static gfxThreadHandle hThread = 0;
23 static GTimer *pTimerHead = 0;
24 static gfxSem waitsem;
25 static systemticks_t ticks2ms;
27 
28 /*===========================================================================*/
29 /* Driver local functions. */
30 /*===========================================================================*/
31 
32 static DECLARE_THREAD_FUNCTION(GTimerThreadHandler, arg) {
33  GTimer *pt;
34  systemticks_t tm;
35  systemticks_t nxtTimeout;
36  systemticks_t lastTime;
37  GTimerFunction fn;
38  void *param;
39  (void) arg;
40 
41  nxtTimeout = TIME_INFINITE;
42  lastTime = 0;
43  while(1) {
44  /* Wait for work to do. */
45  gfxYield(); // Give someone else a go no matter how busy we are
46  gfxSemWait(&waitsem, nxtTimeout);
47 
48  restartTimerChecks:
49 
50  // Our reference time
51  tm = gfxSystemTicks();
52  nxtTimeout = TIME_INFINITE;
53 
54  /* We need to obtain the mutex */
55  gfxMutexEnter(&mutex);
56 
57  if (pTimerHead) {
58  pt = pTimerHead;
59  do {
60  // Do we have something to do for this timer?
61  if ((pt->flags & GTIMER_FLG_JABBED) || (!(pt->flags & GTIMER_FLG_INFINITE) && TimeIsWithin(pt->when, lastTime, tm))) {
62 
63  // Is this timer periodic?
64  if ((pt->flags & GTIMER_FLG_PERIODIC) && pt->period != TIME_IMMEDIATE) {
65  // Yes - Update ready for the next period
66  if (!(pt->flags & GTIMER_FLG_INFINITE)) {
67  // We may have skipped a period.
68  // We use this complicated formulae rather than a loop
69  // because the gcc compiler stuffs up the loop so that it
70  // either loops forever or doesn't get executed at all.
71  pt->when += ((tm + pt->period - pt->when) / pt->period) * pt->period;
72  }
73 
74  // We are definitely no longer jabbed
75  pt->flags &= ~GTIMER_FLG_JABBED;
76 
77  } else {
78  // No - get us off the timers list
79  if (pt->next == pt)
80  pTimerHead = 0;
81  else {
82  pt->next->prev = pt->prev;
83  pt->prev->next = pt->next;
84  if (pTimerHead == pt)
85  pTimerHead = pt->next;
86  }
87  pt->flags = 0;
88  }
89 
90  // Call the callback function
91  fn = pt->fn;
92  param = pt->param;
93  gfxMutexExit(&mutex);
94  fn(param);
95 
96  // We no longer hold the mutex, the callback function may have taken a while
97  // and our list may have been altered so start again!
98  goto restartTimerChecks;
99  }
100 
101  // Find when we next need to wake up
102  if (!(pt->flags & GTIMER_FLG_INFINITE) && pt->when - tm < nxtTimeout)
103  nxtTimeout = (pt->when - tm)/ticks2ms;
104  pt = pt->next;
105  } while(pt != pTimerHead);
106  }
107 
108  // Ready for the next loop
109  lastTime = tm;
110  gfxMutexExit(&mutex);
111  }
112  THREAD_RETURN(0);
113 }
114 
115 void _gtimerInit(void)
116 {
117  gfxSemInit(&waitsem, 0, 1);
118  gfxMutexInit(&mutex);
119  ticks2ms = gfxMillisecondsToTicks(1);
120 }
121 
122 void _gtimerDeinit(void)
123 {
124  gfxSemDestroy(&waitsem);
125  gfxMutexDestroy(&mutex);
126  // Need to destroy GTimer thread here
127 }
128 
130 {
131  pt->flags = 0;
132 }
133 
135 {
136  gtimerStop(pt);
137 }
138 
139 void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, delaytime_t millisec) {
140  gfxMutexEnter(&mutex);
141 
142  // Start our thread if not already going
143  if (!hThread) {
144  hThread = gfxThreadCreate(waTimerThread, GTIMER_THREAD_WORKAREA_SIZE, GTIMER_THREAD_PRIORITY, GTimerThreadHandler, 0);
145  if (hThread) {gfxThreadClose(hThread);} // We never really need the handle again
146  }
147 
148  // Is this already scheduled?
149  if (pt->flags & GTIMER_FLG_SCHEDULED) {
150  // Cancel it!
151  if (pt->next == pt)
152  pTimerHead = 0;
153  else {
154  pt->next->prev = pt->prev;
155  pt->prev->next = pt->next;
156  if (pTimerHead == pt)
157  pTimerHead = pt->next;
158  }
159  }
160 
161  // Set up the timer structure
162  pt->fn = fn;
163  pt->param = param;
164  pt->flags = GTIMER_FLG_SCHEDULED;
165  if (periodic)
166  pt->flags |= GTIMER_FLG_PERIODIC;
167  if (millisec == TIME_INFINITE) {
168  pt->flags |= GTIMER_FLG_INFINITE;
169  pt->period = TIME_INFINITE;
170  } else {
171  pt->period = gfxMillisecondsToTicks(millisec);
172  pt->when = gfxSystemTicks() + pt->period;
173  }
174 
175  // Just pop it on the end of the queue
176  if (pTimerHead) {
177  pt->next = pTimerHead;
178  pt->prev = pTimerHead->prev;
179  pt->prev->next = pt;
180  pt->next->prev = pt;
181  } else
182  pt->next = pt->prev = pTimerHead = pt;
183 
184  // Bump the thread
185  if (!(pt->flags & GTIMER_FLG_INFINITE))
186  gfxSemSignal(&waitsem);
187  gfxMutexExit(&mutex);
188 }
189 
190 void gtimerStop(GTimer *pt) {
191  gfxMutexEnter(&mutex);
192  if (pt->flags & GTIMER_FLG_SCHEDULED) {
193  // Cancel it!
194  if (pt->next == pt)
195  pTimerHead = 0;
196  else {
197  pt->next->prev = pt->prev;
198  pt->prev->next = pt->next;
199  if (pTimerHead == pt)
200  pTimerHead = pt->next;
201  }
202  // Make sure we know the structure is dead!
203  pt->flags = 0;
204  }
205  gfxMutexExit(&mutex);
206 }
207 
208 bool_t gtimerIsActive(GTimer *pt) {
209  return (pt->flags & GTIMER_FLG_SCHEDULED) ? TRUE : FALSE;
210 }
211 
212 void gtimerJab(GTimer *pt) {
213  gfxMutexEnter(&mutex);
214 
215  // Jab it!
216  pt->flags |= GTIMER_FLG_JABBED;
217 
218  // Bump the thread
219  gfxSemSignal(&waitsem);
220  gfxMutexExit(&mutex);
221 }
222 
223 void gtimerJabI(GTimer *pt) {
224  // Jab it!
225  pt->flags |= GTIMER_FLG_JABBED;
226 
227  // Bump the thread
228  gfxSemSignalI(&waitsem);
229 }
230 
231 #endif /* GFX_USE_GTIMER */
void gtimerJabI(GTimer *pt)
Jab a timer causing the current period to immediate expire.
Definition: gtimer.c:223
void * gfxThreadHandle
A thread handle.
Definition: gos.h:117
void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit)
Initialise a Counted Semaphore.
void gtimerDeinit(GTimer *pt)
Deinitialise a timer.
Definition: gtimer.c:134
void gtimerInit(GTimer *pt)
Initialise a timer.
Definition: gtimer.c:129
#define GTIMER_THREAD_PRIORITY
Defines the GTIMER thread priority.
A GTimer structure.
Definition: gtimer.h:52
systemticks_t gfxSystemTicks(void)
Get the current operating system tick time.
bool_t gtimerIsActive(GTimer *pt)
Test if a timer is currently active.
Definition: gtimer.c:208
void gtimerStop(GTimer *pt)
Stop a timer (periodic or otherwise)
Definition: gtimer.c:190
#define FALSE
Generic &#39;false&#39; boolean constant.
Definition: gfx.h:31
systemticks_t gfxMillisecondsToTicks(delaytime_t ms)
Convert a given number of millseconds to a number of operating system ticks.
void gfxSemDestroy(gfxSem *psem)
Destroy a Counted Semaphore.
bool_t gfxSemWait(gfxSem *psem, delaytime_t ms)
Wait on a semaphore.
void gfxSemSignalI(gfxSem *psem)
Signal a semaphore.
A semaphore.
Definition: gos.h:105
void gfxMutexExit(gfxMutex *pmutex)
Exit the critical code region protected by the mutex.
#define DECLARE_THREAD_FUNCTION(fnName, param)
Declare a thread function.
Definition: gos.h:62
void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, delaytime_t millisec)
Set a timer going or alter its properties if it is already going.
Definition: gtimer.c:139
void gfxMutexEnter(gfxMutex *pmutex)
Enter the critical code region protected by the mutex.
void gfxSemSignal(gfxSem *psem)
Signal a semaphore.
gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn), p), void *param)
Start a new thread.
A mutex.
Definition: gos.h:111
void gtimerJab(GTimer *pt)
Jab a timer causing the current period to immediate expire.
Definition: gtimer.c:212
void gfxThreadClose(gfxThreadHandle thread)
Close the thread handle.
void gfxMutexInit(gfxMutex *pmutex)
Initialise a mutex to protect a region of code from other threads.
void gfxYield(void)
Yield the current thread.
void gfxMutexDestroy(gfxMutex *pmutex)
Destroy a Mutex.
#define DECLARE_THREAD_STACK(name, sz)
Declare a thread stack.
Definition: gos.h:74
#define TRUE
Generic &#39;true&#39; boolean constant.
Definition: gfx.h:38
#define GTIMER_THREAD_WORKAREA_SIZE
Defines the size of the timer threads work area (stack+structures).