GADC implementation with demo program

Also includes driver for AT91SAM7 cpu
ugfx_release_2.6
Andrew Hannam 2013-02-18 17:33:35 +10:00
parent 2ed57aea77
commit 9bec5967b2
15 changed files with 1888 additions and 475 deletions

View File

@ -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 */

View File

@ -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
}

View File

@ -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 */

View File

@ -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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -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 */
/** @} */

View File

@ -0,0 +1,5 @@
# List the required driver.
GFXSRC += $(GFXLIB)/drivers/gadc/AT91SAM7/gadc_lld.c
# Required include directories
GFXINC += $(GFXLIB)/drivers/gadc/AT91SAM7

View File

@ -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 */
/** @} */

View File

@ -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 */
/** @} */

View File

@ -96,10 +96,8 @@
#define GDISP_SCREEN_HEIGHT 240 #define GDISP_SCREEN_HEIGHT 240
#define GDISP_USE_FSMC #define GDISP_USE_FSMC
#define GDISP_USE_GPIO #define GDISP_USE_GPIO
#define TDISP_COLUMNS 16 #define TDISP_COLUMNS 16
#define TDISP_ROWS 2 #define TDISP_ROWS 2
*/ */
#endif /* _GFXCONF_H */ #endif /* _GFXCONF_H */

View File

