From 9bec5967b293d6c23c9d7e9338d8ece4873f6eac Mon Sep 17 00:00:00 2001
From: Andrew Hannam <andrewh@inmarket.com.au>
Date: Mon, 18 Feb 2013 17:33:35 +1000
Subject: GADC implementation with demo program

Also includes driver for AT91SAM7 cpu
---
 demos/modules/gadc/gfxconf.h                       | 105 +++++
 demos/modules/gadc/gwinosc.c                       | 183 ++++++++
 demos/modules/gadc/gwinosc.h                       |  89 ++++
 demos/modules/gadc/main.c                          | 176 +++++++
 demos/modules/gadc/results_264x264.jpg             | Bin 0 -> 21130 bytes
 drivers/gadc/AT91SAM7/gadc_lld.c                   | 102 +++++
 drivers/gadc/AT91SAM7/gadc_lld.mk                  |   5 +
 .../gadc/AT91SAM7/gadc_lld_board_olimexsam7ex256.h |  46 ++
 drivers/gadc/AT91SAM7/gadc_lld_config.h            |  78 ++++
 gfxconf.example.h                                  |   2 -
 include/gadc/gadc.h                                | 508 +++++++++++----------
 include/gadc/lld/gadc_lld.h                        | 190 ++++++++
 include/gadc/options.h                             | 112 +++--
 include/gfx_rules.h                                | 264 +++++------
 src/gadc/gadc.c                                    | 503 ++++++++++++++++++--
 15 files changed, 1888 insertions(+), 475 deletions(-)
 create mode 100644 demos/modules/gadc/gfxconf.h
 create mode 100644 demos/modules/gadc/gwinosc.c
 create mode 100644 demos/modules/gadc/gwinosc.h
 create mode 100644 demos/modules/gadc/main.c
 create mode 100644 demos/modules/gadc/results_264x264.jpg
 create mode 100644 drivers/gadc/AT91SAM7/gadc_lld.c
 create mode 100644 drivers/gadc/AT91SAM7/gadc_lld.mk
 create mode 100644 drivers/gadc/AT91SAM7/gadc_lld_board_olimexsam7ex256.h
 create mode 100644 drivers/gadc/AT91SAM7/gadc_lld_config.h
 create mode 100644 include/gadc/lld/gadc_lld.h

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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * --------------------------- 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<<SCOPE_Y_BITS)/2;
+	x = gs->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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * 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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @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 <joel@unormal.org>
-
-    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 <http://www.gnu.org/licenses/>.
-*/
-/**
- * @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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @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 <joel@unormal.org>
-
-    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 <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @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 <joel@unormal.org>
-
-    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 <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @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 <joel@unormal.org>
-
-    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 <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @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 <joel@unormal.org>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @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 */
+/** @} */
+
-- 
cgit v1.2.3