µGFX  2.9
version 2.9
gadc.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.io/license.html
6  */
7 
8 #include "../../gfx.h"
9 
10 #if GFX_USE_GADC
11 
12 /* Include the driver defines */
13 #include "gadc_driver.h"
14 
15 #if GADC_MAX_HIGH_SPEED_SAMPLERATE > GADC_MAX_SAMPLE_FREQUENCY/2
16  #error "GADC: GADC_MAX_HIGH_SPEED_SAMPLERATE has been set too high. It must be less than half the maximum CPU rate"
17 #endif
18 
19 #define GADC_HSADC_GTIMER 0x8000
20 #define GADC_ADC_RUNNING 0x4000
21 #define GADC_HSADC_CONVERTION 0x2000
22 
23 typedef struct NonTimerData_t {
24  gfxQueueGSyncItem next;
25  GADCCallbackFunction callback;
26  union {
27  void *param;
28  gSem sigdone;
29  };
30  GadcNonTimerJob job;
31  } NonTimerData;
32 
33 static volatile gU16 hsFlags;
34 static gMemSize hsBytesPerConv;
35 static GadcTimerJob hsJob;
36 static GDataBuffer *hsData;
37 static gfxQueueGSync hsListDone;
38 static GADCISRCallbackFunction hsISRcallback;
39 #if GFX_USE_GEVENT
40  static GTimer hsGTimer;
41 #endif
42 
43 static GTimer lsGTimer;
44 static gfxQueueGSync lsListToDo;
45 static gfxQueueGSync lsListDone;
46 static NonTimerData *lsData;
47 
48 void gadcGotDataI(gMemSize n) {
49  if ((hsFlags & GADC_HSADC_CONVERTION)) {
50 
51  // A set of timer conversions is done - add them
52  hsJob.done += n;
53 
54  // Are we finished yet? (or driver signalled complete now)
55  if (n && hsJob.done < hsJob.todo)
56  return;
57 
58  // Clear event flags we might set
60 
61  // Is there any data in it
62  if (!hsJob.done) {
63 
64  // Oops - no data in this buffer. Just return it to the free-list
65  gfxBufferReleaseI(hsData);
66  goto starttimerjob; // Restart the timer job
67  }
68 
69  // Save the buffer on the hsListDone list
70  hsData->len = hsJob.done * hsBytesPerConv;
71  gfxQueueGSyncPutI(&hsListDone, (gfxQueueGSyncItem *)hsData);
72  hsFlags |= GADC_HSADC_GOTBUFFER;
73 
74  /* Signal a buffer completion */
75  if (hsISRcallback)
76  hsISRcallback();
77  #if GFX_USE_GEVENT
78  if (hsFlags & GADC_HSADC_GTIMER)
79  gtimerJabI(&hsGTimer);
80  #endif
81 
82  // Stop if we have been told to
83  if (!(hsFlags & GADC_HSADC_RUNNING)) {
85 
86  // Get the next free buffer
87  } else if (!(hsData = gfxBufferGetI())) {
88 
89  // Oops - no free buffers. Stall
90  hsFlags &= ~GADC_HSADC_RUNNING;
91  hsFlags |= GADC_HSADC_STALL;
93 
94  // Prepare the next job
95  } else {
96 
97  // Return this new job
98  #if GFX_USE_OS_CHIBIOS
99  // ChibiOS api bug - samples must be even
100  hsJob.todo = (hsData->size / hsBytesPerConv) & ~1;
101  #else
102  hsJob.todo = hsData->size / hsBytesPerConv;
103  #endif
104  hsJob.done = 0;
105  hsJob.buffer = (adcsample_t *)(hsData+1);
106  }
107 
108  // Start a job preferring a non-timer job
109  if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) {
110  hsFlags &= ~GADC_HSADC_CONVERTION;
111  gadc_lld_nontimerjobI(&lsData->job);
112  } else if ((hsFlags & GADC_HSADC_RUNNING)) {
113  hsFlags |= GADC_HSADC_CONVERTION;
114  gadc_lld_timerjobI(&hsJob);
115  } else
116  hsFlags &= ~GADC_ADC_RUNNING;
117 
118  } else {
119 
120  // Did it fail
121  if (!n) {
122  // Push it back on the head of the queue - it didn't actually get done
123  gfxQueueGSyncPushI(&lsListToDo, (gfxQueueGSyncItem *)lsData);
124  lsData = 0;
125  goto starttimerjob;
126  }
127 
128  // A non-timer job completed - signal
129  if (lsData->callback) {
130  // Put it on the completed list and signal the timer to do the call-backs
131  gfxQueueGSyncPutI(&lsListDone, (gfxQueueGSyncItem *)lsData);
132  gtimerJabI(&lsGTimer);
133  } else {
134  // Signal the thread directly
135  gfxSemSignalI(&lsData->sigdone);
136  }
137  lsData = 0;
138 
139  // Start a job preferring a timer job
140 starttimerjob:
141  if ((hsFlags & GADC_HSADC_RUNNING)) {
142  hsFlags |= GADC_HSADC_CONVERTION;
143  gadc_lld_timerjobI(&hsJob);
144  } else if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) {
145  hsFlags &= ~GADC_HSADC_CONVERTION;
146  gadc_lld_nontimerjobI(&lsData->job);
147  } else
148  hsFlags &= ~GADC_ADC_RUNNING;
149  }
150 }
151 
152 /* Our module initialiser */
153 void _gadcInit(void)
154 {
155  gadc_lld_init();
156 
157  gfxQueueGSyncInit(&hsListDone);
158  #if GFX_USE_GEVENT
159  gtimerInit(&hsGTimer);
160  #endif
161  gtimerInit(&lsGTimer);
162  gfxQueueGSyncInit(&lsListToDo);
163  gfxQueueGSyncInit(&lsListDone);
164 }
165 
166 void _gadcDeinit(void)
167 {
168  /* commented stuff is ToDo */
169 
170  // gadc_lld_deinit();
171  gfxQueueGSyncDeinit(&hsListDone);
172  #if GFX_USE_GEVENT
173  gtimerDeinit(&hsGTimer);
174  #endif
175  gtimerDeinit(&lsGTimer);
176  gfxQueueGSyncDeinit(&lsListToDo);
177  gfxQueueGSyncDeinit(&lsListDone);
178 }
179 
180 #if GFX_USE_GEVENT
181  static void HighSpeedGTimerCallback(void *param) {
182  (void) param;
183  GSourceListener *psl;
184  GEventADC *pe;
185 
186  psl = 0;
187  while ((psl = geventGetSourceListener((GSourceHandle)(&hsGTimer), psl))) {
188  if (!(pe = (GEventADC *)geventGetEventBuffer(psl))) {
189  // This listener is missing - save this.
190  psl->srcflags |= GADC_HSADC_LOSTEVENT;
191  continue;
192  }
193 
194  pe->type = GEVENT_ADC;
195  pe->flags = (hsFlags & (GADC_HSADC_RUNNING|GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL)) | psl->srcflags;
196  psl->srcflags = 0;
197  geventSendEvent(psl);
198  }
199  }
200 #endif
201 
202 void gadcHighSpeedInit(gU32 physdev, gU32 frequency)
203 {
204  if ((hsFlags & GADC_HSADC_RUNNING))
206 
207  /* Just save the details and reset everything for now */
208  hsJob.physdev = physdev;
209  hsJob.frequency = frequency;
210  hsISRcallback = 0;
211  hsBytesPerConv = gadc_lld_samplesperconversion(physdev) * sizeof(adcsample_t);
212 }
213 
214 #if GFX_USE_GEVENT
215  GSourceHandle gadcHighSpeedGetSource(void) {
216  if (!gtimerIsActive(&hsGTimer))
217  gtimerStart(&hsGTimer, HighSpeedGTimerCallback, 0, gTrue, gDelayForever);
218  hsFlags |= GADC_HSADC_GTIMER;
219  return (GSourceHandle)&hsGTimer;
220  }
221 #endif
222 
224  hsISRcallback = isrfn;
225 }
226 
227 GDataBuffer *gadcHighSpeedGetData(gDelay ms) {
228  return (GDataBuffer *)gfxQueueGSyncGet(&hsListDone, ms);
229 }
230 
231 GDataBuffer *gadcHighSpeedGetDataI(void) {
232  return (GDataBuffer *)gfxQueueGSyncGetI(&hsListDone);
233 }
234 
235 void gadcHighSpeedStart(void) {
236  // Safety first
237  if (!hsJob.frequency || !hsBytesPerConv)
238  return;
239 
240  gfxSystemLock();
241  if (!(hsFlags & GADC_HSADC_RUNNING)) {
242  if (!(hsData = gfxBufferGetI())) {
243  // Oops - no free buffers. Stall
244  hsFlags |= GADC_HSADC_STALL;
245  #if GFX_USE_GEVENT
246  if (hsFlags & GADC_HSADC_GTIMER)
247  gtimerJabI(&hsGTimer);
248  #endif
249 
250  // Prepare the next job
251  } else {
252 
253  #if GFX_USE_OS_CHIBIOS
254  // ChibiOS api bug - samples must be even
255  hsJob.todo = (hsData->size / hsBytesPerConv) & ~1;
256  #else
257  hsJob.todo = hsData->size / hsBytesPerConv;
258  #endif
259  hsJob.done = 0;
260  hsJob.buffer = (adcsample_t *)(hsData+1);
261  hsFlags |= GADC_HSADC_RUNNING;
262 
263  // Start the timer
264  gadc_lld_start_timerI(hsJob.frequency);
265 
266  // If nothing is running start the job
267  if (!(hsFlags & GADC_ADC_RUNNING)) {
268  hsFlags |= (GADC_HSADC_CONVERTION|GADC_ADC_RUNNING);
269  gadc_lld_timerjobI(&hsJob);
270  }
271  }
272  }
273  gfxSystemUnlock();
274 }
275 
276 void gadcHighSpeedStop(void) {
277  // Stop it and wait for completion
278  hsFlags &= ~GADC_HSADC_RUNNING;
279  while ((hsFlags & GADC_HSADC_CONVERTION))
280  gfxYield();
281 }
282 
283 static void LowSpeedGTimerCallback(void *param) {
284  (void) param;
285  NonTimerData *pdata;
286 
287  // Look for completed non-timer jobs and call the call-backs for each
288  while ((pdata = (NonTimerData *)gfxQueueGSyncGet(&lsListDone, gDelayNone))) {
289  pdata->callback(pdata->job.buffer, pdata->param);
290  gfxFree(pdata);
291  }
292 }
293 
294 void gadcLowSpeedGet(gU32 physdev, adcsample_t *buffer) {
295  NonTimerData ndata;
296 
297  // Prepare the job
298  gfxSemInit(&ndata.sigdone, 0, 1);
299  ndata.job.physdev = physdev;
300  ndata.job.buffer = buffer;
301  ndata.callback = 0;
302 
303  // Activate it
304  gfxSystemLock();
305  if (!(hsFlags & GADC_ADC_RUNNING)) {
306  // Nothing is running - start the job
307  lsData = &ndata;
308  hsFlags |= GADC_ADC_RUNNING;
309  hsFlags &= ~GADC_HSADC_CONVERTION;
310  gadc_lld_nontimerjobI(&ndata.job);
311  } else {
312  // Just put it on the queue
313  gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)&ndata);
314  }
315  gfxSystemUnlock();
316 
317  // Wait for it to complete
318  gfxSemWait(&ndata.sigdone, gDelayForever);
319  gfxSemDestroy(&ndata.sigdone);
320 }
321 
322 gBool gadcLowSpeedStart(gU32 physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param) {
323  NonTimerData *pdata;
324 
325  /* Start the Low Speed Timer */
326  if (!gtimerIsActive(&lsGTimer))
327  gtimerStart(&lsGTimer, LowSpeedGTimerCallback, 0, gTrue, gDelayForever);
328 
329  // Prepare the job
330  if (!(pdata = gfxAlloc(sizeof(NonTimerData))))
331  return gFalse;
332  pdata->job.physdev = physdev;
333  pdata->job.buffer = buffer;
334  pdata->callback = fn;
335  pdata->param = param;
336 
337  // Activate it
338  gfxSystemLock();
339  if (!(hsFlags & GADC_ADC_RUNNING)) {
340  // Nothing is running - start the job
341  lsData = pdata;
342  hsFlags |= GADC_ADC_RUNNING;
343  hsFlags &= ~GADC_HSADC_CONVERTION;
344  gadc_lld_nontimerjobI(&pdata->job);
345  } else {
346  // Just put it on the queue
347  gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)pdata);
348  }
349  gfxSystemUnlock();
350  return gTrue;
351 }
352 
353 #endif /* GFX_USE_GADC */
#define GADC_HSADC_LOSTEVENT
The event flag values.
Definition: gadc.h:75
#define GADC_HSADC_RUNNING
The High Speed ADC is currently running.
Definition: gadc.h:76
#define GADC_HSADC_GOTBUFFER
A buffer is ready for processing.
Definition: gadc.h:77
#define GADC_HSADC_STALL
The High Speed ADC has stalled due to no free buffers.
Definition: gadc.h:78
GADC - Periodic ADC driver header file.
void gadc_lld_stop_timerI(void)
Stop the periodic timer for high frequency conversions.
void gadc_lld_start_timerI(gU32 freq)
Start a periodic timer for high frequency conversions.
void gadcGotDataI(gMemSize n)
These routines are the callbacks that the driver uses.
void gadc_lld_nontimerjobI(GadcNonTimerJob *pjob)
Start a non-timer conversion.
void gadc_lld_init(void)
Initialise the driver.
void gadc_lld_timerjobI(GadcTimerJob *pjob)
Start a set of high frequency conversions.
gMemSize gadc_lld_samplesperconversion(gU32 physdev)
Using the hardware dependant "physdev", return the number of samples for each conversion.
gBool gadcLowSpeedStart(gU32 physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param)
Perform a low speed ADC conversion with callback (in a thread context)
void(* GADCCallbackFunction)(adcsample_t *buffer, void *param)
A callback function (executed in a thread context) for a low speed conversion.
Definition: gadc.h:86
void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn)
Allow retrieving of results from the high speed ADC using an ISR callback.
GDataBuffer * gadcHighSpeedGetData(gDelay ms)
Get a filled buffer from the ADC.
GSourceHandle gadcHighSpeedGetSource(void)
Turn on sending results to the GEVENT sub-system.
void gadcHighSpeedInit(gU32 physdev, gU32 frequency)
Initialise the high speed ADC.
void gadcHighSpeedStop(void)
Stop the high speed ADC conversions.
void gadcLowSpeedGet(gU32 physdev, adcsample_t *buffer)
Perform a single low speed ADC conversion.
void(* GADCISRCallbackFunction)(void)
A callback function (executed in an ISR context) for a high speed conversion.
Definition: gadc.h:91
void gadcHighSpeedStart(void)
Start the high speed ADC conversions.
GEvent * geventGetEventBuffer(GSourceListener *psl)
Get the event buffer from the GSourceListener.
Definition: gevent.c:187
void geventSendEvent(GSourceListener *psl)
Called by a source to indicate the listener's event buffer has been filled.
Definition: gevent.c:200
GSourceListener * geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr)
Called by a source with a possible event to get a listener record.
Definition: gevent.c:163
void gfxYield(void)
Yield the current thread.
gBool gfxSemWait(gSem *psem, gDelay ms)
Wait on a semaphore.
void gfxSystemUnlock(void)
Unlock the operating system previous locked by gfxSystemLock()
void * gfxAlloc(gMemSize sz)
Allocate memory.
void gfxSystemLock(void)
Lock the operating system to protect a sequence of code.
void gfxSemDestroy(gSem *psem)
Destroy a Counted Semaphore.
void gfxSemInit(gSem *psem, gSemcount val, gSemcount limit)
Initialise a Counted Semaphore.
void gfxFree(void *ptr)
Free memory.
void gfxSemSignalI(gSem *psem)
Signal a semaphore.
void gtimerJabI(GTimer *pt)
Jab a timer causing the current period to immediate expire.
Definition: gtimer.c:223
void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, gBool periodic, gDelay millisec)
Set a timer going or alter its properties if it is already going.
Definition: gtimer.c:139
void gtimerInit(GTimer *pt)
Initialise a timer.
Definition: gtimer.c:129
void gtimerDeinit(GTimer *pt)
Deinitialise a timer.
Definition: gtimer.c:134
gBool gtimerIsActive(GTimer *pt)
Test if a timer is currently active.
Definition: gtimer.c:208
A Data Buffer Queue.
Definition: gqueue.h:78
The High Speed ADC event structure.
Definition: gadc.h:59
GEventType type
The type of this event (GEVENT_ADC)
Definition: gadc.h:64
gU16 flags
The event flags.
Definition: gadc.h:70
A GTimer structure.
Definition: gtimer.h:52
The structure passed to do a single conversion.
Definition: gadc_driver.h:48
The structure passed to start a timer conversion.
Definition: gadc_driver.h:35
A semaphore.
Definition: gos.h:104
A queue item.
Definition: gqueue.h:40