@ -1,250 +1,258 @@
/* /*
ChibiOS/GFX - Copyright (C) 2012 ChibiOS/GFX - Copyright (C) 2012, 2013
Joel Bodenmann aka Tectu <joel@unormal.org> Joel Bodenmann aka Tectu <joel@unormal.org>
This file is part of ChibiOS/GFX. This file is part of ChibiOS/GFX.
ChibiOS/GFX is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or the Free Software Foundation; either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
ChibiOS/GFX is distributed in the hope that it will be useful, ChibiOS/GFX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file include/gadc/gadc.h * @file include/gadc/gadc.h
* @brief GADC - Periodic ADC subsystem header file. * @brief GADC - Periodic ADC subsystem header file.
* *
* @addtogroup GADC * @addtogroup GADC
* *
* @details The reason why ChibiOS/GFX has it's own ADC abstraction is because * @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 * the Chibi-OS drivers are very CPU specific and do not
* provide a way across all hardware platforms to create periodic * provide a way across all hardware platforms to create periodic
* ADC conversions. There are also issues with devices with different * ADC conversions. There are also issues with devices with different
* characteristics or periodic requirements on the same ADC * characteristics or periodic requirements on the same ADC
* device (but different channels). This layer attempts to solve these * device (but different channels). This layer attempts to solve these
* problems to provide a architecture neutral API. It also provides extra * problems to provide a architecture neutral API. It also provides extra
* features such as multi-buffer chaining for high speed ADC sources. * features such as multi-buffer chaining for high speed ADC sources.
* It provides one high speed virtual ADC device (eg a microphone) and * It provides one high speed virtual ADC device (eg a microphone) and
* numerous low speed (less than 100Hz) virtual ADC devices (eg dials, * numerous low speed (less than 100Hz) virtual ADC devices (eg dials,
* temperature sensors etc). The high speed device has timer based polling * temperature sensors etc). The high speed device has timer based polling
* to ensure exact conversion periods and a buffer management system. * to ensure exact conversion periods and a buffer management system.
* The low speed devices are assumed to be non-critical timing devices * The low speed devices are assumed to be non-critical timing devices
* and do not have any buffer management. * and do not have any buffer management.
* Note that while only one high speed device has been provided it can * 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 * be used to read multiple physical ADC channels on the one physical
* ADC device. * ADC device.
* All callback routines are thread based unlike the Chibi-OS interrupt based * All callback routines are thread based unlike the Chibi-OS interrupt based
* routines. * routines.
* *
* @{ * @{
*/ */
#ifndef _GADC_H #ifndef _GADC_H
#define _GADC_H #define _GADC_H
#include "gfx.h" #include "gfx.h"
#if GFX_USE_GADC || defined(__DOXYGEN__) #if GFX_USE_GADC || defined(__DOXYGEN__)
/* Include the driver defines */ /* Include the driver defines */
#include "gadc_lld_config.h" #include "gadc_lld_config.h"
/*===========================================================================*/ /*===========================================================================*/
/* Type definitions */ /* Type definitions */
/*===========================================================================*/ /*===========================================================================*/
// Event types for GADC // Event types for GADC
#define GEVENT_ADC (GEVENT_GADC_FIRST+0) #define GEVENT_ADC (GEVENT_GADC_FIRST+0)
/** /**
* @brief The High Speed ADC event structure. * @brief The High Speed ADC event structure.
* @{ * @{
*/ */
typedef struct GEventADC_t { typedef struct GEventADC_t {
/** #if GFX_USE_GEVENT || defined(__DOXYGEN__)
* @brief The type of this event (GEVENT_ADC) /**
*/ * @brief The type of this event (GEVENT_ADC)
GEventType type; */
/** GEventType type;
* @brief The event flags #endif
*/
uint16_t flags; /**
/** * @brief The event flags
* @brief The event flag values. */
* @{ uint16_t flags;
*/ /**
#define GADC_HSADC_LOSTEVENT 0x0001 /**< @brief The last GEVENT_HSDADC event was lost */ * @brief The event flag values.
/** @} */ * @{
/** */
* @brief The number of conversions in the buffer #define GADC_HSADC_LOSTEVENT 0x0001 /**< @brief The last GEVENT_HSDADC event was lost */
*/ /** @} */
size_t count; /**
/** * @brief The number of conversions in the buffer
* @brief The buffer containing the conversion samples */
*/ size_t count;
adcsample_t *buffer; /**
} GEventADC; * @brief The buffer containing the conversion samples
*/
/** adcsample_t *buffer;
* @brief A callback function (executed in a thread context) } GEventADC;
*/
typedef void (*GADCCallbackFunction)(adcsample_t *buffer, void *param); /**
* @brief A callback function (executed in a thread context)
/*===========================================================================*/ */
/* External declarations. */ typedef void (*GADCCallbackFunction)(adcsample_t *buffer, void *param);
/*===========================================================================*/
/*===========================================================================*/
#ifdef __cplusplus /* External declarations. */
extern "C" { /*===========================================================================*/
#endif
#ifdef __cplusplus
/** extern "C" {
* @brief Initialise the high speed ADC. #endif
* @details Initialises but does not start the conversions.
* /**
* @param[in] physdev A value passed to describe which physical ADC devices/channels to use. * @brief Initialise the high speed ADC.
* @param[in] frequency The frequency to create ADC conversions * @details Initialises but does not start the 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] physdev A value passed to describe which physical ADC devices/channels to use.
* @param[in] countPerEvent The number of conversions to do before returning an event. * @param[in] frequency The frequency to create ADC conversions
* * @param[in] buffer The static buffer to put the ADC samples into.
* @note If the high speed ADC is running it will be stopped. * @param[in] bufcount The total number of conversions that will fit in the buffer.
* @note Due to a bug in Chibi-OS countPerEvent must be even. If bufcount is not * @param[in] countPerEvent The number of conversions to do before returning an event.
* 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. * @note If the high speed ADC is running it will be stopped. The Event subsystem is
* Each channel is then interleaved into the provided buffer. Note 'bufcount' * disconnected from the high speed ADC and any binary semaphore event is forgotten.
* and 'countPerEvent' parameters describe the number of conversions not the * @note bufcount must be greater than countPerEvent (usually 2 or more times) otherwise
* number of samples. * the buffer will be overwitten with new data while the application is still trying
* As an example, if physdev turns on 2 devices then the buffer contains * to process the old data.
* alternate device samples and the buffer must contain 2 * bufcount samples. * @note Due to a bug in Chibi-OS countPerEvent must be even. If bufcount is not
* The exact meaning of physdev is hardware dependent. * evenly divisable by countPerEvent, the remainder must also be even.
* @note The buffer is circular. When the end of the buffer is reached it will start * @note The physdev parameter may be used to turn on more than one ADC channel.
* putting data into the beginning of the buffer again. * Each channel is then interleaved into the provided buffer. Note 'bufcount'
* @note The event listener must process the event (and the data in it) before the * and 'countPerEvent' parameters describe the number of conversions not the
* next event occurs. If not, the following event will be lost. * number of samples.
* @note If bufcount is evenly divisable by countPerEvent, then every event will return * As an example, if physdev turns on 2 devices then the buffer contains
* countPerEvent conversions. If bufcount is not evenly divisable, it will return * alternate device samples and the buffer must contain 2 * bufcount samples.
* a block of samples containing less than countPerEvent samples when it reaches the * The exact meaning of physdev is hardware dependent.
* end of the buffer. * @note The buffer is circular. When the end of the buffer is reached it will start
* @note While the high speed ADC is running, low speed conversions can only occur at * putting data into the beginning of the buffer again.
* the frequency of the high speed events. Thus if high speed events are * @note The event listener must process the event (and the data in it) before the
* being created at 50Hz (eg countPerEvent = 100, frequency = 5kHz) then the maximum * next event occurs. If not, the following event will be lost.
* frequency for low speed conversions is likely to be 50Hz (although it might be * @note If bufcount is evenly divisable by countPerEvent, then every event will return
* 100Hz on some hardware). * countPerEvent conversions. If bufcount is not evenly divisable, it will return
* * a block of samples containing less than countPerEvent samples when it reaches the
* @api * end of the buffer.
*/ * @note While the high speed ADC is running, low speed conversions can only occur at
void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer, size_t bufcount, size_t samplesPerEvent); * 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
#if GFX_USE_GEVENT || defined(__DOXYGEN__) * frequency for low speed conversions is likely to be 50Hz (although it might be
/** * 100Hz on some hardware).
* @brief Turn on sending results to the GEVENT sub-system. *
* @details Returns a GSourceHandle to listen for GEVENT_ADC events. * @api
* */
* @note The high speed ADC will not use the GEVENT system unless this is void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer, size_t bufcount, size_t samplesPerEvent);
* called first. This saves processing time if the application does
* not want to use the GEVENT sub-system for the high speed ADC. #if GFX_USE_GEVENT || defined(__DOXYGEN__)
* Once turned on it cannot be turned off. /**
* @note The high speed ADC is capable of signalling via this method and a binary semaphore * @brief Turn on sending results to the GEVENT sub-system.
* at the same time. * @details Returns a GSourceHandle to listen for GEVENT_ADC events.
* *
* @api * @note The high speed ADC will not use the GEVENT system unless this is
*/ * called first. This saves processing time if the application does
GSourceHandle gadcHighSpeedGetSource(void); * not want to use the GEVENT sub-system for the high speed ADC.
#endif * 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.
* @brief Allow retrieving of results from the high speed ADC using a Binary Semaphore and a static event buffer. *
* * @api
* @param[in] pbsem The binary semaphore is signaled when data is available. */
* @param[in] pEvent The static event buffer to place the result information. GSourceHandle gadcHighSpeedGetSource(void);
* #endif
* @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. * @brief Allow retrieving of results from the high speed ADC using a Binary Semaphore and a static event buffer.
* *
* @api * @param[in] pbsem The binary semaphore is signaled when data is available.
*/ * @param[in] pEvent The static event buffer to place the result information.
void gadcHighSpeedSetBSem(BinarySemaphore *pbsem, GEventADC *pEvent); *
* @note Passing a NULL for pbsem or pEvent will turn off signalling via this method as will calling
/** * @p gadcHighSpeedInit().
* @brief Start the high speed ADC conversions. * @note The high speed ADC is capable of signalling via this method and the GEVENT
* @pre It must have been initialised first with @p gadcHighSpeedInit() * sub-system at the same time.
* *
* @api * @api
*/ */
GSourceHandle gadcHighSpeedStart(void); void gadcHighSpeedSetBSem(BinarySemaphore *pbsem, GEventADC *pEvent);
/** /**
* @brief Stop the high speed ADC conversions. * @brief Start the high speed ADC conversions.
* * @pre It must have been initialised first with @p gadcHighSpeedInit()
* @api *
*/ * @api
void gadcHighSpeedStop(void); */
void gadcHighSpeedStart(void);
/**
* @brief Perform a single low speed ADC conversion /**
* @details Blocks until the conversion is complete * @brief Stop the high speed ADC conversions.
* @pre This should not be called from within a GTimer callback as this routine *
* blocks until the conversion is ready. * @api
* */
* @param[in] physdev A value passed to describe which physical ADC devices/channels to use. void gadcHighSpeedStop(void);
* @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 * @brief Perform a single low speed ADC conversion
* conversion is interleaved with the high speed ADC conversions on a buffer * @details Blocks until the conversion is complete
* completion. * @pre This should not be called from within a GTimer callback as this routine
* @note The result buffer must be large enough to store one sample per device * blocks until the conversion is ready.
* described by the 'physdev' parameter. *
* @note If calling this routine would exceed @p GADC_MAX_LOWSPEED_DEVICES simultaneous low * @param[in] physdev A value passed to describe which physical ADC devices/channels to use.
* speed devices, the routine will wait for an available slot to complete the * @param[in] buffer The static buffer to put the ADC samples into.
* conversion. *
* @note Specifying more than one device in physdev is possible but discouraged as the * @note This may take a while to complete if the high speed ADC is running as the
* calculations to ensure the high speed ADC correctness will be incorrect. Symptoms * conversion is interleaved with the high speed ADC conversions on a buffer
* from over-running the high speed ADC include high speed samples being lost. * completion.
* * @note The result buffer must be large enough to store one sample per device
* @api * described by the 'physdev' parameter.
*/ * @note If calling this routine would exceed @p GADC_MAX_LOWSPEED_DEVICES simultaneous low
void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer); * 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
* @brief Perform a low speed ADC conversion with callback (in a thread context) * calculations to ensure the high speed ADC correctness will be incorrect. Symptoms
* @details Returns FALSE if there are no free low speed ADC slots. See @p GADC_MAX_LOWSPEED_DEVICES for details. * from over-running the high speed ADC include high speed samples being lost.
* *
* @param[in] physdev A value passed to describe which physical ADC devices/channels to use. * @api
* @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. void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer);
* @param[in] param A parameter to pass to the callback function.
* /**
* @note This may be safely called from within a GTimer callback. * @brief Perform a low speed ADC conversion with callback (in a thread context)
* @note The callback may take a while to occur if the high speed ADC is running as the * @details Returns FALSE if there are no free low speed ADC slots. See @p GADC_MAX_LOWSPEED_DEVICES for details.
* conversion is interleaved with the high speed ADC conversions on a buffer *
* completion. * @param[in] physdev A value passed to describe which physical ADC devices/channels to use.
* @note The result buffer must be large enough to store one sample per device * @param[in] buffer The static buffer to put the ADC samples into.
* described by the 'physdev' parameter. * @param[in] fn The callback function to call when the conversion is complete.
* @note As this routine uses a low speed ADC, it asserts if you try to run more than @p GADC_MAX_LOWSPEED_DEVICES * @param[in] param A parameter to pass to the callback function.
* at the same time. *
* @note Specifying more than one device in physdev is possible but discouraged as the * @note This may be safely called from within a GTimer callback.
* calculations to ensure the high speed ADC correctness will be incorrect. Symptoms * @note The callback may take a while to occur if the high speed ADC is running as the
* from over-running the high speed ADC include high speed samples being lost. * conversion is interleaved with the high speed ADC conversions on a buffer
* * completion.
* @api * @note The result buffer must be large enough to store one sample per device
*/ * described by the 'physdev' parameter.
bool gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param); * @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.
#ifdef __cplusplus * @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
#endif * from over-running the high speed ADC include high speed samples being lost.
*
#endif /* GFX_USE_GADC */ * @api
*/
#endif /* _GADC_H */ bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* GFX_USE_GADC */
#endif /* _GADC_H */
/** @} */

