Update GADC
This commit is contained in:
parent
c7566aa553
commit
c354639f7b
5 changed files with 328 additions and 461 deletions
|
@ -167,10 +167,19 @@ int main(void) {
|
||||||
gtimerStart(&lsTimer, LowSpeedTimer, ghText, TRUE, MY_LS_DELAY);
|
gtimerStart(&lsTimer, LowSpeedTimer, ghText, TRUE, MY_LS_DELAY);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Allocate buffers for the high speed GADC device - 4 x 128 byte buffers.
|
/**
|
||||||
// You may need to increase this for slower cpu's.
|
* Allocate buffers for the high speed GADC device - eg. 4 x 128 byte buffers.
|
||||||
// You may be able to decrease this for low latency operating systems.
|
* You may need to increase this for slower cpu's.
|
||||||
gfxBufferAlloc(4, 128);
|
* You may be able to decrease this for low latency operating systems.
|
||||||
|
* 10 x 128 seems to work on the really slow Olimex SAM7EX256 board (display speed limitation)
|
||||||
|
* If your oscilloscope display stops but the low speed reading keep going then it is likely that
|
||||||
|
* your high speed timer has stalled due to running out of free buffers. Increase the number
|
||||||
|
* of buffers..
|
||||||
|
* If you make the buffers too large with a slow sample rate you may not allow enough time for all
|
||||||
|
* the low speed items to occur in which case your memory will fill up with low speed requests until
|
||||||
|
* you run out of memory.
|
||||||
|
*/
|
||||||
|
gfxBufferAlloc(10, 128);
|
||||||
|
|
||||||
/* Set up the scope window in the top right on the screen */
|
/* Set up the scope window in the top right on the screen */
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,13 +16,12 @@
|
||||||
|
|
||||||
#include "src/gadc/driver.h"
|
#include "src/gadc/driver.h"
|
||||||
|
|
||||||
static GDataBuffer *pData;
|
static uint32_t nextfreq;
|
||||||
static size_t bytesperconversion;
|
|
||||||
|
|
||||||
|
// Forward references to ISR routines
|
||||||
static void ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n);
|
static void ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n);
|
||||||
static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err);
|
static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err);
|
||||||
|
|
||||||
|
|
||||||
static ADCConversionGroup acg = {
|
static ADCConversionGroup acg = {
|
||||||
FALSE, // circular
|
FALSE, // circular
|
||||||
1, // num_channels
|
1, // num_channels
|
||||||
|
@ -37,82 +36,53 @@ static void ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
|
||||||
(void) adcp;
|
(void) adcp;
|
||||||
(void) buffer;
|
(void) buffer;
|
||||||
|
|
||||||
if (pData) {
|
gadcGotDataI(n);
|
||||||
// A set of timer base conversions is complete
|
|
||||||
pData->len += n * bytesperconversion;
|
|
||||||
|
|
||||||
// Are we finished yet?
|
|
||||||
// In ChibiOS we (may) get a half-buffer complete. In this situation the conversions
|
|
||||||
// are really not complete and so we just wait for the next lot of data.
|
|
||||||
if (pData->len + bytesperconversion > pData->size)
|
|
||||||
gadcDataReadyI();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// A single non-timer conversion is complete
|
|
||||||
gadcDataReadyI();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err) {
|
static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err) {
|
||||||
(void) adcp;
|
(void) adcp;
|
||||||
(void) err;
|
(void) err;
|
||||||
|
|
||||||
gadcDataFailI();
|
gadcGotDataI(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gadc_lld_init(void) {
|
void gadc_lld_init(void) {
|
||||||
adcStart(&ADCD1, 0);
|
adcStart(&ADCD1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gadc_lld_start_timer(GadcLldTimerData *pgtd) {
|
size_t gadc_lld_samplesperconversion(uint32_t physdev) {
|
||||||
int phys;
|
size_t samples;
|
||||||
|
|
||||||
/* Calculate the bytes per conversion from physdev */
|
for(samples = 0; physdev; physdev >>= 1)
|
||||||
/* The AT91SAM7 has AD0..7 - physdev is a bitmap of those channels */
|
if (physdev & 0x01)
|
||||||
phys = pgtd->physdev;
|
samples++;
|
||||||
for(bytesperconversion = 0; phys; phys >>= 1)
|
return samples;
|
||||||
if (phys & 0x01)
|
|
||||||
bytesperconversion++;
|
|
||||||
bytesperconversion *= (gfxSampleFormatBits(GADC_SAMPLE_FORMAT)+7)/8;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The AT91SAM7 ADC driver supports triggering the ADC using a timer without having to implement
|
|
||||||
* an interrupt handler for the timer. The driver also initialises the timer correctly for us.
|
|
||||||
* Because we aren't trapping the interrupt ourselves we can't increment GADC_Timer_Missed if an
|
|
||||||
* interrupt is missed.
|
|
||||||
*/
|
|
||||||
acg.frequency = pgtd->frequency;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gadc_lld_stop_timer(GadcLldTimerData *pgtd) {
|
void gadc_lld_start_timerI(uint32_t frequency) {
|
||||||
(void) pgtd;
|
// Nothing to do here - the AT91SAM7 adc driver uses an internal timer
|
||||||
if ((acg.trigger & ~ADC_TRIGGER_SOFTWARE) == ADC_TRIGGER_TIMER)
|
// which is set up when the job is started. We save this here just to
|
||||||
adcStop(&ADCD1);
|
// indicate the timer should be re-initialised on the next timer job
|
||||||
|
nextfreq = frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gadc_lld_adc_timerI(GadcLldTimerData *pgtd) {
|
void gadc_lld_stop_timerI(void) {
|
||||||
/**
|
// Nothing to do here. The AT91SAM7 adc driver automatically turns off timer interrupts
|
||||||
* We don't need to calculate num_channels because the AT91SAM7 ADC does this for us.
|
// on completion of the job
|
||||||
*/
|
|
||||||
acg.channelselects = pgtd->physdev;
|
|
||||||
acg.trigger = pgtd->now ? (ADC_TRIGGER_TIMER|ADC_TRIGGER_SOFTWARE) : ADC_TRIGGER_TIMER;
|
|
||||||
|
|
||||||
pData = pgtd->pdata;
|
|
||||||
adcStartConversionI(&ADCD1, &acg, (adcsample_t *)(pgtd->pdata+1), pData->size/bytesperconversion);
|
|
||||||
|
|
||||||
/* Next time assume the same (still running) timer */
|
|
||||||
acg.frequency = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gadc_lld_adc_nontimerI(GadcLldNonTimerData *pgntd) {
|
void gadc_lld_timerjobI(GadcTimerJob *pj) {
|
||||||
/**
|
acg.channelselects = pj->physdev;
|
||||||
* We don't need to calculate num_channels because the AT91SAM7 ADC does this for us.
|
acg.trigger = ADC_TRIGGER_TIMER;
|
||||||
*/
|
acg.frequency = nextfreq;
|
||||||
acg.channelselects = pgntd->physdev;
|
nextfreq = 0; // Next job use the same timer
|
||||||
|
adcStartConversionI(&ADCD1, &acg, pj->buffer, pj->todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gadc_lld_nontimerjobI(GadcNonTimerJob *pj) {
|
||||||
|
acg.channelselects = pj->physdev;
|
||||||
acg.trigger = ADC_TRIGGER_SOFTWARE;
|
acg.trigger = ADC_TRIGGER_SOFTWARE;
|
||||||
|
adcStartConversionI(&ADCD1, &acg, pj->buffer, 1);
|
||||||
pData = 0;
|
|
||||||
adcStartConversionI(&ADCD1, &acg, pgntd->buffer, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* GFX_USE_GADC */
|
#endif /* GFX_USE_GADC */
|
||||||
|
|
|
@ -27,38 +27,27 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The structure passed to start a timer conversion
|
* @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 {
|
typedef struct GadcTimerJob_t {
|
||||||
uint32_t physdev; /* @< Which physical ADC devices/channels to use. Filled in by High Level Code */
|
uint32_t physdev; // @< The physical device/s. The exact meaning of physdev is hardware dependent.
|
||||||
uint32_t frequency; /* @< The conversion frequency. Filled in by High Level Code */
|
uint32_t frequency; // @< The frequency to sample
|
||||||
GDataBuffer *pdata; /* @< The buffer to put the ADC samples into. */
|
adcsample_t *buffer; // @< Where to put the samples
|
||||||
bool_t now; /* @< Trigger the first conversion now rather than waiting for the first timer interrupt (if possible) */
|
size_t todo; // @< How many conversions to do
|
||||||
} GadcLldTimerData;
|
size_t done; // @< How many conversions have already been done
|
||||||
|
} GadcTimerJob;
|
||||||
/* @} */
|
/* @} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The structure passed to start a non-timer conversion
|
* @brief The structure passed to do a single conversion
|
||||||
* @note We use the structure instead of parameters purely to save
|
|
||||||
* interrupt stack space which is very limited in some platforms.
|
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
typedef struct GadcLldNonTimerData_t {
|
typedef struct GadcNonTimerJob_t {
|
||||||
uint32_t physdev; /* @< A value passed to describe which physical ADC devices/channels to use. */
|
uint32_t physdev; // @< The physical device/s. The exact meaning of physdev is hardware dependent.
|
||||||
adcsample_t *buffer; /* @< The static buffer to put the ADC samples into. */
|
adcsample_t *buffer; // @< Where to put the samples.
|
||||||
} GadcLldNonTimerData;
|
} GadcNonTimerJob;
|
||||||
/* @} */
|
/* @} */
|
||||||
|
|
||||||
/**
|
|
||||||
* @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. */
|
/* External declarations. */
|
||||||
/*===========================================================================*/
|
/*===========================================================================*/
|
||||||
|
@ -75,14 +64,15 @@ extern "C" {
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @brief The last conversion requested is now complete
|
* @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 gadcDataReadyI(void);
|
void gadcGotDataI(size_t n);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The last conversion requested failed
|
|
||||||
*/
|
|
||||||
void gadcDataFailI(void);
|
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
@ -95,71 +85,57 @@ extern "C" {
|
||||||
void gadc_lld_init(void);
|
void gadc_lld_init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start a periodic timer for high frequency conversions.
|
* @brief Return the number of samples per conversion
|
||||||
*
|
*
|
||||||
* @param[in] pgtd The structure containing the sample frequency and physical device to use.
|
* @param[in] physdev The hardware dependent physical device descriptor
|
||||||
*
|
|
||||||
* @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 there is a "timer" conversion
|
|
||||||
* active.
|
|
||||||
* @note Timer interrupts occurring before @p gadc_lld_adc_timerI() has been called,
|
|
||||||
* if @p gadc_lld_adc_timerI() has been called quick enough, or while
|
|
||||||
* a non-timer conversion is active should be ignored other than (optionally) incrementing
|
|
||||||
* the GADC_Timer_Missed variable.
|
|
||||||
* @note The pdata and now members of the pgtd structure are now yet valid.
|
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
void gadc_lld_start_timer(GadcLldTimerData *pgtd);
|
size_t gadc_lld_samplesperconversion(uint32_t physdev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start a periodic timer for high frequency conversions.
|
||||||
|
*
|
||||||
|
* @param[in] freq The frequency for the timer
|
||||||
|
*
|
||||||
|
* @note This will only be called if the timer is currently stopped.
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
* @iclass
|
||||||
|
*/
|
||||||
|
void gadc_lld_start_timerI(uint32_t freq);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Stop the periodic timer for high frequency conversions.
|
* @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] pgtd The structure containing the sample frequency and physical device to use.
|
* @note This will only be called if the timer is currently running and all timer jobs
|
||||||
|
* have been completed/aborted.
|
||||||
*
|
*
|
||||||
* @note After this function returns there should be no more calls to @p gadcDataReadyI()
|
|
||||||
* or @p gadcDataFailI() in relation to timer conversions.
|
|
||||||
* @api
|
* @api
|
||||||
*/
|
|
||||||
void gadc_lld_stop_timer(GadcLldTimerData *pgtd);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Start a set of "timer" conversions.
|
|
||||||
* @details Starts a series of conversions triggered by the timer.
|
|
||||||
*
|
|
||||||
* @param[in] pgtd Contains the parameters for the timer conversion.
|
|
||||||
*
|
|
||||||
* @note The exact meaning of physdev is hardware dependent. It is likely described in the
|
|
||||||
* drivers gadc_lld_config.h
|
|
||||||
* @note The driver should call @p gadcDataReadyI() when it completes the operation
|
|
||||||
* or @p gadcDataFailI() on an error.
|
|
||||||
* @note The high level code ensures that this is not called while a non-timer conversion is in
|
|
||||||
* progress
|
|
||||||
*
|
|
||||||
* @iclass
|
* @iclass
|
||||||
*/
|
*/
|
||||||
void gadc_lld_adc_timerI(GadcLldTimerData *pgtd);
|
void gadc_lld_stop_timerI(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start a "non-timer" conversion.
|
* @brief Start a set of high frequency conversions.
|
||||||
* @details Starts a single conversion now.
|
|
||||||
*
|
*
|
||||||
* @param[in] pgntd Contains the parameters for the non-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 The driver should call @p gadcDataReadyI() when it completes the operation
|
|
||||||
* or @p gadcDataFailI() on an error.
|
|
||||||
* @note The high level code ensures that this is not called while a timer conversion is in
|
|
||||||
* progress
|
|
||||||
*
|
*
|
||||||
|
* @api
|
||||||
* @iclass
|
* @iclass
|
||||||
*/
|
*/
|
||||||
void gadc_lld_adc_nontimerI(GadcLldNonTimerData *pgntd);
|
void gadc_lld_timerjobI(GadcTimerJob *pjob);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start a non-timer conversion.
|
||||||
|
*
|
||||||
|
* @note This will only be called if the ADC should be ready for a new job.
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
* @iclass
|
||||||
|
*/
|
||||||
|
void gadc_lld_nontimerjobI(GadcNonTimerJob *pjob);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
544
src/gadc/gadc.c
544
src/gadc/gadc.c
|
@ -23,206 +23,151 @@
|
||||||
#error "GADC: GADC_MAX_HIGH_SPEED_SAMPLERATE has been set too high. It must be less than half the maximum CPU rate"
|
#error "GADC: GADC_MAX_HIGH_SPEED_SAMPLERATE has been set too high. It must be less than half the maximum CPU rate"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define GADC_MAX_LOWSPEED_DEVICES ((GADC_MAX_SAMPLE_FREQUENCY/GADC_MAX_HIGH_SPEED_SAMPLERATE)-1)
|
#define GADC_HSADC_GTIMER 0x8000
|
||||||
#if GADC_MAX_LOWSPEED_DEVICES > 4
|
#define GADC_ADC_RUNNING 0x4000
|
||||||
#undef GADC_MAX_LOWSPEED_DEVICES
|
#define GADC_HSADC_CONVERTION 0x2000
|
||||||
#define GADC_MAX_LOWSPEED_DEVICES 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
volatile bool_t GADC_Timer_Missed;
|
typedef struct NonTimerData_t {
|
||||||
|
gfxQueueGSyncItem next;
|
||||||
static bool_t gadcRunning;
|
GADCCallbackFunction callback;
|
||||||
static gfxSem LowSpeedSlotSem;
|
union {
|
||||||
static gfxMutex LowSpeedMutex;
|
void *param;
|
||||||
static GTimer LowSpeedGTimer;
|
gfxSem sigdone;
|
||||||
static gfxQueueGSync HighSpeedBuffers;
|
};
|
||||||
|
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
|
#if GFX_USE_GEVENT
|
||||||
static GTimer HighSpeedGTimer;
|
static GTimer hsGTimer;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static GTimer lsGTimer;
|
||||||
|
static gfxQueueGSync lsListToDo;
|
||||||
|
static gfxQueueGSync lsListDone;
|
||||||
|
static NonTimerData *lsData;
|
||||||
|
|
||||||
#define GADC_FLG_ISACTIVE 0x0001
|
void gadcGotDataI(size_t n) {
|
||||||
#define GADC_FLG_ISDONE 0x0002
|
if ((hsFlags & GADC_HSADC_CONVERTION)) {
|
||||||
#define GADC_FLG_ERROR 0x0004
|
|
||||||
#define GADC_FLG_GTIMER 0x0008
|
|
||||||
#define GADC_FLG_STALLED 0x0010
|
|
||||||
|
|
||||||
static struct hsdev {
|
// A set of timer conversions is done - add them
|
||||||
// Our status flags
|
hsJob.done += n;
|
||||||
uint16_t flags;
|
|
||||||
|
|
||||||
// Other stuff we need to track progress and for signaling
|
// Are we finished yet? (or driver signalled complete now)
|
||||||
GadcLldTimerData lld;
|
if (n && hsJob.done < hsJob.todo)
|
||||||
uint16_t eventflags;
|
|
||||||
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++;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/* Now we have done a high speed conversion - start looking for low speed conversions */
|
|
||||||
curlsdev = ls;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Look for the next thing to do.
|
|
||||||
*/
|
|
||||||
gadcRunning = TRUE;
|
|
||||||
for(; curlsdev < &ls[GADC_MAX_LOWSPEED_DEVICES]; curlsdev++) {
|
|
||||||
if ((curlsdev->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == GADC_FLG_ISACTIVE) {
|
|
||||||
gadc_lld_adc_nontimerI(&curlsdev->lld);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
curlsdev = 0;
|
|
||||||
|
|
||||||
/* No more low speed devices - do a high speed conversion */
|
|
||||||
if (hs.flags & GADC_FLG_ISACTIVE) {
|
|
||||||
hs.lld.pdata = gfxBufferGetI();
|
|
||||||
if (hs.lld.pdata) {
|
|
||||||
hs.lld.now = GADC_Timer_Missed || (hs.flags & GADC_FLG_STALLED);
|
|
||||||
hs.flags &= ~GADC_FLG_STALLED;
|
|
||||||
GADC_Timer_Missed = 0;
|
|
||||||
gadc_lld_adc_timerI(&hs.lld);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Oops - no free buffers - mark stalled and go back to low speed devices
|
|
||||||
hs.flags |= GADC_FLG_STALLED;
|
|
||||||
hs.eventflags &= ~GADC_HSADC_RUNNING;
|
|
||||||
for(curlsdev = ls; curlsdev < &ls[GADC_MAX_LOWSPEED_DEVICES]; curlsdev++) {
|
|
||||||
if ((curlsdev->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == GADC_FLG_ISACTIVE) {
|
|
||||||
gadc_lld_adc_nontimerI(&curlsdev->lld);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
curlsdev = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Nothing more to do */
|
|
||||||
gadcRunning = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gadcDataReadyI(void) {
|
|
||||||
|
|
||||||
if (curlsdev) {
|
|
||||||
/* This interrupt must be in relation to the low speed device */
|
|
||||||
|
|
||||||
if (curlsdev->flags & GADC_FLG_ISACTIVE) {
|
|
||||||
curlsdev->flags |= GADC_FLG_ISDONE;
|
|
||||||
gtimerJabI(&LowSpeedGTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if GFX_USE_OS_CHIBIOS && CHIBIOS_ADC_ISR_FULL_CODE_BUG
|
|
||||||
/**
|
|
||||||
* Oops - We have just finished a low speed conversion but a bug prevents us
|
|
||||||
* restarting the ADC here. Other code will restart it in the thread based
|
|
||||||
* ADC handler.
|
|
||||||
*/
|
|
||||||
gadcRunning = FALSE;
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Clear event flags we might set
|
||||||
|
hsFlags &= ~(GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL);
|
||||||
|
|
||||||
|
// Is there any data in it
|
||||||
|
if (!hsJob.done) {
|
||||||
|
|
||||||
|
// Oops - no data in this buffer. Just return it to the free-list
|
||||||
|
gfxBufferReleaseI(hsData);
|
||||||
|
goto starttimerjob; // Restart the timer job
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
#endif
|
||||||
|
|
||||||
} else {
|
// Stop if we have been told to
|
||||||
/* This interrupt must be in relation to the high speed device */
|
if (!(hsFlags & GADC_HSADC_RUNNING)) {
|
||||||
|
gadc_lld_stop_timerI();
|
||||||
|
|
||||||
if (hs.flags & GADC_FLG_ISACTIVE) {
|
// Get the next free buffer
|
||||||
if (hs.lld.pdata->len) {
|
} else if (!(hsData = gfxBufferGetI())) {
|
||||||
/* Save the current buffer on the HighSpeedBuffers */
|
|
||||||
gfxQueueGSyncPutI(&HighSpeedBuffers, (gfxQueueGSyncItem *)hs.lld.pdata);
|
|
||||||
hs.lld.pdata = 0;
|
|
||||||
|
|
||||||
/* Save the details */
|
// Oops - no free buffers. Stall
|
||||||
hs.eventflags = GADC_HSADC_RUNNING|GADC_HSADC_GOTBUFFER;
|
hsFlags &= ~GADC_HSADC_RUNNING;
|
||||||
if (GADC_Timer_Missed)
|
hsFlags |= GADC_HSADC_STALL;
|
||||||
hs.eventflags |= GADC_HSADC_LOSTEVENT;
|
gadc_lld_stop_timerI();
|
||||||
if (hs.flags & GADC_FLG_STALLED)
|
|
||||||
hs.eventflags |= GADC_HSADC_STALL;
|
|
||||||
|
|
||||||
/* Our signalling mechanisms */
|
// Prepare the next job
|
||||||
if (hs.isrfn)
|
} else {
|
||||||
hs.isrfn();
|
|
||||||
|
|
||||||
#if GFX_USE_GEVENT
|
// Return this new job
|
||||||
if (hs.flags & GADC_FLG_GTIMER)
|
#if GFX_USE_OS_CHIBIOS
|
||||||
gtimerJabI(&HighSpeedGTimer);
|
// ChibiOS api bug - samples must be even
|
||||||
#endif
|
hsJob.todo = (hsData->size / hsBytesPerConv) & ~1;
|
||||||
} else {
|
#else
|
||||||
// Oops - no data in this buffer. Just return it to the free-list
|
hsJob.todo = hsData->size / hsBytesPerConv;
|
||||||
gfxBufferRelease(hs.lld.pdata);
|
#endif
|
||||||
hs.lld.pdata = 0;
|
hsJob.done = 0;
|
||||||
}
|
hsJob.buffer = (adcsample_t *)(hsData+1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Start a job preferring a non-timer job
|
||||||
* Look for the next thing to do.
|
if ((lsData = (NonTimerData *)gfxQueueGSyncGetI(&lsListToDo))) {
|
||||||
*/
|
hsFlags &= ~GADC_HSADC_CONVERTION;
|
||||||
FindNextConversionI();
|
gadc_lld_nontimerjobI(&lsData->job);
|
||||||
}
|
} else if ((hsFlags & GADC_HSADC_RUNNING)) {
|
||||||
|
hsFlags |= GADC_HSADC_CONVERTION;
|
||||||
void gadcDataFailI(void) {
|
gadc_lld_timerjobI(&hsJob);
|
||||||
if (curlsdev) {
|
} else
|
||||||
if ((curlsdev->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == GADC_FLG_ISACTIVE)
|
hsFlags &= ~GADC_ADC_RUNNING;
|
||||||
/* Mark the error then try to repeat it */
|
|
||||||
curlsdev->flags |= GADC_FLG_ERROR;
|
|
||||||
|
|
||||||
#if GFX_USE_OS_CHIBIOS && CHIBIOS_ADC_ISR_FULL_CODE_BUG
|
|
||||||
/**
|
|
||||||
* Oops - We have just finished a low speed conversion but a bug prevents us
|
|
||||||
* restarting the ADC here. Other code will restart it in the thread based
|
|
||||||
* ADC handler.
|
|
||||||
*/
|
|
||||||
gadcRunning = FALSE;
|
|
||||||
gtimerJabI(&LowSpeedGTimer);
|
|
||||||
return;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (hs.flags & GADC_FLG_ISACTIVE)
|
|
||||||
/* Mark the error and then try to repeat it */
|
|
||||||
hs.flags |= GADC_FLG_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start the next conversion */
|
// Did it fail
|
||||||
FindNextConversionI();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Our module initialiser */
|
/* Our module initialiser */
|
||||||
void _gadcInit(void)
|
void _gadcInit(void)
|
||||||
{
|
{
|
||||||
gadc_lld_init();
|
gadc_lld_init();
|
||||||
gfxQueueGSyncInit(&HighSpeedBuffers);
|
|
||||||
gfxSemInit(&LowSpeedSlotSem, GADC_MAX_LOWSPEED_DEVICES, GADC_MAX_LOWSPEED_DEVICES);
|
gfxQueueGSyncInit(&hsListDone);
|
||||||
gfxMutexInit(&LowSpeedMutex);
|
|
||||||
gtimerInit(&LowSpeedGTimer);
|
|
||||||
#if GFX_USE_GEVENT
|
#if GFX_USE_GEVENT
|
||||||
gtimerInit(&HighSpeedGTimer);
|
gtimerInit(&hsGTimer);
|
||||||
#endif
|
#endif
|
||||||
|
gtimerInit(&lsGTimer);
|
||||||
|
gfxQueueGSyncInit(&lsListToDo);
|
||||||
|
gfxQueueGSyncInit(&lsListDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _gadcDeinit(void)
|
void _gadcDeinit(void)
|
||||||
|
@ -230,27 +175,13 @@ void _gadcDeinit(void)
|
||||||
/* commented stuff is ToDo */
|
/* commented stuff is ToDo */
|
||||||
|
|
||||||
// gadc_lld_deinit();
|
// gadc_lld_deinit();
|
||||||
gfxQueueGSyncDeinit(&HighSpeedBuffers);
|
gfxQueueGSyncDeinit(&hsListDone);
|
||||||
gfxSemDestroy(&LowSpeedSlotSem);
|
|
||||||
gfxMutexDestroy(&LowSpeedMutex);
|
|
||||||
gtimerDeinit(&LowSpeedGTimer);
|
|
||||||
#if GFX_USE_GEVENT
|
#if GFX_USE_GEVENT
|
||||||
gtimerDeinit(&HighSpeedGTimer);
|
gtimerDeinit(&hsGTimer);
|
||||||
#endif
|
#endif
|
||||||
}
|
gtimerDeinit(&lsGTimer);
|
||||||
|
gfxQueueGSyncDeinit(&lsListToDo);
|
||||||
static inline void StartADC(bool_t onNoHS) {
|
gfxQueueGSyncDeinit(&lsListDone);
|
||||||
gfxSystemLock();
|
|
||||||
if (!gadcRunning || (onNoHS && !curlsdev))
|
|
||||||
FindNextConversionI();
|
|
||||||
gfxSystemUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void BSemSignalCallback(adcsample_t *buffer, void *param) {
|
|
||||||
(void) buffer;
|
|
||||||
|
|
||||||
/* Signal the BinarySemaphore parameter */
|
|
||||||
gfxSemSignal((gfxSem *)param);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if GFX_USE_GEVENT
|
#if GFX_USE_GEVENT
|
||||||
|
@ -260,7 +191,7 @@ static void BSemSignalCallback(adcsample_t *buffer, void *param) {
|
||||||
GEventADC *pe;
|
GEventADC *pe;
|
||||||
|
|
||||||
psl = 0;
|
psl = 0;
|
||||||
while ((psl = geventGetSourceListener((GSourceHandle)(&HighSpeedGTimer), psl))) {
|
while ((psl = geventGetSourceListener((GSourceHandle)(&hsGTimer), psl))) {
|
||||||
if (!(pe = (GEventADC *)geventGetEventBuffer(psl))) {
|
if (!(pe = (GEventADC *)geventGetEventBuffer(psl))) {
|
||||||
// This listener is missing - save this.
|
// This listener is missing - save this.
|
||||||
psl->srcflags |= GADC_HSADC_LOSTEVENT;
|
psl->srcflags |= GADC_HSADC_LOSTEVENT;
|
||||||
|
@ -268,175 +199,162 @@ static void BSemSignalCallback(adcsample_t *buffer, void *param) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pe->type = GEVENT_ADC;
|
pe->type = GEVENT_ADC;
|
||||||
pe->flags = hs.eventflags | psl->srcflags;
|
pe->flags = (hsFlags & (GADC_HSADC_RUNNING|GADC_HSADC_GOTBUFFER|GADC_HSADC_STALL)) | psl->srcflags;
|
||||||
psl->srcflags = 0;
|
psl->srcflags = 0;
|
||||||
geventSendEvent(psl);
|
geventSendEvent(psl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void LowSpeedGTimerCallback(void *param) {
|
|
||||||
(void) param;
|
|
||||||
GADCCallbackFunction fn;
|
|
||||||
void *prm;
|
|
||||||
adcsample_t *buffer;
|
|
||||||
struct lsdev *p;
|
|
||||||
|
|
||||||
#if GFX_USE_OS_CHIBIOS && CHIBIOS_ADC_ISR_FULL_CODE_BUG
|
|
||||||
/* Ensure the ADC is running if it needs to be - Bugfix HACK */
|
|
||||||
StartADC(FALSE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(&LowSpeedSlotSem); // Tell everyone
|
|
||||||
fn(buffer, prm); // Perform the callback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency)
|
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 */
|
/* Just save the details and reset everything for now */
|
||||||
hs.lld.physdev = physdev;
|
hsJob.physdev = physdev;
|
||||||
hs.lld.frequency = frequency;
|
hsJob.frequency = frequency;
|
||||||
hs.lld.pdata = 0;
|
hsISRcallback = 0;
|
||||||
hs.lld.now = FALSE;
|
hsBytesPerConv = gadc_lld_samplesperconversion(physdev) * sizeof(adcsample_t);
|
||||||
hs.isrfn = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if GFX_USE_GEVENT
|
#if GFX_USE_GEVENT
|
||||||
GSourceHandle gadcHighSpeedGetSource(void) {
|
GSourceHandle gadcHighSpeedGetSource(void) {
|
||||||
if (!gtimerIsActive(&HighSpeedGTimer))
|
if (!gtimerIsActive(&hsGTimer))
|
||||||
gtimerStart(&HighSpeedGTimer, HighSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
|
gtimerStart(&hsGTimer, HighSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
|
||||||
hs.flags |= GADC_FLG_GTIMER;
|
hsFlags |= GADC_HSADC_GTIMER;
|
||||||
return (GSourceHandle)&HighSpeedGTimer;
|
return (GSourceHandle)&hsGTimer;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn) {
|
void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn) {
|
||||||
hs.isrfn = isrfn;
|
hsISRcallback = isrfn;
|
||||||
}
|
}
|
||||||
|
|
||||||
GDataBuffer *gadcHighSpeedGetData(delaytime_t ms) {
|
GDataBuffer *gadcHighSpeedGetData(delaytime_t ms) {
|
||||||
return (GDataBuffer *)gfxQueueGSyncGet(&HighSpeedBuffers, ms);
|
return (GDataBuffer *)gfxQueueGSyncGet(&hsListDone, ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
GDataBuffer *gadcHighSpeedGetDataI(void) {
|
GDataBuffer *gadcHighSpeedGetDataI(void) {
|
||||||
return (GDataBuffer *)gfxQueueGSyncGetI(&HighSpeedBuffers);
|
return (GDataBuffer *)gfxQueueGSyncGetI(&hsListDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gadcHighSpeedStart(void) {
|
void gadcHighSpeedStart(void) {
|
||||||
/* If its already going we don't need to do anything */
|
// Safety first
|
||||||
if (hs.flags & GADC_FLG_ISACTIVE)
|
if (!hsJob.frequency || !hsBytesPerConv)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
hs.flags = GADC_FLG_ISACTIVE;
|
gfxSystemLock();
|
||||||
gadc_lld_start_timer(&hs.lld);
|
if (!(hsFlags & GADC_HSADC_RUNNING)) {
|
||||||
StartADC(FALSE);
|
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) {
|
void gadcHighSpeedStop(void) {
|
||||||
if (hs.flags & GADC_FLG_ISACTIVE) {
|
// Stop it and wait for completion
|
||||||
/* No more from us */
|
hsFlags &= ~GADC_HSADC_RUNNING;
|
||||||
hs.flags = 0;
|
while ((hsFlags & GADC_HSADC_CONVERTION))
|
||||||
gadc_lld_stop_timer(&hs.lld);
|
gfxYield();
|
||||||
/*
|
}
|
||||||
* There might be a buffer still locked up by the driver - if so release it.
|
|
||||||
*/
|
|
||||||
if (hs.lld.pdata) {
|
|
||||||
gfxBufferRelease(hs.lld.pdata);
|
|
||||||
hs.lld.pdata = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
static void LowSpeedGTimerCallback(void *param) {
|
||||||
* We have to pass TRUE to StartADC() as we might have the ADC marked as active when it isn't
|
(void) param;
|
||||||
* due to stopping the timer while it was converting.
|
NonTimerData *pdata;
|
||||||
*/
|
|
||||||
StartADC(TRUE);
|
// 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) {
|
void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) {
|
||||||
struct lsdev *p;
|
NonTimerData ndata;
|
||||||
gfxSem mysem;
|
|
||||||
|
|
||||||
/* Start the Low Speed Timer */
|
// Prepare the job
|
||||||
gfxSemInit(&mysem, 1, 1);
|
gfxSemInit(&ndata.sigdone, 0, 1);
|
||||||
gfxMutexEnter(&LowSpeedMutex);
|
ndata.job.physdev = physdev;
|
||||||
if (!gtimerIsActive(&LowSpeedGTimer))
|
ndata.job.buffer = buffer;
|
||||||
gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
|
ndata.callback = 0;
|
||||||
gfxMutexExit(&LowSpeedMutex);
|
|
||||||
|
|
||||||
while(1) {
|
// Activate it
|
||||||
/* Wait for an available slot */
|
gfxSystemLock();
|
||||||
gfxSemWait(&LowSpeedSlotSem, TIME_INFINITE);
|
if (!(hsFlags & GADC_ADC_RUNNING)) {
|
||||||
|
// Nothing is running - start the job
|
||||||
/* Find a slot */
|
lsData = &ndata;
|
||||||
gfxMutexEnter(&LowSpeedMutex);
|
hsFlags |= GADC_ADC_RUNNING;
|
||||||
for(p = ls; p < &ls[GADC_MAX_LOWSPEED_DEVICES]; p++) {
|
hsFlags &= ~GADC_HSADC_CONVERTION;
|
||||||
if (!(p->flags & GADC_FLG_ISACTIVE)) {
|
gadc_lld_nontimerjobI(&ndata.job);
|
||||||
p->lld.physdev = physdev;
|
} else {
|
||||||
p->lld.buffer = buffer;
|
// Just put it on the queue
|
||||||
p->fn = BSemSignalCallback;
|
gfxQueueGSyncPutI(&lsListToDo, (gfxQueueGSyncItem *)&ndata);
|
||||||
p->param = &mysem;
|
|
||||||
p->flags = GADC_FLG_ISACTIVE;
|
|
||||||
gfxMutexExit(&LowSpeedMutex);
|
|
||||||
StartADC(FALSE);
|
|
||||||
gfxSemWait(&mysem, TIME_INFINITE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gfxMutexExit(&LowSpeedMutex);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We should never get here - the count semaphore must be wrong.
|
|
||||||
* Decrement it and try again.
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
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) {
|
bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunction fn, void *param) {
|
||||||
struct lsdev *p;
|
NonTimerData *pdata;
|
||||||
|
|
||||||
/* Start the Low Speed Timer */
|
/* Start the Low Speed Timer */
|
||||||
gfxMutexEnter(&LowSpeedMutex);
|
if (!gtimerIsActive(&lsGTimer))
|
||||||
if (!gtimerIsActive(&LowSpeedGTimer))
|
gtimerStart(&lsGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
|
||||||
gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
|
|
||||||
|
|
||||||
/* Find a slot */
|
// Prepare the job
|
||||||
for(p = ls; p < &ls[GADC_MAX_LOWSPEED_DEVICES]; p++) {
|
if (!(pdata = gfxAlloc(sizeof(NonTimerData))))
|
||||||
if (!(p->flags & GADC_FLG_ISACTIVE)) {
|
return FALSE;
|
||||||
/* We know we have a slot - this should never wait anyway */
|
pdata->job.physdev = physdev;
|
||||||
gfxSemWait(&LowSpeedSlotSem, TIME_IMMEDIATE);
|
pdata->job.buffer = buffer;
|
||||||
p->lld.physdev = physdev;
|
pdata->callback = fn;
|
||||||
p->lld.buffer = buffer;
|
pdata->param = param;
|
||||||
p->fn = fn;
|
|
||||||
p->param = param;
|
// Activate it
|
||||||
p->flags = GADC_FLG_ISACTIVE;
|
gfxSystemLock();
|
||||||
gfxMutexExit(&LowSpeedMutex);
|
if (!(hsFlags & GADC_ADC_RUNNING)) {
|
||||||
StartADC(FALSE);
|
// Nothing is running - start the job
|
||||||
return TRUE;
|
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(&LowSpeedMutex);
|
gfxSystemUnlock();
|
||||||
return FALSE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* GFX_USE_GADC */
|
#endif /* GFX_USE_GADC */
|
||||||
|
|
|
@ -209,12 +209,9 @@ void gadcHighSpeedStop(void);
|
||||||
* completion.
|
* completion.
|
||||||
* @note The result buffer must be large enough to store one sample per device
|
* @note The result buffer must be large enough to store one sample per device
|
||||||
* described by the 'physdev' parameter.
|
* 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
|
* @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
|
* 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
|
* @api
|
||||||
*/
|
*/
|
||||||
|
@ -222,7 +219,7 @@ void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Perform a low speed ADC conversion with callback (in a thread context)
|
* @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] physdev A value passed to describe which physical ADC devices/channels to use.
|
||||||
* @param[in] buffer The static buffer to put the ADC samples into.
|
* @param[in] buffer The static buffer to put the ADC samples into.
|
||||||
|
@ -237,8 +234,6 @@ void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer);
|
||||||
* completion.
|
* completion.
|
||||||
* @note The result buffer must be large enough to store one sample per device
|
* @note The result buffer must be large enough to store one sample per device
|
||||||
* described by the 'physdev' parameter.
|
* 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
|
* @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
|
* 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 samples being lost.
|
||||||
|
@ -255,4 +250,3 @@ bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunc
|
||||||
|
|
||||||
#endif /* _GADC_H */
|
#endif /* _GADC_H */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue