aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJoel Bodenmann <joel@unormal.org>2014-04-30 13:41:34 +0200
committerJoel Bodenmann <joel@unormal.org>2014-04-30 13:41:34 +0200
commit33c721c009465dd30d4e96e055a051480c567b57 (patch)
tree5a6744a79b7469d80bae474d4314b47d4cd6d44d /src
parent58cf2d2b35542166f1a4e50a83bcf28ff33574a5 (diff)
parenta394e2c35dde67241bea69409bcae9f46dcfc089 (diff)
downloaduGFX-33c721c009465dd30d4e96e055a051480c567b57.tar.gz
uGFX-33c721c009465dd30d4e96e055a051480c567b57.tar.bz2
uGFX-33c721c009465dd30d4e96e055a051480c567b57.zip
Merge branch 'master' into freertos
Diffstat (limited to 'src')
-rw-r--r--src/gadc/driver.h143
-rw-r--r--src/gadc/gadc.c578
-rw-r--r--src/gadc/sys_defs.h84
-rw-r--r--src/gadc/sys_rules.h11
-rw-r--r--src/gaudio/driver_play.h8
-rw-r--r--src/gaudio/driver_record.h6
-rw-r--r--src/gaudio/gaudio.c79
-rw-r--r--src/gaudio/sys_defs.h71
-rw-r--r--src/gaudio/sys_rules.h10
-rw-r--r--src/gdisp/colors.h2
-rw-r--r--src/gdisp/image.h2
-rw-r--r--src/gfile/inc_romfs.c45
-rw-r--r--src/gfile/sys_defs.h135
-rw-r--r--src/gfx.c12
-rw-r--r--src/ginput/dial.h2
-rw-r--r--src/ginput/keyboard.h2
-rw-r--r--src/ginput/mouse.h2
-rw-r--r--src/ginput/toggle.h2
-rw-r--r--src/gos/chibios.c89
-rw-r--r--src/gos/chibios.h42
-rw-r--r--src/gos/linux.h2
-rw-r--r--src/gos/osx.h2
-rw-r--r--src/gos/raw32.h2
-rw-r--r--src/gos/sys_defs.h1
-rw-r--r--src/gos/win32.h2
-rw-r--r--src/gqueue/gqueue.c72
-rw-r--r--src/gqueue/sys_defs.h122
-rw-r--r--src/gqueue/sys_options.h6
-rw-r--r--src/gqueue/sys_rules.h7
-rw-r--r--src/gwin/button.h2
-rw-r--r--src/gwin/checkbox.h2
-rw-r--r--src/gwin/class_gwin.h2
-rw-r--r--src/gwin/console.h2
-rw-r--r--src/gwin/graph.h2
-rw-r--r--src/gwin/gwidget.h2
-rw-r--r--src/gwin/image.h2
-rw-r--r--src/gwin/label.c45
-rw-r--r--src/gwin/label.h34
-rw-r--r--src/gwin/list.c22
-rw-r--r--src/gwin/list.h16
-rw-r--r--src/gwin/progressbar.c56
-rw-r--r--src/gwin/progressbar.h20
-rw-r--r--src/gwin/radio.h2
-rw-r--r--src/gwin/slider.h2
-rw-r--r--src/gwin/sys_defs.h2
-rw-r--r--src/gwin/sys_options.h2
46 files changed, 1033 insertions, 723 deletions
diff --git a/src/gadc/driver.h b/src/gadc/driver.h
index 4427f4f0..e85eed48 100644
--- a/src/gadc/driver.h
+++ b/src/gadc/driver.h
@@ -27,30 +27,35 @@
/**
* @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;
+typedef struct GadcTimerJob_t {
+ uint32_t physdev; // @< The physical device/s. The exact meaning of physdev is hardware dependent.
+ uint32_t frequency; // @< The frequency to sample
+ adcsample_t *buffer; // @< Where to put the samples
+ size_t todo; // @< How many conversions to do
+ size_t done; // @< How many conversions have already been done
+} GadcTimerJob;
/* @} */
/**
- * @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.
+ * @brief The structure passed to do a single conversion
* @{
*/
-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;
+typedef struct GadcNonTimerJob_t {
+ uint32_t physdev; // @< The physical device/s. The exact meaning of physdev is hardware dependent.
+ adcsample_t *buffer; // @< Where to put the samples.
+ } GadcNonTimerJob;
/* @} */
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* @brief These routines are the callbacks that the driver uses.
* @details Defined in the high level GADC code.
@@ -58,40 +63,21 @@ typedef struct GadcLldNonTimerData_t {
* @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 Indicate that some data has been placed into the buffer for the current job
+ *
+ * @param[in] n The number of samples placed in the buffer
+ *
+ * @note Calling this with n = 0 causes the current job to be terminated early or aborted.
+ * It can be called in this mode on an ADC conversion error. Any job will then be
+ * restarted by the high level code as appropriate.
+ */
+ void gadcGotDataI(size_t n);
/**
* @}
*/
/**
- * @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
@@ -99,86 +85,63 @@ 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.
+ * @brief Using the hardware dependant "physdev", return the number of samples for each conversion
*
- * @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 The hardware dependent physical device descriptor
*
- * @param[in] physdev A value passed to describe which physical ADC devices/channels to use.
+ * @return The number of samples per conversion
*
- * @return Number of samples of the convesion
* @api
*/
-size_t gadc_lld_samples_per_conversion(uint32_t physdev);
+size_t gadc_lld_samplesperconversion(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] freq The frequency for the timer
*
- * @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.
+ * @note This will only be called if the timer is currently stopped.
*
* @api
+ * @iclass
*/
-void gadc_lld_start_timer(uint32_t physdev, uint32_t frequency);
+void gadc_lld_start_timerI(uint32_t freq);
/**
* @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.
+ * @note This will only be called if the timer is currently running and all timer jobs
+ * have been completed/aborted.
*
* @api
+ * @iclass
*/
-void gadc_lld_stop_timer(uint32_t physdev);
+void gadc_lld_stop_timerI(void);
/**
- * @brief Start a "timer" conversion.
- * @details Starts a series of conversions triggered by the timer.
+ * @brief Start a set of high frequency conversions.
*
- * @param[in] pgtd Contains the parameters for the timer conversion.
+ * @note This will only be called if the timer is currently running and the ADC should be ready for
+ * a new job.
*
- * @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
+ * @param[in] pjob The job to be started.
*
+ * @api
* @iclass
*/
-void gadc_lld_adc_timerI(GadcLldTimerData *pgtd);
+void gadc_lld_timerjobI(GadcTimerJob *pjob);
/**
- * @brief Start a "non-timer" conversion.
- * @details Starts a single conversion now.
+ * @brief Start a non-timer conversion.
*
- * @param[in] pgntd Contains the parameters for the non-timer conversion.
+ * @note This will only be called if the ADC should be ready for a new job.
*
- * @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
+ * @param[in] pjob The job to be started
*
+ * @api
* @iclass
*/
-void gadc_lld_adc_nontimerI(GadcLldNonTimerData *pgntd);
+void gadc_lld_nontimerjobI(GadcNonTimerJob *pjob);
#ifdef __cplusplus
}
diff --git a/src/gadc/gadc.c b/src/gadc/gadc.c
index 8ae431b0..d307519b 100644
--- a/src/gadc/gadc.c
+++ b/src/gadc/gadc.c
@@ -23,228 +23,151 @@
#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 gfxSem gadcsem;
-static gfxMutex gadcmutex;
-static GTimer LowSpeedGTimer;
+#define GADC_HSADC_GTIMER 0x8000
+#define GADC_ADC_RUNNING 0x4000
+#define GADC_HSADC_CONVERTION 0x2000
+
+typedef struct NonTimerData_t {
+ gfxQueueGSyncItem next;
+ GADCCallbackFunction callback;
+ union {
+ void *param;
+ gfxSem sigdone;
+ };
+ GadcNonTimerJob job;
+ } NonTimerData;
+
+static volatile uint16_t hsFlags;
+static size_t hsBytesPerConv;
+static GadcTimerJob hsJob;
+static GDataBuffer *hsData;
+static gfxQueueGSync hsListDone;
+static GADCISRCallbackFunction hsISRcallback;
#if GFX_USE_GEVENT
- static GTimer HighSpeedGTimer;
+ static GTimer hsGTimer;
#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
-
-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;
- GADCISRCallbackFunction isrfn;
- } 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++;
+static GTimer lsGTimer;
+static gfxQueueGSync lsListToDo;
+static gfxQueueGSync lsListDone;
+static NonTimerData *lsData;
- } else {
+void gadcGotDataI(size_t n) {
+ if ((hsFlags & GADC_HSADC_CONVERTION)) {
- /* Now we have done a high speed conversion - start looking for low speed conversions */
- curlsdev = ls;
- }
+ // A set of timer conversions is done - add them
+ hsJob.done += n;
- /**
- * 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);
+ // Are we finished yet? (or driver signalled complete now)
+ if (n && hsJob.done < hsJob.todo)
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;
+ // Clear event flags we might set
+ hsFlags &= ~(GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL);
- if (curlsdev) {
- /* This interrupt must be in relation to the low speed device */
+ // Is there any data in it
+ if (!hsJob.done) {
- 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);
+ // Oops - no data in this buffer. Just return it to the free-list
+ gfxBufferReleaseI(hsData);
+ goto starttimerjob; // Restart the timer job
}
- #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;
-
+ // Save the buffer on the hsListDone list
+ hsData->len = hsJob.done * hsBytesPerConv;
+ gfxQueueGSyncPutI(&hsListDone, (gfxQueueGSyncItem *)hsData);
+ hsFlags |= GADC_HSADC_GOTBUFFER;
+
+ /* Signal a buffer completion */
+ if (hsISRcallback)
+ hsISRcallback();
+ #if GFX_USE_GEVENT
+ if (hsFlags & GADC_HSADC_GTIMER)
+ gtimerJabI(&hsGTimer);
#endif
- } 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;
- }
-
- /* Our three signalling mechanisms */
- if (hs.isrfn)
- hs.isrfn(buffer, n);
-
- if (hs.bsem)
- gfxSemSignalI(hs.bsem);
+ // Stop if we have been told to
+ if (!(hsFlags & GADC_HSADC_RUNNING)) {
+ gadc_lld_stop_timerI();
- #if GFX_USE_GEVENT
- if (hs.flags & GADC_FLG_GTIMER)
- gtimerJabI(&HighSpeedGTimer);
- #endif
+ // Get the next free buffer
+ } else if (!(hsData = gfxBufferGetI())) {
- /* Adjust what we have left to do */
- hs.lld.count -= n;
- hs.remaining -= n;
+ // Oops - no free buffers. Stall
+ hsFlags &= ~GADC_HSADC_RUNNING;
+ hsFlags |= GADC_HSADC_STALL;
+ gadc_lld_stop_timerI();
- /* Half completion - We have done all we can for now - wait for the next interrupt */
- if (hs.lld.count)
- return;
+ // Prepare the next job
+ } else {
- /* 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;
+ // Return this new job
+ #if GFX_USE_OS_CHIBIOS
+ // ChibiOS api bug - samples must be even
+ hsJob.todo = (hsData->size / hsBytesPerConv) & ~1;
+ #else
+ hsJob.todo = hsData->size / hsBytesPerConv;
+ #endif
+ hsJob.done = 0;
+ hsJob.buffer = (adcsample_t *)(hsData+1);
}
- }
- /**
- * Look for the next thing to do.
- */
- FindNextConversionI();
-}
+ // Start a job preferring a non-timer job
+ if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) {
+ hsFlags &= ~GADC_HSADC_CONVERTION;
+ gadc_lld_nontimerjobI(&lsData->job);
+ } else if ((hsFlags & GADC_HSADC_RUNNING)) {
+ hsFlags |= GADC_HSADC_CONVERTION;
+ gadc_lld_timerjobI(&hsJob);
+ } else
+ hsFlags &= ~GADC_ADC_RUNNING;
-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;
+ } else {
- #endif
+ // Did it fail
+ if (!n) {
+ // Push it back on the head of the queue - it didn't actually get done
+ gfxQueueGSyncPushI(&lsListToDo, (gfxQueueGSyncItem *)lsData);
+ lsData = 0;
+ goto starttimerjob;
+ }
- } else {
- if (hs.flags & GADC_FLG_ISACTIVE)
- /* Mark the error and then try to repeat it */
- hs.flags |= GADC_FLG_ERROR;
+ // A non-timer job completed - signal
+ if (lsData->callback) {
+ // Put it on the completed list and signal the timer to do the call-backs
+ gfxQueueGSyncPutI(&lsListDone, (gfxQueueGSyncItem *)lsData);
+ gtimerJabI(&lsGTimer);
+ } else {
+ // Signal the thread directly
+ gfxSemSignalI(&lsData->sigdone);
+ }
+ lsData = 0;
+
+ // Start a job preferring a timer job
+starttimerjob:
+ if ((hsFlags & GADC_HSADC_RUNNING)) {
+ hsFlags |= GADC_HSADC_CONVERTION;
+ gadc_lld_timerjobI(&hsJob);
+ } else if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) {
+ hsFlags &= ~GADC_HSADC_CONVERTION;
+ gadc_lld_nontimerjobI(&lsData->job);
+ } else
+ hsFlags &= ~GADC_ADC_RUNNING;
}
-
- /* Start the next conversion */
- FindNextConversionI();
}
/* Our module initialiser */
void _gadcInit(void)
{
gadc_lld_init();
- gfxSemInit(&gadcsem, GADC_MAX_LOWSPEED_DEVICES, GADC_MAX_LOWSPEED_DEVICES);
- gfxMutexInit(&gadcmutex);
- gtimerInit(&LowSpeedGTimer);
+
+ gfxQueueGSyncInit(&hsListDone);
#if GFX_USE_GEVENT
- gtimerInit(&HighSpeedGTimer);
+ gtimerInit(&hsGTimer);
#endif
+ gtimerInit(&lsGTimer);
+ gfxQueueGSyncInit(&lsListToDo);
+ gfxQueueGSyncInit(&lsListDone);
}
void _gadcDeinit(void)
@@ -252,26 +175,13 @@ void _gadcDeinit(void)
/* commented stuff is ToDo */
// gadc_lld_deinit();
- gfxSemDestroy(&gadcsem);
- gfxMutexDestroy(&gadcmutex);
- gtimerDeinit(&LowSpeedGTimer);
+ gfxQueueGSyncDeinit(&hsListDone);
#if GFX_USE_GEVENT
- gtimerDeinit(&HighSpeedGTimer);
+ gtimerDeinit(&hsGTimer);
#endif
-}
-
-static inline void StartADC(bool_t onNoHS) {
- gfxSystemLock();
- if (!(gflags & GADC_GFLG_ISACTIVE) || (onNoHS && !curlsdev))
- FindNextConversionI();
- gfxSystemUnlock();
-}
-
-static void BSemSignalCallback(adcsample_t *buffer, void *param) {
- (void) buffer;
-
- /* Signal the BinarySemaphore parameter */
- gfxSemSignal((gfxSem *)param);
+ gtimerDeinit(&lsGTimer);
+ gfxQueueGSyncDeinit(&lsListToDo);
+ gfxQueueGSyncDeinit(&lsListDone);
}
#if GFX_USE_GEVENT
@@ -281,7 +191,7 @@ static void BSemSignalCallback(adcsample_t *buffer, void *param) {
GEventADC *pe;
psl = 0;
- while ((psl = geventGetSourceListener((GSourceHandle)(&HighSpeedGTimer), psl))) {
+ while ((psl = geventGetSourceListener((GSourceHandle)(&hsGTimer), psl))) {
if (!(pe = (GEventADC *)geventGetEventBuffer(psl))) {
// This listener is missing - save this.
psl->srcflags |= GADC_HSADC_LOSTEVENT;
@@ -289,180 +199,162 @@ 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 = (hsFlags & (GADC_HSADC_RUNNING|GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL)) | 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)
- gfxSemSignal(&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)
+void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency)
{
- gadcHighSpeedStop(); /* This does the init for us */
+ if ((hsFlags & GADC_HSADC_RUNNING))
+ gadcHighSpeedStop();
/* 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;
- hs.isrfn = 0;
+ hsJob.physdev = physdev;
+ hsJob.frequency = frequency;
+ hsISRcallback = 0;
+ hsBytesPerConv = gadc_lld_samplesperconversion(physdev) * sizeof(adcsample_t);
}
#if GFX_USE_GEVENT
GSourceHandle gadcHighSpeedGetSource(void) {
- if (!gtimerIsActive(&HighSpeedGTimer))
- gtimerStart(&HighSpeedGTimer, HighSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
- hs.flags |= GADC_FLG_GTIMER;
- return (GSourceHandle)&HighSpeedGTimer;
+ if (!gtimerIsActive(&hsGTimer))
+ gtimerStart(&hsGTimer, HighSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
+ hsFlags |= GADC_HSADC_GTIMER;
+ return (GSourceHandle)&hsGTimer;
}
#endif
void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn) {
- hs.isrfn = isrfn;
+ hsISRcallback = 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(&hsListDone, ms);
+}
+
+GDataBuffer *gadcHighSpeedGetDataI(void) {
+ return (GDataBuffer *)gfxQueueGSyncGetI(&hsListDone);
}
void gadcHighSpeedStart(void) {
- /* If its already going we don't need to do anything */
- if (hs.flags & GADC_FLG_ISACTIVE)
+ // Safety first
+ if (!hsJob.frequency || !hsBytesPerConv)
return;
- gadc_lld_start_timer(hs.lld.physdev, hs.frequency);
- hs.flags = GADC_FLG_ISACTIVE;
- StartADC(FALSE);
+ gfxSystemLock();
+ if (!(hsFlags & GADC_HSADC_RUNNING)) {
+ if (!(hsData = gfxBufferGetI())) {
+ // Oops - no free buffers. Stall
+ hsFlags |= GADC_HSADC_STALL;
+ #if GFX_USE_GEVENT
+ if (hsFlags & GADC_HSADC_GTIMER)
+ gtimerJabI(&hsGTimer);
+ #endif
+
+ // Prepare the next job
+ } else {
+
+ #if GFX_USE_OS_CHIBIOS
+ // ChibiOS api bug - samples must be even
+ hsJob.todo = (hsData->size / hsBytesPerConv) & ~1;
+ #else
+ hsJob.todo = hsData->size / hsBytesPerConv;
+ #endif
+ hsJob.done = 0;
+ hsJob.buffer = (adcsample_t *)(hsData+1);
+ hsFlags |= GADC_HSADC_RUNNING;
+
+ // Start the timer
+ gadc_lld_start_timerI(hsJob.frequency);
+
+ // If nothing is running start the job
+ if (!(hsFlags & GADC_ADC_RUNNING)) {
+ hsFlags |= (GADC_HSADC_CONVERTION|GADC_ADC_RUNNING);
+ gadc_lld_timerjobI(&hsJob);
+ }
+ }
+ }
+ gfxSystemUnlock();
}
void gadcHighSpeedStop(void) {
- 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);
+ // Stop it and wait for completion
+ hsFlags &= ~GADC_HSADC_RUNNING;
+ while ((hsFlags & GADC_HSADC_CONVERTION))
+ gfxYield();
+}
+
+static void LowSpeedGTimerCallback(void *param) {
+ (void) param;
+ NonTimerData *pdata;
+
+ // Look for completed non-timer jobs and call the call-backs for each
+ while ((pdata = (NonTimerData *)gfxQueueGSyncGet(&lsListDone, TIME_IMMEDIATE))) {
+ pdata->callback(pdata->job.buffer, pdata->param);
+ gfxFree(pdata);
}
}
void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) {
- struct lsdev *p;
- gfxSem mysem;
+ NonTimerData ndata;
- /* Start the Low Speed Timer */
- gfxSemInit(&mysem, 1, 1);
- gfxMutexEnter(&gadcmutex);
- if (!gtimerIsActive(&LowSpeedGTimer))
- gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
- gfxMutexExit(&gadcmutex);
-
- while(1) {
- /* Wait for an available slot */
- gfxSemWait(&gadcsem, TIME_INFINITE);
-
- /* Find a slot */
- gfxMutexEnter(&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;
- gfxMutexExit(&gadcmutex);
- StartADC(FALSE);
- gfxSemWait(&mysem, TIME_INFINITE);
- return;
- }
- }
- gfxMutexExit(&gadcmutex);
+ // Prepare the job
+ gfxSemInit(&ndata.sigdone, 0, 1);
+ ndata.job.physdev = physdev;
+ ndata.job.buffer = buffer;
+ ndata.callback = 0;
- /**
- * We should never get here - the count semaphore must be wrong.
- * Decrement it and try again.
- */
+ // Activate it
+ gfxSystemLock();
+ if (!(hsFlags & GADC_ADC_RUNNING)) {
+ // Nothing is running - start the job
+ lsData = &ndata;
+ hsFlags |= GADC_ADC_RUNNING;
+ hsFlags &= ~GADC_HSADC_CONVERTION;
+ gadc_lld_nontimerjobI(&ndata.job);
+ } else {
+ // Just put it on the queue
+ gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)&ndata);
}
+ gfxSystemUnlock();
+
+ // Wait for it to complete
+ gfxSemWait(&ndata.sigdone, TIME_INFINITE);
+ gfxSemDestroy(&ndata.sigdone);
}
bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param) {
- struct lsdev *p;
+ NonTimerData *pdata;
/* Start the Low Speed Timer */
- gfxMutexEnter(&gadcmutex);
- if (!gtimerIsActive(&LowSpeedGTimer))
- gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, 0, 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 */
- gfxSemWait(&gadcsem, TIME_IMMEDIATE);
- p->lld.physdev = physdev;
- p->lld.buffer = buffer;
- p->fn = fn;
- p->param = param;
- p->flags = GADC_FLG_ISACTIVE;
- gfxMutexExit(&gadcmutex);
- StartADC(FALSE);
- return TRUE;
- }
+ if (!gtimerIsActive(&lsGTimer))
+ gtimerStart(&lsGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
+
+ // Prepare the job
+ if (!(pdata = gfxAlloc(sizeof(NonTimerData))))
+ return FALSE;
+ pdata->job.physdev = physdev;
+ pdata->job.buffer = buffer;
+ pdata->callback = fn;
+ pdata->param = param;
+
+ // Activate it
+ gfxSystemLock();
+ if (!(hsFlags & GADC_ADC_RUNNING)) {
+ // Nothing is running - start the job
+ lsData = pdata;
+ hsFlags |= GADC_ADC_RUNNING;
+ hsFlags &= ~GADC_HSADC_CONVERTION;
+ gadc_lld_nontimerjobI(&pdata->job);
+ } else {
+ // Just put it on the queue
+ gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)pdata);
}
- gfxMutexExit(&gadcmutex);
- return FALSE;
+ gfxSystemUnlock();
+ return TRUE;
}
#endif /* GFX_USE_GADC */
diff --git a/src/gadc/sys_defs.h b/src/gadc/sys_defs.h
index f6349dfe..b1d1ba1c 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.
- *
- * @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.
- *
+ * @brief Get a filled buffer from the ADC
+ * @return A GDataBuffer pointer or NULL if the timeout is exceeded
+ *
+ * @param[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.
@@ -221,12 +209,9 @@ void gadcHighSpeedStop(void);
* completion.
* @note The result buffer must be large enough to store one sample per device
* described by the 'physdev' parameter.
- * @note If calling this routine would exceed @p GADC_MAX_LOWSPEED_DEVICES simultaneous low
- * speed devices, the routine will wait for an available slot to complete the
- * conversion.
* @note Specifying more than one device in physdev is possible but discouraged as the
* calculations to ensure the high speed ADC correctness will be incorrect. Symptoms
- * from over-running the high speed ADC include high speed samples being lost.
+ * from over-running the high speed ADC include high speed device stalling or samples being lost.
*
* @api
*/
@@ -234,7 +219,7 @@ void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer);
/**
* @brief Perform a low speed ADC conversion with callback (in a thread context)
- * @details Returns FALSE if there are no free low speed ADC slots. See @p GADC_MAX_LOWSPEED_DEVICES for details.
+ * @details Returns FALSE if internal memory allocation fails
*
* @param[in] physdev A value passed to describe which physical ADC devices/channels to use.
* @param[in] buffer The static buffer to put the ADC samples into.
@@ -249,8 +234,6 @@ void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer);
* completion.
* @note The result buffer must be large enough to store one sample per device
* described by the 'physdev' parameter.
- * @note As this routine uses a low speed ADC, it asserts if you try to run more than @p GADC_MAX_LOWSPEED_DEVICES
- * at the same time.
* @note Specifying more than one device in physdev is possible but discouraged as the
* calculations to ensure the high speed ADC correctness will be incorrect. Symptoms
* from over-running the high speed ADC include high speed samples being lost.
@@ -267,4 +250,3 @@ bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunc
#endif /* _GADC_H */
/** @} */
-
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 */
diff --git a/src/gaudio/driver_play.h b/src/gaudio/driver_play.h
index 72ad4747..343a0fed 100644
--- a/src/gaudio/driver_play.h
+++ b/src/gaudio/driver_play.h
@@ -42,19 +42,19 @@ extern "C" {
* @iclass
* @notapi
*/
-GAudioData *gaudioPlayGetDataBlockI(void);
+GDataBuffer *gaudioPlayGetDataBlockI(void);
/**
* @brief Release a block of audio data to the free list
*
- * @param[in] paud The GAudioData block to be released.
+ * @param[in] paud The GDataBuffer block to be released.
*
* @note Defined in the high level GAUDIO code for use by the GAUDIO play drivers.
*
* @iclass
* @notapi
*/
-void gaudioPlayReleaseDataBlockI(GAudioData *paud);
+void gaudioPlayReleaseDataBlockI(GDataBuffer *paud);
/**
* @brief Signal that all playing has now stopped
@@ -107,7 +107,7 @@ void gaudio_play_lld_stop(void);
* @brief Set the output volume.
* @return TRUE if successful.
*
- * @param[in] 0->255 (0 = muted)
+ * @param[in] vol 0->255 (0 = muted)
*
* @note Some drivers may not support this. They will return FALSE.
* @note For stereo devices, both channels are set to the same volume.
diff --git a/src/gaudio/driver_record.h b/src/gaudio/driver_record.h
index 252cae5c..20136dd7 100644
--- a/src/gaudio/driver_record.h
+++ b/src/gaudio/driver_record.h
@@ -34,19 +34,19 @@
* @iclass
* @notapi
*/
-GAudioData *gaudioRecordGetFreeBlockI(void);
+#define gaudioRecordGetFreeBlockI() gfxBufferGetI()
/**
* @brief Save a block of recorded audio data ready for the application
*
- * @param[in] paud The GAudioData block with data.
+ * @param[in] paud The GDataBuffer block with data.
*
* @note Defined in the high level GAUDIO code for use by the GAUDIO record drivers.
*
* @iclass
* @notapi
*/
-void gaudioRecordSaveDataBlockI(GAudioData *paud);
+void gaudioRecordSaveDataBlockI(GDataBuffer *paud);
/**
* @brief Signal that all recording has now stopped
diff --git a/src/gaudio/gaudio.c b/src/gaudio/gaudio.c
index a83dcd85..ab1f95ee 100644
--- a/src/gaudio/gaudio.c
+++ b/src/gaudio/gaudio.c
@@ -16,8 +16,6 @@
#if GFX_USE_GAUDIO
-static gfxQueueGSync freeList;
-
#if GAUDIO_NEED_PLAY
#include "src/gaudio/driver_play.h"
@@ -51,7 +49,6 @@ static gfxQueueGSync freeList;
void _gaudioInit(void)
{
- gfxQueueGSyncInit(&freeList);
#if GAUDIO_NEED_PLAY
gfxQueueASyncInit(&playList);
#if GFX_USE_GEVENT
@@ -70,48 +67,20 @@ void _gaudioInit(void)
void _gaudioDeinit(void)
{
#if GAUDIO_NEED_PLAY
+ gfxQueueASyncDeinit(&playList);
#if GFX_USE_GEVENT
gtimerDeinit(&playTimer);
#endif
gfxSemDestroy(&playComplete);
#endif
#if GAUDIO_NEED_RECORD
+ gfxQueueGSyncDeinit(&recordList);
#if GFX_USE_GEVENT
gtimerDeinit(&recordTimer);
#endif
#endif
}
-bool_t gaudioAllocBuffers(unsigned num, size_t size) {
- GAudioData *paud;
-
- if (num < 1)
- return FALSE;
-
- // Round up to a multiple of 4 to prevent problems with structure alignment
- size = (size + 3) & ~0x03;
-
- // Allocate the memory
- if (!(paud = gfxAlloc((size+sizeof(GAudioData)) * num)))
- return FALSE;
-
- // Add each of them to our free list
- for(;num--; paud = (GAudioData *)((char *)(paud+1)+size)) {
- paud->size = size;
- gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
- }
-
- return TRUE;
-}
-
-void gaudioReleaseBuffer(GAudioData *paud) {
- gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
-}
-
-GAudioData *gaudioGetBuffer(delaytime_t ms) {
- return (GAudioData *)gfxQueueGSyncGet(&freeList, ms);
-}
-
#if GAUDIO_NEED_PLAY
bool_t gaudioPlayInit(uint16_t channel, uint32_t frequency, ArrayDataFormat format) {
@@ -123,18 +92,18 @@ GAudioData *gaudioGetBuffer(delaytime_t ms) {
return TRUE;
}
- void gaudioPlay(GAudioData *paud) {
+ void gaudioPlay(GDataBuffer *pd) {
if (!(playFlags & PLAYFLG_ISINIT)) {
// Oops - init failed - return it directly to the free-list
- if (paud) {
- gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
+ if (pd) {
+ gfxBufferRelease(pd);
gfxYield(); // Make sure we get no endless cpu hogging loops
}
return;
}
- if (paud)
- gfxQueueASyncPut(&playList, (gfxQueueASyncItem *)paud);
+ if (pd)
+ gfxQueueASyncPut(&playList, (gfxQueueASyncItem *)pd);
playFlags |= PLAYFLG_PLAYING;
gaudio_play_lld_start();
}
@@ -145,12 +114,12 @@ GAudioData *gaudioGetBuffer(delaytime_t ms) {
}
void gaudioPlayStop(void) {
- GAudioData *paud;
+ GDataBuffer *pd;
if (playFlags & PLAYFLG_PLAYING)
gaudio_play_lld_stop();
- while((paud = (GAudioData *)gfxQueueASyncGet(&playList)))
- gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
+ while((pd = (GDataBuffer *)gfxQueueASyncGet(&playList)))
+ gfxBufferRelease(pd);
}
bool_t gaudioPlaySetVolume(uint8_t vol) {
@@ -182,7 +151,7 @@ GAudioData *gaudioGetBuffer(delaytime_t ms) {
psl->srcflags = 0;
if ((playFlags & PLAYFLG_PLAYING))
pe->flags |= GAUDIO_PLAY_PLAYING;
- if (!gfxQueueGSyncIsEmpty(&freeList))
+ if (gfxBufferIsAvailable())
pe->flags |= GAUDIO_PLAY_FREEBLOCK;
geventSendEvent(psl);
}
@@ -200,12 +169,12 @@ GAudioData *gaudioGetBuffer(delaytime_t ms) {
* Routines provided for use by drivers.
*/
- GAudioData *gaudioPlayGetDataBlockI(void) {
- return (GAudioData *)gfxQueueASyncGetI(&playList);
+ GDataBuffer *gaudioPlayGetDataBlockI(void) {
+ return (GDataBuffer *)gfxQueueASyncGetI(&playList);
}
- void gaudioPlayReleaseDataBlockI(GAudioData *paud) {
- gfxQueueGSyncPutI(&freeList, (gfxQueueGSyncItem *)paud);
+ void gaudioPlayReleaseDataBlockI(GDataBuffer *pd) {
+ gfxBufferReleaseI(pd);
#if GFX_USE_GEVENT
if (playFlags & PLAYFLG_USEEVENTS)
gtimerJabI(&playTimer);
@@ -242,17 +211,17 @@ GAudioData *gaudioGetBuffer(delaytime_t ms) {
}
void gaudioRecordStop(void) {
- GAudioData *paud;
+ GDataBuffer *pd;
if ((recordFlags & (RECORDFLG_RECORDING|RECORDFLG_STALLED)) == RECORDFLG_RECORDING)
gaudio_record_lld_stop();
recordFlags &= ~(RECORDFLG_RECORDING|RECORDFLG_STALLED);
- while((paud = (GAudioData *)gfxQueueGSyncGet(&recordList, TIME_IMMEDIATE)))
- gfxQueueGSyncPut(&freeList, (gfxQueueGSyncItem *)paud);
+ while((pd = (GDataBuffer *)gfxQueueGSyncGet(&recordList, TIME_IMMEDIATE)))
+ gfxBufferRelease(pd);
}
- GAudioData *gaudioRecordGetData(delaytime_t ms) {
- return (GAudioData *)gfxQueueGSyncGet(&recordList, ms);
+ GDataBuffer *gaudioRecordGetData(delaytime_t ms) {
+ return (GDataBuffer *)gfxQueueGSyncGet(&recordList, ms);
}
#if GFX_USE_GEVENT
@@ -276,7 +245,7 @@ GAudioData *gaudioGetBuffer(delaytime_t ms) {
if ((recordFlags & RECORDFLG_STALLED))
pe->flags |= GAUDIO_RECORD_STALL;
if (!gfxQueueGSyncIsEmpty(&recordList))
- pe->flags |= GAUDIO_RECORD_GOTBLOCK;
+ pe->flags |= GAUDIO_RECORD_GOTBUFFER;
geventSendEvent(psl);
}
}
@@ -293,11 +262,7 @@ GAudioData *gaudioGetBuffer(delaytime_t ms) {
* Routines provided for use by drivers.
*/
- GAudioData *gaudioRecordGetFreeBlockI(void) {
- return (GAudioData *)gfxQueueGSyncGetI(&freeList);
- }
-
- void gaudioRecordSaveDataBlockI(GAudioData *paud) {
+ void gaudioRecordSaveDataBlockI(GDataBuffer *paud) {
gfxQueueGSyncPutI(&recordList, (gfxQueueGSyncItem *)paud);
#if GFX_USE_GEVENT
if (recordFlags & RECORDFLG_USEEVENTS)
diff --git a/src/gaudio/sys_defs.h b/src/gaudio/sys_defs.h
index a9a951b7..5a43af18 100644
--- a/src/gaudio/sys_defs.h
+++ b/src/gaudio/sys_defs.h
@@ -34,19 +34,6 @@
/* Type definitions */
/*===========================================================================*/
-/**
- * @brief Contains Audio Data Samples
- * @note This structure is followed immediately by the sample data itself.
- * When allocating the buffers for the sample data put this structure
- * at the beginning of the buffer.
- */
-typedef struct GAudioData {
- gfxQueueASyncItem next; // @< Used for queuing the buffers
- size_t size; // @< The size of the buffer area following this structure (in bytes)
- size_t len; // @< The length of the data in the buffer area (in bytes)
-} GAudioData;
-
-
// Event types for GAUDIO
#define GEVENT_AUDIO_PLAY (GEVENT_GAUDIO_FIRST+0)
#define GEVENT_AUDIO_RECORD (GEVENT_GAUDIO_FIRST+1)
@@ -95,7 +82,7 @@ typedef struct GAudioData {
*/
#define GAUDIO_RECORD_LOSTEVENT 0x0001 /**< @brief The last GEVENT_AUDIO_IN event was lost */
#define GAUDIO_RECORD_RECORDING 0x0002 /**< @brief The audio recording system is currently recording */
- #define GAUDIO_RECORD_GOTBLOCK 0x0004 /**< @brief An audio buffer is ready for processing */
+ #define GAUDIO_RECORD_GOTBUFFER 0x0004 /**< @brief An audio buffer is ready for processing */
#define GAUDIO_RECORD_STALL 0x0008 /**< @brief The recording process has stalled due to no free buffers */
/** @} */
} GEventAudioRecord;
@@ -110,41 +97,6 @@ typedef struct GAudioData {
extern "C" {
#endif
-/**
- * @brief Allocate some audio buffers and put them on the free list
- * @return TRUE is it succeeded. FALSE on allocation failure.
- *
- * @param[in] num The number of buffers to allocate
- * @param[in] size The size (in bytes) of each buffer
- *
- * @api
- */
-bool_t gaudioAllocBuffers(unsigned num, size_t size);
-
-/**
- * @brief Get an audio buffer from the free list
- * @return A GAudioData pointer or NULL if the timeout is exceeded
- *
- * @params[in] ms The maximum amount of time in milliseconds to wait for a buffer if one is not available.
- *
- * @api
- */
-GAudioData *gaudioGetBuffer(delaytime_t ms);
-
-/**
- * @brief Release a buffer back to the free list
- *
- * @param[in] paud The buffer to put (back) on the free-list.
- *
- * @note This call should be used to return any buffers that were taken from
- * the free-list once they have been finished with. It can also be used
- * to put new buffers onto the free-list. Just make sure the "size" field
- * of the GAudioData structure has been filled in first.
- *
- * @api
- */
-void gaudioReleaseBuffer(GAudioData *paud);
-
#if GAUDIO_NEED_PLAY || defined(__DOXYGEN__)
/**
* @brief Set the audio device to play on the specified channel and with the specified
@@ -172,7 +124,7 @@ void gaudioReleaseBuffer(GAudioData *paud);
* @param[in] paud The audio sample buffer to play. It can be NULL (used to restart paused audio)
*
* @note Calling this will cancel any pause.
- * @note Before calling this function the len field of the GAudioData structure must be
+ * @note Before calling this function the len field of the GDataBuffer structure must be
* specified (in bytes).
* @note For stereo channels the sample data is interleaved in the buffer.
* @note This call returns before the data has completed playing. Subject to available buffers (which
@@ -182,7 +134,7 @@ void gaudioReleaseBuffer(GAudioData *paud);
*
* @api
*/
- void gaudioPlay(GAudioData *paud);
+ void gaudioPlay(GDataBuffer *paud);
/**
* @brief Pause any currently playing sounds.
@@ -209,7 +161,7 @@ void gaudioReleaseBuffer(GAudioData *paud);
* @brief Set the output volume.
* @return TRUE if successful.
*
- * @param[in] 0->255 (0 = muted)
+ * @param[in] vol 0->255 (0 = muted)
*
* @note Some drivers may not support this. They will return FALSE.
* @note For stereo devices, both channels are set to the same volume.
@@ -241,7 +193,7 @@ void gaudioReleaseBuffer(GAudioData *paud);
* @brief Wait for any currently playing sounds to complete
* @return TRUE if there is now nothing playing or FALSE if the timeout is exceeded
*
- * @params[in] ms The maximum amount of time in milliseconds to wait for playing to complete.
+ * @param[in] ms The maximum amount of time in milliseconds to wait for playing to complete.
*
* @api
*/
@@ -298,16 +250,21 @@ void gaudioReleaseBuffer(GAudioData *paud);
/**
* @brief Get a filled audio buffer from the recording list
- * @return A GAudioData pointer or NULL if the timeout is exceeded
+ * @return A GDataBuffer pointer or NULL if the timeout is exceeded
*
- * @params[in] ms The maximum amount of time in milliseconds to wait for data if some is not currently available.
+ * @param[in] ms The maximum amount of time in milliseconds to wait for data if some is not currently available.
*
* @note After processing the audio data, your application must return the buffer to the free-list so that
* it can be used to record more audio into. This can be done via the play list using @p gaudioPlay() or
- * directly using @p gaudioReleaseBuffer().
+ * directly 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.
+ *
* @api
*/
- GAudioData *gaudioRecordGetData(delaytime_t ms);
+ GDataBuffer *gaudioRecordGetData(delaytime_t ms);
#if GFX_USE_GEVENT || defined(__DOXYGEN__)
/**
diff --git a/src/gaudio/sys_rules.h b/src/gaudio/sys_rules.h
index a3f0dffc..4786fa5f 100644
--- a/src/gaudio/sys_rules.h
+++ b/src/gaudio/sys_rules.h
@@ -27,17 +27,19 @@
#undef GFX_USE_GQUEUE
#define GFX_USE_GQUEUE TRUE
#endif
- #if !GQUEUE_NEED_ASYNC
+ #if GAUDIO_NEED_PLAY && !GQUEUE_NEED_ASYNC
#if GFX_DISPLAY_RULE_WARNINGS
- #warning "GAUDIO: GQUEUE_NEED_ASYNC is required if GFX_USE_GAUDIO is TRUE. It has been turned on for you."
+ #warning "GAUDIO: GQUEUE_NEED_ASYNC is required if GAUDIO_NEED_PLAY is TRUE. It has been turned on for you."
#endif
#undef GQUEUE_NEED_ASYNC
#define GQUEUE_NEED_ASYNC TRUE
#endif
- #if !GQUEUE_NEED_GSYNC
+ #if !GQUEUE_NEED_GSYNC || !GQUEUE_NEED_BUFFERS
#if GFX_DISPLAY_RULE_WARNINGS
- #warning "GAUDIO: GQUEUE_NEED_GSYNC is required if GFX_USE_GAUDIO is TRUE. It has been turned on for you."
+ #warning "GAUDIO: GQUEUE_NEED_BUFFERS and GQUEUE_NEED_GSYNC are required if GFX_USE_GAUDIO is TRUE. They have been turned on for you."
#endif
+ #undef GQUEUE_NEED_BUFFERS
+ #define GQUEUE_NEED_BUFFERS TRUE
#undef GQUEUE_NEED_GSYNC
#define GQUEUE_NEED_GSYNC TRUE
#endif
diff --git a/src/gdisp/colors.h b/src/gdisp/colors.h
index efd7076c..6e9a7663 100644
--- a/src/gdisp/colors.h
+++ b/src/gdisp/colors.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gdisp/colors.h
+ * @file src/gdisp/colors.h
* @brief GDISP color definitions header file.
*
* @defgroup Colors Colors
diff --git a/src/gdisp/image.h b/src/gdisp/image.h
index 607f1007..1bf378f1 100644
--- a/src/gdisp/image.h
+++ b/src/gdisp/image.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gdisp/image.h
+ * @file src/gdisp/image.h
* @brief GDISP image header file.
*
* @defgroup Image Image
diff --git a/src/gfile/inc_romfs.c b/src/gfile/inc_romfs.c
index 49c1b173..3510a261 100644
--- a/src/gfile/inc_romfs.c
+++ b/src/gfile/inc_romfs.c
@@ -56,7 +56,8 @@ static const GFILEVMT FsROMVMT = {
#undef GFILE_CHAINHEAD
#define GFILE_CHAINHEAD &FsROMVMT
-static const ROMFS_DIRENTRY *ROMFindFile(const char *fname) {
+static const ROMFS_DIRENTRY *ROMFindFile(const char *fname)
+{
const ROMFS_DIRENTRY *p;
for(p = FsROMHead; p; p = p->next) {
@@ -65,22 +66,36 @@ static const ROMFS_DIRENTRY *ROMFindFile(const char *fname) {
}
return p;
}
-static bool_t ROMExists(const char *fname) { return ROMFindFile(fname) != 0; }
-static long int ROMFilesize(const char *fname) {
+
+static bool_t ROMExists(const char *fname)
+{
+ return ROMFindFile(fname) != 0;
+}
+
+static long int ROMFilesize(const char *fname)
+{
const ROMFS_DIRENTRY *p;
if (!(p = ROMFindFile(fname))) return -1;
return p->size;
}
-static bool_t ROMOpen(GFILE *f, const char *fname) {
+
+static bool_t ROMOpen(GFILE *f, const char *fname)
+{
const ROMFS_DIRENTRY *p;
if (!(p = ROMFindFile(fname))) return FALSE;
f->obj = (void *)p;
return TRUE;
}
-static void ROMClose(GFILE *f) { (void)f; }
-static int ROMRead(GFILE *f, void *buf, int size) {
+
+static void ROMClose(GFILE *f)
+{
+ (void)f;
+}
+
+static int ROMRead(GFILE *f, void *buf, int size)
+{
const ROMFS_DIRENTRY *p;
p = (const ROMFS_DIRENTRY *)f->obj;
@@ -90,6 +105,18 @@ static int ROMRead(GFILE *f, void *buf, int size) {
memcpy(buf, p->file+f->pos, size);
return size;
}
-static bool_t ROMSetpos(GFILE *f, long int pos) { return pos <= ((const ROMFS_DIRENTRY *)f->obj)->size; }
-static long int ROMGetsize(GFILE *f) { return ((const ROMFS_DIRENTRY *)f->obj)->size; }
-static bool_t ROMEof(GFILE *f) { return f->pos >= ((const ROMFS_DIRENTRY *)f->obj)->size; }
+
+static bool_t ROMSetpos(GFILE *f, long int pos)
+{
+ return pos <= ((const ROMFS_DIRENTRY *)f->obj)->size;
+}
+
+static long int ROMGetsize(GFILE *f)
+{
+ return ((const ROMFS_DIRENTRY *)f->obj)->size;
+}
+
+static bool_t ROMEof(GFILE *f)
+{
+ return f->pos >= ((const ROMFS_DIRENTRY *)f->obj)->size;
+}
diff --git a/src/gfile/sys_defs.h b/src/gfile/sys_defs.h
index 675bc4b1..1e53b439 100644
--- a/src/gfile/sys_defs.h
+++ b/src/gfile/sys_defs.h
@@ -49,17 +49,152 @@ extern GFILE *gfileStdOut;
extern "C" {
#endif
+ /**
+ * @brief Check if file exists
+ *
+ * @param[in] fname The file name
+ *
+ * @return TRUE if file exists, FALSE otherwise
+ *
+ * @api
+ */
bool_t gfileExists(const char *fname);
+
+ /**
+ * @brief Delete file
+ *
+ * @param[in] fname The file name
+ *
+ * @return TRUE on success, FALSE otherwise
+ *
+ * @api
+ */
bool_t gfileDelete(const char *fname);
+
+ /**
+ * @brief Get the size of a file
+ * @note Please use @p gfileGetSize() if the file is not opened
+ *
+ * @param[in] fname The file name
+ *
+ * @return File size on success, -1 on error
+ *
+ * @api
+ */
long int gfileGetFilesize(const char *fname);
+
+ /**
+ * @brief Rename file
+ *
+ * @param[in] oldname The current file name
+ * @param[in] newname The new name of the file
+ *
+ * @return TRUE on success, FALSE otherwise
+ *
+ * @api
+ */
bool_t gfileRename(const char *oldname, const char *newname);
+
+ /**
+ * @brief Open file
+ * @details A file must be opened before it can be accessed
+ * @details ToDo (document possible modes)
+ * @details The resulting GFILE will be used for all functions that access the file.
+ *
+ * @param[in] fname The file name
+ * @param[in] mode The mode
+ *
+ * @return Valid GFILE on success, 0 otherwise
+ *
+ * @api
+ */
GFILE * gfileOpen(const char *fname, const char *mode);
+
+ /**
+ * @brief Close file
+ * @details Closes a file after is has been opened using @p gfileOpen()
+ *
+ * @param[in] f The file
+ *
+ * @api
+ */
void gfileClose(GFILE *f);
+
+ /**
+ * @brief Read from file
+ * @details Reads a given amount of bytes from the file
+ * @details The read/write cursor will not be reset when calling this function
+ *
+ * @param[in] f The file
+ * @param[out] buf The buffer in which to save the content that has been read from the file
+ * @param[in] len Amount of bytes to read
+ *
+ * @return Amount of bytes read
+ *
+ * @api
+ */
size_t gfileRead(GFILE *f, void *buf, size_t len);
+
+ /**
+ * @brief Write to file
+ * @details Write a given amount of bytes to the file
+ * @details The read/write cursor will not be reset when calling this function
+ *
+ * @param[in] f The file
+ * @param[in] buf The buffer which contains the content that will be written to the file
+ * @param[in] len Amount of bytes to write
+ *
+ * @return Amount of bytes written
+ *
+ * @api
+ */
size_t gfileWrite(GFILE *f, const void *buf, size_t len);
+
+ /**
+ * @brief Get the current position of the read/write cursor
+ *
+ * @param[in] f The file
+ *
+ * @return The current position in the file
+ *
+ * @api
+ */
long int gfileGetPos(GFILE *f);
+
+ /**
+ * @brief Set the position of the read/write cursor
+ *
+ * @param[in] f The file
+ * @param[in] pos The position to which the cursor will be set
+ *
+ * @return TRUE on success, FALSE otherwise
+ *
+ * @api
+ */
bool_t gfileSetPos(GFILE *f, long int pos);
+
+ /**
+ * @brief Get the size of file
+ * @note Please use @p gfileGetFilesize() if the file is not opened
+ *
+ * @param[in] f The file
+ *
+ * @return The size of the file
+ *
+ * @api
+ */
long int gfileGetSize(GFILE *f);
+
+ /**
+ * @brief Check for EOF
+ * @details Checks if the cursor is at the end of the file
+ *
+ * @param[in] f The file
+ *
+ * @return TRUE if EOF, FALSE otherwise
+ *
+ * @api
+ */
bool_t gfileEOF(GFILE *f);
#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS
diff --git a/src/gfx.c b/src/gfx.c
index 8e92fc29..b291a736 100644
--- a/src/gfx.c
+++ b/src/gfx.c
@@ -48,6 +48,10 @@ extern void _gosDeinit(void);
extern void _gaudioInit(void);
extern void _gaudioDeinit(void);
#endif
+#if GFX_USE_GQUEUE
+ extern void _gqueueInit(void);
+ extern void _gqueueDeinit(void);
+#endif
#if GFX_USE_GMISC
extern void _gmiscInit(void);
extern void _gmiscDeinit(void);
@@ -63,6 +67,9 @@ void gfxInit(void)
// These must be initialised in the order of their dependancies
_gosInit();
+ #if GFX_USE_GQUEUE
+ _gqueueInit();
+ #endif
#if GFX_USE_GMISC
_gmiscInit();
#endif
@@ -118,7 +125,10 @@ void gfxDeinit(void)
_geventDeinit();
#endif
#if GFX_USE_GMISC
- _gmiscInit();
+ _gmiscDeinit();
+ #endif
+ #if GFX_USE_GQUEUE
+ _gqueueDeinit();
#endif
_gosDeinit();
}
diff --git a/src/ginput/dial.h b/src/ginput/dial.h
index a90b5e46..f2d3fb58 100644
--- a/src/ginput/dial.h
+++ b/src/ginput/dial.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/ginput/dial.h
+ * @file src/ginput/dial.h
* @brief GINPUT GFX User Input subsystem header file.
*
* @defgroup Dial Dial
diff --git a/src/ginput/keyboard.h b/src/ginput/keyboard.h
index d2bebeb8..eff5cc6f 100644
--- a/src/ginput/keyboard.h
+++ b/src/ginput/keyboard.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/ginput/keyboard.h
+ * @file src/ginput/keyboard.h
* @brief GINPUT GFX User Input subsystem header file.
*
* @defgroup Keyboard Keyboard
diff --git a/src/ginput/mouse.h b/src/ginput/mouse.h
index 669eaec6..79ad1f08 100644
--- a/src/ginput/mouse.h
+++ b/src/ginput/mouse.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/ginput/mouse.h
+ * @file src/ginput/mouse.h
* @brief GINPUT GFX User Input subsystem header file for mouse and touch.
*
* @defgroup Mouse Mouse
diff --git a/src/ginput/toggle.h b/src/ginput/toggle.h
index 73cf1e27..40149754 100644
--- a/src/ginput/toggle.h
+++ b/src/ginput/toggle.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/ginput/toggle.h
+ * @file src/ginput/toggle.h
* @brief GINPUT GFX User Input subsystem header file.
*
* @defgroup Toggle Toggle
diff --git a/src/gos/chibios.c b/src/gos/chibios.c
index 7d64fe1c..2254f52f 100644
--- a/src/gos/chibios.c
+++ b/src/gos/chibios.c
@@ -8,6 +8,7 @@
/**
* @file src/gos/chibios.c
* @brief GOS ChibiOS Operating System support.
+ * @details Supports both, ChibiOS/RT 2.x and 3.x
*/
#include "gfx.h"
@@ -15,20 +16,41 @@
#include <string.h>
-#if !CH_USE_MUTEXES
- #error "GOS: CH_USE_MUTEXES must be defined in chconf.h"
-#endif
-#if !CH_USE_SEMAPHORES
- #error "GOS: CH_USE_SEMAPHORES must be defined in chconf.h"
+#if CH_KERNEL_MAJOR == 2
+
+ #if !CH_USE_MUTEXES
+ #error "GOS: CH_USE_MUTEXES must be defined in chconf.h"
+ #endif
+ #if !CH_USE_SEMAPHORES
+ #error "GOS: CH_USE_SEMAPHORES must be defined in chconf.h"
+ #endif
+
+#elif CH_KERNEL_MAJOR == 3
+
+ #if !CH_CFG_USE_MUTEXES
+ #error "GOS: CH_USE_MUTEXES must be defined in chconf.h"
+ #endif
+ #if !CH_CFG_USE_SEMAPHORES
+ #error "GOS: CH_USE_SEMAPHORES must be defined in chconf.h"
+ #endif
+
#endif
void _gosInit(void)
{
/* Don't initialise if the user already has */
+
+ #if CH_KERNEL_MAJOR == 2
if (!chThdSelf()) {
halInit();
chSysInit();
}
+ #elif CH_KERNEL_MAJOR == 3
+ if (!chThdGetSelfX()) {
+ halInit();
+ chSysInit();
+ }
+ #endif
}
void _gosDeinit(void)
@@ -36,7 +58,8 @@ void _gosDeinit(void)
/* ToDo */
}
-void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz) {
+void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz)
+{
void *np;
if (newsz <= oldsz)
@@ -52,7 +75,8 @@ void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz) {
return np;
}
-void gfxSleepMilliseconds(delaytime_t ms) {
+void gfxSleepMilliseconds(delaytime_t ms)
+{
switch(ms) {
case TIME_IMMEDIATE: chThdYield(); return;
case TIME_INFINITE: chThdSleep(TIME_INFINITE); return;
@@ -60,7 +84,8 @@ void gfxSleepMilliseconds(delaytime_t ms) {
}
}
-void gfxSleepMicroseconds(delaytime_t ms) {
+void gfxSleepMicroseconds(delaytime_t ms)
+{
switch(ms) {
case TIME_IMMEDIATE: return;
case TIME_INFINITE: chThdSleep(TIME_INFINITE); return;
@@ -68,35 +93,52 @@ void gfxSleepMicroseconds(delaytime_t ms) {
}
}
-void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) {
+void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit)
+{
if (val > limit)
val = limit;
psem->limit = limit;
- chSemInit(&psem->sem, val);
+
+ #if CH_KERNEL_MAJOR == 2
+ chSemInit(&psem->sem, val);
+ #elif CH_KERNEL_MAJOR == 3
+ chSemObjectInit(&psem->sem, val);
+ #endif
}
-void gfxSemDestroy(gfxSem *psem) {
+void gfxSemDestroy(gfxSem *psem)
+{
chSemReset(&psem->sem, 1);
}
-bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) {
- if (ms == TIME_INFINITE) {
- chSemWait(&psem->sem);
- return TRUE;
- }
-
- return chSemWaitTimeout(&psem->sem, MS2ST(ms)) != RDY_TIMEOUT;
+bool_t gfxSemWait(gfxSem *psem, delaytime_t ms)
+{
+ #if CH_KERNEL_MAJOR == 2
+ switch(ms) {
+ case TIME_IMMEDIATE: return chSemWaitTimeout(&psem->sem, TIME_IMMEDIATE) != RDY_TIMEOUT;
+ case TIME_INFINITE: chSemWait(&psem->sem); return TRUE;
+ default: return chSemWaitTimeout(&psem->sem, MS2ST(ms)) != RDY_TIMEOUT;
+ }
+ #elif CH_KERNEL_MAJOR == 3
+ switch(ms) {
+ case TIME_IMMEDIATE: return chSemWaitTimeout(&psem->sem, TIME_IMMEDIATE) != MSG_TIMEOUT;
+ case TIME_INFINITE: chSemWait(&psem->sem); return TRUE;
+ default: return chSemWaitTimeout(&psem->sem, MS2ST(ms)) != MSG_TIMEOUT;
+ }
+ #endif
}
-bool_t gfxSemWaitI(gfxSem *psem) {
+bool_t gfxSemWaitI(gfxSem *psem)
+{
if (chSemGetCounterI(&psem->sem) <= 0)
return FALSE;
chSemFastWaitI(&psem->sem);
return TRUE;
}
-void gfxSemSignal(gfxSem *psem) {
+void gfxSemSignal(gfxSem *psem)
+{
chSysLock();
if (gfxSemCounterI(psem) < psem->limit)
@@ -106,12 +148,14 @@ void gfxSemSignal(gfxSem *psem) {
chSysUnlock();
}
-void gfxSemSignalI(gfxSem *psem) {
+void gfxSemSignalI(gfxSem *psem)
+{
if (gfxSemCounterI(psem) < psem->limit)
chSemSignalI(&psem->sem);
}
-gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) {
+gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param)
+{
if (!stackarea) {
if (!stacksz) stacksz = 256;
return chThdCreateFromHeap(0, stacksz, prio, fn, param);
@@ -125,4 +169,3 @@ gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_
#endif /* GFX_USE_OS_CHIBIOS */
/** @} */
-
diff --git a/src/gos/chibios.h b/src/gos/chibios.h
index 1db9482e..9f7f5e37 100644
--- a/src/gos/chibios.h
+++ b/src/gos/chibios.h
@@ -6,8 +6,9 @@
*/
/**
- * @file include/gos/chibios.h
+ * @file src/gos/chibios.h
* @brief GOS - Operating System Support header file for ChibiOS.
+ * @details Supports both, ChibiOS/RT 2.x and 3.x
*/
#ifndef _GOS_CHIBIOS_H
@@ -47,13 +48,27 @@ typedef tprio_t threadpriority_t;
#define DECLARE_THREAD_STACK(name, sz) WORKING_AREA(name, sz)
#define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param)
-typedef struct {
- Semaphore sem;
- semcount_t limit;
+#if CH_KERNEL_MAJOR == 2
+ typedef struct {
+ Semaphore sem;
+ semcount_t limit;
} gfxSem;
-typedef Mutex gfxMutex;
-typedef Thread * gfxThreadHandle;
+ typedef Mutex gfxMutex;
+ typedef Thread* gfxThreadHandle;
+#elif CH_KERNEL_MAJOR == 3
+ #undef DECLARE_THREAD_STACK
+ #define DECLARE_THREAD_STACK(a, b) THD_WORKING_AREA(a, b)
+
+ typedef struct {
+ semaphore_t sem;
+ semcount_t limit;
+ } gfxSem;
+
+ typedef mutex_t gfxMutex;
+ typedef thread_t* gfxThreadHandle;
+#endif
+
/*===========================================================================*/
/* Function declarations. */
@@ -63,19 +78,27 @@ typedef Thread * gfxThreadHandle;
extern "C" {
#endif
+// First the kernel version specific ones
+#if CH_KERNEL_MAJOR == 2
+ #define gfxSystemTicks() chTimeNow()
+ #define gfxMutexInit(pmutex) chMtxInit(pmutex)
+ #define gfxMutexExit(pmutex) chMtxUnlock()
+#elif CH_KERNEL_MAJOR == 3
+ #define gfxSystemTicks() chVTGetSystemTimeX()
+ #define gfxMutexInit(pmutex) chMtxObjectInit(pmutex)
+ #define gfxMutexExit(pmutex) chMtxUnlock(pmutex)
+#endif
+
#define gfxHalt(msg) { chDbgPanic(msg); chSysHalt(); }
#define gfxExit() chSysHalt()
#define gfxAlloc(sz) chHeapAlloc(0, sz)
#define gfxFree(ptr) chHeapFree(ptr)
#define gfxYield() chThdYield()
-#define gfxSystemTicks() chTimeNow()
#define gfxMillisecondsToTicks(ms) MS2ST(ms)
#define gfxSystemLock() chSysLock()
#define gfxSystemUnlock() chSysUnlock()
-#define gfxMutexInit(pmutex) chMtxInit(pmutex)
#define gfxMutexDestroy(pmutex) (void)pmutex
#define gfxMutexEnter(pmutex) chMtxLock(pmutex)
-#define gfxMutexExit(pmutex) chMtxUnlock()
void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz);
void gfxSleepMilliseconds(delaytime_t ms);
void gfxSleepMicroseconds(delaytime_t ms);
@@ -98,4 +121,3 @@ gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_
#endif /* GFX_USE_OS_CHIBIOS */
#endif /* _GOS_CHIBIOS_H */
-
diff --git a/src/gos/linux.h b/src/gos/linux.h
index f92fc4e9..9dd054da 100644
--- a/src/gos/linux.h
+++ b/src/gos/linux.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gos/linux.h
+ * @file src/gos/linux.h
* @brief GOS - Operating System Support header file for LINUX.
*/
diff --git a/src/gos/osx.h b/src/gos/osx.h
index 80b07eec..f01f4424 100644
--- a/src/gos/osx.h
+++ b/src/gos/osx.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gos/osx.h
+ * @file src/gos/osx.h
* @brief GOS - Operating System Support header file for Mac OS-X.
*/
diff --git a/src/gos/raw32.h b/src/gos/raw32.h
index d4e8e548..9a4beb7f 100644
--- a/src/gos/raw32.h
+++ b/src/gos/raw32.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gos/raw32.h
+ * @file src/gos/raw32.h
* @brief GOS - Operating System Support header file for any 32 bit processor in bare-metal mode
*/
diff --git a/src/gos/sys_defs.h b/src/gos/sys_defs.h
index 4014e791..aba96df5 100644
--- a/src/gos/sys_defs.h
+++ b/src/gos/sys_defs.h
@@ -333,7 +333,6 @@
* @return FALSE if the wait would occur occurred otherwise TRUE
*
* @param[in] psem A pointer to the semaphore
- * @param[in] ms The maximum time to wait for the semaphore
*
* @iclass
* @api
diff --git a/src/gos/win32.h b/src/gos/win32.h
index 8a5d9025..4a198200 100644
--- a/src/gos/win32.h
+++ b/src/gos/win32.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gos/win32.h
+ * @file src/gos/win32.h
* @brief GOS - Operating System Support header file for WIN32.
*/
diff --git a/src/gqueue/gqueue.c b/src/gqueue/gqueue.c
index b7ecb032..8540bcea 100644
--- a/src/gqueue/gqueue.c
+++ b/src/gqueue/gqueue.c
@@ -14,6 +14,21 @@
#if GFX_USE_GQUEUE
+#if GQUEUE_NEED_BUFFERS
+ static gfxQueueGSync bufferFreeList;
+#endif
+
+void _gqueueInit(void)
+{
+ #if GQUEUE_NEED_BUFFERS
+ gfxQueueGSyncInit(&bufferFreeList);
+ #endif
+}
+
+void _gqueueDeinit(void)
+{
+}
+
#if GQUEUE_NEED_ASYNC
void gfxQueueASyncInit(gfxQueueASync *pqueue) {
pqueue->head = pqueue->tail = 0;
@@ -49,6 +64,7 @@
gfxSystemUnlock();
}
void gfxQueueASyncPutI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) {
+ if (!pitem) return; // Safety
pitem->next = 0;
if (!pqueue->head) {
pqueue->head = pqueue->tail = pitem;
@@ -64,6 +80,7 @@
gfxSystemUnlock();
}
void gfxQueueASyncPushI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) {
+ if (!pitem) return; // Safety
pitem->next = pqueue->head;
pqueue->head = pitem;
if (!pitem->next)
@@ -78,8 +95,7 @@
void gfxQueueASyncRemoveI(gfxQueueASync *pqueue, gfxQueueASyncItem *pitem) {
gfxQueueASyncItem *pi;
- if (!pitem)
- return;
+ if (!pitem) return; // Safety
if (pqueue->head) {
if (pqueue->head == pitem) {
pqueue->head = pitem->next;
@@ -123,6 +139,10 @@
pqueue->head = pqueue->tail = 0;
gfxSemInit(&pqueue->sem, 0, MAX_SEMAPHORE_COUNT);
}
+ void gfxQueueGSyncDeinit(gfxQueueGSync *pqueue) {
+ pqueue->head = pqueue->tail = 0;
+ gfxSemDestroy(&pqueue->sem);
+ }
gfxQueueGSyncItem *gfxQueueGSyncGet(gfxQueueGSync *pqueue, delaytime_t ms) {
gfxQueueGSyncItem *pi;
@@ -156,6 +176,7 @@
gfxSystemUnlock();
}
void gfxQueueGSyncPutI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) {
+ if (!pitem) return; // Safety
pitem->next = 0;
if (!pqueue->head) {
pqueue->head = pqueue->tail = pitem;
@@ -172,6 +193,7 @@
gfxSystemUnlock();
}
void gfxQueueGSyncPushI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) {
+ if (!pitem) return; // Safety
pitem->next = pqueue->head;
pqueue->head = pitem;
if (!pitem->next)
@@ -187,8 +209,7 @@
void gfxQueueGSyncRemoveI(gfxQueueGSync *pqueue, gfxQueueGSyncItem *pitem) {
gfxQueueGSyncItem *pi;
- if (!pitem)
- return;
+ if (!pitem) return; // Safety
if (pqueue->head) {
if (pqueue->head == pitem) {
pqueue->head = pitem->next;
@@ -232,6 +253,11 @@
pqueue->head = pqueue->tail = 0;
gfxSemInit(&pqueue->sem, 0, MAX_SEMAPHORE_COUNT);
}
+ void gfxQueueFSyncDeinit(gfxQueueGSync *pqueue) {
+ while(gfxQueueFSyncGet(pqueue, TIME_IMMEDIATE));
+ pqueue->head = pqueue->tail = 0;
+ gfxSemDestroy(&pqueue->sem);
+ }
gfxQueueFSyncItem *gfxQueueFSyncGet(gfxQueueFSync *pqueue, delaytime_t ms) {
gfxQueueFSyncItem *pi;
@@ -252,6 +278,7 @@
}
bool_t gfxQueueFSyncPut(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms) {
+ if (!pitem) return; // Safety
gfxSemInit(&pitem->sem, 0, 1);
pitem->next = 0;
@@ -270,6 +297,7 @@
}
bool_t gfxQueueFSyncPush(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem, delaytime_t ms) {
+ if (!pitem) return; // Safety
gfxSemInit(&pitem->sem, 0, 1);
gfxSystemLock();
@@ -287,9 +315,7 @@
void gfxQueueFSyncRemove(gfxQueueFSync *pqueue, gfxQueueFSyncItem *pitem) {
gfxQueueFSyncItem *pi;
- if (!pitem)
- return;
-
+ if (!pitem) return; // Safety
gfxSystemLock();
if (pqueue->head) {
if (pqueue->head == pitem) {
@@ -333,4 +359,36 @@
}
#endif
+#if GQUEUE_NEED_BUFFERS
+ bool_t gfxBufferAlloc(unsigned num, size_t size) {
+ GDataBuffer *pd;
+
+ if (num < 1)
+ return FALSE;
+
+ // Round up to a multiple of 4 to prevent problems with structure alignment
+ size = (size + 3) & ~0x03;
+
+ // Allocate the memory
+ if (!(pd = gfxAlloc((size+sizeof(GDataBuffer)) * num)))
+ return FALSE;
+
+ // Add each of them to our free list
+ for(;num--; pd = (GDataBuffer *)((char *)(pd+1)+size)) {
+ pd->size = size;
+ gfxBufferRelease(pd);
+ }
+
+ return TRUE;
+ }
+
+ void gfxBufferRelease(GDataBuffer *pd) { gfxQueueGSyncPut(&bufferFreeList, (gfxQueueGSyncItem *)pd); }
+ void gfxBufferReleaseI(GDataBuffer *pd) { gfxQueueGSyncPutI(&bufferFreeList, (gfxQueueGSyncItem *)pd); }
+ GDataBuffer *gfxBufferGet(delaytime_t ms) { return (GDataBuffer *)gfxQueueGSyncGet(&bufferFreeList, ms); }
+ GDataBuffer *gfxBufferGetI(void) { return (GDataBuffer *)gfxQueueGSyncGetI(&bufferFreeList); }
+ bool_t gfxBufferIsAvailable(void) { return bufferFreeList.head != 0; }
+
+#endif
+
+
#endif /* GFX_USE_GQUEUE */
diff --git a/src/gqueue/sys_defs.h b/src/gqueue/sys_defs.h
index 4351d4ad..b97f2f4d 100644
--- a/src/gqueue/sys_defs.h
+++ b/src/gqueue/sys_defs.h
@@ -23,6 +23,8 @@
* operations because fully synchronous queues have the highest storage requirements. The other queue types are
* optimizations. Efficiency IS important to use (particularly RAM efficiency).
* In practice we only implement ASync, GSync and FSync queues as PSync queues are of dubious value.
+ * <br>
+ * We also define GDataBuffer which is a data buffer that supports being queued.
* @{
*/
@@ -32,45 +34,52 @@
#if GFX_USE_GQUEUE || defined(__DOXYGEN__)
/**
+ * @brief A queue item
+ * @{
+ */
+typedef struct gfxQueueASyncItem {
+ struct gfxQueueASyncItem *next;
+} gfxQueueASyncItem, gfxQueueGSyncItem;
+
+typedef struct gfxQueueFSyncItem {
+ struct gfxQueueFSyncItem *next;
+ gfxSem sem;
+} gfxQueueFSyncItem;
+/* @} */
+
+/**
* @brief A queue
* @{
*/
typedef struct gfxQueueASync {
- struct gfxQueueASyncItem *head;
- struct gfxQueueASyncItem *tail;
+ gfxQueueASyncItem *head;
+ gfxQueueASyncItem *tail;
} gfxQueueASync;
typedef struct gfxQueueGSync {
- struct gfxQueueGSyncItem *head;
- struct gfxQueueGSyncItem *tail;
- gfxSem sem;
+ gfxQueueGSyncItem *head;
+ gfxQueueGSyncItem *tail;
+ gfxSem sem;
} gfxQueueGSync;
typedef struct gfxQueueFSync {
- struct gfxQueueFSyncItem *head;
- struct gfxQueueFSyncItem *tail;
- gfxSem sem;
+ gfxQueueFSyncItem *head;
+ gfxQueueFSyncItem *tail;
+ gfxSem sem;
} gfxQueueFSync;
/* @} */
/**
- * @brief A queue item
- * @{
+ * @brief A Data Buffer Queue
+ * @note This structure is followed immediately by the data itself.
+ * When allocating the buffers for the data put this structure
+ * at the beginning of the buffer.
*/
-typedef struct gfxQueueASyncItem {
- struct gfxQueueASyncItem *next;
-} gfxQueueASyncItem;
-
-typedef struct gfxQueueGSyncItem {
- struct gfxQueueGSyncItem *next;
-} gfxQueueGSyncItem;
-
-typedef struct gfxQueueFSyncItem {
- struct gfxQueueFSyncItem *next;
- gfxSem sem;
-} gfxQueueFSyncItem;
-/* @} */
-
+typedef struct GDataBuffer {
+ gfxQueueGSyncItem next; // @< Used for queueing the buffers
+ size_t size; // @< The size of the buffer area following this structure (in bytes)
+ size_t len; // @< The length of the data in the buffer area (in bytes)
+} GDataBuffer;
/*===========================================================================*/
/* Function declarations. */
@@ -98,6 +107,19 @@ void gfxQueueFSyncInit(gfxQueueFSync *pqueue);
/* @} */
/**
+ * @brief De-Initialise a queue.
+ *
+ * @param[in] pqueue A pointer to the queue
+ *
+ * @api
+ * @{
+ */
+#define gfxQueueASyncDeinit(pqueue)
+void gfxQueueGSyncDeinit(gfxQueueGSync *pqueue);
+void gfxQueueFSyncDeinit(gfxQueueFSync *pqueue);
+/* @} */
+
+/**
* @brief Get an item from the head of the queue (and remove it from the queue).
* @return NULL if the timeout expires before an item is available
*
@@ -286,6 +308,58 @@ bool_t gfxQueueFSyncIsInI(gfxQueueFSync *pqueue, const gfxQueueFSyncItem *pitem)
#define gfxQueueFSyncNextI(pitem) ((const gfxQueueFSyncItem *)((pitem)->next))
/* @} */
+/**
+ * @brief Allocate some buffers and put them on the free list
+ * @return TRUE is it succeeded. FALSE on allocation failure.
+ *
+ * @param[in] num The number of buffers to allocate
+ * @param[in] size The size (in bytes) of each buffer
+ *
+ * @api
+ */
+bool_t gfxBufferAlloc(unsigned num, size_t size);
+
+/**
+ * @brief Is there one or more buffers currently available on the free list
+ * @return TRUE if there are buffers in the free list
+ *
+ * @api
+ * @{
+ */
+bool_t gfxBufferIsAvailable(void);
+/* @} */
+
+/**
+ * @brief Get a buffer from the free list
+ * @return A GDataBuffer pointer or NULL if the timeout is exceeded
+ *
+ * @param[in] ms The maximum amount of time in milliseconds to wait for a buffer if one is not available.
+ *
+ * @api
+ * @{
+ */
+GDataBuffer *gfxBufferGet(delaytime_t ms);
+GDataBuffer *gfxBufferGetI(void);
+/* @} */
+
+/**
+ * @brief Release a buffer back to the free list
+ *
+ * @param[in] pd The buffer to put (back) on the free-list.
+ *
+ * @note This call should be used to return any buffers that were taken from
+ * the free-list once they have been finished with. It can also be used
+ * to put new buffers onto the free-list. Just make sure the "size" field
+ * of the GDataBuffer structure has been filled in first.
+ *
+ * @api
+ * @{
+ */
+void gfxBufferRelease(GDataBuffer *pd);
+void gfxBufferReleaseI(GDataBuffer *pd);
+/* @} */
+
+
#ifdef __cplusplus
}
#endif
diff --git a/src/gqueue/sys_options.h b/src/gqueue/sys_options.h
index 7c8627ce..169cf116 100644
--- a/src/gqueue/sys_options.h
+++ b/src/gqueue/sys_options.h
@@ -41,6 +41,12 @@
#ifndef GQUEUE_NEED_FSYNC
#define GQUEUE_NEED_FSYNC FALSE
#endif
+ /**
+ * @brief Enable Queue-able Data Buffers
+ */
+ #ifndef GQUEUE_NEED_BUFFERS
+ #define GQUEUE_NEED_BUFFERS FALSE
+ #endif
/**
* @}
*
diff --git a/src/gqueue/sys_rules.h b/src/gqueue/sys_rules.h
index baeb073d..831952d8 100644
--- a/src/gqueue/sys_rules.h
+++ b/src/gqueue/sys_rules.h
@@ -17,6 +17,13 @@
#define _GQUEUE_RULES_H
#if GFX_USE_GQUEUE
+ #if GQUEUE_NEED_BUFFERS && !GQUEUE_NEED_GSYNC
+ #if GFX_DISPLAY_RULE_WARNINGS
+ #warning "GQUEUE: GQUEUE_NEED_GSYNC is required if GQUEUE_NEED_BUFFERS is TRUE. It has been turned on for you."
+ #endif
+ #undef GQUEUE_NEED_GSYNC
+ #define GQUEUE_NEED_GSYNC TRUE
+ #endif
#endif
#endif /* _GQUEUE_RULES_H */
diff --git a/src/gwin/button.h b/src/gwin/button.h
index d11764d6..dad0cc91 100644
--- a/src/gwin/button.h
+++ b/src/gwin/button.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/button.h
+ * @file src/gwin/button.h
* @brief GWIN Graphic window subsystem header file.
*
* @defgroup Button Button
diff --git a/src/gwin/checkbox.h b/src/gwin/checkbox.h
index 946f7e4a..529fc757 100644
--- a/src/gwin/checkbox.h
+++ b/src/gwin/checkbox.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/checkbox.h
+ * @file src/gwin/checkbox.h
* @brief GWIN Graphic window subsystem header file.
*
* @defgroup Checkbox Checkbox
diff --git a/src/gwin/class_gwin.h b/src/gwin/class_gwin.h
index ae5ac756..49fc6084 100644
--- a/src/gwin/class_gwin.h
+++ b/src/gwin/class_gwin.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/class_gwin.h
+ * @file src/gwin/class_gwin.h
* @brief GWIN Graphic window subsystem header file.
*
* @defgroup Internal Internal
diff --git a/src/gwin/console.h b/src/gwin/console.h
index 14bc7eb3..3fca6aa4 100644
--- a/src/gwin/console.h
+++ b/src/gwin/console.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/console.h
+ * @file src/gwin/console.h
* @brief GWIN Graphic window subsystem header file.
*
* @defgroup Console Console
diff --git a/src/gwin/graph.h b/src/gwin/graph.h
index 65a64126..69dbb617 100644
--- a/src/gwin/graph.h
+++ b/src/gwin/graph.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/graph.h
+ * @file src/gwin/graph.h
* @brief GWIN GRAPH module header file.
*
* @defgroup Graph Graph
diff --git a/src/gwin/gwidget.h b/src/gwin/gwidget.h
index 4eaf6c81..96832fe3 100644
--- a/src/gwin/gwidget.h
+++ b/src/gwin/gwidget.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/gwidget.h
+ * @file src/gwin/gwidget.h
* @brief GWIN Widgets header file.
*/
diff --git a/src/gwin/image.h b/src/gwin/image.h
index 66aba3d1..fdaabc92 100644
--- a/src/gwin/image.h
+++ b/src/gwin/image.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/image.h
+ * @file src/gwin/image.h
* @brief GWIN image widget header file.
*
* @defgroup Image Image
diff --git a/src/gwin/label.c b/src/gwin/label.c
index 97588a27..574dc8b7 100644
--- a/src/gwin/label.c
+++ b/src/gwin/label.c
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/label.h
+ * @file src/gwin/label.c
* @brief GWIN label widget header file.
*
* @defgroup Label Label
@@ -23,6 +23,7 @@
// macros to assist in data type conversions
#define gh2obj ((GLabelObject *)gh)
+#define gw2obj ((GLabelObject *)gw)
// flags for the GLabelObject
#define GLABEL_FLG_WAUTO (GWIN_FIRST_CONTROL_FLAG << 0)
@@ -57,10 +58,26 @@ static void gwinLabelDefaultDraw(GWidgetObject *gw, void *param) {
return;
}
- // render the text
- gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->text, gw->g.font,
- (gw->g.flags & GWIN_FLG_ENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text, gw->pstyle->background,
- justifyLeft);
+ #if GWIN_LABEL_ATTRIBUTE
+ if (gw2obj->attr != 0) {
+ gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw2obj->attr, gw->g.font,
+ (gw->g.flags & GWIN_FLG_ENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text, gw->pstyle->background,
+ justifyLeft);
+
+ gdispGFillStringBox(gw->g.display, gw->g.x + gw2obj->tab, gw->g.y, gw->g.width, gw->g.height, gw->text, gw->g.font,
+ (gw->g.flags & GWIN_FLG_ENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text, gw->pstyle->background,
+ justifyLeft);
+ } else {
+ gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->text, gw->g.font,
+ (gw->g.flags & GWIN_FLG_ENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text, gw->pstyle->background,
+ justifyLeft);
+
+ }
+ #else
+ gdispGFillStringBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->text, gw->g.font,
+ (gw->g.flags & GWIN_FLG_ENABLED) ? gw->pstyle->enabled.text : gw->pstyle->disabled.text, gw->pstyle->background,
+ justifyLeft);
+ #endif
// render the border (if any)
if (gw->g.flags & GLABEL_FLG_BORDER)
@@ -124,6 +141,11 @@ GHandle gwinGLabelCreate(GDisplay *g, GLabelObject *widget, GWidgetInit *pInit)
// no borders by default
flags &=~ GLABEL_FLG_BORDER;
+ #if GWIN_LABEL_ATTRIBUTE
+ widget->tab = 0;
+ widget->attr = 0;
+ #endif
+
widget->w.g.flags |= flags;
gwinSetVisible(&widget->w.g, pInit->g.show);
@@ -141,6 +163,19 @@ void gwinLabelSetBorder(GHandle gh, bool_t border) {
gh2obj->w.g.flags &=~ GLABEL_FLG_BORDER;
}
+#if GWIN_LABEL_ATTRIBUTE
+ void gwinLabelSetAttribute(GHandle gh, coord_t tab, const char* attr) {
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&labelVMT)
+ return;
+
+ gh2obj->tab = tab;
+ gh2obj->attr = attr;
+
+ gwinRedraw(gh);
+ }
+#endif // GWIN_LABEL_ATTRIBUTE
+
#endif // GFX_USE_GWIN && GFX_NEED_LABEL
/** @} */
diff --git a/src/gwin/label.h b/src/gwin/label.h
index 3fe0f3d7..b7218193 100644
--- a/src/gwin/label.h
+++ b/src/gwin/label.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/label.h
+ * @file src/gwin/label.h
* @brief GWIN label widget header file.
*
* @defgroup Label Label
@@ -32,6 +32,11 @@
// An label window
typedef struct GLabelObject {
GWidgetObject w;
+
+ #if GWIN_LABEL_ATTRIBUTE
+ coord_t tab;
+ const char* attr;
+ #endif
} GLabelObject;
#ifdef __cplusplus
@@ -63,6 +68,33 @@ GHandle gwinGLabelCreate(GDisplay *g, GLabelObject *widget, GWidgetInit *pInit);
*/
void gwinLabelSetBorder(GHandle gh, bool_t border);
+#if GWIN_LABEL_ATTRIBUTE || defined(__DOXYGEN__)
+ /**
+ * @brief Add an text attribute in front of the normal label text
+ * @details Often you want to display a text like this:
+ * Current IP: 192.168.1.42
+ * In that case, the actual IP will be variable, the text in front of it
+ * always remains the same. The static text is called the attribute and can be
+ * set using this function.
+ * Furthermore, the tab can be set in order to vertically align multiple labels.
+ * Please check out the website for further explanation, illustraions and usage
+ * examples.
+ *
+ * @note The attribute text is not copied into private memory and so it
+ * must be a constant string, not one allocated in a stack buffer.
+ * @note Use of this construct is discouraged. The appropriate way is to
+ * create two labels - one for the static text and one for the
+ * dynamic text.
+ *
+ * @param[in] gh The widget handle (must be a label handle)
+ * @param[in] tab The distance of the label text from the left widget edge
+ * @param[in] attr The attribute to be displayed
+ *
+ * @api
+ */
+ void gwinLabelSetAttribute(GHandle gh, coord_t tab, const char* attr);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/gwin/list.c b/src/gwin/list.c
index 5b49811c..50c669f0 100644
--- a/src/gwin/list.c
+++ b/src/gwin/list.c
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/list.h
+ * @file src/gwin/list.c
* @brief GWIN list widget header file.
*
* @defgroup List List
@@ -41,6 +41,7 @@
#define GLIST_FLG_HASIMAGES (GWIN_FIRST_CONTROL_FLAG << 1)
#define GLIST_FLG_SCROLLALWAYS (GWIN_FIRST_CONTROL_FLAG << 2)
#define GLIST_FLG_SCROLLSMOOTH (GWIN_FIRST_CONTROL_FLAG << 3)
+#define GLIST_FLG_ENABLERENDER (GWIN_FIRST_CONTROL_FLAG << 4)
// Flags on a ListItem.
#define GLIST_FLG_SELECTED 0x0001
@@ -92,6 +93,11 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
coord_t sy;
#endif
+ // dont render if render has been disabled
+ if (!(gw->g.flags & GLIST_FLG_ENABLERENDER)) {
+ return;
+ }
+
ps = (gw->g.flags & GWIN_FLG_ENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled;
iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING;
x = 1;
@@ -401,12 +407,26 @@ GHandle gwinGListCreate(GDisplay *g, GListObject* gobj, GWidgetInit* pInit, bool
if (multiselect)
gobj->w.g.flags |= GLIST_FLG_MULTISELECT;
gobj->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
+ gobj->w.g.flags |= GLIST_FLG_ENABLERENDER;
gwinSetVisible(&gobj->w.g, pInit->g.show);
return (GHandle)gobj;
}
+void gwinListEnableRender(GHandle gh, bool_t ena) {
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return;
+
+ if (ena) {
+ gh->flags |= GLIST_FLG_ENABLERENDER;
+ gwinRedraw(gh);
+ } else {
+ gh->flags &=~ GLIST_FLG_ENABLERENDER;
+ }
+}
+
void gwinListSetScroll(GHandle gh, scroll_t flag) {
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
diff --git a/src/gwin/list.h b/src/gwin/list.h
index cfe6aeb2..2cc525a2 100644
--- a/src/gwin/list.h
+++ b/src/gwin/list.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/list.h
+ * @file src/gwin/list.h
* @brief GWIN list widget header file
*
* @defgroup List List
@@ -103,6 +103,20 @@ GHandle gwinGListCreate(GDisplay *g, GListObject *widget, GWidgetInit *pInit, bo
#define gwinListCreate(w, pInit, m) gwinGListCreate(GDISP, w, pInit, m)
/**
+ * @brief Enable or disable the rendering of the list
+ *
+ * @details Usually the list is being re-rendered when an item is added to the list. This can cause
+ * flickering and performance issues when many items are added at once. This can be prevented
+ * by temporarely disabling the render using this function.
+ *
+ * @param[in] gh The widget handle (must be a list handle)
+ * @param[in] ena TRUE or FALSE
+ *
+ * @api
+ */
+void gwinListEnableRender(GHandle gh, bool_t ena);
+
+/**
* @brief Change the behaviour of the scroll bar
*
* @note Current possible values: @p scrollAlways, @p scrollAuto and @p scrollSmooth
diff --git a/src/gwin/progressbar.c b/src/gwin/progressbar.c
index 37bad3c8..7c34607f 100644
--- a/src/gwin/progressbar.c
+++ b/src/gwin/progressbar.c
@@ -29,12 +29,21 @@ static void ResetDisplayPos(GProgressbarObject *gsw) {
gsw->dpos = ((gsw->w.g.width-1)*(gsw->pos-gsw->min))/(gsw->max-gsw->min);
}
+// We have to deinitialize the timer which auto updates the progressbar if any
+static void _destroy(GHandle gh) {
+ #if GFX_USE_GTIMER
+ gtimerDeinit( &((GProgressbarObject *)gh)->gt );
+ #endif
+
+ _gwidgetDestroy(gh);
+}
+
// The progressbar VMT table
static const gwidgetVMT progressbarVMT = {
{
"Progressbar", // The classname
sizeof(GProgressbarObject), // The object size
- _gwidgetDestroy, // The destroy routine
+ _destroy, // The destroy routine
_gwidgetRedraw, // The redraw routine
0, // The after-clear routine
},
@@ -197,6 +206,23 @@ void gwinProgressbarStart(GHandle gh, delaytime_t delay) {
gtimerInit(&(gsw->gt));
gtimerStart(&(gsw->gt), _progressbarCallback, gh, FALSE, gsw->delay);
+ // if this is not made, the progressbar will not start when the it's already visible
+ if (gsw->w.g.flags & GWIN_FLG_VISIBLE) {
+ gwinSetVisible(gh, FALSE);
+ gwinSetVisible(gh, TRUE);
+ }
+
+ #undef gsw
+}
+
+void gwinProgressbarStop(GHandle gh) {
+ #define gsw ((GProgressbarObject *)gh)
+
+ if (gh->vmt != (gwinVMT *)&progressbarVMT)
+ return;
+
+ gtimerStop(&(gsw->gt));
+
#undef gsw
}
@@ -206,33 +232,43 @@ void gwinProgressbarStart(GHandle gh, delaytime_t delay) {
void gwinProgressbarDraw_Std(GWidgetObject *gw, void *param) {
#define gsw ((GProgressbarObject *)gw)
+
const GColorSet * pcol;
(void) param;
if (gw->g.vmt != (gwinVMT *)&progressbarVMT)
return;
- if ((gw->g.flags & GWIN_FLG_ENABLED))
+ // disable the auto-update timer if any
+ #if GFX_USE_GTIMER
+ if (gtimerIsActive(&(gsw->gt)) && !(gw->g.flags & GWIN_FLG_ENABLED)) {
+ gtimerStop(&(gsw->gt));
+ }
+ #endif
+
+ // get the colors right
+ if ((gw->g.flags & GWIN_FLG_ENABLED))
pcol = &gw->pstyle->pressed;
else
pcol = &gw->pstyle->disabled;
- if (gw->g.width < gw->g.height) { // Vertical progressbar
+ // Vertical progressbar
+ if (gw->g.width < gw->g.height) {
if (gsw->dpos != gw->g.height-1)
- gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.width, gw->g.height - gsw->dpos, pcol->progress); // Active Area
+ gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.width, gw->g.height - gsw->dpos, pcol->progress); // Active Area
if (gsw->dpos != 0)
- gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area
- gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge
- gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb
+ gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, gw->pstyle->enabled.progress); // Inactive area
+ gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge
+ gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb
// Horizontal progressbar
} else {
if (gsw->dpos != gw->g.width-1)
gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, gw->pstyle->enabled.progress); // Inactive area
if (gsw->dpos != 0)
- gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gsw->dpos, gw->g.height, pcol->progress); // Active Area
- gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge
- gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb
+ gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gsw->dpos, gw->g.height, pcol->progress); // Active Area
+ gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge
+ gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb
}
gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, justifyCenter);
diff --git a/src/gwin/progressbar.h b/src/gwin/progressbar.h
index c9efe46b..fcf76b12 100644
--- a/src/gwin/progressbar.h
+++ b/src/gwin/progressbar.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/progressbar.h
+ * @file src/gwin/progressbar.h
* @brief GWIN Graphic window subsystem header file.
*
* @defgroup Progressbar Progressbar
@@ -138,6 +138,15 @@ void gwinProgressbarDecrement(GHandle gh);
*/
#define gwinProgressbarGetPosition(gh) (((GProgressbarObject *)(gh))->pos)
+ /**
+ * @brief Reset the progressbar to the minimum position
+ *
+ * @param[in] gh The window handle (must be a progressbar window)
+ *
+ * @api
+ */
+#define gwinProgressbarReset(gh) gwinProgressbarSetPosition(gh, ((GProgressbarObject *)(gh))->min)
+
/**
* @brief Automatically increments the progress bar
*
@@ -156,6 +165,15 @@ void gwinProgressbarDecrement(GHandle gh);
void gwinProgressbarStart(GHandle gh, delaytime_t delay);
/**
+ * @brief Stop the timer which is started by @p gwinProgressbarStart()
+ *
+ * @param[in] gh The window handle (must be a progressbar window)
+ *
+ * @api
+ */
+void gwinProgressbarStop(GHandle gh);
+
+/**
* @brief Some custom progressbar drawing routines
* @details These function may be passed to @p gwinSetCustomDraw() to get different progressbar drawing styles
*
diff --git a/src/gwin/radio.h b/src/gwin/radio.h
index 3ee2918f..f2bd7f35 100644
--- a/src/gwin/radio.h
+++ b/src/gwin/radio.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/radio.h
+ * @file src/gwin/radio.h
* @brief GWIN Graphic window subsystem header file.
*
* @defgroup RadioButton RadioButton
diff --git a/src/gwin/slider.h b/src/gwin/slider.h
index 8f87745c..8c5bd9ca 100644
--- a/src/gwin/slider.h
+++ b/src/gwin/slider.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/slider.h
+ * @file src/gwin/slider.h
* @brief GWIN Graphic window subsystem header file.
*
* @defgroup Slider Slider
diff --git a/src/gwin/sys_defs.h b/src/gwin/sys_defs.h
index 10b5b564..ac2c98c7 100644
--- a/src/gwin/sys_defs.h
+++ b/src/gwin/sys_defs.h
@@ -439,6 +439,8 @@ extern "C" {
*
* @param[in] gh The window
*
+ * @return GWIN_NORMAL, GWIN_MAXIMIZE or GWIN_MINIMIZE
+ *
* @api
*/
GWindowMinMax gwinGetMinMax(GHandle gh);
diff --git a/src/gwin/sys_options.h b/src/gwin/sys_options.h
index e7bb93b4..656e0e3f 100644
--- a/src/gwin/sys_options.h
+++ b/src/gwin/sys_options.h
@@ -6,7 +6,7 @@
*/
/**
- * @file include/gwin/sys_options.h
+ * @file src/gwin/sys_options.h
* @brief GWIN sub-system options header file.
*
* @addtogroup GWIN