aboutsummaryrefslogtreecommitdiffstats
path: root/src/gadc/gadc_gadc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gadc/gadc_gadc.c')
-rw-r--r--src/gadc/gadc_gadc.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/src/gadc/gadc_gadc.c b/src/gadc/gadc_gadc.c
new file mode 100644
index 00000000..5ac8e53c
--- /dev/null
+++ b/src/gadc/gadc_gadc.c
@@ -0,0 +1,362 @@
+/*
+ * 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:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gadc/gadc_gadc.c
+ * @brief GADC sub-system code.
+ *
+ * @addtogroup GADC
+ * @{
+ */
+#include "gfx.h"
+
+#if GFX_USE_GADC
+
+/* Include the driver defines */
+#include "driver.h"
+
+#if GADC_MAX_HIGH_SPEED_SAMPLERATE > GADC_MAX_SAMPLE_FREQUENCY/2
+ #error "GADC: GADC_MAX_HIGH_SPEED_SAMPLERATE has been set too high. It must be less than half the maximum CPU rate"
+#endif
+
+#define GADC_HSADC_GTIMER 0x8000
+#define GADC_ADC_RUNNING 0x4000
+#define GADC_HSADC_CONVERTION 0x2000
+
+typedef struct NonTimerData_t {
+ gfxQueueGSyncItem next;
+ GADCCallbackFunction callback;
+ union {
+ void *param;
+ gfxSem sigdone;
+ };
+ GadcNonTimerJob job;
+ } NonTimerData;
+
+static volatile uint16_t hsFlags;
+static size_t hsBytesPerConv;
+static GadcTimerJob hsJob;
+static GDataBuffer *hsData;
+static gfxQueueGSync hsListDone;
+static GADCISRCallbackFunction hsISRcallback;
+#if GFX_USE_GEVENT
+ static GTimer hsGTimer;
+#endif
+
+static GTimer lsGTimer;
+static gfxQueueGSync lsListToDo;
+static gfxQueueGSync lsListDone;
+static NonTimerData *lsData;
+
+void gadcGotDataI(size_t n) {
+ if ((hsFlags & GADC_HSADC_CONVERTION)) {
+
+ // A set of timer conversions is done - add them
+ hsJob.done += n;
+
+ // Are we finished yet? (or driver signalled complete now)
+ if (n && hsJob.done < hsJob.todo)
+ return;
+
+ // Clear event flags we might set
+ hsFlags &= ~(GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL);
+
+ // Is there any data in it
+ if (!hsJob.done) {
+
+ // Oops - no data in this buffer. Just return it to the free-list
+ gfxBufferReleaseI(hsData);
+ goto starttimerjob; // Restart the timer job
+ }
+
+ // Save the buffer on the hsListDone list
+ hsData->len = hsJob.done * hsBytesPerConv;
+ gfxQueueGSyncPutI(&hsListDone, (gfxQueueGSyncItem *)hsData);
+ hsFlags |= GADC_HSADC_GOTBUFFER;
+
+ /* Signal a buffer completion */
+ if (hsISRcallback)
+ hsISRcallback();
+ #if GFX_USE_GEVENT
+ if (hsFlags & GADC_HSADC_GTIMER)
+ gtimerJabI(&hsGTimer);
+ #endif
+
+ // Stop if we have been told to
+ if (!(hsFlags & GADC_HSADC_RUNNING)) {
+ gadc_lld_stop_timerI();
+
+ // Get the next free buffer
+ } else if (!(hsData = gfxBufferGetI())) {
+
+ // Oops - no free buffers. Stall
+ hsFlags &= ~GADC_HSADC_RUNNING;
+ hsFlags |= GADC_HSADC_STALL;
+ gadc_lld_stop_timerI();
+
+ // Prepare the next job
+ } else {
+
+ // Return this new job
+ #if GFX_USE_OS_CHIBIOS
+ // ChibiOS api bug - samples must be even
+ hsJob.todo = (hsData->size / hsBytesPerConv) & ~1;
+ #else
+ hsJob.todo = hsData->size / hsBytesPerConv;
+ #endif
+ hsJob.done = 0;
+ hsJob.buffer = (adcsample_t *)(hsData+1);
+ }
+
+ // Start a job preferring a non-timer job
+ if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) {
+ hsFlags &= ~GADC_HSADC_CONVERTION;
+ gadc_lld_nontimerjobI(&lsData->job);
+ } else if ((hsFlags & GADC_HSADC_RUNNING)) {
+ hsFlags |= GADC_HSADC_CONVERTION;
+ gadc_lld_timerjobI(&hsJob);
+ } else
+ hsFlags &= ~GADC_ADC_RUNNING;
+
+ } else {
+
+ // Did it fail
+ if (!n) {
+ // Push it back on the head of the queue - it didn't actually get done
+ gfxQueueGSyncPushI(&lsListToDo, (gfxQueueGSyncItem *)lsData);
+ lsData = 0;
+ goto starttimerjob;
+ }
+
+ // A non-timer job completed - signal
+ if (lsData->callback) {
+ // Put it on the completed list and signal the timer to do the call-backs
+ gfxQueueGSyncPutI(&lsListDone, (gfxQueueGSyncItem *)lsData);
+ gtimerJabI(&lsGTimer);
+ } else {
+ // Signal the thread directly
+ gfxSemSignalI(&lsData->sigdone);
+ }
+ lsData = 0;
+
+ // Start a job preferring a timer job
+starttimerjob:
+ if ((hsFlags & GADC_HSADC_RUNNING)) {
+ hsFlags |= GADC_HSADC_CONVERTION;
+ gadc_lld_timerjobI(&hsJob);
+ } else if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) {
+ hsFlags &= ~GADC_HSADC_CONVERTION;
+ gadc_lld_nontimerjobI(&lsData->job);
+ } else
+ hsFlags &= ~GADC_ADC_RUNNING;
+ }
+}
+
+/* Our module initialiser */
+void _gadcInit(void)
+{
+ gadc_lld_init();
+
+ gfxQueueGSyncInit(&hsListDone);
+ #if GFX_USE_GEVENT
+ gtimerInit(&hsGTimer);
+ #endif
+ gtimerInit(&lsGTimer);
+ gfxQueueGSyncInit(&lsListToDo);
+ gfxQueueGSyncInit(&lsListDone);
+}
+
+void _gadcDeinit(void)
+{
+ /* commented stuff is ToDo */
+
+ // gadc_lld_deinit();
+ gfxQueueGSyncDeinit(&hsListDone);
+ #if GFX_USE_GEVENT
+ gtimerDeinit(&hsGTimer);
+ #endif
+ gtimerDeinit(&lsGTimer);
+ gfxQueueGSyncDeinit(&lsListToDo);
+ gfxQueueGSyncDeinit(&lsListDone);
+}
+
+#if GFX_USE_GEVENT
+ static void HighSpeedGTimerCallback(void *param) {
+ (void) param;
+ GSourceListener *psl;
+ GEventADC *pe;
+
+ psl = 0;
+ while ((psl = geventGetSourceListener((GSourceHandle)(&hsGTimer), psl))) {
+ if (!(pe = (GEventADC *)geventGetEventBuffer(psl))) {
+ // This listener is missing - save this.
+ psl->srcflags |= GADC_HSADC_LOSTEVENT;
+ continue;
+ }
+
+ pe->type = GEVENT_ADC;
+ pe->flags = (hsFlags & (GADC_HSADC_RUNNING|GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL)) | psl->srcflags;
+ psl->srcflags = 0;
+ geventSendEvent(psl);
+ }
+ }
+#endif
+
+void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency)
+{
+ if ((hsFlags & GADC_HSADC_RUNNING))
+ gadcHighSpeedStop();
+
+ /* Just save the details and reset everything for now */
+ hsJob.physdev = physdev;
+ hsJob.frequency = frequency;
+ hsISRcallback = 0;
+ hsBytesPerConv = gadc_lld_samplesperconversion(physdev) * sizeof(adcsample_t);
+}
+
+#if GFX_USE_GEVENT
+ GSourceHandle gadcHighSpeedGetSource(void) {
+ if (!gtimerIsActive(&hsGTimer))
+ gtimerStart(&hsGTimer, HighSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
+ hsFlags |= GADC_HSADC_GTIMER;
+ return (GSourceHandle)&hsGTimer;
+ }
+#endif
+
+void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn) {
+ hsISRcallback = isrfn;
+}
+
+GDataBuffer *gadcHighSpeedGetData(delaytime_t ms) {
+ return (GDataBuffer *)gfxQueueGSyncGet(&hsListDone, ms);
+}
+
+GDataBuffer *gadcHighSpeedGetDataI(void) {
+ return (GDataBuffer *)gfxQueueGSyncGetI(&hsListDone);
+}
+
+void gadcHighSpeedStart(void) {
+ // Safety first
+ if (!hsJob.frequency || !hsBytesPerConv)
+ return;
+
+ gfxSystemLock();
+ if (!(hsFlags & GADC_HSADC_RUNNING)) {
+ if (!(hsData = gfxBufferGetI())) {
+ // Oops - no free buffers. Stall
+ hsFlags |= GADC_HSADC_STALL;
+ #if GFX_USE_GEVENT
+ if (hsFlags & GADC_HSADC_GTIMER)
+ gtimerJabI(&hsGTimer);
+ #endif
+
+ // Prepare the next job
+ } else {
+
+ #if GFX_USE_OS_CHIBIOS
+ // ChibiOS api bug - samples must be even
+ hsJob.todo = (hsData->size / hsBytesPerConv) & ~1;
+ #else
+ hsJob.todo = hsData->size / hsBytesPerConv;
+ #endif
+ hsJob.done = 0;
+ hsJob.buffer = (adcsample_t *)(hsData+1);
+ hsFlags |= GADC_HSADC_RUNNING;
+
+ // Start the timer
+ gadc_lld_start_timerI(hsJob.frequency);
+
+ // If nothing is running start the job
+ if (!(hsFlags & GADC_ADC_RUNNING)) {
+ hsFlags |= (GADC_HSADC_CONVERTION|GADC_ADC_RUNNING);
+ gadc_lld_timerjobI(&hsJob);
+ }
+ }
+ }
+ gfxSystemUnlock();
+}
+
+void gadcHighSpeedStop(void) {
+ // Stop it and wait for completion
+ hsFlags &= ~GADC_HSADC_RUNNING;
+ while ((hsFlags & GADC_HSADC_CONVERTION))
+ gfxYield();
+}
+
+static void LowSpeedGTimerCallback(void *param) {
+ (void) param;
+ NonTimerData *pdata;
+
+ // Look for completed non-timer jobs and call the call-backs for each
+ while ((pdata = (NonTimerData *)gfxQueueGSyncGet(&lsListDone, TIME_IMMEDIATE))) {
+ pdata->callback(pdata->job.buffer, pdata->param);
+ gfxFree(pdata);
+ }
+}
+
+void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) {
+ NonTimerData ndata;
+
+ // Prepare the job
+ gfxSemInit(&ndata.sigdone, 0, 1);
+ ndata.job.physdev = physdev;
+ ndata.job.buffer = buffer;
+ ndata.callback = 0;
+
+ // Activate it
+ gfxSystemLock();
+ if (!(hsFlags & GADC_ADC_RUNNING)) {
+ // Nothing is running - start the job
+ lsData = &ndata;
+ hsFlags |= GADC_ADC_RUNNING;
+ hsFlags &= ~GADC_HSADC_CONVERTION;
+ gadc_lld_nontimerjobI(&ndata.job);
+ } else {
+ // Just put it on the queue
+ gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)&ndata);
+ }
+ gfxSystemUnlock();
+
+ // Wait for it to complete
+ gfxSemWait(&ndata.sigdone, TIME_INFINITE);
+ gfxSemDestroy(&ndata.sigdone);
+}
+
+bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param) {
+ NonTimerData *pdata;
+
+ /* Start the Low Speed Timer */
+ if (!gtimerIsActive(&lsGTimer))
+ gtimerStart(&lsGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
+
+ // Prepare the job
+ if (!(pdata = gfxAlloc(sizeof(NonTimerData))))
+ return FALSE;
+ pdata->job.physdev = physdev;
+ pdata->job.buffer = buffer;
+ pdata->callback = fn;
+ pdata->param = param;
+
+ // Activate it
+ gfxSystemLock();
+ if (!(hsFlags & GADC_ADC_RUNNING)) {
+ // Nothing is running - start the job
+ lsData = pdata;
+ hsFlags |= GADC_ADC_RUNNING;
+ hsFlags &= ~GADC_HSADC_CONVERTION;
+ gadc_lld_nontimerjobI(&pdata->job);
+ } else {
+ // Just put it on the queue
+ gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)pdata);
+ }
+ gfxSystemUnlock();
+ return TRUE;
+}
+
+#endif /* GFX_USE_GADC */
+/** @} */
+