View File

@ -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 */
/** @} */

View File

@ -1,57 +1,55 @@
/* /*
ChibiOS/GFX - Copyright (C) 2012 ChibiOS/GFX - Copyright (C) 2012, 2013
Joel Bodenmann aka Tectu <joel@unormal.org> Joel Bodenmann aka Tectu <joel@unormal.org>
This file is part of ChibiOS/GFX. This file is part of ChibiOS/GFX.
ChibiOS/GFX is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or the Free Software Foundation; either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
ChibiOS/GFX is distributed in the hope that it will be useful, ChibiOS/GFX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file include/gadc/options.h * @file include/gadc/options.h
* @brief GADC - Periodic ADC subsystem options header file. * @brief GADC - Periodic ADC subsystem options header file.
* *
* @addtogroup GADC * @addtogroup GADC
* @{ * @{
*/ */
#ifndef _GADC_OPTIONS_H #ifndef _GADC_OPTIONS_H
#define _GADC_OPTIONS_H #define _GADC_OPTIONS_H
/** /**
* @name GADC Functionality to be included * @name GADC Functionality to be included
* @{ * @{
*/ */
/** /**
* @} * @}
* *
* @name GADC Optional Sizing Parameters * @name GADC Optional Sizing Parameters
* @{ * @{
*/ */
/** /**
* @brief The maximum simultaneous GADC low speed device conversions * @brief The maximum GADC sample rate
* @details Defaults to 4 * @details Defaults to 44000
* @note This value must be less than the number of conversions that can occur * @note This value must be less than half the maximum sample rate allowed by the CPU.
* in a single high speed ADC cycle including the high speed ADC conversion. * This is to ensure there is time between high speed samples to perform low
* For example, if the ADC can run at 132k samples per second and the high speed * speed device sampling.
* virtual ADC is using 44kHz then GADC_MAX_LOWSPEED_DEVICES should be set to */
* 132/44 - 1 = 2 #ifndef GADC_MAX_HIGH_SPEED_SAMPLERATE
*/ #define GADC_MAX_HIGH_SPEED_SAMPLERATE 44000
#ifndef GADC_MAX_LOWSPEED_DEVICES #endif
#define GADC_MAX_LOWSPEED_DEVICES 4 /** @} */
#endif
/** @} */ #endif /* _GADC_OPTIONS_H */
/** @} */
#endif /* _GADC_OPTIONS_H */
/** @} */

