diff options
author | Joel Bodenmann <joel@unormal.org> | 2014-04-30 13:41:34 +0200 |
---|---|---|
committer | Joel Bodenmann <joel@unormal.org> | 2014-04-30 13:41:34 +0200 |
commit | 33c721c009465dd30d4e96e055a051480c567b57 (patch) | |
tree | 5a6744a79b7469d80bae474d4314b47d4cd6d44d /src | |
parent | 58cf2d2b35542166f1a4e50a83bcf28ff33574a5 (diff) | |
parent | a394e2c35dde67241bea69409bcae9f46dcfc089 (diff) | |
download | uGFX-33c721c009465dd30d4e96e055a051480c567b57.tar.gz uGFX-33c721c009465dd30d4e96e055a051480c567b57.tar.bz2 uGFX-33c721c009465dd30d4e96e055a051480c567b57.zip |
Merge branch 'master' into freertos
Diffstat (limited to 'src')
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 @@ -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 |