From 271f0c743f31d3ae21ede35909e1f14845c6d338 Mon Sep 17 00:00:00 2001 From: inmarket Date: Thu, 20 Mar 2014 23:41:27 +1000 Subject: [PATCH] Updates to GADC to use new simpler gfx queued bufferring. NOTE: code is still buggy (or the one and only driver is buggy). --- .../gaudio_record_board.h | 7 +- demos/modules/gadc/gfxconf.h | 1 - demos/modules/gadc/gwinosc.c | 34 +-- demos/modules/gadc/gwinosc.h | 3 - demos/modules/gadc/main.c | 5 + drivers/gadc/AT91SAM7/gadc_lld.c | 72 ++++-- drivers/gadc/AT91SAM7/gadc_lld_config.h | 7 +- .../gadc/gaudio_record_board_template.h | 8 +- drivers/gaudio/gadc/gaudio_record_config.h | 18 +- drivers/gaudio/gadc/gaudio_record_lld.c | 34 +-- src/gadc/driver.h | 97 ++++---- src/gadc/gadc.c | 218 ++++++++---------- src/gadc/sys_defs.h | 70 +++--- src/gadc/sys_rules.h | 11 + 14 files changed, 280 insertions(+), 305 deletions(-) diff --git a/boards/base/Olimex-SAM7EX256-GE8/gaudio_record_board.h b/boards/base/Olimex-SAM7EX256-GE8/gaudio_record_board.h index cdea5e06..68063881 100644 --- a/boards/base/Olimex-SAM7EX256-GE8/gaudio_record_board.h +++ b/boards/base/Olimex-SAM7EX256-GE8/gaudio_record_board.h @@ -19,13 +19,18 @@ #define GAUDIO_RECORD_NUM_CHANNELS 1 +/** + * @brief Whether each channel is mono or stereo + */ +#define GAUDIO_RECORD_CHANNEL0_IS_STEREO FALSE + /** * The list of audio channels and their uses */ #define GAUDIO_RECORD_MICROPHONE 0 #ifdef GAUDIO_RECORD_IMPLEMENTATION - static uint32_t gaudin_lld_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = { + static uint32_t gaudio_gadc_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = { GADC_PHYSDEV_MICROPHONE, }; #endif diff --git a/demos/modules/gadc/gfxconf.h b/demos/modules/gadc/gfxconf.h index 297265b5..d0da3943 100644 --- a/demos/modules/gadc/gfxconf.h +++ b/demos/modules/gadc/gfxconf.h @@ -51,7 +51,6 @@ #define GDISP_NEED_VALIDATION TRUE #define GDISP_NEED_CLIP TRUE #define GDISP_NEED_TEXT TRUE -#define GDISP_NEED_CONTROL TRUE #define GDISP_NEED_MULTITHREAD TRUE /* GDISP - builtin fonts */ diff --git a/demos/modules/gadc/gwinosc.c b/demos/modules/gadc/gwinosc.c index afa12bfc..84e7d645 100644 --- a/demos/modules/gadc/gwinosc.c +++ b/demos/modules/gadc/gwinosc.c @@ -38,9 +38,6 @@ /* Include internal GWIN routines so we can build our own superset class */ #include "src/gwin/class_gwin.h" -/* The size of our dynamically allocated audio buffer */ -#define AUDIOBUFSZ 64*2 - /* How many flat-line sample before we trigger */ #define FLATLINE_SAMPLES 8 @@ -50,10 +47,6 @@ static void _destroy(GHandle gh) { gfxFree(((GScopeObject *)gh)->lastscopetrace); ((GScopeObject *)gh)->lastscopetrace = 0; } - if (((GScopeObject *)gh)->audiobuf) { - gfxFree(((GScopeObject *)gh)->audiobuf); - ((GScopeObject *)gh)->audiobuf = 0; - } } static const gwinVMT scopeVMT = { @@ -68,12 +61,9 @@ GHandle gwinGScopeCreate(GDisplay *g, GScopeObject *gs, GWindowInit *pInit, uint /* Initialise the base class GWIN */ if (!(gs = (GScopeObject *)_gwindowCreate(g, &gs->g, pInit, &scopeVMT, 0))) return 0; - gfxSemInit(&gs->bsem, 0, 1); gs->nextx = 0; if (!(gs->lastscopetrace = gfxAlloc(gs->g.width * sizeof(coord_t)))) return 0; - if (!(gs->audiobuf = gfxAlloc(AUDIOBUFSZ * sizeof(adcsample_t)))) - return 0; #if TRIGGER_METHOD == TRIGGER_POSITIVERAMP gs->lasty = gs->g.height/2; #elif TRIGGER_METHOD == TRIGGER_MINVALUE @@ -82,8 +72,7 @@ GHandle gwinGScopeCreate(GDisplay *g, GScopeObject *gs, GWindowInit *pInit, uint #endif /* Start the GADC high speed converter */ - gadcHighSpeedInit(physdev, frequency, gs->audiobuf, AUDIOBUFSZ, AUDIOBUFSZ/2); - gadcHighSpeedSetBSem(&gs->bsem, &gs->myEvent); + gadcHighSpeedInit(physdev, frequency); gadcHighSpeedStart(); gwinSetVisible((GHandle)gs, pInit->show); @@ -97,6 +86,8 @@ void gwinScopeWaitForTrace(GHandle gh) { coord_t yoffset; adcsample_t *pa; coord_t *pc; + GDataBuffer *pd; + uint8_t shr; #if TRIGGER_METHOD == TRIGGER_POSITIVERAMP bool_t rdytrigger; int flsamples; @@ -109,20 +100,21 @@ void gwinScopeWaitForTrace(GHandle gh) { if (gh->vmt != &scopeVMT) return; - /* Wait for a set of audio conversions */ - gfxSemWait(&gs->bsem, TIME_INFINITE); + /* Wait for a set of conversions */ + pd = gadcHighSpeedGetData(TIME_INFINITE); /* Ensure we are drawing in the right area */ #if GDISP_NEED_CLIP gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); #endif + shr = 16 - gfxSampleFormatBits(GADC_SAMPLE_FORMAT); yoffset = gh->height/2; - if (!(GADC_SAMPLE_FORMAT & 1)) + if (!gfxSampleFormatIsSigned(GADC_SAMPLE_FORMAT)) yoffset += (1<nextx; pc = gs->lastscopetrace+x; - pa = gs->myEvent.buffer; + pa = (adcsample_t *)(pd+1); #if TRIGGER_METHOD == TRIGGER_POSITIVERAMP rdytrigger = FALSE; flsamples = 0; @@ -132,14 +124,10 @@ void gwinScopeWaitForTrace(GHandle gh) { scopemin = 0; #endif - for(i = gs->myEvent.count; i; i--) { + for(i = pd->len/sizeof(adcsample_t); 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 + y = yoffset - (((coord_t)(*pa++) << shr) >> (16-SCOPE_Y_BITS)); #if TRIGGER_METHOD == TRIGGER_MINVALUE /* Calculate the scopemin ready for the next trace */ @@ -205,5 +193,7 @@ void gwinScopeWaitForTrace(GHandle gh) { gs->scopemin = scopemin; #endif + gfxBufferRelease(pd); + #undef gs } diff --git a/demos/modules/gadc/gwinosc.h b/demos/modules/gadc/gwinosc.h index 56de0f11..8f5c1be3 100644 --- a/demos/modules/gadc/gwinosc.h +++ b/demos/modules/gadc/gwinosc.h @@ -64,9 +64,6 @@ typedef struct GScopeObject_t { GWindowObject g; // Base Class coord_t *lastscopetrace; // To store last scope trace - gfxSem 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 diff --git a/demos/modules/gadc/main.c b/demos/modules/gadc/main.c index 928635fa..8e5ecaa4 100644 --- a/demos/modules/gadc/main.c +++ b/demos/modules/gadc/main.c @@ -167,6 +167,11 @@ int main(void) { gtimerStart(&lsTimer, LowSpeedTimer, ghText, TRUE, MY_LS_DELAY); #endif + // Allocate buffers for the high speed GADC device - 4 x 128 byte buffers. + // You may need to increase this for slower cpu's. + // You may be able to decrease this for low latency operating systems. + gfxBufferAlloc(4, 128); + /* Set up the scope window in the top right on the screen */ { GWindowInit wi; diff --git a/drivers/gadc/AT91SAM7/gadc_lld.c b/drivers/gadc/AT91SAM7/gadc_lld.c index f18f2717..52b06539 100644 --- a/drivers/gadc/AT91SAM7/gadc_lld.c +++ b/drivers/gadc/AT91SAM7/gadc_lld.c @@ -8,10 +8,6 @@ /** * @file drivers/gadc/AT91SAM7/gadc_lld.c * @brief GADC - Periodic ADC driver source file for the AT91SAM7 cpu. - * - * @defgroup Driver Driver - * @ingroup GADC - * @{ */ #include "gfx.h" @@ -20,44 +16,76 @@ #include "src/gadc/driver.h" +static GDataBuffer *pData; +static size_t bytesperconversion; + +static void ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n); +static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err); + + static ADCConversionGroup acg = { FALSE, // circular 1, // num_channels - GADC_ISR_CompleteI, // end_cb - GADC_ISR_ErrorI, // error_cb + ISR_CompleteI, // end_cb + ISR_ErrorI, // error_cb 0, // channelselects 0, // trigger 0, // frequency }; +static void ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) { + (void) adcp; + (void) buffer; + + if (pData) { + // A set of timer base conversions is complete + pData->len += n * bytesperconversion; + + // Are we finished yet? + // In ChibiOS we (may) get a half-buffer complete. In this situation the conversions + // are really not complete and so we just wait for the next lot of data. + if (pData->len + bytesperconversion > pData->size) + gadcDataReadyI(); + + } else { + // A single non-timer conversion is complete + gadcDataReadyI(); + } +} + +static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err) { + (void) adcp; + (void) err; + + gadcDataFailI(); +} + void gadc_lld_init(void) { adcStart(&ADCD1, 0); } -size_t gadc_lld_samples_per_conversion(uint32_t physdev) { - size_t cnt; - int i; +void gadc_lld_start_timer(GadcLldTimerData *pgtd) { + int phys; + /* Calculate the bytes per conversion from physdev */ /* 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; -} + phys = pgtd->physdev; + for(bytesperconversion = 0; phys; phys >>= 1) + if (phys & 0x01) + bytesperconversion++; + bytesperconversion *= (gfxSampleFormatBits(GADC_SAMPLE_FORMAT)+7)/8; -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; + acg.frequency = pgtd->frequency; } -void gadc_lld_stop_timer(uint32_t physdev) { - (void) physdev; +void gadc_lld_stop_timer(GadcLldTimerData *pgtd) { + (void) pgtd; if ((acg.trigger & ~ADC_TRIGGER_SOFTWARE) == ADC_TRIGGER_TIMER) adcStop(&ADCD1); } @@ -69,7 +97,8 @@ void gadc_lld_adc_timerI(GadcLldTimerData *pgtd) { acg.channelselects = pgtd->physdev; acg.trigger = pgtd->now ? (ADC_TRIGGER_TIMER|ADC_TRIGGER_SOFTWARE) : ADC_TRIGGER_TIMER; - adcStartConversionI(&ADCD1, &acg, pgtd->buffer, pgtd->count); + pData = pgtd->pdata; + adcStartConversionI(&ADCD1, &acg, (adcsample_t *)(pgtd->pdata+1), pData->size/bytesperconversion); /* Next time assume the same (still running) timer */ acg.frequency = 0; @@ -81,8 +110,9 @@ void gadc_lld_adc_nontimerI(GadcLldNonTimerData *pgntd) { */ acg.channelselects = pgntd->physdev; acg.trigger = ADC_TRIGGER_SOFTWARE; + + pData = 0; adcStartConversionI(&ADCD1, &acg, pgntd->buffer, 1); } #endif /* GFX_USE_GADC */ -/** @} */ diff --git a/drivers/gadc/AT91SAM7/gadc_lld_config.h b/drivers/gadc/AT91SAM7/gadc_lld_config.h index ba6cbda2..de5af3d3 100644 --- a/drivers/gadc/AT91SAM7/gadc_lld_config.h +++ b/drivers/gadc/AT91SAM7/gadc_lld_config.h @@ -35,18 +35,13 @@ * @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 +#define CHIBIOS_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 - /** * @brief The sample format */ diff --git a/drivers/gaudio/gadc/gaudio_record_board_template.h b/drivers/gaudio/gadc/gaudio_record_board_template.h index 26e87d88..59168be1 100644 --- a/drivers/gaudio/gadc/gaudio_record_board_template.h +++ b/drivers/gaudio/gadc/gaudio_record_board_template.h @@ -26,6 +26,12 @@ */ #define GAUDIO_RECORD_NUM_CHANNELS 1 +/** + * @brief Whether each channel is mono or stereo + * @note This is an example + */ +#define GAUDIO_RECORD_CHANNEL0_IS_STEREO FALSE + /** * @brief The list of audio channels and their uses * @note This is an example @@ -40,7 +46,7 @@ * @{ */ #ifdef GAUDIO_RECORD_LLD_IMPLEMENTATION - static uint32_t gaudin_lld_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = { + static uint32_t gaudio_gadc_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = { GADC_PHYSDEV_MICROPHONE, }; #endif diff --git a/drivers/gaudio/gadc/gaudio_record_config.h b/drivers/gaudio/gadc/gaudio_record_config.h index 22d8750f..88092a63 100644 --- a/drivers/gaudio/gadc/gaudio_record_config.h +++ b/drivers/gaudio/gadc/gaudio_record_config.h @@ -22,29 +22,21 @@ /* Driver hardware support. */ /*===========================================================================*/ -/** - * @brief The audio record sample type - * @details For this driver it matches the cpu sample type - */ -typedef adcsample_t audio_record_sample_t; - /** * @brief The maximum sample frequency supported by this audio device * @details For this driver it matches the GADC maximum high speed sample rate */ -#define GAUDIO_RECORD_MAX_SAMPLE_FREQUENCY GADC_MAX_HIGH_SPEED_SAMPLERATE +#define GAUDIO_RECORD_MAX_SAMPLE_FREQUENCY GADC_MAX_HIGH_SPEED_SAMPLERATE /** - * @brief The number of bits in a sample - * @details For this driver it matches the cpu sample bits + * @brief The number of audio formats supported by this driver */ -#define GAUDIO_RECORD_BITS_PER_SAMPLE GADC_BITS_PER_SAMPLE +#define GAUDIO_RECORD_NUM_FORMATS 1 /** - * @brief The format of an audio sample - * @details For this driver it matches the cpu sample format + * @brief The available audio sample formats in order of preference */ -#define GAUDIO_RECORD_SAMPLE_FORMAT GADC_SAMPLE_FORMAT +#define GAUDIO_RECORD_FORMAT1 GADC_SAMPLE_FORMAT /** * For the GAUDIO driver that uses GADC - all the remaining config definitions are specific diff --git a/drivers/gaudio/gadc/gaudio_record_lld.c b/drivers/gaudio/gadc/gaudio_record_lld.c index ee994dc1..6f4cb2de 100644 --- a/drivers/gaudio/gadc/gaudio_record_lld.c +++ b/drivers/gaudio/gadc/gaudio_record_lld.c @@ -8,10 +8,6 @@ /** * @file drivers/gaudio/gadc/gaudio_record_lld.c * @brief GAUDIO - Record Driver file for using the cpu ADC (via GADC). - * - * @addtogroup GAUDIO - * - * @{ */ /** @@ -19,8 +15,6 @@ * from the board definitions. */ #define GAUDIO_RECORD_IMPLEMENTATION - - #include "gfx.h" #if GFX_USE_GAUDIO && GAUDIO_NEED_RECORD @@ -33,30 +27,38 @@ /* Include the driver defines */ #include "src/gaudio/driver_record.h" +static void gadcCallbackI(void) { + GDataBuffer *pd; + + pd = gadcHighSpeedGetDataI(); + if (pd) + gaudioRecordSaveDataBlockI(pd); +} + /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ -void gaudin_lld_init(const gaudin_params *paud) { +bool_t gaudio_record_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format) { + /* Check the parameters */ + if (channel >= GAUDIO_RECORD_NUM_CHANNELS || frequency > GAUDIO_RECORD_MAX_SAMPLE_FREQUENCY || format != GAUDIO_RECORD_FORMAT1) + return FALSE; + /* Setup the high speed GADC */ - gadcHighSpeedInit(gaudin_lld_physdevs[paud->channel], paud->frequency, paud->buffer, paud->bufcount, paud->samplesPerEvent); + gadcHighSpeedInit(gaudio_gadc_physdevs[channel], frequency); /* Register ourselves for ISR callbacks */ - gadcHighSpeedSetISRCallback(GAUDIN_ISR_CompleteI); + gadcHighSpeedSetISRCallback(gadcCallbackI); - /** - * The gadc driver handles any errors for us by restarting the transaction so there is - * no need for us to setup anything for GAUDIN_ISR_ErrorI() - */ + return TRUE; } -void gaudin_lld_start(void) { +void gaudio_record_lld_start(void) { gadcHighSpeedStart(); } -void gaudin_lld_stop(void) { +void gaudio_record_lld_stop(void) { gadcHighSpeedStop(); } #endif /* GFX_USE_GAUDIO && GAUDIO_NEED_RECORD */ -/** @} */ diff --git a/src/gadc/driver.h b/src/gadc/driver.h index 4427f4f0..6e935576 100644 --- a/src/gadc/driver.h +++ b/src/gadc/driver.h @@ -32,9 +32,9 @@ * @{ */ 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. */ + uint32_t physdev; /* @< Which physical ADC devices/channels to use. Filled in by High Level Code */ + uint32_t frequency; /* @< The conversion frequency. Filled in by High Level Code */ + GDataBuffer *pdata; /* @< The buffer to put the ADC samples into. */ bool_t now; /* @< Trigger the first conversion now rather than waiting for the first timer interrupt (if possible) */ } GadcLldTimerData; /* @} */ @@ -51,30 +51,6 @@ typedef struct GadcLldNonTimerData_t { } GadcLldNonTimerData; /* @} */ -/** - * @brief These routines are the callbacks that the driver uses. - * @details Defined in the high level GADC code. - * - * @notapi - * @{ - */ - -/** - * @param[in] adcp The ADC driver - * @param[in] buffer The sample buffer - * @param[in] n The amount of samples - */ -extern void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n); - -/** - * @param[in] adcp The ADC driver - * @param[in] err ADC error - */ -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. @@ -91,6 +67,26 @@ extern volatile bool_t GADC_Timer_Missed; extern "C" { #endif +/** + * @brief These routines are the callbacks that the driver uses. + * @details Defined in the high level GADC code. + * + * @notapi + * @{ + */ + /** + * @brief The last conversion requested is now complete + */ + void gadcDataReadyI(void); + + /** + * @brief The last conversion requested failed + */ + void gadcDataFailI(void); +/** + * @} + */ + /** * @brief Initialise the driver * @@ -98,64 +94,49 @@ extern "C" { */ 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. - * - * @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. - * - * @param[in] physdev A value passed to describe which physical ADC devices/channels to use. - * - * @return Number of samples of the convesion - * @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 + * @param[in] pgtd The structure containing the sample frequency and physical device to use. * * @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 + * @details When a timer interrupt occurs a conversion should start if there 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 + * @note Timer interrupts occurring before @p gadc_lld_adc_timerI() has been called, + * if @p gadc_lld_adc_timerI() has been called quick enough, or while + * a non-timer conversion is active should be ignored other than (optionally) incrementing * the GADC_Timer_Missed variable. + * @note The pdata and now members of the pgtd structure are now yet valid. * * @api */ -void gadc_lld_start_timer(uint32_t physdev, uint32_t frequency); +void gadc_lld_start_timer(GadcLldTimerData *pgtd); /** * @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. + * @param[in] pgtd The structure containing the sample frequency and physical device to use. * + * @note After this function returns there should be no more calls to @p gadcDataReadyI() + * or @p gadcDataFailI() in relation to timer conversions. * @api */ -void gadc_lld_stop_timer(uint32_t physdev); +void gadc_lld_stop_timer(GadcLldTimerData *pgtd); /** - * @brief Start a "timer" conversion. + * @brief Start a set of "timer" conversions. * @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 driver should call @p gadcDataReadyI() when it completes the operation + * or @p gadcDataFailI() on an error. * @note The high level code ensures that this is not called while a non-timer conversion is in * progress * @@ -171,8 +152,8 @@ void gadc_lld_adc_timerI(GadcLldTimerData *pgtd); * * @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 driver should call @p gadcDataReadyI() when it completes the operation + * or @p gadcDataFailI() on an error. * @note The high level code ensures that this is not called while a timer conversion is in * progress * diff --git a/src/gadc/gadc.c b/src/gadc/gadc.c index 8ae431b0..e2d2d461 100644 --- a/src/gadc/gadc.c +++ b/src/gadc/gadc.c @@ -31,42 +31,30 @@ volatile bool_t GADC_Timer_Missed; -static gfxSem gadcsem; -static gfxMutex gadcmutex; -static GTimer LowSpeedGTimer; +static bool_t gadcRunning; +static gfxSem LowSpeedSlotSem; +static gfxMutex LowSpeedMutex; +static GTimer LowSpeedGTimer; +static gfxQueueGSync HighSpeedBuffers; + #if GFX_USE_GEVENT - static GTimer HighSpeedGTimer; + static GTimer HighSpeedGTimer; #endif -static volatile uint16_t gflags = 0; - #define GADC_GFLG_ISACTIVE 0x0001 #define GADC_FLG_ISACTIVE 0x0001 #define GADC_FLG_ISDONE 0x0002 #define GADC_FLG_ERROR 0x0004 #define GADC_FLG_GTIMER 0x0008 +#define GADC_FLG_STALLED 0x0010 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 signaling GadcLldTimerData lld; - size_t samplesPerConversion; - size_t remaining; - gfxSem *bsem; - GEventADC *pEvent; + uint16_t eventflags; GADCISRCallbackFunction isrfn; } hs; @@ -101,49 +89,59 @@ static inline void FindNextConversionI(void) { /** * Look for the next thing to do. */ - while(curlsdev < &ls[GADC_MAX_LOWSPEED_DEVICES]) { + gadcRunning = TRUE; + for(; curlsdev < &ls[GADC_MAX_LOWSPEED_DEVICES]; curlsdev++) { 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; + hs.lld.pdata = gfxBufferGetI(); + if (hs.lld.pdata) { + hs.lld.now = GADC_Timer_Missed || (hs.flags & GADC_FLG_STALLED); + hs.flags &= ~GADC_FLG_STALLED; + GADC_Timer_Missed = 0; + gadc_lld_adc_timerI(&hs.lld); + return; + } + + // Oops - no free buffers - mark stalled and go back to low speed devices + hs.flags |= GADC_FLG_STALLED; + hs.eventflags &= ~GADC_HSADC_RUNNING; + for(curlsdev = ls; curlsdev < &ls[GADC_MAX_LOWSPEED_DEVICES]; curlsdev++) { + if ((curlsdev->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == GADC_FLG_ISACTIVE) { + gadc_lld_adc_nontimerI(&curlsdev->lld); + return; + } + } + curlsdev = 0; } /* Nothing more to do */ - gflags &= ~GADC_GFLG_ISACTIVE; + gadcRunning = FALSE; } -void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) { - (void) adcp; +void gadcDataReadyI(void) { 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 + #if GFX_USE_OS_CHIBIOS && CHIBIOS_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; + gadcRunning = FALSE; return; #endif @@ -152,49 +150,31 @@ void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) { /* 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; + if (hs.lld.pdata->len) { + /* Save the current buffer on the HighSpeedBuffers */ + gfxQueueGSyncPutI(&HighSpeedBuffers, (gfxQueueGSyncItem *)hs.lld.pdata); + hs.lld.pdata = 0; + + /* Save the details */ + hs.eventflags = GADC_HSADC_RUNNING|GADC_HSADC_GOTBUFFER; + if (GADC_Timer_Missed) + hs.eventflags |= GADC_HSADC_LOSTEVENT; + if (hs.flags & GADC_FLG_STALLED) + hs.eventflags |= GADC_HSADC_STALL; + + /* Our signalling mechanisms */ + if (hs.isrfn) + hs.isrfn(); - /* Signal the user with the data */ - if (hs.pEvent) { #if GFX_USE_GEVENT - hs.pEvent->type = GEVENT_ADC; + if (hs.flags & GADC_FLG_GTIMER) + gtimerJabI(&HighSpeedGTimer); #endif - hs.pEvent->count = hs.lastcount; - hs.pEvent->buffer = hs.lastbuffer; - hs.pEvent->flags = hs.lastflags; - } - - /* Our three signalling mechanisms */ - if (hs.isrfn) - hs.isrfn(buffer, n); - - if (hs.bsem) - gfxSemSignalI(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; + // Oops - no data in this buffer. Just return it to the free-list + gfxBufferRelease(hs.lld.pdata); + hs.lld.pdata = 0; } - hs.lld.count = hs.remaining < hs.samplesPerEvent ? hs.remaining : hs.samplesPerEvent; } } @@ -204,22 +184,19 @@ void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) { FindNextConversionI(); } -void GADC_ISR_ErrorI(ADCDriver *adcp, adcerror_t err) { - (void) adcp; - (void) err; - +void gadcDataFailI(void) { 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 + #if GFX_USE_OS_CHIBIOS && CHIBIOS_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; + gadcRunning = FALSE; gtimerJabI(&LowSpeedGTimer); return; @@ -239,8 +216,9 @@ void GADC_ISR_ErrorI(ADCDriver *adcp, adcerror_t err) { void _gadcInit(void) { gadc_lld_init(); - gfxSemInit(&gadcsem, GADC_MAX_LOWSPEED_DEVICES, GADC_MAX_LOWSPEED_DEVICES); - gfxMutexInit(&gadcmutex); + gfxQueueGSyncInit(&HighSpeedBuffers); + gfxSemInit(&LowSpeedSlotSem, GADC_MAX_LOWSPEED_DEVICES, GADC_MAX_LOWSPEED_DEVICES); + gfxMutexInit(&LowSpeedMutex); gtimerInit(&LowSpeedGTimer); #if GFX_USE_GEVENT gtimerInit(&HighSpeedGTimer); @@ -252,8 +230,9 @@ void _gadcDeinit(void) /* commented stuff is ToDo */ // gadc_lld_deinit(); - gfxSemDestroy(&gadcsem); - gfxMutexDestroy(&gadcmutex); + gfxQueueGSyncDeinit(&HighSpeedBuffers); + gfxSemDestroy(&LowSpeedSlotSem); + gfxMutexDestroy(&LowSpeedMutex); gtimerDeinit(&LowSpeedGTimer); #if GFX_USE_GEVENT gtimerDeinit(&HighSpeedGTimer); @@ -262,7 +241,7 @@ void _gadcDeinit(void) static inline void StartADC(bool_t onNoHS) { gfxSystemLock(); - if (!(gflags & GADC_GFLG_ISACTIVE) || (onNoHS && !curlsdev)) + if (!gadcRunning || (onNoHS && !curlsdev)) FindNextConversionI(); gfxSystemUnlock(); } @@ -289,9 +268,7 @@ static void BSemSignalCallback(adcsample_t *buffer, void *param) { } pe->type = GEVENT_ADC; - pe->count = hs.lastcount; - pe->buffer = hs.lastbuffer; - pe->flags = hs.lastflags | psl->srcflags; + pe->flags = hs.eventflags | psl->srcflags; psl->srcflags = 0; geventSendEvent(psl); } @@ -305,7 +282,7 @@ static void LowSpeedGTimerCallback(void *param) { adcsample_t *buffer; struct lsdev *p; - #if ADC_ISR_FULL_CODE_BUG + #if GFX_USE_OS_CHIBIOS && CHIBIOS_ADC_ISR_FULL_CODE_BUG /* Ensure the ADC is running if it needs to be - Bugfix HACK */ StartADC(FALSE); #endif @@ -325,33 +302,22 @@ static void LowSpeedGTimerCallback(void *param) { 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) - gfxSemSignal(&gadcsem); // Tell everyone + gfxSemSignal(&LowSpeedSlotSem); // 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) +void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency) { 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.frequency = frequency; + hs.lld.pdata = 0; hs.lld.now = FALSE; - hs.samplesPerConversion = gadc_lld_samples_per_conversion(physdev); - hs.remaining = bufcount; - hs.bsem = 0; - hs.pEvent = 0; hs.isrfn = 0; } @@ -368,12 +334,12 @@ void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn) { hs.isrfn = isrfn; } -void gadcHighSpeedSetBSem(gfxSem *pbsem, GEventADC *pEvent) { - /* Use the system lock to ensure they occur atomically */ - gfxSystemLock(); - hs.pEvent = pEvent; - hs.bsem = pbsem; - gfxSystemUnlock(); +GDataBuffer *gadcHighSpeedGetData(delaytime_t ms) { + return (GDataBuffer *)gfxQueueGSyncGet(&HighSpeedBuffers, ms); +} + +GDataBuffer *gadcHighSpeedGetDataI(void) { + return (GDataBuffer *)gfxQueueGSyncGetI(&HighSpeedBuffers); } void gadcHighSpeedStart(void) { @@ -381,8 +347,8 @@ void gadcHighSpeedStart(void) { if (hs.flags & GADC_FLG_ISACTIVE) return; - gadc_lld_start_timer(hs.lld.physdev, hs.frequency); hs.flags = GADC_FLG_ISACTIVE; + gadc_lld_start_timer(&hs.lld); StartADC(FALSE); } @@ -390,7 +356,15 @@ void gadcHighSpeedStop(void) { if (hs.flags & GADC_FLG_ISACTIVE) { /* No more from us */ hs.flags = 0; - gadc_lld_stop_timer(hs.lld.physdev); + gadc_lld_stop_timer(&hs.lld); + /* + * There might be a buffer still locked up by the driver - if so release it. + */ + if (hs.lld.pdata) { + gfxBufferRelease(hs.lld.pdata); + hs.lld.pdata = 0; + } + /* * 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. @@ -405,17 +379,17 @@ void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) { /* Start the Low Speed Timer */ gfxSemInit(&mysem, 1, 1); - gfxMutexEnter(&gadcmutex); + gfxMutexEnter(&LowSpeedMutex); if (!gtimerIsActive(&LowSpeedGTimer)) gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE); - gfxMutexExit(&gadcmutex); + gfxMutexExit(&LowSpeedMutex); while(1) { /* Wait for an available slot */ - gfxSemWait(&gadcsem, TIME_INFINITE); + gfxSemWait(&LowSpeedSlotSem, TIME_INFINITE); /* Find a slot */ - gfxMutexEnter(&gadcmutex); + gfxMutexEnter(&LowSpeedMutex); for(p = ls; p < &ls[GADC_MAX_LOWSPEED_DEVICES]; p++) { if (!(p->flags & GADC_FLG_ISACTIVE)) { p->lld.physdev = physdev; @@ -423,13 +397,13 @@ void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) { p->fn = BSemSignalCallback; p->param = &mysem; p->flags = GADC_FLG_ISACTIVE; - gfxMutexExit(&gadcmutex); + gfxMutexExit(&LowSpeedMutex); StartADC(FALSE); gfxSemWait(&mysem, TIME_INFINITE); return; } } - gfxMutexExit(&gadcmutex); + gfxMutexExit(&LowSpeedMutex); /** * We should never get here - the count semaphore must be wrong. @@ -442,7 +416,7 @@ bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunc struct lsdev *p; /* Start the Low Speed Timer */ - gfxMutexEnter(&gadcmutex); + gfxMutexEnter(&LowSpeedMutex); if (!gtimerIsActive(&LowSpeedGTimer)) gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE); @@ -450,18 +424,18 @@ bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunc 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 */ - gfxSemWait(&gadcsem, TIME_IMMEDIATE); + gfxSemWait(&LowSpeedSlotSem, TIME_IMMEDIATE); p->lld.physdev = physdev; p->lld.buffer = buffer; p->fn = fn; p->param = param; p->flags = GADC_FLG_ISACTIVE; - gfxMutexExit(&gadcmutex); + gfxMutexExit(&LowSpeedMutex); StartADC(FALSE); return TRUE; } } - gfxMutexExit(&gadcmutex); + gfxMutexExit(&LowSpeedMutex); return FALSE; } diff --git a/src/gadc/sys_defs.h b/src/gadc/sys_defs.h index f6349dfe..21e81fb6 100644 --- a/src/gadc/sys_defs.h +++ b/src/gadc/sys_defs.h @@ -73,15 +73,10 @@ typedef struct GEventADC_t { * @{ */ #define GADC_HSADC_LOSTEVENT 0x0001 /**< @brief The last GEVENT_HSDADC event was lost */ + #define GADC_HSADC_RUNNING 0x0002 /**< @brief The High Speed ADC is currently running */ + #define GADC_HSADC_GOTBUFFER 0x0004 /**< @brief A buffer is ready for processing */ + #define GADC_HSADC_STALL 0x0008 /**< @brief The High Speed ADC has stalled due to no free buffers */ /** @} */ - /** - * @brief The number of conversions in the buffer - */ - size_t count; - /** - * @brief The buffer containing the conversion samples - */ - adcsample_t *buffer; } GEventADC; /** @} */ @@ -93,7 +88,7 @@ typedef void (*GADCCallbackFunction)(adcsample_t *buffer, void *param); /** * @brief A callback function (executed in an ISR context) for a high speed conversion */ -typedef void (*GADCISRCallbackFunction)(adcsample_t *buffer, size_t size); +typedef void (*GADCISRCallbackFunction)(void); /*===========================================================================*/ /* External declarations. */ @@ -109,40 +104,28 @@ extern "C" { * * @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] samplesPerEvent 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 overwritten with new data while the application is still trying - * to process the old data. - * @note Due to a bug/feature in Chibi-OS countPerEvent must be even. If bufcount is not - * evenly divisable by countPerEvent, the remainder must also be even. + * @note ChibiOS ONLY: Due to a bug in ChibiOS each buffer on the free-list must contain an even number of + * samples and for multi-channel devices it must hold a number of samples that is evenly divisible + * by 2 times the number of active channels. * @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. + * Each channel is then interleaved into the provided buffer. Make sure your buffers all hold + * a number of samples evenly divisible by the number of active channels. * As an example, if physdev turns on 2 devices then the buffer contains - * alternate device samples and the buffer must contain 2 * bufcount samples. + * alternate device samples and the buffer must contain multiples of 2 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 + * being created at 50Hz (eg 100 samples/buffer, frequency = 5kHz) then the maximum * frequency for low speed conversions will be 50Hz. + * @note Only a single sample format is supported - that provided by the GADC driver. That sample + * format applies to both high speed and low speed sampling. * * @api */ -void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer, size_t bufcount, size_t samplesPerEvent); +void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency); #if GFX_USE_GEVENT || defined(__DOXYGEN__) /** @@ -170,7 +153,7 @@ void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer * * @note Passing a NULL for isrfn will turn off signalling via this method as will calling * @p gadcHighSpeedInit(). - * @note The high speed ADC is capable of signalling via this method, a binary semaphore and the GEVENT + * @note The high speed ADC is capable of signalling via this method, a blocked thread and the GEVENT * sub-system at the same time. * * @api @@ -178,19 +161,24 @@ void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn); /** - * @brief Allow retrieving of results from the high speed ADC using a Binary Semaphore and a static event buffer. + * @brief Get a filled buffer from the ADC + * @return A GDataBuffer pointer or NULL if the timeout is exceeded * - * @param[in] pbsem The 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, an ISR callback and the GEVENT - * sub-system at the same time. + * @params[in] ms The maximum amount of time in milliseconds to wait for data if some is not currently available. * + * @note After processing the data, your application must return the buffer to the free-list so that + * it can be used again. This can be done using @p gfxBufferRelease(). + * @note A buffer may be returned to the free-list before you have finished processing it provided you finish + * processing it before GADC re-uses it. This is useful when RAM usage is critical to reduce the number + * of buffers required. It works before the free list is a FIFO queue and therefore buffers are kept + * in the queue as long as possible before they are re-used. + * @note The function ending with "I" is the interrupt class function. * @api + * @{ */ -void gadcHighSpeedSetBSem(gfxSem *pbsem, GEventADC *pEvent); +GDataBuffer *gadcHighSpeedGetData(delaytime_t ms); +GDataBuffer *gadcHighSpeedGetDataI(void); +/* @} */ /** * @brief Start the high speed ADC conversions. diff --git a/src/gadc/sys_rules.h b/src/gadc/sys_rules.h index 7272337e..363b2434 100644 --- a/src/gadc/sys_rules.h +++ b/src/gadc/sys_rules.h @@ -24,6 +24,17 @@ #undef GFX_USE_GTIMER #define GFX_USE_GTIMER TRUE #endif + #if !GFX_USE_GQUEUE || !GQUEUE_NEED_GSYNC || !GQUEUE_NEED_BUFFERS + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GADC: GFX_USE_GQUEUE, GQUEUE_NEED_BUFFERS and GQUEUE_NEED_GSYNC are required if GFX_USE_GADC is TRUE. They have been turned on for you." + #endif + #undef GFX_USE_GQUEUE + #define GFX_USE_GQUEUE TRUE + #undef GQUEUE_NEED_BUFFERS + #define GQUEUE_NEED_BUFFERS TRUE + #undef GQUEUE_NEED_GSYNC + #define GQUEUE_NEED_GSYNC TRUE + #endif #endif #endif /* _GADC_RULES_H */