View File

@ -1,128 +1,136 @@
/* /*
ChibiOS/GFX - Copyright (C) 2012 ChibiOS/GFX - Copyright (C) 2012
Joel Bodenmann aka Tectu <joel@unormal.org> Joel Bodenmann aka Tectu <joel@unormal.org>
This file is part of ChibiOS/GFX. This file is part of ChibiOS/GFX.
ChibiOS/GFX is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or the Free Software Foundation; either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
ChibiOS/GFX is distributed in the hope that it will be useful, ChibiOS/GFX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file include/gfx_rules.h * @file include/gfx_rules.h
* @brief GFX system safety rules header file. * @brief GFX system safety rules header file.
* *
* @addtogroup GFX * @addtogroup GFX
* @{ * @{
*/ */
#ifndef _GFX_RULES_H #ifndef _GFX_RULES_H
#define _GFX_RULES_H #define _GFX_RULES_H
/** /**
* Safety checks on all the defines. * Safety checks on all the defines.
* *
* These are defined in the order of their inter-dependancies. * These are defined in the order of their inter-dependancies.
*/ */
#if GFX_USE_GWIN #if GFX_USE_GWIN
#if !GFX_USE_GDISP #if !GFX_USE_GDISP
#error "GWIN: GFX_USE_GDISP must be TRUE when using GWIN" #error "GWIN: GFX_USE_GDISP must be TRUE when using GWIN"
#endif #endif
#if !GDISP_NEED_CLIP #if !GDISP_NEED_CLIP
#warning "GWIN: Drawing can occur outside the defined windows as GDISP_NEED_CLIP is FALSE" #warning "GWIN: Drawing can occur outside the defined windows as GDISP_NEED_CLIP is FALSE"
#endif #endif
#if GWIN_NEED_BUTTON #if GWIN_NEED_BUTTON
#if !GDISP_NEED_TEXT #if !GDISP_NEED_TEXT
#error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_BUTTON is TRUE." #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_BUTTON is TRUE."
#endif #endif
#if !GFX_USE_GEVENT #if !GFX_USE_GEVENT
#warning "GWIN: GFX_USE_GEVENT is required if GWIN_NEED_BUTTON is TRUE. It has been turned on for you." #warning "GWIN: GFX_USE_GEVENT is required if GWIN_NEED_BUTTON is TRUE. It has been turned on for you."
#undef GFX_USE_GEVENT #undef GFX_USE_GEVENT
#define GFX_USE_GEVENT TRUE #define GFX_USE_GEVENT TRUE
#endif #endif
#if !GFX_USE_GINPUT || !(GINPUT_NEED_MOUSE || GINPUT_NEED_TOGGLE) #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" #warning "GWIN: You have set GWIN_NEED_BUTTON to TRUE but no supported GINPUT (mouse/toggle) devices have been included"
#endif #endif
#if !GDISP_NEED_MULTITHREAD && !GDISP_NEED_ASYNC #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: 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." #warning "GWIN: GDISP_NEED_MULTITHREAD has been turned on for you."
#undef GDISP_NEED_MULTITHREAD #undef GDISP_NEED_MULTITHREAD
#define GDISP_NEED_MULTITHREAD TRUE #define GDISP_NEED_MULTITHREAD TRUE
#endif #endif
#endif #endif
#if GWIN_NEED_CONSOLE #if GWIN_NEED_CONSOLE
#if !GDISP_NEED_TEXT #if !GDISP_NEED_TEXT
#error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_CONSOLE is TRUE." #error "GWIN: GDISP_NEED_TEXT is required if GWIN_NEED_CONSOLE is TRUE."
#endif #endif
#endif #endif
#if GWIN_NEED_GRAPH #if GWIN_NEED_GRAPH
#endif #endif
#endif #endif
#if GFX_USE_GINPUT #if GFX_USE_GINPUT
#if !GFX_USE_GEVENT #if !GFX_USE_GEVENT
#warning "GINPUT: GFX_USE_GEVENT is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." #warning "GINPUT: GFX_USE_GEVENT is required if GFX_USE_GINPUT is TRUE. It has been turned on for you."
#undef GFX_USE_GEVENT #undef GFX_USE_GEVENT
#define GFX_USE_GEVENT TRUE #define GFX_USE_GEVENT TRUE
#endif #endif
#if !GFX_USE_GTIMER #if !GFX_USE_GTIMER
#warning "GINPUT: GFX_USE_GTIMER is required if GFX_USE_GINPUT is TRUE. It has been turned on for you." #warning "GINPUT: GFX_USE_GTIMER is required if GFX_USE_GINPUT is TRUE. It has been turned on for you."
#undef GFX_USE_GTIMER #undef GFX_USE_GTIMER
#define GFX_USE_GTIMER TRUE #define GFX_USE_GTIMER TRUE
#endif #endif
#endif #endif
#if GFX_USE_GDISP #if GFX_USE_GDISP
#if GDISP_NEED_MULTITHREAD && GDISP_NEED_ASYNC #if GDISP_NEED_MULTITHREAD && GDISP_NEED_ASYNC
#error "GDISP: Only one of GDISP_NEED_MULTITHREAD and GDISP_NEED_ASYNC should be defined." #error "GDISP: Only one of GDISP_NEED_MULTITHREAD and GDISP_NEED_ASYNC should be defined."
#endif #endif
#if GDISP_NEED_ASYNC #if GDISP_NEED_ASYNC
#if !GDISP_NEED_MSGAPI #if !GDISP_NEED_MSGAPI
#warning "GDISP: Messaging API is required for Async Multi-Thread. It has been turned on for you." #warning "GDISP: Messaging API is required for Async Multi-Thread. It has been turned on for you."
#undef GDISP_NEED_MSGAPI #undef GDISP_NEED_MSGAPI
#define GDISP_NEED_MSGAPI TRUE #define GDISP_NEED_MSGAPI TRUE
#endif #endif
#endif #endif
#endif #endif
#if GFX_USE_TDISP #if GFX_USE_TDISP
#endif #endif
#if GFX_USE_GEVENT #if GFX_USE_GADC
#if !CH_USE_MUTEXES || !CH_USE_SEMAPHORES #if !CH_USE_MUTEXES || !CH_USE_SEMAPHORES
#error "GEVENT: CH_USE_MUTEXES and CH_USE_SEMAPHORES must be defined in chconf.h" #error "GADC: CH_USE_MUTEXES and CH_USE_SEMAPHORES must be defined in chconf.h"
#endif #endif
#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."
#if GFX_USE_GTIMER #undef GFX_USE_GTIMER
#if GFX_USE_GDISP && !GDISP_NEED_MULTITHREAD && !GDISP_NEED_ASYNC #define GFX_USE_GTIMER TRUE
#warning "GTIMER: Neither GDISP_NEED_MULTITHREAD nor GDISP_NEED_ASYNC has been specified." #endif
#warning "GTIMER: Make sure you are not performing any GDISP/GWIN drawing operations in the timer callback!" #endif
#endif
#endif #if GFX_USE_GEVENT
#if !CH_USE_MUTEXES || !CH_USE_SEMAPHORES
#if GFX_USE_GAUDIN #error "GEVENT: CH_USE_MUTEXES and CH_USE_SEMAPHORES must be defined in chconf.h"
#endif #endif
#endif
#if GFX_USE_GAUDOUT
#endif #if GFX_USE_GTIMER
#if GFX_USE_GDISP && !GDISP_NEED_MULTITHREAD && !GDISP_NEED_ASYNC
#if GFX_USE_GADC #warning "GTIMER: Neither GDISP_NEED_MULTITHREAD nor GDISP_NEED_ASYNC has been specified."
#endif #warning "GTIMER: Make sure you are not performing any GDISP/GWIN drawing operations in the timer callback!"
#endif
#if GFX_USE_GMISC #endif
#endif
#if GFX_USE_GAUDIN
#endif /* _GFX_H */ #endif
/** @} */
#if GFX_USE_GAUDOUT
#endif
#if GFX_USE_GMISC
#endif
#endif /* _GFX_H */
/** @} */

View File

@ -1,38 +1,465 @@
/* /*
ChibiOS/GFX - Copyright (C) 2012 ChibiOS/GFX - Copyright (C) 2012, 2013
Joel Bodenmann aka Tectu <joel@unormal.org> Joel Bodenmann aka Tectu <joel@unormal.org>
This file is part of ChibiOS/GFX. This file is part of ChibiOS/GFX.
ChibiOS/GFX is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or the Free Software Foundation; either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
ChibiOS/GFX is distributed in the hope that it will be useful, ChibiOS/GFX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file src/gadc/gadc.c * @file src/gadc/gadc.c
* @brief GADC sub-system code. * @brief GADC sub-system code.
* *
* @addtogroup GADC * @addtogroup GADC
* @{ * @{
*/ */
#include "ch.h" #include "ch.h"
#include "hal.h" #include "hal.h"
#include "gfx.h" #include "gfx.h"
#if GFX_USE_GADC || defined(__DOXYGEN__) #if GFX_USE_GADC
#error "GADC: Not implemented yet" /* Include the driver defines */
#include "gadc/lld/gadc_lld.h"
#endif /* GFX_USE_GADC */
/** @} */ #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 */
/** @} */