diff --git a/demos/modules/gadc/gfxconf.h b/demos/modules/gadc/gfxconf.h new file mode 100644 index 00000000..58475fba --- /dev/null +++ b/demos/modules/gadc/gfxconf.h @@ -0,0 +1,105 @@ +/** + * This file has a different license to the rest of the GFX system. + * You can copy, modify and distribute this file as you see fit. + * You do not need to publish your source modifications to this file. + * The only thing you are not permitted to do is to relicense it + * under a different license. + */ + +/** + * Copy this file into your project directory and rename it as gfxconf.h + * Edit your copy to turn on the GFX features you want to use. + */ + +#ifndef _GFXCONF_H +#define _GFXCONF_H + +/* GFX sub-systems to turn on */ +#define GFX_USE_GDISP TRUE +#define GFX_USE_TDISP FALSE +#define GFX_USE_GWIN TRUE +#define GFX_USE_GEVENT FALSE +#define GFX_USE_GTIMER TRUE +#define GFX_USE_GINPUT FALSE +#define GFX_USE_GADC TRUE +#define GFX_USE_GAUDIN FALSE +#define GFX_USE_GAUDOUT FALSE +#define GFX_USE_GMISC FALSE + +/* Features for the GDISP sub-system. */ +#define GDISP_NEED_VALIDATION TRUE +#define GDISP_NEED_CLIP TRUE +#define GDISP_NEED_TEXT TRUE +#define GDISP_NEED_CIRCLE FALSE +#define GDISP_NEED_ELLIPSE FALSE +#define GDISP_NEED_ARC FALSE +#define GDISP_NEED_SCROLL FALSE +#define GDISP_NEED_PIXELREAD FALSE +#define GDISP_NEED_CONTROL TRUE +#define GDISP_NEED_MULTITHREAD TRUE +#define GDISP_NEED_ASYNC FALSE +#define GDISP_NEED_MSGAPI FALSE + +/* GDISP - builtin fonts */ +#define GDISP_OLD_FONT_DEFINITIONS FALSE +#define GDISP_INCLUDE_FONT_SMALL FALSE +#define GDISP_INCLUDE_FONT_LARGER FALSE +#define GDISP_INCLUDE_FONT_UI1 FALSE +#define GDISP_INCLUDE_FONT_UI2 TRUE +#define GDISP_INCLUDE_FONT_LARGENUMBERS FALSE + +/* Features for the TDISP subsystem. */ +#define TDISP_NEED_MULTITHREAD FALSE + +/* Features for the GWIN sub-system. */ +#define GWIN_NEED_BUTTON FALSE +#define GWIN_NEED_CONSOLE TRUE +#define GWIN_NEED_GRAPH FALSE + +/* Features for the GEVENT sub-system. */ +#define GEVENT_ASSERT_NO_RESOURCE FALSE + +/* Features for the GTIMER sub-system. */ +/* NONE */ + +/* Features for the GINPUT sub-system. */ +#define GINPUT_NEED_MOUSE FALSE +#define GINPUT_NEED_KEYBOARD FALSE +#define GINPUT_NEED_TOGGLE FALSE +#define GINPUT_NEED_DIAL FALSE + +/* Features for the GADC sub-system. */ +/* NONE */ + +/* Features for the GAUDIN sub-system. */ +/* NONE */ + +/* Features for the GAUDOUT sub-system. */ +/* NONE */ + +/* Features for the GMISC sub-system. */ +#define GMISC_NEED_ARRAYOPS FALSE + +/* Optional Parameters for various sub-systems */ +/* + #define GDISP_MAX_FONT_HEIGHT 16 + #define GEVENT_MAXIMUM_SIZE 32 + #define GEVENT_MAX_SOURCE_LISTENERS 32 + #define GTIMER_THREAD_WORKAREA_SIZE 512 + #define GADC_MAX_LOWSPEED_DEVICES 4 +*/ + +/* Optional Low Level Driver Definitions */ +/* + #define GDISP_USE_CUSTOM_BOARD FALSE + #define GDISP_SCREEN_WIDTH 320 + #define GDISP_SCREEN_HEIGHT 240 + #define GDISP_USE_FSMC + #define GDISP_USE_GPIO + #define GDISP_VMT_NAME1(x) x##YourDriver1 + #define GDISP_VMT_NAME2(x) x##YourDriver2 + #define TDISP_COLUMNS 16 + #define TDISP_ROWS 2 +*/ + +#endif /* _GFXCONF_H */ diff --git a/demos/modules/gadc/gwinosc.c b/demos/modules/gadc/gwinosc.c new file mode 100644 index 00000000..d08fbc0d --- /dev/null +++ b/demos/modules/gadc/gwinosc.c @@ -0,0 +1,183 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * --------------------------- Our Custom GWIN Oscilloscope --------------- + * + * This GWIN superset implements a simple audio oscilloscope using the GADC high speed device. + */ +#include "ch.h" +#include "hal.h" +#include "gfx.h" + +#include "gwinosc.h" + +/* Include internal GWIN routines so we can build our own superset class */ +#include "gwin/internal.h" + +/* Our GWIN identifier */ +#define GW_SCOPE (GW_FIRST_USER_WINDOW+0) + +/* The size of our dynamically allocated audio buffer */ +#define AUDIOBUFSZ 64*2 + +/* How many flat-line sample before we trigger */ +#define FLATLINE_SAMPLES 8 + +GHandle gwinCreateScope(GScopeObject *gs, coord_t x, coord_t y, coord_t cx, coord_t cy, uint32_t physdev, uint32_t frequency) { + /* Initialise the base class GWIN */ + if (!(gs = (GScopeObject *)_gwinInit((GWindowObject *)gs, x, y, cx, cy, sizeof(GScopeObject)))) + return 0; + + /* Initialise the scope object members and allocate memory for buffers */ + gs->gwin.type = GW_SCOPE; + chBSemInit(&gs->bsem, TRUE); + gs->nextx = 0; + if (!(gs->lastscopetrace = (coord_t *)chHeapAlloc(NULL, gs->gwin.width * sizeof(coord_t)))) + return 0; + if (!(gs->audiobuf = (adcsample_t *)chHeapAlloc(NULL, AUDIOBUFSZ * sizeof(adcsample_t)))) + return 0; +#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP + gs->lasty = gs->gwin.height/2; +#elif TRIGGER_METHOD == TRIGGER_MINVALUE + gs->lasty = gs->gwin.height/2; + gs->scopemin = 0; +#endif + + /* Start the GADC high speed converter */ + gadcHighSpeedInit(physdev, frequency, gs->audiobuf, AUDIOBUFSZ, AUDIOBUFSZ/2); + gadcHighSpeedSetBSem(&gs->bsem, &gs->myEvent); + gadcHighSpeedStart(); + + return (GHandle)gs; +} + +void gwinWaitForScopeTrace(GHandle gh) { + #define gs ((GScopeObject *)(gh)) + int i; + coord_t x, y; + coord_t yoffset; + adcsample_t *pa; + coord_t *pc; +#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP + bool_t rdytrigger; + int flsamples; +#elif TRIGGER_METHOD == TRIGGER_MINVALUE + bool_t rdytrigger; + int flsamples; + coord_t scopemin; +#endif + + /* Wait for a set of audio conversions */ + chBSemWait(&gs->bsem); + + /* Ensure we are drawing in the right area */ + #if GDISP_NEED_CLIP + gdispSetClip(gh->x, gh->y, gh->width, gh->height); + #endif + + yoffset = gh->height/2 + (1<nextx; + pc = gs->lastscopetrace+x; + pa = gs->myEvent.buffer; +#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP + rdytrigger = FALSE; + flsamples = 0; +#elif TRIGGER_METHOD == TRIGGER_MINVALUE + rdytrigger = FALSE; + flsamples = 0; + scopemin = 0; +#endif + + for(i = gs->myEvent.count; i; i--) { + + /* Calculate the new scope value - re-scale using simple shifts for efficiency, re-center and y-invert */ + #if GADC_BITS_PER_SAMPLE > SCOPE_Y_BITS + y = yoffset - (*pa++ >> (GADC_BITS_PER_SAMPLE - SCOPE_Y_BITS)); + #else + y = yoffset - (*pa++ << (SCOPE_Y_BITS - GADC_BITS_PER_SAMPLE)); + #endif + +#if TRIGGER_METHOD == TRIGGER_MINVALUE + /* Calculate the scopemin ready for the next trace */ + if (y > scopemin) + scopemin = y; +#endif + + /* Have we reached the end of a scope trace? */ + if (x >= gh->width) { + +#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP || TRIGGER_METHOD == TRIGGER_MINVALUE + /* Handle triggering - we trigger on the next sample minimum (y value maximum) or a flat-line */ + + #if TRIGGER_METHOD == TRIGGER_MINVALUE + /* Arm when we reach the sample minimum (y value maximum) of the previous trace */ + if (!rdytrigger && y >= gs->scopemin) + rdytrigger = TRUE; + #endif + + if (y == gs->lasty) { + /* Trigger if we get too many flat-line samples regardless of the armed state */ + if (++flsamples < FLATLINE_SAMPLES) + continue; + flsamples = 0; + } else if (y > gs->lasty) { + gs->lasty = y; + flsamples = 0; + #if TRIGGER_METHOD == TRIGGER_POSITIVERAMP + /* Arm the trigger when samples fall (y increases) ie. negative slope */ + rdytrigger = TRUE; + #endif + continue; + } else { + /* If the trigger is armed, Trigger when samples increases (y decreases) ie. positive slope */ + gs->lasty = y; + flsamples = 0; + if (!rdytrigger) + continue; + } + + /* Ready for a the next trigger cycle */ + rdytrigger = FALSE; +#endif + + /* Prepare for a scope trace */ + x = 0; + pc = gs->lastscopetrace; + } + + /* Clear the old scope pixel and then draw the new scope value */ + gdispDrawPixel(gh->x+x, gh->y+pc[0], gh->bgcolor); + gdispDrawPixel(gh->x+x, gh->y+y, gh->color); + + /* Save the value */ + *pc++ = y; + x++; + #if TRIGGER_METHOD == TRIGGER_POSITIVERAMP || TRIGGER_METHOD == TRIGGER_MINVALUE + gs->lasty = y; + #endif + } + gs->nextx = x; +#if TRIGGER_METHOD == TRIGGER_MINVALUE + gs->scopemin = scopemin; +#endif + + #undef gs +} diff --git a/demos/modules/gadc/gwinosc.h b/demos/modules/gadc/gwinosc.h new file mode 100644 index 00000000..ec44937d --- /dev/null +++ b/demos/modules/gadc/gwinosc.h @@ -0,0 +1,89 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _GWINOSC_H +#define _GWINOSC_H + +/** + * --------------------------- Our Custom GWIN Oscilloscope --------------- + * + * This GWIN superset implements a simple audio oscilloscope using the GADC high speed device. + */ + +/* The extent of scaling for our audio data - fixed scale at the moment */ +#ifndef SCOPE_Y_BITS + #define SCOPE_Y_BITS 7 // 7 bits = 0..128 +#endif + +/* Trigger methods */ +#define TRIGGER_NONE 0 /* No triggering */ +#define TRIGGER_POSITIVERAMP 1 /* Trigger on a positive going signal */ +#define TRIGGER_MINVALUE 2 /* Trigger on reaching the minimum value from the last scope */ + +/** + * Which trigger we want to use. + * Experiments suggests that TRIGGER_MINVALUE gives the best result + */ +#ifndef TRIGGER_METHOD + #define TRIGGER_METHOD TRIGGER_MINVALUE +#endif + +/* A scope window object. Treat it as a black box */ +typedef struct GScopeObject_t { + GWindowObject gwin; // Base Class + + coord_t *lastscopetrace; // To store last scope trace + BinarySemaphore bsem; // We get signalled on this + adcsample_t *audiobuf; // To store audio samples + GEventADC myEvent; // Information on received samples + coord_t nextx; // Where we are up to +#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP + coord_t lasty; // The last y value - used for trigger slope detection +#elif TRIGGER_METHOD == TRIGGER_MINVALUE + coord_t lasty; // The last y value - used for trigger slope detection + coord_t scopemin; // The last scopes minimum value +#endif + } GScopeObject; + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * Create a scope window. + */ + GHandle gwinCreateScope(GScopeObject *gs, coord_t x, coord_t y, coord_t cx, coord_t cy, uint32_t physdev, uint32_t frequency); + + /** + * Wait for a scope trace to be ready and then draw it. + */ + void gwinWaitForScopeTrace(GHandle gh); + + /** + * We should also have a special destroy routine here as we have dynamically + * allocated some memory. There is no point implementing this however as, for + * this demo, we never destroy the window. + */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GWINOSC_H */ diff --git a/demos/modules/gadc/main.c b/demos/modules/gadc/main.c new file mode 100644 index 00000000..07802521 --- /dev/null +++ b/demos/modules/gadc/main.c @@ -0,0 +1,176 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * This demo demonstrates the use of the GADC module using it read both a microphone, + * an analogue dial wheel and a temperature sensor. + * The microphone gets read at high frequency to display a very simple oscilloscope. + * The dial and temperature gets read at a low frequency to just print when + * it changes value. + * + * It also demonstrates how to write your own custom GWIN window type. + */ +#include "ch.h" +#include "hal.h" +#include "chprintf.h" + +#include "gfx.h" + +/* Include our custom gwin oscilloscope */ +#include "gwinosc.h" + +/* + * Match these to your hardware + * If you don't have a DIAL device or a TEMP device - just don't define it. + */ +#define MY_MIC_DEVICE GADC_PHYSDEV_MICROPHONE +#define MY_DIAL_DEVICE GADC_PHYSDEV_DIAL +#define MY_TEMP_DEVICE GADC_PHYSDEV_TEMPERATURE +#define MY_DIAL_JITTER 1 +#define MY_TEMP_JITTER 3 + +/* Specify our timing parameters */ +#define MY_MIC_FREQUENCY 4000 /* 4khz */ +#define MY_LS_DELAY 200 /* 200ms (5 times per second) for the dial and temperature */ + +/* The desired size for our scope window */ +#define SCOPE_CX 64 +#define SCOPE_CY 64 + +/* Data */ +static GScopeObject gScopeWindow; +static GConsoleObject gTextWindow; +static GTimer lsTimer; + +#ifdef MY_DIAL_DEVICE + static adcsample_t dialvalue; + static adcsample_t lastdial = -(MY_DIAL_JITTER+1); + + /** + * We have got a dial reading - handle it + */ + static void GotDialReading(adcsample_t *buffer, void *param) { + (void) buffer; + + /* Buffer should always point to "dialvalue" anyway */ + + /* Remove jitter from the value */ + if ((dialvalue > lastdial && dialvalue - lastdial > MY_DIAL_JITTER) + || (lastdial > dialvalue && lastdial - dialvalue > MY_DIAL_JITTER)) { + + /* Write the value */ + chprintf((BaseSequentialStream *)param, "DIAL: %u\n", dialvalue); + + /* Save for next time */ + lastdial = dialvalue; + } + } +#endif + +#ifdef MY_TEMP_DEVICE + static adcsample_t tempvalue; + static adcsample_t lasttemp = -(MY_TEMP_JITTER+1); + + /** + * We have got a temperature reading - handle it + */ + static void GotTempReading(adcsample_t *buffer, void *param) { + (void) buffer; + + /* Buffer should always point to "tempvalue" anyway */ + + /* Remove jitter from the value */ + if ((tempvalue > lasttemp && tempvalue - lasttemp > MY_TEMP_JITTER) + || (lasttemp > tempvalue && lasttemp - tempvalue > MY_TEMP_JITTER)) { + + /* Write the value */ + chprintf((BaseSequentialStream *)param, "TEMP: %u\n", tempvalue); + + /* Save for next time */ + lasttemp = tempvalue; + } + } +#endif + +#if defined(MY_DIAL_DEVICE) || defined(MY_TEMP_DEVICE) + /** + * Start a read of the dial and temperature + */ + static void LowSpeedTimer(void *param) { + /* We are not checking for an error here - but who cares, this is just a demo */ + #ifdef MY_DIAL_DEVICE + gadcLowSpeedStart(MY_DIAL_DEVICE, &dialvalue, GotDialReading, param); + #endif + #ifdef MY_TEMP_DEVICE + gadcLowSpeedStart(MY_TEMP_DEVICE, &tempvalue, GotTempReading, param); + #endif + } +#endif + +/* + * Application entry point. + */ +int main(void) { + GHandle ghScope; + coord_t swidth, sheight; + #if defined(MY_DIAL_DEVICE) || defined(MY_TEMP_DEVICE) + GHandle ghText; + BaseSequentialStream *gsText; + font_t font; + #endif + + halInit(); + chSysInit(); + gdispInit(); + gdispClear(Black); + + /* Get the screen dimensions */ + swidth = gdispGetWidth(); + sheight = gdispGetHeight(); + + #if defined(MY_DIAL_DEVICE) || defined(MY_TEMP_DEVICE) + /* Set up the console window we use for dial readings */ + font = gdispOpenFont("UI2"); + ghText = gwinCreateConsole(&gTextWindow, 0, 0, swidth-SCOPE_CX, sheight, font); + gwinSetBgColor(ghText, Black); + gwinSetColor(ghText, Yellow); + gwinClear(ghText); + gsText = gwinGetConsoleStream(ghText); + + /* Start our timer for reading the dial */ + gtimerInit(&lsTimer); + gtimerStart(&lsTimer, LowSpeedTimer, gsText, TRUE, MY_LS_DELAY); + #endif + + /* Set up the scope window in the top right on the screen */ + ghScope = gwinCreateScope(&gScopeWindow, swidth-SCOPE_CX, 0, SCOPE_CX, SCOPE_CY, MY_MIC_DEVICE, MY_MIC_FREQUENCY); + gwinSetBgColor(ghScope, White); + gwinSetColor(ghScope, Red); + gwinClear(ghScope); + + /* Just keep displaying the scope traces */ + while (TRUE) { + /** + * The function below internally performs a wait thus giving the timer thread a + * chance to run. + */ + gwinWaitForScopeTrace(ghScope); + } +} diff --git a/demos/modules/gadc/results_264x264.jpg b/demos/modules/gadc/results_264x264.jpg new file mode 100644 index 00000000..25aadf81 Binary files /dev/null and b/demos/modules/gadc/results_264x264.jpg differ diff --git a/drivers/gadc/AT91SAM7/gadc_lld.c b/drivers/gadc/AT91SAM7/gadc_lld.c new file mode 100644 index 00000000..4b3c6cae --- /dev/null +++ b/drivers/gadc/AT91SAM7/gadc_lld.c @@ -0,0 +1,102 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** + * @file include/gadc/lld/gadc_lld.c + * @brief GADC - Periodic ADC driver source file for the AT91SAM7 cpu. + * + * @defgroup Driver Driver + * @ingroup GADC + * @{ + */ + +#include "ch.h" +#include "hal.h" +#include "gfx.h" + +#if GFX_USE_GADC + +#include "gadc/lld/gadc_lld.h" + +static ADCConversionGroup acg = { + FALSE, // circular + 1, // num_channels + GADC_ISR_CompleteI, // end_cb + GADC_ISR_ErrorI, // error_cb + 0, // channelselects + 0, // trigger + 0, // frequency + }; + +void gadc_lld_init(void) { + adcStart(&ADCD1, NULL); +} + +size_t gadc_lld_samples_per_conversion(uint32_t physdev) { + size_t cnt; + int i; + + /* The AT91SAM7 has AD0..7 - physdev is a bitmap of those channels */ + for(cnt = 0, i = 0; i < 8; i++, physdev >>= 1) + if (physdev & 0x01) + cnt++; + return cnt; +} + +void gadc_lld_start_timer(uint32_t physdev, uint32_t frequency) { + (void) physdev; + /** + * The AT91SAM7 ADC driver supports triggering the ADC using a timer without having to implement + * an interrupt handler for the timer. The driver also initialises the timer correctly for us. + * Because we aren't trapping the interrupt ourselves we can't increment GADC_Timer_Missed if an + * interrupt is missed. + */ + acg.frequency = frequency; +} + +void gadc_lld_stop_timer(uint32_t physdev) { + (void) physdev; + if ((acg.trigger & ~ADC_TRIGGER_SOFTWARE) == ADC_TRIGGER_TIMER) + adcStop(&ADCD1); +} + +void gadc_lld_adc_timerI(GadcLldTimerData *pgtd) { + /** + * We don't need to calculate num_channels because the AT91SAM7 ADC does this for us. + */ + acg.channelselects = pgtd->physdev; + acg.trigger = pgtd->now ? (ADC_TRIGGER_TIMER|ADC_TRIGGER_SOFTWARE) : ADC_TRIGGER_TIMER; + + adcStartConversionI(&ADCD1, &acg, pgtd->buffer, pgtd->count); + + /* Next time assume the same (still running) timer */ + acg.frequency = 0; +} + +void gadc_lld_adc_nontimerI(GadcLldNonTimerData *pgntd) { + /** + * We don't need to calculate num_channels because the AT91SAM7 ADC does this for us. + */ + acg.channelselects = pgntd->physdev; + acg.trigger = ADC_TRIGGER_SOFTWARE; + adcStartConversionI(&ADCD1, &acg, pgntd->buffer, 1); +} + +#endif /* GFX_USE_GADC */ +/** @} */ diff --git a/drivers/gadc/AT91SAM7/gadc_lld.mk b/drivers/gadc/AT91SAM7/gadc_lld.mk new file mode 100644 index 00000000..001d44b1 --- /dev/null +++ b/drivers/gadc/AT91SAM7/gadc_lld.mk @@ -0,0 +1,5 @@ +# List the required driver. +GFXSRC += $(GFXLIB)/drivers/gadc/AT91SAM7/gadc_lld.c + +# Required include directories +GFXINC += $(GFXLIB)/drivers/gadc/AT91SAM7 diff --git a/drivers/gadc/AT91SAM7/gadc_lld_board_olimexsam7ex256.h b/drivers/gadc/AT91SAM7/gadc_lld_board_olimexsam7ex256.h new file mode 100644 index 00000000..6f23db17 --- /dev/null +++ b/drivers/gadc/AT91SAM7/gadc_lld_board_olimexsam7ex256.h @@ -0,0 +1,46 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file drivers/gadc/AT91SAM7/gadc_lld_board_olimexsam7ex256.h + * @brief GADC Driver config file. + * + * @addtogroup GADC + * @{ + */ + +#ifndef _GADC_LLD_BOARD_OLIMEXSAM7EX256_H +#define _GADC_LLD_BOARD_OLIMEXSAM7EX256_H + +#if GFX_USE_GADC + +/*===========================================================================*/ +/* Analogue devices on this board */ +/*===========================================================================*/ + +#define GADC_PHYSDEV_MICROPHONE 0x00000080 +#define GADC_PHYSDEV_DIAL 0x00000040 +#define GADC_PHYSDEV_TEMPERATURE 0x00000020 + +#endif /* GFX_USE_GADC */ + +#endif /* _GADC_LLD_BOARD_OLIMEXSAM7EX256_H */ +/** @} */ + diff --git a/drivers/gadc/AT91SAM7/gadc_lld_config.h b/drivers/gadc/AT91SAM7/gadc_lld_config.h new file mode 100644 index 00000000..882573c8 --- /dev/null +++ b/drivers/gadc/AT91SAM7/gadc_lld_config.h @@ -0,0 +1,78 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file drivers/gadc/AT91SAM7/gadc_lld_config.h + * @brief GADC Driver config file. + * + * @addtogroup GADC + * @{ + */ + +#ifndef GADC_LLD_CONFIG_H +#define GADC_LLD_CONFIG_H + +#if GFX_USE_GADC + +/*===========================================================================*/ +/* Driver hardware support. */ +/*===========================================================================*/ + +/** + * @brief ChibiOS has a nasty bug in its _adc_isr_full_code() routine (defined in adc.h as a macro). + * Do we have the version of ChibiOS with this bug. + * @detail Set to TRUE if ChibiOS has this bug. + * @note Fixed in ChibiOS 2.4.4stable and 2.5.2unstable (and the repository from 18th Feb 2013) + * @note This bug prevents us re-calling adcStartConversionI() from with the ISR even though + * it is clearly designed to handle it. For some reason (on this micro) the high speed timer + * is not affected only the single sample low speed timer. In that situation we wait until + * we get back to thread land. This is terrible for the accuracy of the high speed timer + * but what can we do (other than fix the bug). + * @note For the AT91SAM7 ADC driver, it post-dates the finding of the bug so we safely + * say that the bug doesn't exist for this driver. + */ +#define ADC_ISR_FULL_CODE_BUG FALSE + +/** + * @brief The maximum sample frequency supported by this CPU + */ +#define GADC_MAX_SAMPLE_FREQUENCY 132000 + +/** + * @brief The number of bits in a sample + */ +#define GADC_BITS_PER_SAMPLE AT91_ADC1_RESOLUTION + +/* Pull in board specific defines */ +#if defined(GADC_USE_CUSTOM_BOARD) && GADC_USE_CUSTOM_BOARD + /* Include the user supplied board definitions */ + #include "gadc_lld_board.h" +#elif defined(BOARD_OLIMEX_SAM7_EX256) + #include "gadc_lld_board_olimexsam7ex256.h" +#else + /* Include the user supplied board definitions */ + #include "gadc_lld_board.h" +#endif + +#endif /* GFX_USE_GADC */ + +#endif /* _GDISP_LLD_CONFIG_H */ +/** @} */ + diff --git a/gfxconf.example.h b/gfxconf.example.h index c601dbb9..389d4db5 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -96,10 +96,8 @@ #define GDISP_SCREEN_HEIGHT 240 #define GDISP_USE_FSMC #define GDISP_USE_GPIO - #define TDISP_COLUMNS 16 #define TDISP_ROWS 2 */ #endif /* _GFXCONF_H */ - diff --git a/include/gadc/gadc.h b/include/gadc/gadc.h index 5c490cb9..be7af516 100644 --- a/include/gadc/gadc.h +++ b/include/gadc/gadc.h @@ -1,250 +1,258 @@ -/* - ChibiOS/GFX - Copyright (C) 2012 - Joel Bodenmann aka Tectu - - This file is part of ChibiOS/GFX. - - ChibiOS/GFX is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - ChibiOS/GFX is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ -/** - * @file include/gadc/gadc.h - * @brief GADC - Periodic ADC subsystem header file. - * - * @addtogroup GADC - * - * @details The reason why ChibiOS/GFX has it's own ADC abstraction is because - * the Chibi-OS drivers are very CPU specific and do not - * provide a way across all hardware platforms to create periodic - * ADC conversions. There are also issues with devices with different - * characteristics or periodic requirements on the same ADC - * device (but different channels). This layer attempts to solve these - * problems to provide a architecture neutral API. It also provides extra - * features such as multi-buffer chaining for high speed ADC sources. - * It provides one high speed virtual ADC device (eg a microphone) and - * numerous low speed (less than 100Hz) virtual ADC devices (eg dials, - * temperature sensors etc). The high speed device has timer based polling - * to ensure exact conversion periods and a buffer management system. - * The low speed devices are assumed to be non-critical timing devices - * and do not have any buffer management. - * Note that while only one high speed device has been provided it can - * be used to read multiple physical ADC channels on the one physical - * ADC device. - * All callback routines are thread based unlike the Chibi-OS interrupt based - * routines. - * - * @{ - */ - -#ifndef _GADC_H -#define _GADC_H - -#include "gfx.h" - -#if GFX_USE_GADC || defined(__DOXYGEN__) - -/* Include the driver defines */ -#include "gadc_lld_config.h" - -/*===========================================================================*/ -/* Type definitions */ -/*===========================================================================*/ - -// Event types for GADC -#define GEVENT_ADC (GEVENT_GADC_FIRST+0) - -/** - * @brief The High Speed ADC event structure. - * @{ - */ -typedef struct GEventADC_t { - /** - * @brief The type of this event (GEVENT_ADC) - */ - GEventType type; - /** - * @brief The event flags - */ - uint16_t flags; - /** - * @brief The event flag values. - * @{ - */ - #define GADC_HSADC_LOSTEVENT 0x0001 /**< @brief The last GEVENT_HSDADC event was lost */ - /** @} */ - /** - * @brief The number of conversions in the buffer - */ - size_t count; - /** - * @brief The buffer containing the conversion samples - */ - adcsample_t *buffer; - } GEventADC; - -/** - * @brief A callback function (executed in a thread context) - */ -typedef void (*GADCCallbackFunction)(adcsample_t *buffer, void *param); - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Initialise the high speed ADC. - * @details Initialises but does not start the conversions. - * - * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. - * @param[in] frequency The frequency to create ADC conversions - * @param[in] buffer The static buffer to put the ADC samples into. - * @param[in] bufcount The total number of conversions that will fit in the buffer. - * @param[in] countPerEvent The number of conversions to do before returning an event. - * - * @note If the high speed ADC is running it will be stopped. - * @note Due to a bug in Chibi-OS countPerEvent must be even. If bufcount is not - * evenly divisable by countPerEvent, the remainder must also be even. - * @note The physdev parameter may be used to turn on more than one ADC channel. - * Each channel is then interleaved into the provided buffer. Note 'bufcount' - * and 'countPerEvent' parameters describe the number of conversions not the - * number of samples. - * As an example, if physdev turns on 2 devices then the buffer contains - * alternate device samples and the buffer must contain 2 * bufcount samples. - * The exact meaning of physdev is hardware dependent. - * @note The buffer is circular. When the end of the buffer is reached it will start - * putting data into the beginning of the buffer again. - * @note The event listener must process the event (and the data in it) before the - * next event occurs. If not, the following event will be lost. - * @note If bufcount is evenly divisable by countPerEvent, then every event will return - * countPerEvent conversions. If bufcount is not evenly divisable, it will return - * a block of samples containing less than countPerEvent samples when it reaches the - * end of the buffer. - * @note While the high speed ADC is running, low speed conversions can only occur at - * the frequency of the high speed events. Thus if high speed events are - * being created at 50Hz (eg countPerEvent = 100, frequency = 5kHz) then the maximum - * frequency for low speed conversions is likely to be 50Hz (although it might be - * 100Hz on some hardware). - * - * @api - */ -void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer, size_t bufcount, size_t samplesPerEvent); - -#if GFX_USE_GEVENT || defined(__DOXYGEN__) - /** - * @brief Turn on sending results to the GEVENT sub-system. - * @details Returns a GSourceHandle to listen for GEVENT_ADC events. - * - * @note The high speed ADC will not use the GEVENT system unless this is - * called first. This saves processing time if the application does - * not want to use the GEVENT sub-system for the high speed ADC. - * Once turned on it cannot be turned off. - * @note The high speed ADC is capable of signalling via this method and a binary semaphore - * at the same time. - * - * @api - */ - GSourceHandle gadcHighSpeedGetSource(void); -#endif - -/** - * @brief Allow retrieving of results from the high speed ADC using a Binary Semaphore and a static event buffer. - * - * @param[in] pbsem The binary semaphore is signaled when data is available. - * @param[in] pEvent The static event buffer to place the result information. - * - * @note Passing a NULL for pbsem or pEvent will turn off signalling via this method. - * @note The high speed ADC is capable of signalling via this method and the GEVENT - * sub-system at the same time. - * - * @api - */ -void gadcHighSpeedSetBSem(BinarySemaphore *pbsem, GEventADC *pEvent); - -/** - * @brief Start the high speed ADC conversions. - * @pre It must have been initialised first with @p gadcHighSpeedInit() - * - * @api - */ -GSourceHandle gadcHighSpeedStart(void); - -/** - * @brief Stop the high speed ADC conversions. - * - * @api - */ -void gadcHighSpeedStop(void); - -/** - * @brief Perform a single low speed ADC conversion - * @details Blocks until the conversion is complete - * @pre This should not be called from within a GTimer callback as this routine - * blocks until the conversion is ready. - * - * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. - * @param[in] buffer The static buffer to put the ADC samples into. - * - * @note This may take a while to complete if the high speed ADC is running as the - * conversion is interleaved with the high speed ADC conversions on a buffer - * completion. - * @note The result buffer must be large enough to store one sample per device - * described by the 'physdev' parameter. - * @note If calling this routine would exceed @p GADC_MAX_LOWSPEED_DEVICES simultaneous low - * speed devices, the routine will wait for an available slot to complete the - * conversion. - * @note Specifying more than one device in physdev is possible but discouraged as the - * calculations to ensure the high speed ADC correctness will be incorrect. Symptoms - * from over-running the high speed ADC include high speed samples being lost. - * - * @api - */ -void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer); - -/** - * @brief Perform a low speed ADC conversion with callback (in a thread context) - * @details Returns FALSE if there are no free low speed ADC slots. See @p GADC_MAX_LOWSPEED_DEVICES for details. - * - * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. - * @param[in] buffer The static buffer to put the ADC samples into. - * @param[in] fn The callback function to call when the conversion is complete. - * @param[in] param A parameter to pass to the callback function. - * - * @note This may be safely called from within a GTimer callback. - * @note The callback may take a while to occur if the high speed ADC is running as the - * conversion is interleaved with the high speed ADC conversions on a buffer - * completion. - * @note The result buffer must be large enough to store one sample per device - * described by the 'physdev' parameter. - * @note As this routine uses a low speed ADC, it asserts if you try to run more than @p GADC_MAX_LOWSPEED_DEVICES - * at the same time. - * @note Specifying more than one device in physdev is possible but discouraged as the - * calculations to ensure the high speed ADC correctness will be incorrect. Symptoms - * from over-running the high speed ADC include high speed samples being lost. - * - * @api - */ -bool gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GADC */ - -#endif /* _GADC_H */ -/** @} */ - +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** + * @file include/gadc/gadc.h + * @brief GADC - Periodic ADC subsystem header file. + * + * @addtogroup GADC + * + * @details The reason why ChibiOS/GFX has it's own ADC abstraction is because + * the Chibi-OS drivers are very CPU specific and do not + * provide a way across all hardware platforms to create periodic + * ADC conversions. There are also issues with devices with different + * characteristics or periodic requirements on the same ADC + * device (but different channels). This layer attempts to solve these + * problems to provide a architecture neutral API. It also provides extra + * features such as multi-buffer chaining for high speed ADC sources. + * It provides one high speed virtual ADC device (eg a microphone) and + * numerous low speed (less than 100Hz) virtual ADC devices (eg dials, + * temperature sensors etc). The high speed device has timer based polling + * to ensure exact conversion periods and a buffer management system. + * The low speed devices are assumed to be non-critical timing devices + * and do not have any buffer management. + * Note that while only one high speed device has been provided it can + * be used to read multiple physical ADC channels on the one physical + * ADC device. + * All callback routines are thread based unlike the Chibi-OS interrupt based + * routines. + * + * @{ + */ + +#ifndef _GADC_H +#define _GADC_H + +#include "gfx.h" + +#if GFX_USE_GADC || defined(__DOXYGEN__) + +/* Include the driver defines */ +#include "gadc_lld_config.h" + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +// Event types for GADC +#define GEVENT_ADC (GEVENT_GADC_FIRST+0) + +/** + * @brief The High Speed ADC event structure. + * @{ + */ +typedef struct GEventADC_t { + #if GFX_USE_GEVENT || defined(__DOXYGEN__) + /** + * @brief The type of this event (GEVENT_ADC) + */ + GEventType type; + #endif + + /** + * @brief The event flags + */ + uint16_t flags; + /** + * @brief The event flag values. + * @{ + */ + #define GADC_HSADC_LOSTEVENT 0x0001 /**< @brief The last GEVENT_HSDADC event was lost */ + /** @} */ + /** + * @brief The number of conversions in the buffer + */ + size_t count; + /** + * @brief The buffer containing the conversion samples + */ + adcsample_t *buffer; + } GEventADC; + +/** + * @brief A callback function (executed in a thread context) + */ +typedef void (*GADCCallbackFunction)(adcsample_t *buffer, void *param); + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialise the high speed ADC. + * @details Initialises but does not start the conversions. + * + * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. + * @param[in] frequency The frequency to create ADC conversions + * @param[in] buffer The static buffer to put the ADC samples into. + * @param[in] bufcount The total number of conversions that will fit in the buffer. + * @param[in] countPerEvent The number of conversions to do before returning an event. + * + * @note If the high speed ADC is running it will be stopped. The Event subsystem is + * disconnected from the high speed ADC and any binary semaphore event is forgotten. + * @note bufcount must be greater than countPerEvent (usually 2 or more times) otherwise + * the buffer will be overwitten with new data while the application is still trying + * to process the old data. + * @note Due to a bug in Chibi-OS countPerEvent must be even. If bufcount is not + * evenly divisable by countPerEvent, the remainder must also be even. + * @note The physdev parameter may be used to turn on more than one ADC channel. + * Each channel is then interleaved into the provided buffer. Note 'bufcount' + * and 'countPerEvent' parameters describe the number of conversions not the + * number of samples. + * As an example, if physdev turns on 2 devices then the buffer contains + * alternate device samples and the buffer must contain 2 * bufcount samples. + * The exact meaning of physdev is hardware dependent. + * @note The buffer is circular. When the end of the buffer is reached it will start + * putting data into the beginning of the buffer again. + * @note The event listener must process the event (and the data in it) before the + * next event occurs. If not, the following event will be lost. + * @note If bufcount is evenly divisable by countPerEvent, then every event will return + * countPerEvent conversions. If bufcount is not evenly divisable, it will return + * a block of samples containing less than countPerEvent samples when it reaches the + * end of the buffer. + * @note While the high speed ADC is running, low speed conversions can only occur at + * the frequency of the high speed events. Thus if high speed events are + * being created at 50Hz (eg countPerEvent = 100, frequency = 5kHz) then the maximum + * frequency for low speed conversions is likely to be 50Hz (although it might be + * 100Hz on some hardware). + * + * @api + */ +void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer, size_t bufcount, size_t samplesPerEvent); + +#if GFX_USE_GEVENT || defined(__DOXYGEN__) + /** + * @brief Turn on sending results to the GEVENT sub-system. + * @details Returns a GSourceHandle to listen for GEVENT_ADC events. + * + * @note The high speed ADC will not use the GEVENT system unless this is + * called first. This saves processing time if the application does + * not want to use the GEVENT sub-system for the high speed ADC. + * Once turned on it can only be turned off by calling @p gadcHighSpeedInit() again. + * @note The high speed ADC is capable of signalling via this method and a binary semaphore + * at the same time. + * + * @api + */ + GSourceHandle gadcHighSpeedGetSource(void); +#endif + +/** + * @brief Allow retrieving of results from the high speed ADC using a Binary Semaphore and a static event buffer. + * + * @param[in] pbsem The binary semaphore is signaled when data is available. + * @param[in] pEvent The static event buffer to place the result information. + * + * @note Passing a NULL for pbsem or pEvent will turn off signalling via this method as will calling + * @p gadcHighSpeedInit(). + * @note The high speed ADC is capable of signalling via this method and the GEVENT + * sub-system at the same time. + * + * @api + */ +void gadcHighSpeedSetBSem(BinarySemaphore *pbsem, GEventADC *pEvent); + +/** + * @brief Start the high speed ADC conversions. + * @pre It must have been initialised first with @p gadcHighSpeedInit() + * + * @api + */ +void gadcHighSpeedStart(void); + +/** + * @brief Stop the high speed ADC conversions. + * + * @api + */ +void gadcHighSpeedStop(void); + +/** + * @brief Perform a single low speed ADC conversion + * @details Blocks until the conversion is complete + * @pre This should not be called from within a GTimer callback as this routine + * blocks until the conversion is ready. + * + * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. + * @param[in] buffer The static buffer to put the ADC samples into. + * + * @note This may take a while to complete if the high speed ADC is running as the + * conversion is interleaved with the high speed ADC conversions on a buffer + * completion. + * @note The result buffer must be large enough to store one sample per device + * described by the 'physdev' parameter. + * @note If calling this routine would exceed @p GADC_MAX_LOWSPEED_DEVICES simultaneous low + * speed devices, the routine will wait for an available slot to complete the + * conversion. + * @note Specifying more than one device in physdev is possible but discouraged as the + * calculations to ensure the high speed ADC correctness will be incorrect. Symptoms + * from over-running the high speed ADC include high speed samples being lost. + * + * @api + */ +void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer); + +/** + * @brief Perform a low speed ADC conversion with callback (in a thread context) + * @details Returns FALSE if there are no free low speed ADC slots. See @p GADC_MAX_LOWSPEED_DEVICES for details. + * + * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. + * @param[in] buffer The static buffer to put the ADC samples into. + * @param[in] fn The callback function to call when the conversion is complete. + * @param[in] param A parameter to pass to the callback function. + * + * @note This may be safely called from within a GTimer callback. + * @note The callback may take a while to occur if the high speed ADC is running as the + * conversion is interleaved with the high speed ADC conversions on a buffer + * completion. + * @note The result buffer must be large enough to store one sample per device + * described by the 'physdev' parameter. + * @note As this routine uses a low speed ADC, it asserts if you try to run more than @p GADC_MAX_LOWSPEED_DEVICES + * at the same time. + * @note Specifying more than one device in physdev is possible but discouraged as the + * calculations to ensure the high speed ADC correctness will be incorrect. Symptoms + * from over-running the high speed ADC include high speed samples being lost. + * + * @api + */ +bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GADC */ + +#endif /* _GADC_H */ +/** @} */ + diff --git a/include/gadc/lld/gadc_lld.h b/include/gadc/lld/gadc_lld.h new file mode 100644 index 00000000..f9cc8b47 --- /dev/null +++ b/include/gadc/lld/gadc_lld.h @@ -0,0 +1,190 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** + * @file include/gadc/lld/gadc_lld.h + * @brief GADC - Periodic ADC driver header file. + * + * @defgroup Driver Driver + * @ingroup GADC + * @{ + */ + +#ifndef _GADC_LLD_H +#define _GADC_LLD_H + +#include "gfx.h" + +#if GFX_USE_GADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +/** + * @brief The structure passed to start a timer conversion + * @note We use the structure instead of parameters purely to save + * interrupt stack space which is very limited in some platforms. + * @{ + */ +typedef struct GadcLldTimerData_t { + uint32_t physdev; /* @< A value passed to describe which physical ADC devices/channels to use. */ + adcsample_t *buffer; /* @< The static buffer to put the ADC samples into. */ + size_t count; /* @< The number of conversions to do before doing a callback and stopping the ADC. */ + bool_t now; /* @< Trigger the first conversion now rather than waiting for the first timer interrupt (if possible) */ + } GadcLldTimerData; +/* @} */ + +/** + * @brief The structure passed to start a non-timer conversion + * @note We use the structure instead of parameters purely to save + * interrupt stack space which is very limited in some platforms. + * @{ + */ +typedef struct GadcLldNonTimerData_t { + uint32_t physdev; /* @< A value passed to describe which physical ADC devices/channels to use. */ + adcsample_t *buffer; /* @< The static buffer to put the ADC samples into. */ + } GadcLldNonTimerData; +/* @} */ + +/** + * @brief These routines are the callbacks that the driver uses. + * @details Defined in the high level GADC code. + * + * @notapi + * @{ + */ +extern void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n); +extern void GADC_ISR_ErrorI(ADCDriver *adcp, adcerror_t err); +/** + * @} + */ + +/** + * @brief This can be incremented by the low level driver if a timer interrupt is missed. + * @details Defined in the high level GADC code. + * + * @notapi + */ +extern volatile bool_t GADC_Timer_Missed; + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialise the driver + * + * @api + */ +void gadc_lld_init(void); + +/** + * @brief Get the number of samples in a conversion. + * @details Calculates and returns the number of samples per conversion for the specified physdev. + * + * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. + * + * @note A physdev describing a mono device would return 1, a stereo device would return 2. + * For most ADC's physdev is a bitmap so it is only a matter of counting the bits. + * + * @api + */ +size_t gadc_lld_samples_per_conversion(uint32_t physdev); + +/** + * @brief Start a periodic timer for high frequency conversions. + * + * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. + * @param[in] frequency The frequency to create ADC conversions + * + * @note The exact meaning of physdev is hardware dependent. It describes the channels + * the will be used later on when a "timer" conversion is actually scheduled. + * @note It is assumed that the timer is capable of free-running even when the ADC + * is stopped or doing something else. + * @details When a timer interrupt occurs a conversion should start if these is a "timer" conversion + * active. + * @note If the ADC is stopped, doesn't have a "timer" conversion active or is currently executing + * a non-timer conversion then the interrupt can be ignored other than (optionally) incrementing + * the GADC_Timer_Missed variable. + * + * @api + */ +void gadc_lld_start_timer(uint32_t physdev, uint32_t frequency); + +/** + * @brief Stop the periodic timer for high frequency conversions. + * @details Also stops any current "timer" conversion (but not a current "non-timer" conversion). + * + * @param[in] physdev A value passed to describe which physical ADC devices/channels in use. + * + * @note The exact meaning of physdev is hardware dependent. + * + * @api + */ +void gadc_lld_stop_timer(uint32_t physdev); + +/** + * @brief Start a "timer" conversion. + * @details Starts a series of conversions triggered by the timer. + * + * @param[in] pgtd Contains the parameters for the timer conversion. + * + * @note The exact meaning of physdev is hardware dependent. It is likely described in the + * drivers gadc_lld_config.h + * @note Some versions of ChibiOS actually call the callback function more than once, once + * at the half-way point and once on completion. The high level code handles this. + * @note The driver should call @p GADC_ISR_CompleteI() when it completes the operation + * (or at the half-way point), or @p GAD_ISR_ErrorI() on an error. + * @note The high level code ensures that this is not called while a non-timer conversion is in + * progress + * + * @iclass + */ +void gadc_lld_adc_timerI(GadcLldTimerData *pgtd); + +/** + * @brief Start a "non-timer" conversion. + * @details Starts a single conversion now. + * + * @param[in] pgntd Contains the parameters for the non-timer conversion. + * + * @note The exact meaning of physdev is hardware dependent. It is likely described in the + * drivers gadc_lld_config.h + * @note The driver should call @p GADC_ISR_CompleteI() when it completes the operation + * or @p GAD_ISR_ErrorI() on an error. + * @note The high level code ensures that this is not called while a timer conversion is in + * progress + * + * @iclass + */ +void gadc_lld_adc_nontimerI(GadcLldNonTimerData *pgntd); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GADC */ + +#endif /* _GADC_LLD_H */ +/** @} */ diff --git a/include/gadc/options.h b/include/gadc/options.h index dc5bd300..87708efe 100644 --- a/include/gadc/options.h +++ b/include/gadc/options.h @@ -1,57 +1,55 @@ -/* - ChibiOS/GFX - Copyright (C) 2012 - Joel Bodenmann aka Tectu - - This file is part of ChibiOS/GFX. - - ChibiOS/GFX is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - ChibiOS/GFX is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/** - * @file include/gadc/options.h - * @brief GADC - Periodic ADC subsystem options header file. - * - * @addtogroup GADC - * @{ - */ - -#ifndef _GADC_OPTIONS_H -#define _GADC_OPTIONS_H - -/** - * @name GADC Functionality to be included - * @{ - */ -/** - * @} - * - * @name GADC Optional Sizing Parameters - * @{ - */ - /** - * @brief The maximum simultaneous GADC low speed device conversions - * @details Defaults to 4 - * @note This value must be less than the number of conversions that can occur - * in a single high speed ADC cycle including the high speed ADC conversion. - * For example, if the ADC can run at 132k samples per second and the high speed - * virtual ADC is using 44kHz then GADC_MAX_LOWSPEED_DEVICES should be set to - * 132/44 - 1 = 2 - */ - #ifndef GADC_MAX_LOWSPEED_DEVICES - #define GADC_MAX_LOWSPEED_DEVICES 4 - #endif -/** @} */ - -#endif /* _GADC_OPTIONS_H */ -/** @} */ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file include/gadc/options.h + * @brief GADC - Periodic ADC subsystem options header file. + * + * @addtogroup GADC + * @{ + */ + +#ifndef _GADC_OPTIONS_H +#define _GADC_OPTIONS_H + +/** + * @name GADC Functionality to be included + * @{ + */ +/** + * @} + * + * @name GADC Optional Sizing Parameters + * @{ + */ + /** + * @brief The maximum GADC sample rate + * @details Defaults to 44000 + * @note This value must be less than half the maximum sample rate allowed by the CPU. + * This is to ensure there is time between high speed samples to perform low + * speed device sampling. + */ + #ifndef GADC_MAX_HIGH_SPEED_SAMPLERATE + #define GADC_MAX_HIGH_SPEED_SAMPLERATE 44000 + #endif +/** @} */ + +#endif /* _GADC_OPTIONS_H */ +/** @} */ diff --git a/include/gfx_rules.h b/include/gfx_rules.h index c132de54..ce6bea50 100644 --- a/include/gfx_rules.h +++ b/include/gfx_rules.h @@ -1,128 +1,136 @@ -/* - ChibiOS/GFX - Copyright (C) 2012 - Joel Bodenmann aka Tectu - - This file is part of ChibiOS/GFX. - - ChibiOS/GFX is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - ChibiOS/GFX is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/** - * @file include/gfx_rules.h - * @brief GFX system safety rules header file. - * - * @addtogroup GFX - * @{ - */ - -#ifndef _GFX_RULES_H -#define _GFX_RULES_H - -/** - * Safety checks on all the defines. - * - * These are defined in the order of their inter-dependancies. - */ - -#if GFX_USE_GWIN - #if !GFX_USE_GDISP - #error "GWIN: GFX_USE_GDISP must be TRUE when using GWIN" - #endif - #if !GDISP_NEED_CLIP - #warning "GWIN: Drawing can occur outside the defined windows as GDISP_NEED_CLIP is FALSE" - #endif - #if GWIN_NEED_BUTTON - #if !GDISP_NEED_TEXT - #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_BUTTON is TRUE." - #endif - #if !GFX_USE_GEVENT - #warning "GWIN: GFX_USE_GEVENT is required if GWIN_NEED_BUTTON is TRUE. It has been turned on for you." - #undef GFX_USE_GEVENT - #define GFX_USE_GEVENT TRUE - #endif - #if !GFX_USE_GINPUT || !(GINPUT_NEED_MOUSE || GINPUT_NEED_TOGGLE) - #warning "GWIN: You have set GWIN_NEED_BUTTON to TRUE but no supported GINPUT (mouse/toggle) devices have been included" - #endif - #if !GDISP_NEED_MULTITHREAD && !GDISP_NEED_ASYNC - #warning "GWIN: Either GDISP_NEED_MULTITHREAD or GDISP_NEED_ASYNC is required if GWIN_NEED_BUTTON is TRUE." - #warning "GWIN: GDISP_NEED_MULTITHREAD has been turned on for you." - #undef GDISP_NEED_MULTITHREAD - #define GDISP_NEED_MULTITHREAD TRUE - #endif - #endif - #if GWIN_NEED_CONSOLE - #if !GDISP_NEED_TEXT - #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_CONSOLE is TRUE." - #endif - #endif - #if GWIN_NEED_GRAPH - #endif -#endif - -#if GFX_USE_GINPUT - #if !GFX_USE_GEVENT - #warning "GINPUT: GFX_USE_GEVENT is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." - #undef GFX_USE_GEVENT - #define GFX_USE_GEVENT TRUE - #endif - #if !GFX_USE_GTIMER - #warning "GINPUT: GFX_USE_GTIMER is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." - #undef GFX_USE_GTIMER - #define GFX_USE_GTIMER TRUE - #endif -#endif - -#if GFX_USE_GDISP - #if GDISP_NEED_MULTITHREAD && GDISP_NEED_ASYNC - #error "GDISP: Only one of GDISP_NEED_MULTITHREAD and GDISP_NEED_ASYNC should be defined." - #endif - #if GDISP_NEED_ASYNC - #if !GDISP_NEED_MSGAPI - #warning "GDISP: Messaging API is required for Async Multi-Thread. It has been turned on for you." - #undef GDISP_NEED_MSGAPI - #define GDISP_NEED_MSGAPI TRUE - #endif - #endif -#endif - -#if GFX_USE_TDISP -#endif - -#if GFX_USE_GEVENT - #if !CH_USE_MUTEXES || !CH_USE_SEMAPHORES - #error "GEVENT: CH_USE_MUTEXES and CH_USE_SEMAPHORES must be defined in chconf.h" - #endif -#endif - -#if GFX_USE_GTIMER - #if GFX_USE_GDISP && !GDISP_NEED_MULTITHREAD && !GDISP_NEED_ASYNC - #warning "GTIMER: Neither GDISP_NEED_MULTITHREAD nor GDISP_NEED_ASYNC has been specified." - #warning "GTIMER: Make sure you are not performing any GDISP/GWIN drawing operations in the timer callback!" - #endif -#endif - -#if GFX_USE_GAUDIN -#endif - -#if GFX_USE_GAUDOUT -#endif - -#if GFX_USE_GADC -#endif - -#if GFX_USE_GMISC -#endif - -#endif /* _GFX_H */ -/** @} */ +/* + ChibiOS/GFX - Copyright (C) 2012 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file include/gfx_rules.h + * @brief GFX system safety rules header file. + * + * @addtogroup GFX + * @{ + */ + +#ifndef _GFX_RULES_H +#define _GFX_RULES_H + +/** + * Safety checks on all the defines. + * + * These are defined in the order of their inter-dependancies. + */ + +#if GFX_USE_GWIN + #if !GFX_USE_GDISP + #error "GWIN: GFX_USE_GDISP must be TRUE when using GWIN" + #endif + #if !GDISP_NEED_CLIP + #warning "GWIN: Drawing can occur outside the defined windows as GDISP_NEED_CLIP is FALSE" + #endif + #if GWIN_NEED_BUTTON + #if !GDISP_NEED_TEXT + #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_BUTTON is TRUE." + #endif + #if !GFX_USE_GEVENT + #warning "GWIN: GFX_USE_GEVENT is required if GWIN_NEED_BUTTON is TRUE. It has been turned on for you." + #undef GFX_USE_GEVENT + #define GFX_USE_GEVENT TRUE + #endif + #if !GFX_USE_GINPUT || !(GINPUT_NEED_MOUSE || GINPUT_NEED_TOGGLE) + #warning "GWIN: You have set GWIN_NEED_BUTTON to TRUE but no supported GINPUT (mouse/toggle) devices have been included" + #endif + #if !GDISP_NEED_MULTITHREAD && !GDISP_NEED_ASYNC + #warning "GWIN: Either GDISP_NEED_MULTITHREAD or GDISP_NEED_ASYNC is required if GWIN_NEED_BUTTON is TRUE." + #warning "GWIN: GDISP_NEED_MULTITHREAD has been turned on for you." + #undef GDISP_NEED_MULTITHREAD + #define GDISP_NEED_MULTITHREAD TRUE + #endif + #endif + #if GWIN_NEED_CONSOLE + #if !GDISP_NEED_TEXT + #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_CONSOLE is TRUE." + #endif + #endif + #if GWIN_NEED_GRAPH + #endif +#endif + +#if GFX_USE_GINPUT + #if !GFX_USE_GEVENT + #warning "GINPUT: GFX_USE_GEVENT is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." + #undef GFX_USE_GEVENT + #define GFX_USE_GEVENT TRUE + #endif + #if !GFX_USE_GTIMER + #warning "GINPUT: GFX_USE_GTIMER is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." + #undef GFX_USE_GTIMER + #define GFX_USE_GTIMER TRUE + #endif +#endif + +#if GFX_USE_GDISP + #if GDISP_NEED_MULTITHREAD && GDISP_NEED_ASYNC + #error "GDISP: Only one of GDISP_NEED_MULTITHREAD and GDISP_NEED_ASYNC should be defined." + #endif + #if GDISP_NEED_ASYNC + #if !GDISP_NEED_MSGAPI + #warning "GDISP: Messaging API is required for Async Multi-Thread. It has been turned on for you." + #undef GDISP_NEED_MSGAPI + #define GDISP_NEED_MSGAPI TRUE + #endif + #endif +#endif + +#if GFX_USE_TDISP +#endif + +#if GFX_USE_GADC + #if !CH_USE_MUTEXES || !CH_USE_SEMAPHORES + #error "GADC: CH_USE_MUTEXES and CH_USE_SEMAPHORES must be defined in chconf.h" + #endif + #if !GFX_USE_GTIMER + #warning "GADC: GFX_USE_GTIMER is required if GFX_USE_GADC is TRUE. It has been turned on for you." + #undef GFX_USE_GTIMER + #define GFX_USE_GTIMER TRUE + #endif +#endif + +#if GFX_USE_GEVENT + #if !CH_USE_MUTEXES || !CH_USE_SEMAPHORES + #error "GEVENT: CH_USE_MUTEXES and CH_USE_SEMAPHORES must be defined in chconf.h" + #endif +#endif + +#if GFX_USE_GTIMER + #if GFX_USE_GDISP && !GDISP_NEED_MULTITHREAD && !GDISP_NEED_ASYNC + #warning "GTIMER: Neither GDISP_NEED_MULTITHREAD nor GDISP_NEED_ASYNC has been specified." + #warning "GTIMER: Make sure you are not performing any GDISP/GWIN drawing operations in the timer callback!" + #endif +#endif + +#if GFX_USE_GAUDIN +#endif + +#if GFX_USE_GAUDOUT +#endif + +#if GFX_USE_GMISC +#endif + +#endif /* _GFX_H */ +/** @} */ diff --git a/src/gadc/gadc.c b/src/gadc/gadc.c index 5533bb49..509557d3 100644 --- a/src/gadc/gadc.c +++ b/src/gadc/gadc.c @@ -1,38 +1,465 @@ -/* - ChibiOS/GFX - Copyright (C) 2012 - Joel Bodenmann aka Tectu - - This file is part of ChibiOS/GFX. - - ChibiOS/GFX is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - ChibiOS/GFX is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/** - * @file src/gadc/gadc.c - * @brief GADC sub-system code. - * - * @addtogroup GADC - * @{ - */ -#include "ch.h" -#include "hal.h" -#include "gfx.h" - -#if GFX_USE_GADC || defined(__DOXYGEN__) - - #error "GADC: Not implemented yet" - -#endif /* GFX_USE_GADC */ -/** @} */ - +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file src/gadc/gadc.c + * @brief GADC sub-system code. + * + * @addtogroup GADC + * @{ + */ +#include "ch.h" +#include "hal.h" +#include "gfx.h" + +#if GFX_USE_GADC + +/* Include the driver defines */ +#include "gadc/lld/gadc_lld.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_MAX_LOWSPEED_DEVICES ((GADC_MAX_SAMPLE_FREQUENCY/GADC_MAX_HIGH_SPEED_SAMPLERATE)-1) +#if GADC_MAX_LOWSPEED_DEVICES > 4 + #undef GADC_MAX_LOWSPEED_DEVICES + #define GADC_MAX_LOWSPEED_DEVICES 4 +#endif + +volatile bool_t GADC_Timer_Missed; + +static SEMAPHORE_DECL(gadcsem, GADC_MAX_LOWSPEED_DEVICES); +static MUTEX_DECL(gadcmutex); +static GTIMER_DECL(LowSpeedGTimer); +#if GFX_USE_GEVENT + static GTIMER_DECL(HighSpeedGTimer); +#endif + +static volatile uint16_t gflags = 0; + #define GADC_GFLG_INITDONE 0x0001 + #define GADC_GFLG_ISACTIVE 0x0002 + +#define GADC_FLG_ISACTIVE 0x0001 +#define GADC_FLG_ISDONE 0x0002 +#define GADC_FLG_ERROR 0x0004 +#define GADC_FLG_GTIMER 0x0008 + +static struct hsdev { + // Our status flags + uint16_t flags; + + // What we started with + uint32_t frequency; + adcsample_t *buffer; + size_t bufcount; + size_t samplesPerEvent; + + // The last set of results + size_t lastcount; + adcsample_t *lastbuffer; + uint16_t lastflags; + + // Other stuff we need to track progress and for signalling + GadcLldTimerData lld; + size_t samplesPerConversion; + size_t remaining; + BinarySemaphore *bsem; + GEventADC *pEvent; + } hs; + +static struct lsdev { + // Our status flags + uint16_t flags; + + // What we started with + GadcLldNonTimerData lld; + GADCCallbackFunction fn; + void *param; + } ls[GADC_MAX_LOWSPEED_DEVICES]; + +static struct lsdev *curlsdev; + +/* Find the next conversion to activate */ +static __inline void FindNextConversionI(void) { + if (curlsdev) { + /** + * Now we have done a low speed conversion - start looking for the next conversion + * We only look forward to ensure we get a high speed conversion at least once + * every GADC_MAX_LOWSPEED_DEVICES conversions. + */ + curlsdev++; + + } else { + + /* Now we have done a high speed conversion - start looking for low speed conversions */ + curlsdev = ls; + } + + /** + * Look for the next thing to do. + */ + while(curlsdev < &ls[GADC_MAX_LOWSPEED_DEVICES]) { + if ((curlsdev->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == GADC_FLG_ISACTIVE) { + gadc_lld_adc_nontimerI(&curlsdev->lld); + return; + } + curlsdev++; + } + curlsdev = 0; + + /* No more low speed devices - do a high speed conversion */ + if (hs.flags & GADC_FLG_ISACTIVE) { + hs.lld.now = GADC_Timer_Missed ? TRUE : FALSE; + GADC_Timer_Missed = 0; + gadc_lld_adc_timerI(&hs.lld); + return; + } + + /* Nothing more to do */ + gflags &= ~GADC_GFLG_ISACTIVE; +} + +void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) { + (void) adcp; + + if (curlsdev) { + /* This interrupt must be in relation to the low speed device */ + + if (curlsdev->flags & GADC_FLG_ISACTIVE) { + /** + * As we only handle a single low speed conversion at a time, we know + * we know we won't get any half completion interrupts. + */ + curlsdev->flags |= GADC_FLG_ISDONE; + gtimerJabI(&LowSpeedGTimer); + } + + #if ADC_ISR_FULL_CODE_BUG + /** + * Oops - We have just finished a low speed conversion but a bug prevents us + * restarting the ADC here. Other code will restart it in the thread based + * ADC handler. + */ + gflags &= ~GADC_GFLG_ISACTIVE; + return; + + #endif + + } else { + /* This interrupt must be in relation to the high speed device */ + + if (hs.flags & GADC_FLG_ISACTIVE) { + /* Save the details */ + hs.lastcount = n; + hs.lastbuffer = buffer; + hs.lastflags = GADC_Timer_Missed ? GADC_HSADC_LOSTEVENT : 0; + + /* Signal the user with the data */ + if (hs.pEvent) { + #if GFX_USE_GEVENT + hs.pEvent->type = GEVENT_ADC; + #endif + hs.pEvent->count = hs.lastcount; + hs.pEvent->buffer = hs.lastbuffer; + hs.pEvent->flags = hs.lastflags; + } + if (hs.bsem) + chBSemSignalI(hs.bsem); + + #if GFX_USE_GEVENT + if (hs.flags & GADC_FLG_GTIMER) + gtimerJabI(&HighSpeedGTimer); + #endif + + /* Adjust what we have left to do */ + hs.lld.count -= n; + hs.remaining -= n; + + /* Half completion - We have done all we can for now - wait for the next interrupt */ + if (hs.lld.count) + return; + + /* Our buffer is cyclic - set up the new buffer pointers */ + if (hs.remaining) { + hs.lld.buffer = buffer + (n * hs.samplesPerConversion); + } else { + hs.remaining = hs.bufcount; + hs.lld.buffer = hs.buffer; + } + hs.lld.count = hs.remaining < hs.samplesPerEvent ? hs.remaining : hs.samplesPerEvent; + } + } + + /** + * Look for the next thing to do. + */ + FindNextConversionI(); +} + +void GADC_ISR_ErrorI(ADCDriver *adcp, adcerror_t err) { + (void) adcp; + (void) err; + + if (curlsdev) { + if ((curlsdev->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == GADC_FLG_ISACTIVE) + /* Mark the error then try to repeat it */ + curlsdev->flags |= GADC_FLG_ERROR; + + #if ADC_ISR_FULL_CODE_BUG + /** + * Oops - We have just finished a low speed conversion but a bug prevents us + * restarting the ADC here. Other code will restart it in the thread based + * ADC handler. + */ + gflags &= ~GADC_GFLG_ISACTIVE; + gtimerJabI(&LowSpeedGTimer); + return; + + #endif + + } else { + if (hs.flags & GADC_FLG_ISACTIVE) + /* Mark the error and then try to repeat it */ + hs.flags |= GADC_FLG_ERROR; + } + + /* Start the next conversion */ + FindNextConversionI(); +} + +static __inline void DoInit(void) { + if (!(gflags & GADC_GFLG_INITDONE)) { + gflags |= GADC_GFLG_INITDONE; + gadc_lld_init(); + } +} + +static __inline void StartADC(bool_t onNoHS) { + chSysLock(); + if (!(gflags & GADC_GFLG_ISACTIVE) || (onNoHS && !curlsdev)) + FindNextConversionI(); + chSysUnlock(); +} + +static void BSemSignalCallback(adcsample_t *buffer, void *param) { + (void) buffer; + + /* Signal the BinarySemaphore parameter */ + chBSemSignal((BinarySemaphore *)param); +} + +#if GFX_USE_GEVENT + static void HighSpeedGTimerCallback(void *param) { + (void) param; + GSourceListener *psl; + GEventADC *pe; + + psl = 0; + while ((psl = geventGetSourceListener((GSourceHandle)(&HighSpeedGTimer), psl))) { + if (!(pe = (GEventADC *)geventGetEventBuffer(psl))) { + // This listener is missing - save this. + psl->srcflags |= GADC_HSADC_LOSTEVENT; + continue; + } + + pe->type = GEVENT_ADC; + pe->count = hs.lastcount; + pe->buffer = hs.lastbuffer; + pe->flags = hs.lastflags | psl->srcflags; + psl->srcflags = 0; + geventSendEvent(psl); + } + } +#endif + +static void LowSpeedGTimerCallback(void *param) { + (void) param; + GADCCallbackFunction fn; + void *prm; + adcsample_t *buffer; + struct lsdev *p; + + #if ADC_ISR_FULL_CODE_BUG + /* Ensure the ADC is running if it needs to be - Bugfix HACK */ + StartADC(FALSE); + #endif + + /** + * Look for completed low speed timers. + * We don't need to take the mutex as we are the only place that things are freed and we + * do that atomically. + */ + for(p=ls; p < &ls[GADC_MAX_LOWSPEED_DEVICES]; p++) { + if ((p->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) { + /* This item is done - perform its callback */ + fn = p->fn; // Save the callback details + prm = p->param; + buffer = p->lld.buffer; + p->fn = 0; // Needed to prevent the compiler removing the local variables + p->param = 0; // Needed to prevent the compiler removing the local variables + p->lld.buffer = 0; // Needed to prevent the compiler removing the local variables + p->flags = 0; // The slot is available (indivisible operation) + chSemSignal(&gadcsem); // Tell everyone + fn(buffer, prm); // Perform the callback + } + } + +} + +void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer, size_t bufcount, size_t samplesPerEvent) +{ + gadcHighSpeedStop(); /* This does the init for us */ + + /* Just save the details and reset everything for now */ + hs.frequency = frequency; + hs.buffer = buffer; + hs.bufcount = bufcount; + hs.samplesPerEvent = samplesPerEvent; + hs.lastcount = 0; + hs.lastbuffer = 0; + hs.lastflags = 0; + hs.lld.physdev = physdev; + hs.lld.buffer = buffer; + hs.lld.count = samplesPerEvent; + hs.lld.now = FALSE; + hs.samplesPerConversion = gadc_lld_samples_per_conversion(physdev); + hs.remaining = bufcount; + hs.bsem = 0; + hs.pEvent = 0; +} + +#if GFX_USE_GEVENT + GSourceHandle gadcHighSpeedGetSource(void) { + DoInit(); + if (!gtimerIsActive(&HighSpeedGTimer)) + gtimerStart(&HighSpeedGTimer, HighSpeedGTimerCallback, NULL, TRUE, TIME_INFINITE); + hs.flags |= GADC_FLG_GTIMER; + return (GSourceHandle)&HighSpeedGTimer; + } +#endif + +void gadcHighSpeedSetBSem(BinarySemaphore *pbsem, GEventADC *pEvent) { + DoInit(); + + /* Use the system lock to ensure they occur atomically */ + chSysLock(); + hs.pEvent = pEvent; + hs.bsem = pbsem; + chSysUnlock(); +} + +void gadcHighSpeedStart(void) { + DoInit(); + + /* If its already going we don't need to do anything */ + if (hs.flags & GADC_FLG_ISACTIVE) + return; + + gadc_lld_start_timer(hs.lld.physdev, hs.frequency); + hs.flags = GADC_FLG_ISACTIVE; + StartADC(FALSE); +} + +void gadcHighSpeedStop(void) { + DoInit(); + + if (hs.flags & GADC_FLG_ISACTIVE) { + /* No more from us */ + hs.flags = 0; + gadc_lld_stop_timer(hs.lld.physdev); + /* + * We have to pass TRUE to StartADC() as we might have the ADC marked as active when it isn't + * due to stopping the timer while it was converting. + */ + StartADC(TRUE); + } +} + +void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) { + struct lsdev *p; + BSEMAPHORE_DECL(mysem, TRUE); + + /* Start the Low Speed Timer */ + chMtxLock(&gadcmutex); + if (!gtimerIsActive(&LowSpeedGTimer)) + gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, NULL, TRUE, TIME_INFINITE); + chMtxUnlock(); + + while(1) { + /* Wait for an available slot */ + chSemWait(&gadcsem); + + /* Find a slot */ + chMtxLock(&gadcmutex); + for(p = ls; p < &ls[GADC_MAX_LOWSPEED_DEVICES]; p++) { + if (!(p->flags & GADC_FLG_ISACTIVE)) { + p->lld.physdev = physdev; + p->lld.buffer = buffer; + p->fn = BSemSignalCallback; + p->param = &mysem; + p->flags = GADC_FLG_ISACTIVE; + chMtxUnlock(); + StartADC(FALSE); + chBSemWait(&mysem); + return; + } + } + chMtxUnlock(); + + /** + * We should never get here - the count semaphore must be wrong. + * Decrement it and try again. + */ + } +} + +bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param) { + struct lsdev *p; + + DoInit(); + + /* Start the Low Speed Timer */ + chMtxLock(&gadcmutex); + if (!gtimerIsActive(&LowSpeedGTimer)) + gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, NULL, TRUE, TIME_INFINITE); + + /* Find a slot */ + for(p = ls; p < &ls[GADC_MAX_LOWSPEED_DEVICES]; p++) { + if (!(p->flags & GADC_FLG_ISACTIVE)) { + /* We know we have a slot - this should never wait anyway */ + chSemWaitTimeout(&gadcsem, TIME_IMMEDIATE); + p->lld.physdev = physdev; + p->lld.buffer = buffer; + p->fn = fn; + p->param = param; + p->flags = GADC_FLG_ISACTIVE; + chMtxUnlock(); + StartADC(FALSE); + return TRUE; + } + } + chMtxUnlock(); + return FALSE; +} + +#endif /* GFX_USE_GADC */ +/** @} */ +